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)