nsmcp::AccessRecordStoreSQLite method stats (public)

 <instance of nsmcp::AccessRecordStoreSQLite[i]> stats options \
    redactor limits

Defined in /usr/local/ns/tcl/nsmcp/log-traffic-record-store.tcl

Aggregate request statistics for the current stored-record scope. Combine detailed request rows with eligible minute rollups. Bound ranked dimensions, redact returned labels and report omitted or unavailable observations explicitly.

Parameters:
options (required)
Normalized time window, filters, bucket size and result limits.
redactor (required)
Redaction object for returned dimension labels.
limits (required)
Aggregation limits, including the maximum number of retained keys.
Returns:
A list containing request statistics, aggregation metadata and the selected SQL predicate.

Testcases:
No testcase defined.
Source code:
lassign [:filters $options] context selected
set from [dict get $options from]
set to [dict get $options to]
set stats [accessScanEmpty]
set rollup [:rollupSource $options]
if {$rollup ne {}} {
    set row [lindex [${:db} execute "
        SELECT coalesce(sum(n), 0) AS contextRecords, coalesce(sum(CASE WHEN status>=400 THEN n ELSE 0 END), 0)
        AS contextFailedRecords FROM ($rollup) WHERE $context
    "] 0]
} else {
    set row [lindex [${:db} execute "
        SELECT count(*) AS contextRecords, coalesce(sum(status>=400), 0) AS contextFailedRecords FROM
        temp.nsmcp_record_window WHERE $context
    "] 0]
}
set stats [dict merge $stats $row]
set row [lindex [${:db} execute "
    SELECT count(*) AS requestCount, coalesce(sum(status<400), 0) AS statusBelow400Records, coalesce(sum(bytes),
    0) AS responseBytes,
    coalesce(sum(phase), 0) AS timingCount, coalesce(sum(fallback), 0) AS fallbackCount, coalesce(sum(a), 0) AS
    acceptTotal, coalesce(sum(q), 0) AS queueTotal,
    coalesce(sum(f), 0) AS filterTotal, coalesce(sum(r), 0) AS runTotal, coalesce(max(r), 0) AS runMax,
    coalesce(min(t), '') AS matchedFirstAt, coalesce(max(t), '') AS matchedLastAt,
    coalesce(sum(peer=''), 0) AS unknownPeerRecords, coalesce(sum(agent=''), 0) AS unknownUserAgentRecords,
    coalesce(sum(clipped), 0) AS truncatedUserAgentRecords, coalesce(sum(redacted), 0) AS labelRedactedRecords,
    coalesce(sum(source='driver' AND status=503), 0) AS driver503Records, coalesce(sum(source='driver'), 0) AS
    driverRecords FROM temp.nsmcp_record_window WHERE $selected
"] 0]
set stats [dict merge $stats $row]
set row [lindex [${:db} execute {
    SELECT count(*) AS examinedRecords, coalesce(sum(kind=1), 0) AS parseErrors, coalesce(sum(kind=2), 0) AS
    oversizedRecords, coalesce(min(t), '') AS earliestAt, coalesce(max(t), '') AS latestAt FROM
    temp.nsmcp_record_window
}] 0]
set stats [dict merge $stats $row]
set counters [:checkpointCounts]
foreach key {parseErrors oversizedRecords} {
    dict incr stats $key [dict get $counters $key]
}
dict incr stats examinedRecords [dict get $counters checkpointRecords]
dict set stats diagnosticCountsComplete [dict get $counters diagnosticCountsComplete]
dict set stats pendingRecords [:boundaryPending]
foreach {field column cap} [list paths path [dict get $limits keys] methods method 32 statuses status 500 pools pool 64  hours {CAST(t/3600 AS INTEGER)*3600} 170 drivers driver 64] {
    set where $selected
    if {$field eq "drivers"} {
        append where " AND source='driver'"
    }
    set known 0
    if {$rollup ne {} && $field in {methods statuses pools hours}} {
        set sql "
            SELECT $column AS value, sum(n) AS n FROM ($rollup) WHERE $where GROUP BY $column ORDER BY n DESC,
            value LIMIT $cap
        "
    } else {
        set sql "
            SELECT $column AS value, count(*) AS n FROM temp.nsmcp_record_window WHERE $where GROUP BY $column
            ORDER BY n DESC, value LIMIT $cap
        "
    }
    foreach row [${:db} execute $sql] {
        set value [dict get $row value]
        set n [dict get $row n]
        if {$field ni {hours statuses}} {
            set value [$redactor message $value]
        }
        if {$field eq "paths" && [string length [encoding convertto utf-8 $value]]>1024} {
            continue
        }
        set counts [dict get $stats $field]
        dict incr counts $value $n
        dict set stats $field $counts
        incr known $n
    }
    set total  [dict get $stats [expr {$field eq "drivers" ? "driverRecords" : "requestCount"}]]
    if {$known<$total} {
        dict set stats omittedRecords $field [expr {$total-$known}]
    }
}
foreach {field column} {authenticationStates auth recordSources source} {
    if {$rollup ne {} && $field eq "recordSources"} {
        set sql  "SELECT $column AS value,sum(n) AS n FROM ($rollup) WHERE $selected GROUP BY $column"
    } else {
        set sql "
            SELECT $column AS value,count(*) AS n FROM temp.nsmcp_record_window WHERE $selected GROUP BY $column
        "
    }
    foreach row [${:db} execute $sql] {
        dict set stats $field [dict get $row value] [dict get $row n]
    }
}
foreach row [${:db} execute "
    SELECT status, count(*) AS requestCount, sum(phase) AS timingCount, sum(r) AS runTotal, max(r) AS runMax
    FROM temp.nsmcp_record_window WHERE $selected GROUP BY status
"] {
    set status [dict get $row status]
    dict unset row status
    dict set stats statusTimings $status $row
}
set seconds [dict get $options bucketSeconds]
set index "CAST((t-[ns_dbquotevalue $from])/$seconds AS INTEGER)"
foreach row [${:db} execute "
    SELECT $index AS i, count(*) AS n, sum(status>=400) AS failed, sum(phase) AS timings, sum(r) AS runtime FROM
    temp.nsmcp_record_window WHERE $context GROUP BY i LIMIT 3600
"] {
    dict set stats buckets [dict get $row i] [list contextRecords [dict get $row n] contextFailedRecords [dict get $row failed]  selectedRecords 0 driver503Records 0 timingCount 0 runTotal 0.0 contextTimingCount  [dict get $row timings] contextRunTotal [dict get $row runtime]]
}
foreach row [${:db} execute "
    SELECT $index AS i, count(*) AS n, sum(source='driver' AND status=503) AS driver503, sum(phase) AS timings,
    sum(r) AS runtime FROM temp.nsmcp_record_window WHERE $selected GROUP BY i LIMIT 3600
"] {
    set i [dict get $row i]
    foreach {key col}  {selectedRecords n driver503Records driver503 timingCount timings runTotal runtime} {
        dict set stats buckets $i $key [dict get $row $col]
    }
}
set aggregate [dict create  backend sqlite  cacheKiB 8192  status active  reason {}  maxBytes [dict get ${:settings} maxBytes]  diskBytes [:diskBytes]  peakBufferedKeys 512  peerOmitted 0  agentOmitted 0  labelTruncatedRecords 0  labelRedactedRecords 0]
foreach {field col} {peers peer agents agent} {
    set valid "$selected AND $col<>''"
    set totals [dict create  distinct 0  records 0  top1 0  top10 0  top100 0  rankedRecords 0  clippedRecords 0  redactedRecords 0]
    set entries {}
    set i 0
    foreach row [${:db} execute "
        SELECT *, count(*) OVER() AS distinctCount, sum(n) OVER() AS records FROM (SELECT hex($col) AS k,
        count(*) AS n, min(label) AS label, max(clipped) AS clipped, max(redacted) AS redacted FROM
        temp.nsmcp_record_window WHERE $valid GROUP BY $col) ORDER BY n DESC, k LIMIT 100
    "] {
        dict set totals distinct [dict get $row distinctCount]
        dict set totals records [dict get $row records]
        incr i
        set n [dict get $row n]
        if {$i==1} {
            dict set totals top1 $n
        }
        if {$i<=10} {
            dict incr totals top10 $n
        }
        dict incr totals top100 $n
        if {$i<=[dict get $options maxGroups]} {
            dict incr totals rankedRecords $n
            lappend entries [dict create  count $n  value [dict get $row label]  valueTruncated [dict get $row clipped]  valueRedacted [dict get $row redacted]]
        }
    }
    dict set aggregate $field $entries
    dict set aggregate ${field}Totals $totals
}
dict set aggregate labelTruncatedRecords [dict get $stats truncatedUserAgentRecords]
dict set aggregate labelRedactedRecords [dict get $stats labelRedactedRecords]
return [list $stats $aggregate $selected]
XQL Not present:
Generic, PostgreSQL, Oracle
[ hide source ] | [ make this the default ]
Show another procedure: