Last active
February 22, 2019 16:13
-
-
Save briandemant/1a92fd5e218d9947798eb3696e1d1c47 to your computer and use it in GitHub Desktop.
This file contains 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
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