Skip to content

Commit

Permalink
feat: scheduler init jitter (#6071)
Browse files Browse the repository at this point in the history
  • Loading branch information
kwasniew committed Jan 30, 2024
1 parent 55b2bb4 commit ccc41dc
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 3 deletions.
30 changes: 28 additions & 2 deletions src/lib/features/scheduler/scheduler-service.test.ts
Expand Up @@ -72,7 +72,7 @@ test('Schedules job immediately', async () => {

const job = jest.fn();

await schedulerService.schedule(job, 10, 'test-id');
await schedulerService.schedule(job, 10, 'test-id', 0);

expect(job).toBeCalledTimes(1);
schedulerService.stop();
Expand Down Expand Up @@ -172,7 +172,7 @@ test('Can handle crash of a async job', async () => {
await Promise.reject('async reason');
};

await schedulerService.schedule(job, 50, 'test-id-10');
await schedulerService.schedule(job, 50, 'test-id-10', 0);
await ms(75);

schedulerService.stop();
Expand Down Expand Up @@ -236,3 +236,29 @@ it('should emit scheduler job time event when scheduled function is run', async
await schedulerService.schedule(mockJob, 50, 'testJobId');
await eventPromise;
});

test('Delays initial job execution by jitter duration', async () => {
const { schedulerService } = createSchedulerTestService();

const job = jest.fn();
const jitterMs = 10;

await schedulerService.schedule(job, 10000, 'test-id', jitterMs);
expect(job).toBeCalledTimes(0);

await ms(50);
expect(job).toBeCalledTimes(1);
schedulerService.stop();
});

test('Does not apply jitter if schedule interval is smaller than max jitter', async () => {
const { schedulerService } = createSchedulerTestService();

const job = jest.fn();

// default jitter 2s-30s
await schedulerService.schedule(job, 1000, 'test-id');
expect(job).toBeCalledTimes(1);

schedulerService.stop();
});
23 changes: 22 additions & 1 deletion src/lib/features/scheduler/scheduler-service.ts
Expand Up @@ -3,6 +3,19 @@ import { Logger, LogProvider } from '../../logger';
import { IMaintenanceStatus } from '../maintenance/maintenance-service';
import { SCHEDULER_JOB_TIME } from '../../metric-events';

// returns between min and max seconds in ms
// when schedule interval is smaller than max jitter then no jitter
function randomJitter(
minMs: number,
maxMs: number,
scheduleIntervalMs: number,
): number {
if (scheduleIntervalMs < maxMs) {
return 0;
}
return Math.random() * (maxMs - minMs) + minMs;
}

export class SchedulerService {
private intervalIds: NodeJS.Timeout[] = [];

Expand All @@ -26,6 +39,7 @@ export class SchedulerService {
scheduledFunction: () => void,
timeMs: number,
id: string,
jitter = randomJitter(2 * 1000, 30 * 1000, timeMs),
): Promise<void> {
const runScheduledFunctionWithEvent = async () => {
const startTime = process.hrtime();
Expand Down Expand Up @@ -61,8 +75,15 @@ export class SchedulerService {
try {
const maintenanceMode =
await this.maintenanceStatus.isMaintenanceMode();

if (!maintenanceMode) {
await runScheduledFunctionWithEvent();
if (jitter) {
setTimeout(() => {
runScheduledFunctionWithEvent();
}, jitter);
} else {
await runScheduledFunctionWithEvent();
}
}
} catch (e) {
this.logger.error(`initial scheduled job failed | id: ${id}`, e);
Expand Down

0 comments on commit ccc41dc

Please sign in to comment.