Created
January 19, 2022 22:51
-
-
Save webuniverseio/b1d14a983abe0c00f62059ba111e83ae to your computer and use it in GitHub Desktop.
Event queue with max limit and slow/fast scheduling
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let status = 'idle' | |
export const getStatus = () => status | |
let timerId | |
const clearTimer = () => clearTimeout(timerId) | |
const scheduleQueueCheck = ms => { | |
clearTimer() | |
timerId = setTimeout(checkQueue, ms) | |
} | |
export const config = { | |
rateLimit: 500, | |
shortTimeout: 1000, | |
longTimeout: 60000 | |
} | |
const queue = [], rateLimit = config.rateLimit | |
scheduleQueueCheck(config.longTimeout) | |
export function addToQueue(data) { | |
queue.push(data) | |
//too full to wait when idle | |
if (status === 'idle' && queue.length >= rateLimit) checkQueue() | |
} | |
function checkQueue() { | |
//keep in mind it is also called on schedule, so it can be empty or waiting | |
if (queue.length && status === 'idle') { | |
status = 'processing' | |
Promise.allSettled(queue.splice(0, rateLimit).map(sendData)).then(() => { | |
status = 'idle' | |
queue.length >= rateLimit ? | |
checkQueue() : //too much, keep processing | |
scheduleQueueCheck(config.longTimeout) //too empty, wait for a longer period | |
}) | |
} | |
scheduleQueueCheck(status === 'processing' ? | |
config.shortTimeout : //if still processing check again sooner | |
config.longTimeout) | |
} | |
function sendData({ id, data }) { | |
return new Promise(resolve => setTimeout(resolve, Math.random() < 0.5 ? 0 : 1000)) | |
/* return fetch(`https://localhost/${id}`, { | |
body: JSON.stringify(data), | |
headers: { 'Content-Type': 'application/json' }, | |
method: 'POST', | |
}) */ | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {jest} from '@jest/globals' | |
jest.useFakeTimers() | |
describe('test queue', () => { | |
let addToQueue, resetQueue, getStatus, config | |
beforeEach(() => { | |
return import('./index').then((x) => { | |
addToQueue = x.addToQueue | |
resetQueue = x.resetQueue | |
getStatus = x.getStatus | |
config = x.config | |
}) | |
}) | |
afterEach(() => { | |
jest.resetModules() | |
}) | |
test('check that queue waits until it hits max rate', () => { | |
let i = 0 | |
for (; i < config.rateLimit - 1; i++) { | |
addToQueue({ data: { title: 'test' }, id: i }) | |
} | |
expect(getStatus()).toBe('idle') | |
addToQueue({ data: { title: 'test' }, id: i++ }) | |
expect(getStatus()).toBe('processing') | |
return waitSettled().then(wait).then(() => { | |
expect(getStatus()).toBe('idle') | |
}) | |
}) | |
test('check that queue waits until 60 seconds when not up to max rate', () => { | |
addToQueue({ data: { title: 'test' }, id: 0 }) | |
jest.advanceTimersByTime(config.longTimeout - 1) | |
addToQueue({ data: { title: 'test' }, id: 0 }) | |
expect(getStatus()).toBe('idle') | |
jest.advanceTimersByTime(1) | |
return waitSettled().then(wait).then(() => { | |
expect(getStatus()).toBe('idle') | |
}) | |
}) | |
test('if queue has more than double of max rate, it should clear rate every second, then become idle, wait for a minute and process remaining', () => { | |
let i = 0 | |
const max = (config.rateLimit + 1) * 2 | |
for (; i < max; i++) { | |
addToQueue({ data: { title: 'test' }, id: i }) | |
} | |
expect(getStatus()).toBe('processing') | |
return waitSettled().then(wait).then(() => { | |
expect(getStatus()).toBe('processing') | |
return waitSettled().then(wait) | |
}).then(() => { | |
expect(getStatus()).toBe('idle') | |
jest.advanceTimersByTime(config.shortTimeout) | |
expect(getStatus()).toBe('idle') | |
jest.advanceTimersByTime((config.longTimeout)) | |
expect(getStatus()).toBe('processing') | |
return waitSettled().then(wait) | |
}).then(() => { | |
expect(getStatus()).toBe('idle') | |
}) | |
}) | |
}) | |
function waitSettled() { | |
return Promise.allSettled(Array(1)) | |
} | |
function wait() { | |
const promise = new Promise(resolve => setTimeout(resolve, 1000)) | |
jest.advanceTimersByTime(1000) | |
return promise | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "jest-temp-experiments", | |
"version": "1.0.0", | |
"main": "index.js", | |
"type": "module", | |
"dependencies": { | |
"jest": "^27.0.6" | |
}, | |
"wallaby": { | |
"autoDetect": true, | |
"env": { | |
"params": { | |
"runner": "--experimental-vm-modules" | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment