mirror of
https://github.com/Unleash/unleash.git
synced 2024-11-01 19:07:38 +01:00
ce815e5f29
## About the changes
Introduce a snapshot version of instanceStats inside
instance-stats-service to provide a cached state of the statistics
without compromising the DB.
### Important notes
Some rule-of-thumb applied in the PR that can be changed:
1. The snapshot refresh time
2. The threshold to report appName with the metrics
## Discussion points
1. The snapshot could be limited to just the information needed (things
like `hasOIDC` don't change until there's a restart), to optimize the memory usage
3. metrics.ts (used to expose Prometheus metrics) has a [refresh
interval of
2hs](2d16730cc2/src/lib/metrics.ts (L189-L195)
),
but with this implementation, we could remove that background task and
rely on the snapshot
4. We could additionally update the snapshot every time someone queries
the DB to fetch stats (`getStats()` method), but it may increase
complexity without a significant benefit
Co-authored-by: Mateusz Kwasniewski <kwasniewski.mateusz@gmail.com>
Co-authored-by: Simon Hornby <liquidwicked64@gmail.com>
108 lines
2.8 KiB
TypeScript
108 lines
2.8 KiB
TypeScript
import SchedulerService from './scheduler-service';
|
|
|
|
function ms(timeMs) {
|
|
return new Promise((resolve) => setTimeout(resolve, timeMs));
|
|
}
|
|
|
|
const getLogger = () => {
|
|
const records = [];
|
|
const logger = () => ({
|
|
error(...args: any[]) {
|
|
records.push(args);
|
|
},
|
|
debug() {},
|
|
info() {},
|
|
warn() {},
|
|
fatal() {},
|
|
getRecords() {
|
|
return records;
|
|
},
|
|
});
|
|
logger.getRecords = () => records;
|
|
|
|
return logger;
|
|
};
|
|
|
|
test('Schedules job immediately', async () => {
|
|
const schedulerService = new SchedulerService(getLogger());
|
|
const job = jest.fn();
|
|
|
|
schedulerService.schedule(job, 10);
|
|
|
|
expect(job).toBeCalledTimes(1);
|
|
schedulerService.stop();
|
|
});
|
|
|
|
test('Can schedule a single regular job', async () => {
|
|
const schedulerService = new SchedulerService(getLogger());
|
|
const job = jest.fn();
|
|
|
|
schedulerService.schedule(job, 10);
|
|
await ms(15);
|
|
|
|
expect(job).toBeCalledTimes(2);
|
|
schedulerService.stop();
|
|
});
|
|
|
|
test('Can schedule multiple jobs at the same interval', async () => {
|
|
const schedulerService = new SchedulerService(getLogger());
|
|
const job = jest.fn();
|
|
const anotherJob = jest.fn();
|
|
|
|
schedulerService.schedule(job, 10);
|
|
schedulerService.schedule(anotherJob, 10);
|
|
await ms(15);
|
|
|
|
expect(job).toBeCalledTimes(2);
|
|
expect(anotherJob).toBeCalledTimes(2);
|
|
schedulerService.stop();
|
|
});
|
|
|
|
test('Can schedule multiple jobs at the different intervals', async () => {
|
|
const schedulerService = new SchedulerService(getLogger());
|
|
const job = jest.fn();
|
|
const anotherJob = jest.fn();
|
|
|
|
schedulerService.schedule(job, 10);
|
|
schedulerService.schedule(anotherJob, 20);
|
|
await ms(25);
|
|
|
|
expect(job).toBeCalledTimes(3);
|
|
expect(anotherJob).toBeCalledTimes(2);
|
|
schedulerService.stop();
|
|
});
|
|
|
|
test('Can handle crash of a async job', async () => {
|
|
const logger = getLogger();
|
|
const schedulerService = new SchedulerService(logger);
|
|
const job = async () => {
|
|
await Promise.reject('async reason');
|
|
};
|
|
|
|
schedulerService.schedule(job, 10);
|
|
await ms(15);
|
|
|
|
schedulerService.stop();
|
|
expect(logger.getRecords()).toEqual([
|
|
['scheduled job failed', 'async reason'],
|
|
['scheduled job failed', 'async reason'],
|
|
]);
|
|
});
|
|
|
|
test('Can handle crash of a sync job', async () => {
|
|
const logger = getLogger();
|
|
const schedulerService = new SchedulerService(logger);
|
|
const job = () => {
|
|
throw new Error('sync reason');
|
|
};
|
|
|
|
schedulerService.schedule(job, 10);
|
|
await ms(15);
|
|
|
|
schedulerService.stop();
|
|
expect(logger.getRecords()).toEqual([
|
|
['scheduled job failed', new Error('sync reason')],
|
|
['scheduled job failed', new Error('sync reason')],
|
|
]);
|
|
});
|