Commit 0f9cecafbd7d5399506108f6227de2d4dad6ad4a
1 parent
fbb3d85e
Improve data aggregator to send updates on late data
Showing
1 changed file
with
69 additions
and
16 deletions
@@ -36,8 +36,56 @@ interface AggData { | @@ -36,8 +36,56 @@ interface AggData { | ||
36 | aggValue: any; | 36 | aggValue: any; |
37 | } | 37 | } |
38 | 38 | ||
39 | -interface AggregationMap { | ||
40 | - [key: string]: Map<number, AggData>; | 39 | +class AggDataMap { |
40 | + rangeChanged = false; | ||
41 | + private minTs = Number.MAX_SAFE_INTEGER; | ||
42 | + private map = new Map<number, AggData>(); | ||
43 | + | ||
44 | + set(ts: number, data: AggData) { | ||
45 | + if (ts < this.minTs) { | ||
46 | + this.rangeChanged = true; | ||
47 | + this.minTs = ts; | ||
48 | + } | ||
49 | + this.map.set(ts, data); | ||
50 | + } | ||
51 | + | ||
52 | + get(ts: number): AggData { | ||
53 | + return this.map.get(ts); | ||
54 | + } | ||
55 | + | ||
56 | + delete(ts: number) { | ||
57 | + this.map.delete(ts); | ||
58 | + } | ||
59 | + | ||
60 | + forEach(callback: (value: AggData, key: number, map: Map<number, AggData>) => void, thisArg?: any) { | ||
61 | + this.map.forEach(callback, thisArg); | ||
62 | + } | ||
63 | + | ||
64 | + size(): number { | ||
65 | + return this.map.size; | ||
66 | + } | ||
67 | +} | ||
68 | + | ||
69 | +class AggregationMap { | ||
70 | + aggMap: {[key: string]: AggDataMap} = {}; | ||
71 | + | ||
72 | + detectRangeChanged(): boolean { | ||
73 | + let changed = false; | ||
74 | + for (const key of Object.keys(this.aggMap)) { | ||
75 | + const aggDataMap = this.aggMap[key]; | ||
76 | + if (aggDataMap.rangeChanged) { | ||
77 | + changed = true; | ||
78 | + aggDataMap.rangeChanged = false; | ||
79 | + } | ||
80 | + } | ||
81 | + return changed; | ||
82 | + } | ||
83 | + | ||
84 | + clearRangeChangedFlags() { | ||
85 | + for (const key of Object.keys(this.aggMap)) { | ||
86 | + this.aggMap[key].rangeChanged = false; | ||
87 | + } | ||
88 | + } | ||
41 | } | 89 | } |
42 | 90 | ||
43 | declare type AggFunction = (aggData: AggData, value?: any) => void; | 91 | declare type AggFunction = (aggData: AggData, value?: any) => void; |
@@ -170,7 +218,7 @@ export class DataAggregator { | @@ -170,7 +218,7 @@ export class DataAggregator { | ||
170 | updateIntervalScheduledTime = false; | 218 | updateIntervalScheduledTime = false; |
171 | } | 219 | } |
172 | if (update) { | 220 | if (update) { |
173 | - this.aggregationMap = {}; | 221 | + this.aggregationMap = new AggregationMap(); |
174 | this.updateAggregatedData(data.data); | 222 | this.updateAggregatedData(data.data); |
175 | } else { | 223 | } else { |
176 | this.aggregationMap = this.processAggregatedData(data.data); | 224 | this.aggregationMap = this.processAggregatedData(data.data); |
@@ -178,12 +226,17 @@ export class DataAggregator { | @@ -178,12 +226,17 @@ export class DataAggregator { | ||
178 | if (updateIntervalScheduledTime) { | 226 | if (updateIntervalScheduledTime) { |
179 | this.intervalScheduledTime = this.utils.currentPerfTime(); | 227 | this.intervalScheduledTime = this.utils.currentPerfTime(); |
180 | } | 228 | } |
229 | + this.aggregationMap.clearRangeChangedFlags(); | ||
181 | this.onInterval(history, detectChanges); | 230 | this.onInterval(history, detectChanges); |
182 | } else { | 231 | } else { |
183 | this.updateAggregatedData(data.data); | 232 | this.updateAggregatedData(data.data); |
184 | if (history) { | 233 | if (history) { |
185 | this.intervalScheduledTime = this.utils.currentPerfTime(); | 234 | this.intervalScheduledTime = this.utils.currentPerfTime(); |
186 | this.onInterval(history, detectChanges); | 235 | this.onInterval(history, detectChanges); |
236 | + } else { | ||
237 | + if (this.aggregationMap.detectRangeChanged()) { | ||
238 | + this.onInterval(false, detectChanges, true); | ||
239 | + } | ||
187 | } | 240 | } |
188 | } | 241 | } |
189 | } | 242 | } |
@@ -203,7 +256,7 @@ export class DataAggregator { | @@ -203,7 +256,7 @@ export class DataAggregator { | ||
203 | } | 256 | } |
204 | } | 257 | } |
205 | 258 | ||
206 | - private onInterval(history?: boolean, detectChanges?: boolean) { | 259 | + private onInterval(history?: boolean, detectChanges?: boolean, rangeChanged?: boolean) { |
207 | const now = this.utils.currentPerfTime(); | 260 | const now = this.utils.currentPerfTime(); |
208 | this.elapsed += now - this.intervalScheduledTime; | 261 | this.elapsed += now - this.intervalScheduledTime; |
209 | this.intervalScheduledTime = now; | 262 | this.intervalScheduledTime = now; |
@@ -211,9 +264,10 @@ export class DataAggregator { | @@ -211,9 +264,10 @@ export class DataAggregator { | ||
211 | clearTimeout(this.intervalTimeoutHandle); | 264 | clearTimeout(this.intervalTimeoutHandle); |
212 | this.intervalTimeoutHandle = null; | 265 | this.intervalTimeoutHandle = null; |
213 | } | 266 | } |
267 | + const intervalTimeout = rangeChanged ? this.aggregationTimeout - this.elapsed : this.aggregationTimeout; | ||
214 | if (!history) { | 268 | if (!history) { |
215 | const delta = Math.floor(this.elapsed / this.subsTw.aggregation.interval); | 269 | const delta = Math.floor(this.elapsed / this.subsTw.aggregation.interval); |
216 | - if (delta || !this.data) { | 270 | + if (delta || !this.data || rangeChanged) { |
217 | const tickTs = delta * this.subsTw.aggregation.interval; | 271 | const tickTs = delta * this.subsTw.aggregation.interval; |
218 | if (this.subsTw.quickInterval) { | 272 | if (this.subsTw.quickInterval) { |
219 | const currentDate = this.getCurrentTime(); | 273 | const currentDate = this.getCurrentTime(); |
@@ -234,7 +288,7 @@ export class DataAggregator { | @@ -234,7 +288,7 @@ export class DataAggregator { | ||
234 | this.updatedData = false; | 288 | this.updatedData = false; |
235 | } | 289 | } |
236 | if (!history) { | 290 | if (!history) { |
237 | - this.intervalTimeoutHandle = setTimeout(this.onInterval.bind(this), this.aggregationTimeout); | 291 | + this.intervalTimeoutHandle = setTimeout(this.onInterval.bind(this), intervalTimeout); |
238 | } | 292 | } |
239 | } | 293 | } |
240 | 294 | ||
@@ -242,8 +296,8 @@ export class DataAggregator { | @@ -242,8 +296,8 @@ export class DataAggregator { | ||
242 | this.tsKeyNames.forEach((key) => { | 296 | this.tsKeyNames.forEach((key) => { |
243 | this.dataBuffer[key] = []; | 297 | this.dataBuffer[key] = []; |
244 | }); | 298 | }); |
245 | - for (const key of Object.keys(this.aggregationMap)) { | ||
246 | - const aggKeyData = this.aggregationMap[key]; | 299 | + for (const key of Object.keys(this.aggregationMap.aggMap)) { |
300 | + const aggKeyData = this.aggregationMap.aggMap[key]; | ||
247 | let keyData = this.dataBuffer[key]; | 301 | let keyData = this.dataBuffer[key]; |
248 | aggKeyData.forEach((aggData, aggTimestamp) => { | 302 | aggKeyData.forEach((aggData, aggTimestamp) => { |
249 | if (aggTimestamp <= this.startTs) { | 303 | if (aggTimestamp <= this.startTs) { |
@@ -300,12 +354,12 @@ export class DataAggregator { | @@ -300,12 +354,12 @@ export class DataAggregator { | ||
300 | 354 | ||
301 | private processAggregatedData(data: SubscriptionData): AggregationMap { | 355 | private processAggregatedData(data: SubscriptionData): AggregationMap { |
302 | const isCount = this.subsTw.aggregation.type === AggregationType.COUNT; | 356 | const isCount = this.subsTw.aggregation.type === AggregationType.COUNT; |
303 | - const aggregationMap: AggregationMap = {}; | 357 | + const aggregationMap = new AggregationMap(); |
304 | for (const key of Object.keys(data)) { | 358 | for (const key of Object.keys(data)) { |
305 | - let aggKeyData = aggregationMap[key]; | 359 | + let aggKeyData = aggregationMap.aggMap[key]; |
306 | if (!aggKeyData) { | 360 | if (!aggKeyData) { |
307 | - aggKeyData = new Map<number, AggData>(); | ||
308 | - aggregationMap[key] = aggKeyData; | 361 | + aggKeyData = new AggDataMap(); |
362 | + aggregationMap.aggMap[key] = aggKeyData; | ||
309 | } | 363 | } |
310 | const keyData = data[key]; | 364 | const keyData = data[key]; |
311 | keyData.forEach((kvPair) => { | 365 | keyData.forEach((kvPair) => { |
@@ -326,10 +380,10 @@ export class DataAggregator { | @@ -326,10 +380,10 @@ export class DataAggregator { | ||
326 | private updateAggregatedData(data: SubscriptionData) { | 380 | private updateAggregatedData(data: SubscriptionData) { |
327 | const isCount = this.subsTw.aggregation.type === AggregationType.COUNT; | 381 | const isCount = this.subsTw.aggregation.type === AggregationType.COUNT; |
328 | for (const key of Object.keys(data)) { | 382 | for (const key of Object.keys(data)) { |
329 | - let aggKeyData = this.aggregationMap[key]; | 383 | + let aggKeyData = this.aggregationMap.aggMap[key]; |
330 | if (!aggKeyData) { | 384 | if (!aggKeyData) { |
331 | - aggKeyData = new Map<number, AggData>(); | ||
332 | - this.aggregationMap[key] = aggKeyData; | 385 | + aggKeyData = new AggDataMap(); |
386 | + this.aggregationMap.aggMap[key] = aggKeyData; | ||
333 | } | 387 | } |
334 | const keyData = data[key]; | 388 | const keyData = data[key]; |
335 | keyData.forEach((kvPair) => { | 389 | keyData.forEach((kvPair) => { |
@@ -374,4 +428,3 @@ export class DataAggregator { | @@ -374,4 +428,3 @@ export class DataAggregator { | ||
374 | } | 428 | } |
375 | 429 | ||
376 | } | 430 | } |
377 | - |