perf(traffic): optimize traffic data handling and improve performance

This commit is contained in:
Tunglies
2025-12-24 13:40:03 +08:00
parent 0992556b4a
commit 4ffb8b415f
11 changed files with 289 additions and 95 deletions
+56 -13
View File
@@ -15,7 +15,9 @@ export const formatTrafficName = (timestamp: number) =>
export class TrafficDataSampler {
private rawBuffer: ITrafficDataPoint[] = [];
private rawHead = 0;
private compressedBuffer: ICompressedDataPoint[] = [];
private compressedHead = 0;
private compressionQueue: ITrafficDataPoint[] = [];
constructor(private config: ISamplingConfig) {}
@@ -24,7 +26,17 @@ export class TrafficDataSampler {
this.rawBuffer.push(point);
const rawCutoff = Date.now() - this.config.rawDataMinutes * 60 * 1000;
this.rawBuffer = this.rawBuffer.filter((p) => p.timestamp > rawCutoff);
// O(1) amortized trimming using moving head; compact occasionally
while (
this.rawHead < this.rawBuffer.length &&
this.rawBuffer[this.rawHead]?.timestamp <= rawCutoff
) {
this.rawHead++;
}
if (this.rawHead > 512 && this.rawHead > this.rawBuffer.length / 2) {
this.rawBuffer = this.rawBuffer.slice(this.rawHead);
this.rawHead = 0;
}
this.compressionQueue.push(point);
if (this.compressionQueue.length >= this.config.compressionRatio) {
@@ -33,9 +45,19 @@ export class TrafficDataSampler {
const compressedCutoff =
Date.now() - this.config.compressedDataMinutes * 60 * 1000;
this.compressedBuffer = this.compressedBuffer.filter(
(p) => p.timestamp > compressedCutoff,
);
while (
this.compressedHead < this.compressedBuffer.length &&
this.compressedBuffer[this.compressedHead]?.timestamp <= compressedCutoff
) {
this.compressedHead++;
}
if (
this.compressedHead > 256 &&
this.compressedHead > this.compressedBuffer.length / 2
) {
this.compressedBuffer = this.compressedBuffer.slice(this.compressedHead);
this.compressedHead = 0;
}
}
private compressData() {
@@ -60,18 +82,34 @@ export class TrafficDataSampler {
getDataForTimeRange(minutes: number): ITrafficDataPoint[] {
const cutoff = Date.now() - minutes * 60 * 1000;
const rawData = this.rawBuffer.filter((p) => p.timestamp > cutoff);
let rawStart = this.rawHead;
while (
rawStart < this.rawBuffer.length &&
this.rawBuffer[rawStart]?.timestamp <= cutoff
) {
rawStart++;
}
const rawData = this.rawBuffer.slice(rawStart);
if (minutes <= this.config.rawDataMinutes) {
return rawData;
}
const compressedCutoffUpper =
Date.now() - this.config.rawDataMinutes * 60 * 1000;
let compressedStart = this.compressedHead;
while (
compressedStart < this.compressedBuffer.length &&
this.compressedBuffer[compressedStart]?.timestamp <= cutoff
) {
compressedStart++;
}
const compressedData = this.compressedBuffer
.filter(
(p) =>
p.timestamp > cutoff &&
p.timestamp <= Date.now() - this.config.rawDataMinutes * 60 * 1000,
)
.slice(compressedStart)
.filter((p) => p.timestamp <= compressedCutoffUpper)
.map((p) => ({
up: p.up,
down: p.down,
@@ -86,16 +124,21 @@ export class TrafficDataSampler {
getStats(): ISamplerStats {
return {
rawBufferSize: this.rawBuffer.length,
compressedBufferSize: this.compressedBuffer.length,
rawBufferSize: this.rawBuffer.length - this.rawHead,
compressedBufferSize: this.compressedBuffer.length - this.compressedHead,
compressionQueueSize: this.compressionQueue.length,
totalMemoryPoints: this.rawBuffer.length + this.compressedBuffer.length,
totalMemoryPoints:
this.rawBuffer.length -
this.rawHead +
(this.compressedBuffer.length - this.compressedHead),
};
}
clear() {
this.rawBuffer = [];
this.rawHead = 0;
this.compressedBuffer = [];
this.compressedHead = 0;
this.compressionQueue = [];
}
}