pack.coffee 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. Settings = require "settings-sharelatex"
  2. fs = require("fs")
  3. mongojs = require("mongojs")
  4. ObjectId = mongojs.ObjectId
  5. db = mongojs(Settings.mongo.url, ['docHistory'])
  6. async = require("async")
  7. BSON=db.bson.BSON
  8. util = require 'util'
  9. _ = require 'underscore'
  10. lineReader = require "line-reader"
  11. MAX_SIZE = 1024*1024
  12. MAX_COUNT = 1024
  13. MIN_COUNT = 100
  14. KEEP_OPS = 100
  15. insertPack = (packObj, callback) ->
  16. if shutdownRequested
  17. return callback('shutdown')
  18. bulk = db.docHistory.initializeOrderedBulkOp();
  19. expect_nInserted = 1
  20. expect_nRemoved = packObj.pack.length
  21. util.log "insert #{expect_nInserted} pack, remove #{expect_nRemoved} ops"
  22. bulk.insert packObj
  23. packObj.pack.forEach (op) ->
  24. bulk.find({_id:op._id}).removeOne()
  25. bulk.execute (err, result) ->
  26. if err? or result.nInserted != expect_nInserted or result.nRemoved != expect_nRemoved
  27. console.log err, result
  28. console.log 'nInserted', result.nInserted, 'nRemoved', result.nRemoved
  29. setTimeout () ->
  30. callback(err, result)
  31. , 2000
  32. packDocHistory = (doc_id, callback) ->
  33. util.log "starting pack operation for #{doc_id}"
  34. db.docHistory.find({doc_id:mongojs.ObjectId(doc_id),pack:{$exists:false}}).sort {v:1}, (err, docs) ->
  35. packs = []
  36. top = null
  37. origDocs = docs.length
  38. # keep the last KEEP_OPS as individual ops
  39. docs = docs.slice(0,-KEEP_OPS)
  40. docs.forEach (d,i) ->
  41. sz = BSON.calculateObjectSize(d)
  42. if top? && top.pack.length < MAX_COUNT && top.sz + sz < MAX_SIZE
  43. top.pack = top.pack.concat {v: d.v, meta: d.meta, op: d.op, _id: d._id}
  44. top.sz += sz
  45. top.v_end = d.v
  46. top.meta.end_ts = d.meta.end_ts
  47. return
  48. else if sz < MAX_SIZE
  49. # create a new pack
  50. top = _.clone(d)
  51. top.pack = [ {v: d.v, meta: d.meta, op: d.op, _id: d._id} ]
  52. top.meta = { start_ts: d.meta.start_ts, end_ts: d.meta.end_ts }
  53. top.sz = sz
  54. delete top.op
  55. delete top._id
  56. packs.push top
  57. else
  58. # keep the op
  59. util.log 'keeping large op unchanged (#{sz} bytes)'
  60. # only store packs with a sufficient number of ops, discard others
  61. packs = packs.filter (packObj) ->
  62. packObj.pack.length > MIN_COUNT
  63. util.log "docs #{origDocs} packs #{packs.length}"
  64. if packs.length
  65. async.eachSeries packs, insertPack, (err, result) ->
  66. if err?
  67. console.log doc_id, err
  68. else
  69. util.log "done writing packs"
  70. callback err, result
  71. else
  72. util.log "no packs to write"
  73. callback null, null
  74. readFile = (file, callback) ->
  75. ids = []
  76. lineReader.eachLine file, (line) ->
  77. result = line.match(/[0-9a-f]{24}/)
  78. if result?
  79. ids.push result[0]
  80. .then () ->
  81. callback(null, ids)
  82. todoFile = process.argv[2]
  83. doneFile = process.argv[3]
  84. fs.appendFileSync doneFile, '# starting pack run at ' + new Date() + '\n'
  85. shutdownRequested = false
  86. process.on 'SIGINT', () ->
  87. util.log "Gracefully shutting down from SIGINT"
  88. shutdownRequested = true
  89. readFile todoFile, (err, todo) ->
  90. readFile doneFile, (err, done) ->
  91. pending = _.difference todo, done
  92. async.eachSeries pending, (doc_id, callback) ->
  93. packDocHistory doc_id, (err, result) ->
  94. if err?
  95. return callback(err)
  96. else
  97. fs.appendFileSync doneFile, doc_id + '\n'
  98. if shutdownRequested
  99. return callback('shutdown')
  100. setTimeout () ->
  101. callback(err, result)
  102. , 1000
  103. , (err, results) ->
  104. if err?
  105. console.log 'error:', err
  106. util.log 'closing db'
  107. db.close()