Three defects in includes/polling.php affect which thresholds get evaluated, and when.
tcheck is cleared for the whole table (includes/polling.php:346)
thold_check_all_thresholds() selects the work with WHERE td.tcheck = 1 (:275, :296, :317), iterates it, and then finishes with:
db_execute('UPDATE thold_data AS td SET td.tcheck = 0');
Unqualified. Any thold_poller_output() write that lands between the SELECT and this UPDATE is discarded without ever being evaluated. With multiple pollers or a remote data collector, thresholds whose data arrives late in the cycle are skipped, and the breach is invisible until the next cycle in which they happen to arrive early.
Clearing only the ids that were processed, or bounding the UPDATE by the SELECT's snapshot time, would fix it.
Operator precedence disables the tcheck and status filters on poller 1 (includes/polling.php:275-277)
AND h.poller_id = 1 OR h.poller_id IS NULL
AND td.tcheck = 1
AND h.status = 3
AND binds tighter than OR, so this reads as:
( thold_per_enabled='on' AND (...) AND h.poller_id = 1 )
OR
( h.poller_id IS NULL AND td.tcheck = 1 AND h.status = 3 )
The first branch has no tcheck and no status predicate. The second cannot match, since a NULL host has no status.
In remote-storage mode on poller 1 this re-evaluates every enabled threshold on every run, including devices that are down, incrementing fail counters against stale lastread values. A threshold that breached once keeps counting until it crosses the trigger and alerts on data that may be hours old.
The poller_id != 1 and non-remote variants at :296 and :317 are parenthesised correctly, which is what makes this look like a typo rather than intent.
array_chunk() is given a chunk count where it expects a chunk size (includes/polling.php:78-84)
$chunks = ceil(sizeof($rrd_update_array) / 50);
if ($chunks < 1) {
$chunks = 1;
}
$rrd_update_array_chunks = array_chunk($rrd_update_array, $chunks, true);
The second argument is the size of each chunk, not how many to make. With 500 items this produces 50 chunks of 10 where 10 chunks of 50 was intended; with 25 items it produces 25 chunks of 1. Each chunk costs a SELECT plus a bulk INSERT, so a small poller cycle under the daemon issues roughly one round trip per data source.
Three defects in
includes/polling.phpaffect which thresholds get evaluated, and when.tcheckis cleared for the whole table (includes/polling.php:346)thold_check_all_thresholds()selects the work withWHERE td.tcheck = 1(:275,:296,:317), iterates it, and then finishes with:Unqualified. Any
thold_poller_output()write that lands between the SELECT and this UPDATE is discarded without ever being evaluated. With multiple pollers or a remote data collector, thresholds whose data arrives late in the cycle are skipped, and the breach is invisible until the next cycle in which they happen to arrive early.Clearing only the ids that were processed, or bounding the UPDATE by the SELECT's snapshot time, would fix it.
Operator precedence disables the
tcheckandstatusfilters on poller 1 (includes/polling.php:275-277)ANDbinds tighter thanOR, so this reads as:The first branch has no
tcheckand nostatuspredicate. The second cannot match, since a NULL host has no status.In remote-storage mode on poller 1 this re-evaluates every enabled threshold on every run, including devices that are down, incrementing fail counters against stale
lastreadvalues. A threshold that breached once keeps counting until it crosses the trigger and alerts on data that may be hours old.The
poller_id != 1and non-remote variants at:296and:317are parenthesised correctly, which is what makes this look like a typo rather than intent.array_chunk()is given a chunk count where it expects a chunk size (includes/polling.php:78-84)The second argument is the size of each chunk, not how many to make. With 500 items this produces 50 chunks of 10 where 10 chunks of 50 was intended; with 25 items it produces 25 chunks of 1. Each chunk costs a SELECT plus a bulk INSERT, so a small poller cycle under the daemon issues roughly one round trip per data source.