Skip to content

Instantly share code, notes, and snippets.

@webuniverseio
Created January 19, 2022 22:51
Show Gist options
  • Save webuniverseio/b1d14a983abe0c00f62059ba111e83ae to your computer and use it in GitHub Desktop.
Save webuniverseio/b1d14a983abe0c00f62059ba111e83ae to your computer and use it in GitHub Desktop.
Event queue with max limit and slow/fast scheduling
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',
}) */
}
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
}
{
"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