Fix statistics records trimming
This commit is contained in:
parent
086f4f7bbe
commit
3844e28f76
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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="' +
|
||||||
|
35
tests/Queue/StatisticsTest.js.ts
Normal file
35
tests/Queue/StatisticsTest.js.ts
Normal 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);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user