Fix statistics records trimming

This commit is contained in:
Anton Vakhrushev 2020-05-02 11:48:24 +03:00
parent 086f4f7bbe
commit 3844e28f76
6 changed files with 95 additions and 14 deletions

View File

@ -5,6 +5,8 @@ import { ActionQueue } from './Queue/ActionQueue';
import { Executor } from './Executor'; import { Executor } from './Executor';
import { ControlPanel } from './ControlPanel'; import { ControlPanel } from './ControlPanel';
import { DataStorageTaskProvider } from './Queue/DataStorageTaskProvider'; import { DataStorageTaskProvider } from './Queue/DataStorageTaskProvider';
import { Statistics } from './Statistics';
import { StatisticsStorage } from './Storage/StatisticsStorage';
export class Container { export class Container {
private readonly version: string; private readonly version: string;
@ -33,7 +35,7 @@ export class Container {
this._executor = this._executor =
this._executor || this._executor ||
(() => { (() => {
return new Executor(this.version, this.scheduler); return new Executor(this.version, this.scheduler, this.statistics);
})(); })();
return this._executor; return this._executor;
} }
@ -48,4 +50,15 @@ export class Container {
})(); })();
return this._controlPanel; return this._controlPanel;
} }
private _statistics: Statistics | undefined;
get statistics(): Statistics {
this._statistics =
this._statistics ||
(() => {
return new Statistics(new StatisticsStorage());
})();
return this._statistics;
}
} }

View File

@ -23,11 +23,11 @@ export class Executor {
private executionState: ExecutionStorage; private executionState: ExecutionStorage;
private logger: Logger; private logger: Logger;
constructor(version: string, scheduler: Scheduler) { constructor(version: string, scheduler: Scheduler, statistics: Statistics) {
this.version = version; this.version = version;
this.scheduler = scheduler; this.scheduler = scheduler;
this.grabbers = new GrabberManager(); this.grabbers = new GrabberManager();
this.statistics = new Statistics(); this.statistics = statistics;
this.executionState = new ExecutionStorage(); this.executionState = new ExecutionStorage();
this.logger = new ConsoleLogger(this.constructor.name); this.logger = new ConsoleLogger(this.constructor.name);
} }
@ -103,7 +103,7 @@ export class Executor {
throw new ActionError(`Action task id ${cmd.args.taskId} not equal current task id ${task.id}`); throw new ActionError(`Action task id ${cmd.args.taskId} not equal current task id ${task.id}`);
} }
if (actionHandler) { if (actionHandler) {
this.statistics.incrementAction(); this.statistics.incrementAction(timestamp());
await actionHandler.run(cmd.args, task); await actionHandler.run(cmd.args, task);
} else { } else {
this.logger.warn('ACTION NOT FOUND', cmd.name); this.logger.warn('ACTION NOT FOUND', cmd.name);

View File

@ -1,25 +1,54 @@
import { StatisticsStorage } from './Storage/StatisticsStorage';
import * as dateFormat from 'dateformat'; import * as dateFormat from 'dateformat';
const KEY_FORMAT = 'yyyy-mm-dd-HH';
const KEEP_RECORD_COUNT = 24;
export interface ActionStatistics { export interface ActionStatistics {
[key: string]: number; [key: string]: number;
} }
export class Statistics { export interface StatisticsStorageInterface {
private state: StatisticsStorage; getActionStatistics(): ActionStatistics;
setActionStatistics(statistics: ActionStatistics): void;
}
constructor() { export class Statistics {
this.state = new StatisticsStorage(); private state: StatisticsStorageInterface;
static readonly keepRecords = KEEP_RECORD_COUNT;
constructor(storage: StatisticsStorageInterface) {
this.state = storage;
} }
incrementAction(): void { incrementAction(ts: number): void {
const stat = this.state.getActionStatistics(); const stat = this.state.getActionStatistics();
const key = dateFormat(Date.now(), 'yyyy-mm-dd-HH'); const key = dateFormat(ts * 1000, KEY_FORMAT);
stat[key] = (stat[key] || 0) + 1; stat[key] = (stat[key] || 0) + 1;
this.trimStatistics(stat);
this.state.setActionStatistics(stat); this.state.setActionStatistics(stat);
} }
private trimStatistics(stat: ActionStatistics) {
const topKeys = this.getTopStatKeys(stat);
const statKeys = Object.keys(stat);
for (let key of statKeys) {
if (!topKeys.includes(key)) {
delete stat[key];
}
}
return stat;
}
private getTopStatKeys(stat: ActionStatistics) {
const keys = Object.keys(stat);
return keys
.sort()
.reverse()
.slice(0, KEEP_RECORD_COUNT);
}
getActionStatistics(): ActionStatistics { getActionStatistics(): ActionStatistics {
return {}; return this.state.getActionStatistics();
} }
} }

View File

@ -1,11 +1,11 @@
import { DataStorage } from '../DataStorage'; import { DataStorage } from '../DataStorage';
import { ActionStatistics } from '../Statistics'; import { ActionStatistics, StatisticsStorageInterface } from '../Statistics';
const NAMESPACE = 'statistics.v1'; const NAMESPACE = 'statistics.v1';
const ACTION_STATISTICS_KEY = 'actions'; const ACTION_STATISTICS_KEY = 'actions';
export class StatisticsStorage { export class StatisticsStorage implements StatisticsStorageInterface {
private storage: DataStorage; private storage: DataStorage;
constructor() { constructor() {
this.storage = new DataStorage(NAMESPACE); this.storage = new DataStorage(NAMESPACE);

View File

@ -115,6 +115,10 @@ export function notify(msg: string): void {
setTimeout(() => n && n.close(), 4000); setTimeout(() => n && n.close(), 4000);
} }
export interface NowTimeGenerator {
(): number;
}
export function markPage(text: string, version: string) { export function markPage(text: string, version: string) {
jQuery('body').append( jQuery('body').append(
'<div style="' + '<div style="' +

View File

@ -0,0 +1,35 @@
import { it, describe } from 'mocha';
import { expect } from 'chai';
import { ActionStatistics, Statistics, StatisticsStorageInterface } from '../../src/Statistics';
class MemoryStatisticsStorage implements StatisticsStorageInterface {
stat: ActionStatistics = {};
getActionStatistics(): ActionStatistics {
return this.stat;
}
setActionStatistics(statistics: ActionStatistics): void {
this.stat = statistics;
}
}
describe('Statistics', function() {
it('Can save statistics item', function() {
const storage = new MemoryStatisticsStorage();
const statistics = new Statistics(storage);
statistics.incrementAction(1588408294);
expect(Object.keys(statistics.getActionStatistics())).to.has.lengthOf(1);
});
it('Can trim statistics', function() {
const storage = new MemoryStatisticsStorage();
const statistics = new Statistics(storage);
const baseTime = 1588408294;
for (let i = 0; i < 120; ++i) {
statistics.incrementAction(baseTime + 3600 * i);
}
expect(Object.keys(statistics.getActionStatistics())).to.has.lengthOf(Statistics.keepRecords);
});
});