nsmcp::ServerHistory method compactHistory (protected)
<instance of nsmcp::ServerHistory> compactHistory context \ options
Defined in /usr/local/ns/tcl/nsmcp/stats-tools.tcl
Bounded paging stays inside the server: raw topology/sample payloads are never sent to the model in summary mode. Each call covers at most 1000 samples; continuation explicitly describes the remaining window.
- Parameters:
- context (required)
- options (required)
- Testcases:
- No testcase defined.
Source code: set from [dict get $options from] set to [dict get $options to] set cursor [dict get $options cursor] set initialCursor $cursor set metrics {} set samples 0 set unusable 0 set boundary 0 set omitted 0 set pages 0 for {set page 0} {$page < 10} {incr page} { set h [${:command} history query -server [$context server] -from $from -to $to -limit 100 -cursor $cursor] incr pages foreach record [dict get $h records] { incr samples set usable true foreach key {ready from to elapsedSeconds} { if {![dict exists $record sampling $key] || [dict get $record sampling $key] eq ""} { set usable false } } if {!$usable || ![dict get $record sampling ready] || ([dict exists $record sampling reset] && [dict get $record sampling reset]) || [dict get $record sampling elapsedSeconds] <= 0} { incr unusable continue } set a [dict get $record sampling from] set b [dict get $record sampling to] if {$a < $from || $b > $to} { incr boundary } set groups {} if {[dict exists $record servers [$context server] pools]} { set totalRate 0.0 set totalKnown true set poolCount 0 dict for {pool entry} [dict get $record servers [$context server] pools] { incr poolCount if {![dict exists $entry rates requests] || ![string is double -strict [dict get $entry rates requests]] || [catch { expr {abs([dict get $entry rates requests]) < 1e300} } finite] || !$finite} { set totalKnown false } else { set totalRate [expr {$totalRate+[dict get $entry rates requests]}] } # Never summarize cumulative counters as interval work. foreach kind {rates gauges timingAverageSeconds} { if {[dict exists $entry $kind]} { lappend groups [list [list servers [$context server] pools $pool $kind] [dict get $entry $kind]] } } } } if {[info exists poolCount] && $poolCount > 0} { lappend groups [list [list servers [$context server] allPools rates] [dict create requests [expr {$totalKnown ? $totalRate : ""}]]] } unset -nocomplain poolCount if {[dict exists $record cpuPercent groups]} { lappend groups [list {cpuPercent groups} [dict get $record cpuPercent groups]] } foreach group $groups { lassign $group prefix values dict for {name value} $values { set path [concat $prefix [list $name]] if {![dict exists $metrics $path]} { if {[dict size $metrics] >= 128} { incr omitted continue } dict set metrics $path [dict create count 0 unknown 0 sum 0.0 min {} max {} peakFrom {} peakTo {}] } set row [dict get $metrics $path] # Missing and nonfinite values are unknown, never zero. if {![string is double -strict $value] || [catch { expr {abs($value) < 1e300} } finite] || !$finite} { dict incr row unknown } else { dict incr row count dict set row sum [expr {[dict get $row sum]+$value}] if {[dict get $row min] eq "" || $value < [dict get $row min]} { dict set row min $value } if {[dict get $row max] eq "" || $value > [dict get $row max]} { dict set row max $value dict set row peakFrom $a dict set row peakTo $b } } dict set metrics $path $row } } } if {![dict get $h more]} { break } set next [dict get $h nextCursor] if {![string is entier -strict $next] || $next <= $cursor} { error "History cursor did not advance" } set cursor $next } set rows {} set i 0 dict for {path row} $metrics { set fields {} set parts {} set j 0 foreach part $path { lappend parts $j string $part incr j } lappend fields path array $parts foreach key {count unknown min max peakFrom peakTo} { set value [dict get $row $key] lappend fields $key [expr {$value eq "" ? "null" : "number"}] $value } # Missing fields in other usable records are accounted separately # from explicit nulls; topology changes need not be parser errors. lappend fields absentSamples number [expr {$samples-$unusable-[dict get $row count]-[dict get $row unknown]}] set mean [expr {[dict get $row count] ? [dict get $row sum]/double([dict get $row count]) : ""}] lappend fields sampleMean [expr {$mean eq "" ? "null" : "number"}] $mean lappend rows $i object $fields incr i } set more [dict get $h more] set summary [list from number $from to number $to initialCursor number $initialCursor sampleCount number $samples unusableSamples number $unusable boundaryOverlapSamples number $boundary pagesExamined number $pages more boolean $more metrics array $rows omittedMetricObservations number $omitted coverage object [:encode coverage [dict get $h coverage]] limitations string {Metrics summarize retained sampling intervals, not instantaneous or one-second peaks. Rates and timings are supplied by ns_stats; sampleMean is unweighted, not a request-weighted latency. Peak bounds may overlap the requested window. Cumulative counters are excluded. Missing, reset and not-ready samples are not zero. CPU groups remain process-wide. At most 1000 samples and 128 metric series per call; missing series can reflect topology changes. Coverage is native history coverage; more=true requires continuation before claiming the window was examined.}] if {[dict exists $h storage]} { lappend summary storage object [:encode metrics [dict get $h storage]] } set nextFields {} if {$more} { set nextFields [list from number $from to number $to cursor number $cursor mode string summary] } return [list $summary $nextFields]XQL Not present: Generic, PostgreSQL, Oracle
![[i]](/resources/acs-subsite/ZoomIn16.gif)