LockManager.js 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. const { promisify } = require('util')
  2. const OError = require('@overleaf/o-error')
  3. const Lockfile = require('lockfile')
  4. const Errors = require('./Errors')
  5. const fsPromises = require('fs/promises')
  6. const Path = require('path')
  7. const LOCK_OPTS = {
  8. pollPeriod: 1000, // 1s between each test of the lock
  9. wait: 15000, // 15s maximum time to spend trying to get the lock
  10. stale: 5 * 60 * 1000, // 5 mins time until lock auto expires
  11. }
  12. const PromisifiedLockfile = {
  13. lock: promisify(Lockfile.lock),
  14. unlock: promisify(Lockfile.unlock),
  15. }
  16. async function acquire(path) {
  17. try {
  18. await PromisifiedLockfile.lock(path, LOCK_OPTS)
  19. } catch (err) {
  20. if (err.code === 'EEXIST') {
  21. throw new Errors.AlreadyCompilingError('compile in progress')
  22. } else {
  23. const dir = Path.dirname(path)
  24. const [statLock, statDir, readdirDir] = await Promise.allSettled([
  25. fsPromises.lstat(path),
  26. fsPromises.lstat(dir),
  27. fsPromises.readdir(dir),
  28. ])
  29. OError.tag(err, 'unable to get lock', {
  30. statLock: unwrapPromiseResult(statLock),
  31. statDir: unwrapPromiseResult(statDir),
  32. readdirDir: unwrapPromiseResult(readdirDir),
  33. })
  34. throw err
  35. }
  36. }
  37. return new Lock(path)
  38. }
  39. class Lock {
  40. constructor(path) {
  41. this._path = path
  42. }
  43. async release() {
  44. await PromisifiedLockfile.unlock(this._path)
  45. }
  46. }
  47. function unwrapPromiseResult(result) {
  48. if (result.status === 'fulfilled') {
  49. return result.value
  50. } else {
  51. return result.reason
  52. }
  53. }
  54. module.exports = { acquire }