LockManagerTests.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. const { expect } = require('chai')
  2. const sinon = require('sinon')
  3. const SandboxedModule = require('sandboxed-module')
  4. const modulePath = require('node:path').join(
  5. __dirname,
  6. '../../../app/js/LockManager'
  7. )
  8. const Errors = require('../../../app/js/Errors')
  9. describe('LockManager', function () {
  10. beforeEach(function () {
  11. this.key = '/local/compile/directory'
  12. this.clock = sinon.useFakeTimers()
  13. this.LockManager = SandboxedModule.require(modulePath, {
  14. requires: {
  15. '@overleaf/metrics': (this.Metrics = {
  16. inc: sinon.stub(),
  17. gauge: sinon.stub(),
  18. }),
  19. '@overleaf/settings': (this.Settings = {
  20. compileConcurrencyLimit: 5,
  21. }),
  22. './Errors': (this.Erros = Errors),
  23. },
  24. })
  25. })
  26. afterEach(function () {
  27. this.clock.restore()
  28. })
  29. describe('when the lock is available', function () {
  30. it('the lock can be acquired', function () {
  31. const lock = this.LockManager.acquire(this.key)
  32. expect(lock).to.exist
  33. lock.release()
  34. })
  35. })
  36. describe('after the lock is acquired', function () {
  37. beforeEach(function () {
  38. this.lock = this.LockManager.acquire(this.key)
  39. })
  40. afterEach(function () {
  41. if (this.lock != null) {
  42. this.lock.release()
  43. }
  44. })
  45. it("the lock can't be acquired again", function () {
  46. expect(() => this.LockManager.acquire(this.key)).to.throw(
  47. Errors.AlreadyCompilingError
  48. )
  49. })
  50. it('another lock can be acquired', function () {
  51. const lock = this.LockManager.acquire('another key')
  52. expect(lock).to.exist
  53. lock.release()
  54. })
  55. it('the lock can be acquired again after an expiry period', function () {
  56. // The expiry time is a little bit over 10 minutes. Let's wait 15 minutes.
  57. this.clock.tick(15 * 60 * 1000)
  58. this.lock = this.LockManager.acquire(this.key)
  59. expect(this.lock).to.exist
  60. })
  61. it('the lock can be acquired again after it was released', function () {
  62. this.lock.release()
  63. this.lock = this.LockManager.acquire(this.key)
  64. expect(this.lock).to.exist
  65. })
  66. })
  67. describe('concurrency limit', function () {
  68. it('exceeding the limit', function () {
  69. for (let i = 0; i <= this.Settings.compileConcurrencyLimit; i++) {
  70. this.LockManager.acquire('test_key' + i)
  71. }
  72. this.Metrics.inc
  73. .calledWith('exceeded-compilier-concurrency-limit')
  74. .should.equal(false)
  75. expect(() =>
  76. this.LockManager.acquire(
  77. 'test_key_' + (this.Settings.compileConcurrencyLimit + 1),
  78. false
  79. )
  80. ).to.throw(Errors.TooManyCompileRequestsError)
  81. this.Metrics.inc
  82. .calledWith('exceeded-compilier-concurrency-limit')
  83. .should.equal(true)
  84. })
  85. it('within the limit', function () {
  86. for (let i = 0; i <= this.Settings.compileConcurrencyLimit - 1; i++) {
  87. this.LockManager.acquire('test_key' + i)
  88. }
  89. this.Metrics.inc
  90. .calledWith('exceeded-compilier-concurrency-limit')
  91. .should.equal(false)
  92. const lock = this.LockManager.acquire(
  93. 'test_key_' + this.Settings.compileConcurrencyLimit,
  94. false
  95. )
  96. expect(lock.key).to.equal(
  97. 'test_key_' + this.Settings.compileConcurrencyLimit
  98. )
  99. })
  100. })
  101. })