PackWorker.coffee 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. Settings = require "settings-sharelatex"
  2. async = require "async"
  3. _ = require "underscore"
  4. {db, ObjectId, BSON} = require "./mongojs"
  5. fs = require "fs"
  6. Metrics = require "metrics-sharelatex"
  7. Metrics.initialize("track-changes")
  8. logger = require "logger-sharelatex"
  9. logger.initialize("track-changes-packworker")
  10. if Settings.sentry?.dsn?
  11. logger.initializeErrorReporting(Settings.sentry.dsn)
  12. DAYS = 24 * 3600 * 1000
  13. LockManager = require "./LockManager"
  14. PackManager = require "./PackManager"
  15. # this worker script is forked by the main process to look for
  16. # document histories which can be archived
  17. source = process.argv[2]
  18. DOCUMENT_PACK_DELAY = Number(process.argv[3]) || 1000
  19. TIMEOUT = Number(process.argv[4]) || 30*60*1000
  20. if source.match(/[^0-9]/)
  21. file = fs.readFileSync source
  22. result = for line in file.toString().split('\n')
  23. [project_id, doc_id] = line.split(' ')
  24. {doc_id, project_id}
  25. pending = _.filter result, (row) -> row?.doc_id?.match(/^[a-f0-9]{24}$/)
  26. else
  27. LIMIT = Number(process.argv[2]) || 1000
  28. shutDownRequested = false
  29. shutDownTimer = setTimeout () ->
  30. logger.log "pack timed out, requesting shutdown"
  31. # start the shutdown on the next pack
  32. shutDownRequested = true
  33. # do a hard shutdown after a further 5 minutes
  34. setTimeout () ->
  35. logger.error "HARD TIMEOUT in pack archive worker"
  36. process.exit()
  37. , 5*60*1000
  38. , TIMEOUT
  39. logger.log "checking for updates, limit=#{LIMIT}, delay=#{DOCUMENT_PACK_DELAY}, timeout=#{TIMEOUT}"
  40. # work around for https://github.com/mafintosh/mongojs/issues/224
  41. db.close = (callback) ->
  42. this._getServer (err, server) ->
  43. return callback(err) if err?
  44. server = if server.destroy? then server else server.topology
  45. server.destroy(true, true)
  46. callback()
  47. finish = () ->
  48. if shutDownTimer?
  49. logger.log 'cancelling timeout'
  50. clearTimeout shutDownTimer
  51. logger.log 'closing db'
  52. db.close () ->
  53. logger.log 'closing LockManager Redis Connection'
  54. LockManager.close () ->
  55. logger.log 'ready to exit from pack archive worker'
  56. hardTimeout = setTimeout () ->
  57. logger.error 'hard exit from pack archive worker'
  58. process.exit(1)
  59. , 5*1000
  60. hardTimeout.unref()
  61. process.on 'exit', (code) ->
  62. logger.log {code}, 'pack archive worker exited'
  63. processUpdates = (pending) ->
  64. async.eachSeries pending, (result, callback) ->
  65. {_id, project_id, doc_id} = result
  66. logger.log {project_id, doc_id}, "processing"
  67. if not project_id? or not doc_id?
  68. logger.log {project_id, doc_id}, "skipping pack, missing project/doc id"
  69. return callback()
  70. handler = (err, result) ->
  71. if err? and err.code is "InternalError" and err.retryable
  72. logger.warn {err, result}, "ignoring S3 error in pack archive worker"
  73. # Ignore any s3 errors due to random problems
  74. err = null
  75. if err?
  76. logger.error {err, result}, "error in pack archive worker"
  77. return callback(err)
  78. if shutDownRequested
  79. logger.error "shutting down pack archive worker"
  80. return callback(new Error("shutdown"))
  81. setTimeout () ->
  82. callback(err, result)
  83. , DOCUMENT_PACK_DELAY
  84. if not _id?
  85. PackManager.pushOldPacks project_id, doc_id, handler
  86. else
  87. PackManager.processOldPack project_id, doc_id, _id, handler
  88. , (err, results) ->
  89. if err? and err.message != "shutdown"
  90. logger.error {err}, 'error in pack archive worker processUpdates'
  91. finish()
  92. # find the packs which can be archived
  93. ObjectIdFromDate = (date) ->
  94. id = Math.floor(date.getTime() / 1000).toString(16) + "0000000000000000";
  95. return ObjectId(id)
  96. # new approach, two passes
  97. # find packs to be marked as finalised:true, those which have a newer pack present
  98. # then only consider finalised:true packs for archiving
  99. if pending?
  100. logger.log "got #{pending.length} entries from #{source}"
  101. processUpdates pending
  102. else
  103. db.docHistory.find({
  104. expiresAt: {$exists: false}
  105. project_id: {$exists: true}
  106. v_end: {$exists: true}
  107. _id: {$lt: ObjectIdFromDate(new Date(Date.now() - 7 * DAYS))}
  108. }, {_id:1, doc_id:1, project_id:1}).sort({
  109. last_checked:1
  110. }).limit LIMIT, (err, results) ->
  111. if err?
  112. logger.log {err}, 'error checking for updates'
  113. finish()
  114. return
  115. pending = _.uniq results, false, (result) -> result.doc_id.toString()
  116. logger.log "found #{pending.length} documents to archive"
  117. processUpdates pending