commit_changes.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. // @ts-check
  2. 'use strict'
  3. const metrics = require('@overleaf/metrics')
  4. const redisBackend = require('./chunk_store/redis')
  5. const logger = require('@overleaf/logger')
  6. const queueChanges = require('./queue_changes')
  7. const persistChanges = require('./persist_changes')
  8. /**
  9. * @typedef {import('overleaf-editor-core').Change} Change
  10. */
  11. /**
  12. * Handle incoming changes by processing them according to the specified options.
  13. * @param {string} projectId
  14. * @param {Change[]} changes
  15. * @param {Object} limits
  16. * @param {number} endVersion
  17. * @param {Object} options
  18. * @param {Boolean} [options.queueChangesInRedis]
  19. * If true, queue the changes in Redis for testing purposes.
  20. * @return {Promise.<Object?>}
  21. */
  22. async function commitChanges(
  23. projectId,
  24. changes,
  25. limits,
  26. endVersion,
  27. options = {}
  28. ) {
  29. if (options.queueChangesInRedis) {
  30. try {
  31. await queueChanges(projectId, changes, endVersion)
  32. await fakePersistRedisChanges(projectId, changes, endVersion)
  33. } catch (err) {
  34. logger.error({ err }, 'Chunk buffer verification failed')
  35. }
  36. }
  37. const result = await persistChanges(projectId, changes, limits, endVersion)
  38. return result
  39. }
  40. /**
  41. * Simulates the persistence of changes by verifying a given set of changes against
  42. * what is currently stored as non-persisted in Redis, and then updates the
  43. * persisted version number in Redis.
  44. *
  45. * @async
  46. * @param {string} projectId - The ID of the project.
  47. * @param {Change[]} changesToPersist - An array of changes that are expected to be
  48. * persisted. These are used for verification
  49. * against the changes currently in Redis.
  50. * @param {number} baseVersion - The base version number from which to calculate
  51. * the new persisted version.
  52. * @returns {Promise<void>} A promise that resolves when the persisted version
  53. * in Redis has been updated.
  54. */
  55. async function fakePersistRedisChanges(
  56. projectId,
  57. changesToPersist,
  58. baseVersion
  59. ) {
  60. const nonPersistedChanges = await redisBackend.getNonPersistedChanges(
  61. projectId,
  62. baseVersion
  63. )
  64. if (
  65. serializeChanges(nonPersistedChanges) === serializeChanges(changesToPersist)
  66. ) {
  67. metrics.inc('persist_redis_changes_verification', 1, { status: 'match' })
  68. } else {
  69. logger.warn({ projectId }, 'mismatch of non-persisted changes from Redis')
  70. metrics.inc('persist_redis_changes_verification', 1, {
  71. status: 'mismatch',
  72. })
  73. }
  74. const persistedVersion = baseVersion + nonPersistedChanges.length
  75. await redisBackend.setPersistedVersion(projectId, persistedVersion)
  76. }
  77. /**
  78. * @param {Change[]} changes
  79. */
  80. function serializeChanges(changes) {
  81. return JSON.stringify(changes.map(change => change.toRaw()))
  82. }
  83. module.exports = commitChanges