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 { ControlPanel } from './ControlPanel';
import { DataStorageTaskProvider } from './Queue/DataStorageTaskProvider';
import { Statistics } from './Statistics';
import { StatisticsStorage } from './Storage/StatisticsStorage';
export class Container {
private readonly version: string;
@ -33,7 +35,7 @@ export class Container {
this._executor =
this._executor ||
(() => {
return new Executor(this.version, this.scheduler);
return new Executor(this.version, this.scheduler, this.statistics);
})();
return this._executor;
}
@ -48,4 +50,15 @@ export class Container {
})();
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 logger: Logger;
constructor(version: string, scheduler: Scheduler) {
constructor(version: string, scheduler: Scheduler, statistics: Statistics) {
this.version = version;
this.scheduler = scheduler;
this.grabbers = new GrabberManager();
this.statistics = new Statistics();
this.statistics = statistics;
this.executionState = new ExecutionStorage();
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}`);
}
if (actionHandler) {
this.statistics.incrementAction();
this.statistics.incrementAction(timestamp());
await actionHandler.run(cmd.args, task);
} else {
this.logger.warn('ACTION NOT FOUND', cmd.name);

View File

@ -1,25 +1,54 @@
import { StatisticsStorage } from './Storage/StatisticsStorage';
import * as dateFormat from 'dateformat';
const KEY_FORMAT = 'yyyy-mm-dd-HH';
const KEEP_RECORD_COUNT = 24;
export interface ActionStatistics {
[key: string]: number;
}
export class Statistics {
private state: StatisticsStorage;
export interface StatisticsStorageInterface {
getActionStatistics(): ActionStatistics;
setActionStatistics(statistics: ActionStatistics): void;
}
constructor() {
this.state = new StatisticsStorage();
export class Statistics {
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 key = dateFormat(Date.now(), 'yyyy-mm-dd-HH');
const key = dateFormat(ts * 1000, KEY_FORMAT);
stat[key] = (stat[key] || 0) + 1;
this.trimStatistics(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 {
return {};
return this.state.getActionStatistics();
}
}

View File

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

View File

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