@@ -4,6 +4,8 @@ const { forComponent } = harperLogger;
44import { getAnalyticsHostnameTable } from './hostnames.ts' ;
55import type { Condition , Conditions } from '../ResourceInterface.ts' ;
66import { METRIC , type BuiltInMetricName } from './metadata.ts' ;
7+ import { CONFIG_PARAMS } from '../../utility/hdbTerms.ts' ;
8+ import { get as envGet } from '../../utility/environment/environmentManager.js' ;
79
810// default to one week time window for finding custom metrics
911const defaultCustomMetricWindow = 1000 * 60 * 60 * 24 * 7 ;
@@ -24,14 +26,21 @@ interface GetAnalyticsRequest {
2426 start_time ?: number ;
2527 end_time ?: number ;
2628 get_attributes ?: string [ ] ;
29+ coalesce_time ?: boolean ;
2730 conditions ?: Conditions ;
2831}
2932
3033type GetAnalyticsResponse = Metric [ ] ;
3134
3235export function getOp ( req : GetAnalyticsRequest ) : Promise < GetAnalyticsResponse > {
3336 log . trace ?.( 'get_analytics request:' , req ) ;
34- return get ( req . metric , req . get_attributes , req . start_time , req . end_time , req . conditions ) ;
37+ return get ( req . metric , {
38+ getAttributes : req . get_attributes ,
39+ startTime : req . start_time ,
40+ endTime : req . end_time ,
41+ coalesceTime : req . coalesce_time ,
42+ additionalConditions : req . conditions ,
43+ } ) ;
3544}
3645
3746function conformCondition ( condition : Condition ) : Condition {
@@ -48,13 +57,37 @@ function conformCondition(condition: Condition): Condition {
4857 } ;
4958}
5059
51- export async function get (
52- metric : string ,
53- getAttributes ?: string [ ] ,
54- startTime ?: number ,
55- endTime ?: number ,
56- additionalConditions ?: Conditions
57- ) : Promise < Metric [ ] > {
60+ async function coalesceResults ( results : Metric [ ] , window : number ) : Promise < Metric [ ] > {
61+ const coalescedResults : Metric [ ] = [ ] ;
62+ let coalesceId ;
63+ let lastCoalescedId = new Map < string , number > ( ) ;
64+ for await ( const result of results ) {
65+ const id = result . id ;
66+ if ( ! coalesceId ) {
67+ coalesceId = id ;
68+ }
69+ const delta = Math . abs ( id - coalesceId ) ;
70+ if ( delta < window && lastCoalescedId . get ( result . node ) !== id ) {
71+ coalescedResults . push ( { ...result , id : coalesceId } ) ;
72+ lastCoalescedId [ result . node ] = id ;
73+ } else {
74+ coalescedResults . push ( result ) ;
75+ coalesceId = id ;
76+ }
77+ }
78+ return coalescedResults ;
79+ }
80+
81+ interface GetAnalyticsOpts {
82+ getAttributes ?: string [ ] ;
83+ startTime ?: number ;
84+ endTime ?: number ;
85+ coalesceTime ?: boolean ;
86+ additionalConditions ?: Conditions ;
87+ }
88+
89+ export async function get ( metric : string , opts ?: GetAnalyticsOpts ) : Promise < Metric [ ] > {
90+ const { getAttributes, startTime, endTime, additionalConditions } = opts ?? { } ;
5891 const conditions : Conditions = [ { attribute : 'metric' , comparator : 'equals' , value : metric } ] ;
5992 if ( additionalConditions ) {
6093 conditions . push ( ...additionalConditions . map ( conformCondition ) ) ;
@@ -88,7 +121,7 @@ export async function get(
88121 log . trace ?.( 'get_analytics hdb_analytics.search request:' , JSON . stringify ( request ) ) ;
89122 const searchResults = await databases . system . hdb_analytics . search ( request ) ;
90123
91- return searchResults . map ( async ( result : Metric ) => {
124+ let results = searchResults . map ( async ( result : Metric ) => {
92125 // remove nodeId from 'id' attr and resolve it to the actual hostname and
93126 // add back in as 'node' attr if selected
94127 const nodeId = result . id [ 1 ] ;
@@ -100,6 +133,14 @@ export async function get(
100133 log . trace ?.( `get_analytics result:` , JSON . stringify ( result ) ) ;
101134 return result ;
102135 } ) ;
136+
137+ if ( opts ?. coalesceTime ) {
138+ // coalescing window is the aggregate period plus 10% & converted to milliseconds
139+ const window = envGet ( CONFIG_PARAMS . ANALYTICS_AGGREGATEPERIOD ) * 1.1 * 1000 ;
140+ results = await coalesceResults ( results , window ) ;
141+ }
142+
143+ return results ;
103144}
104145
105146type MetricType = 'builtin' | 'custom' ;
@@ -115,7 +156,10 @@ export function listMetricsOp(req: ListMetricsRequest): Promise<ListMetricsRespo
115156 return listMetrics ( req . metric_types , req . custom_metrics_window ) ;
116157}
117158
118- export async function listMetrics ( metricTypes : MetricType [ ] = [ 'builtin' ] , customWindow : number = defaultCustomMetricWindow ) : Promise < string [ ] > {
159+ export async function listMetrics (
160+ metricTypes : MetricType [ ] = [ 'builtin' ] ,
161+ customWindow : number = defaultCustomMetricWindow
162+ ) : Promise < string [ ] > {
119163 let metrics : string [ ] = [ ] ;
120164
121165 const builtins : BuiltInMetricName [ ] = Object . values ( METRIC ) ;
@@ -126,11 +170,13 @@ export async function listMetrics(metricTypes: MetricType[] = ['builtin'], custo
126170
127171 if ( metricTypes . includes ( 'custom' ) ) {
128172 const oldestCustomId = Date . now ( ) - customWindow ;
129- const conditions : Conditions = [ {
130- attribute : 'id' ,
131- comparator : 'greater_than' ,
132- value : oldestCustomId ,
133- } ] ;
173+ const conditions : Conditions = [
174+ {
175+ attribute : 'id' ,
176+ comparator : 'greater_than' ,
177+ value : oldestCustomId ,
178+ } ,
179+ ] ;
134180 const metricConditions = builtins . map ( ( c ) => {
135181 return {
136182 attribute : 'metric' ,
0 commit comments