CoolCMS API

EsMonitor
in package

FinalYes

Monitors the ES integration beyond the self-heal crons, as the source of e-mail alerting over the `elasticsearch` log channel (otherwise only logged silently).

Runs pure checks per enabled source (EsConfig::isSourceEnabled()): cluster availability and health, drift between ES and DB document counts, and the age of the last successful reindex.

Fail-open per check: an error in one check does not stop the others and is itself reported as an issue (unavailable or unknown state). The class sends no e-mail (Core does not know the Api EmailManager, same reason as in DocumentSourceInterface); it only returns a structured report. Orchestration (monitor, then an optional e-mail) is done by ElasticWatchManager.

Table of Contents

Constants

ALERT_RELEASE_LUA  : mixed = <<<'LUA' if redis.call('GET', KEYS[1]) == ARGV[...
Releases a reservation by comparing the own token and deleting in one step, so a newer reservation of a concurrent run is never deleted.
ALERT_TTL  : mixed = 6 * 3600
Dedup window of one issue type in Redis (`SET NX EX`): 6 h.
MIN_TOLERANCE  : mixed = 5
Count drift tolerance is max(`MIN_TOLERANCE`, `TOLERANCE_RATIO` of the minimum); it covers sync lag.
REINDEX_MAX_AGE_HOURS  : mixed = 26
A last successful reindex older than this is an issue (sync cannot catch up overnight by itself).
TOLERANCE_RATIO  : mixed = 0.05

Properties

$client  : Client
$config  : EsConfig
$redis  : Client|null
$reservations  : array<string, string>
$sources  : array<string, DocumentSourceInterface>

Methods

__construct()  : mixed
check()  : array{issues: list, data: array}
Returns the report for the cron endpoint: the list of issues (each with a `shouldAlert` flag after deduplication, see {@see finalizeIssue()}) plus raw diagnostic data.
isWithinBounds()  : bool
Evaluates the drift as a pure function, separated for unit tests (no ES, DB or Redis).
releaseAlerts()  : void
Releases the alert reservations of the last `check()`.
tolerance()  : int
Returns max(5, 5 % of the minimum); small sources get a fixed floor of 5 documents.
alertKey()  : string
checkCountDrift()  : array{type: string, source: string, message: string}|null
checkHealth()  : array{0: string, 1: ?array{type: string, source: null, message: string}}
checkReindexAge()  : array{type: string, source: string, message: string}|null
finalizeIssue()  : array{type: string, source: ?string, message: string, shouldAlert: bool}
Deduplicates: an issue of the same type (per source) does not repeat within 6 h.

Constants

ALERT_RELEASE_LUA

Releases a reservation by comparing the own token and deleting in one step, so a newer reservation of a concurrent run is never deleted.

private mixed ALERT_RELEASE_LUA = <<<'LUA' if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) end return 0 LUA

ALERT_TTL

Dedup window of one issue type in Redis (`SET NX EX`): 6 h.

private mixed ALERT_TTL = 6 * 3600

MIN_TOLERANCE

Count drift tolerance is max(`MIN_TOLERANCE`, `TOLERANCE_RATIO` of the minimum); it covers sync lag.

private mixed MIN_TOLERANCE = 5

REINDEX_MAX_AGE_HOURS

A last successful reindex older than this is an issue (sync cannot catch up overnight by itself).

private mixed REINDEX_MAX_AGE_HOURS = 26

TOLERANCE_RATIO

private mixed TOLERANCE_RATIO = 0.05

Properties

$redis read-only

private Client|null $redis = null

$reservations

private array<string, string> $reservations = []

dedup key mapped to the reservation token from the last check()

Methods

check()

Returns the report for the cron endpoint: the list of issues (each with a `shouldAlert` flag after deduplication, see {@see finalizeIssue()}) plus raw diagnostic data.

public check() : array{issues: list, data: array}

Without enabled sources (master toggle off or empty enabledSources) it returns an empty report without connecting to ES.

Return values
array{issues: list, data: array}

isWithinBounds()

Evaluates the drift as a pure function, separated for unit tests (no ES, DB or Redis).

public static isWithinBounds(int $esCount, int $min, int $max) : bool

The tolerance covers the usual sync lag between the hook or sync and the nightly reindex.

Parameters
$esCount : int
$min : int
$max : int
Return values
bool

releaseAlerts()

Releases the alert reservations of the last `check()`.

public releaseAlerts(array<int, array{type: string, source: ?string, message: string}> $issues) : void

The dedup key is reserved before the e-mail is sent. If the alert could not be handed over (missing address, queue or SMTP failure), the reservation would block the next attempt for 6 h even when the cause is fixed at once. The caller therefore releases only its own reservations from the last check() (atomically by token), so a concurrent run does not lose its own. On a Redis error the reservation stays until its ttl (logged).

Parameters
$issues : array<int, array{type: string, source: ?string, message: string}>

tolerance()

Returns max(5, 5 % of the minimum); small sources get a fixed floor of 5 documents.

public static tolerance(int $min) : int
Parameters
$min : int
Return values
int

alertKey()

private alertKey(array{type: string, source: ?string, message: string} $issue) : string
Parameters
$issue : array{type: string, source: ?string, message: string}
Return values
string

checkCountDrift()

private checkCountDrift(DocumentSourceInterface $source, array<string, mixed> &$sourceData) : array{type: string, source: string, message: string}|null
Parameters
$source : DocumentSourceInterface
$sourceData : array<string, mixed>
Return values
array{type: string, source: string, message: string}|null

checkHealth()

private checkHealth() : array{0: string, 1: ?array{type: string, source: null, message: string}}
Return values
array{0: string, 1: ?array{type: string, source: null, message: string}}

checkReindexAge()

private checkReindexAge(DocumentSourceInterface $source, array<string, mixed> &$sourceData) : array{type: string, source: string, message: string}|null
Parameters
$source : DocumentSourceInterface
$sourceData : array<string, mixed>
Return values
array{type: string, source: string, message: string}|null

finalizeIssue()

Deduplicates: an issue of the same type (per source) does not repeat within 6 h.

private finalizeIssue(array{type: string, source: ?string, message: string} $issue) : array{type: string, source: ?string, message: string, shouldAlert: bool}

Uses an atomic SET NX EX, so there is no window between the check and the write. Fail-open: on a Redis error it sends a duplicate alert rather than stay silent about a problem.

Parameters
$issue : array{type: string, source: ?string, message: string}
Return values
array{type: string, source: ?string, message: string, shouldAlert: bool}
On this page

Search results