PackWorker.coffee 4.4 KB

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