RealTimeRedisManager.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. /* eslint-disable
  2. camelcase,
  3. no-unused-vars,
  4. */
  5. // TODO: This file was created by bulk-decaffeinate.
  6. // Fix any style issues and re-enable lint.
  7. /*
  8. * decaffeinate suggestions:
  9. * DS101: Remove unnecessary use of Array.from
  10. * DS102: Remove unnecessary code created because of implicit returns
  11. * DS207: Consider shorter variations of null checks
  12. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  13. */
  14. let RealTimeRedisManager
  15. const Settings = require('settings-sharelatex')
  16. const rclient = require('@overleaf/redis-wrapper').createClient(
  17. Settings.redis.documentupdater
  18. )
  19. const pubsubClient = require('@overleaf/redis-wrapper').createClient(
  20. Settings.redis.pubsub
  21. )
  22. const Keys = Settings.redis.documentupdater.key_schema
  23. const logger = require('logger-sharelatex')
  24. const os = require('os')
  25. const crypto = require('crypto')
  26. const metrics = require('./Metrics')
  27. const HOST = os.hostname()
  28. const RND = crypto.randomBytes(4).toString('hex') // generate a random key for this process
  29. let COUNT = 0
  30. const MAX_OPS_PER_ITERATION = 8 // process a limited number of ops for safety
  31. module.exports = RealTimeRedisManager = {
  32. getPendingUpdatesForDoc(doc_id, callback) {
  33. const multi = rclient.multi()
  34. multi.lrange(Keys.pendingUpdates({ doc_id }), 0, MAX_OPS_PER_ITERATION - 1)
  35. multi.ltrim(Keys.pendingUpdates({ doc_id }), MAX_OPS_PER_ITERATION, -1)
  36. return multi.exec(function (error, replys) {
  37. let jsonUpdate
  38. if (error != null) {
  39. return callback(error)
  40. }
  41. const jsonUpdates = replys[0]
  42. for (jsonUpdate of Array.from(jsonUpdates)) {
  43. // record metric for each update removed from queue
  44. metrics.summary('redis.pendingUpdates', jsonUpdate.length, {
  45. status: 'pop'
  46. })
  47. }
  48. const updates = []
  49. for (jsonUpdate of Array.from(jsonUpdates)) {
  50. var update
  51. try {
  52. update = JSON.parse(jsonUpdate)
  53. } catch (e) {
  54. return callback(e)
  55. }
  56. updates.push(update)
  57. }
  58. return callback(error, updates)
  59. })
  60. },
  61. getUpdatesLength(doc_id, callback) {
  62. return rclient.llen(Keys.pendingUpdates({ doc_id }), callback)
  63. },
  64. sendData(data) {
  65. // create a unique message id using a counter
  66. const message_id = `doc:${HOST}:${RND}-${COUNT++}`
  67. if (data != null) {
  68. data._id = message_id
  69. }
  70. const blob = JSON.stringify(data)
  71. metrics.summary('redis.publish.applied-ops', blob.length)
  72. // publish on separate channels for individual projects and docs when
  73. // configured (needs realtime to be configured for this too).
  74. if (Settings.publishOnIndividualChannels) {
  75. return pubsubClient.publish(`applied-ops:${data.doc_id}`, blob)
  76. } else {
  77. return pubsubClient.publish('applied-ops', blob)
  78. }
  79. }
  80. }