Skip to content

Instantly share code, notes, and snippets.

@briandemant
Last active February 22, 2019 16:13
Show Gist options
  • Save briandemant/1a92fd5e218d9947798eb3696e1d1c47 to your computer and use it in GitHub Desktop.
Save briandemant/1a92fd5e218d9947798eb3696e1d1c47 to your computer and use it in GitHub Desktop.
const lockUpdaters = {}
function sleep(timeout) {
return new Promise((resolve) => {
setTimeout(resolve, timeout)
})
}
async function waitForLock(lockname, timeoutWindow) {
let token = await lock(lockname, timeoutWindow)
while (!token) {
await sleep(1)
token = await lock(lockname)
}
return token
}
async function lock(lockname, timeoutWindow) {
const token = `${(Math.abs(Math.random() * Number.MAX_SAFE_INTEGER | 0)).toString(36)}`
const free = await redis.setnx(lockname, token)
if (free) {
lockUpdaters[lockname] = {
token: token,
intervalId: setInterval(() => {
// this process is not stuck and still running
// extend the lease on the lock in redis
redis.expire(lockname, timeoutWindow * 1000)
}, 1000)
}
return token
}
}
async function unlock(lockname, token) {
if (token != lockUpdaters[lockname].token) throw "wtf!!!?"
// we do not need to extend the lease on the lock
clearInterval(lockUpdater[lockname].intervalId)
const was = await redis.get(lockname)
// the process may have been stuck (event loop was locked up by sync code)
// longer than the max expected (so redis expired the key) and
// something else got the lock
if (was == token) {
redis.del(lockname)
}
}
async function exampleOnUsage() {
let theLongestWeExpectThisToTake = 100;
let token = await waitForLock("mylock", theLongestWeExpectThisToTake)
// bad code that runs too long and holds onto the eventloop
for (let i = 0; i < Number.MAX_SAFE_INTEGER ; i++) {
Math.sqrt(i);
}
unlock("mylock", token)
}
async function exampleOnUsage2() {
let theLongestWeExpectThisToTake = 100;
let token = await lock("mylock", theLongestWeExpectThisToTake)
if (token) {
// bad code that runs too long and holds onto the eventloop
for (let i = 0; i < Number.MAX_SAFE_INTEGER ; i++) {
Math.sqrt(i);
}
unlock("mylock", token)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment