MongoManager.coffee 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. {db, ObjectId} = require "./mongojs"
  2. PackManager = require "./PackManager"
  3. async = require "async"
  4. module.exports = MongoManager =
  5. getLastCompressedUpdate: (doc_id, callback = (error, update) ->) ->
  6. db.docHistory
  7. .find(doc_id: ObjectId(doc_id.toString()))
  8. .sort( v: -1 )
  9. .limit(1)
  10. .toArray (error, compressedUpdates) ->
  11. return callback(error) if error?
  12. if compressedUpdates[0]?.pack?
  13. # cannot pop from a pack, throw error
  14. error = new Error("last compressed update is a pack")
  15. return callback error, null
  16. return callback null, compressedUpdates[0] or null
  17. deleteCompressedUpdate: (id, callback = (error) ->) ->
  18. db.docHistory.remove({ _id: ObjectId(id.toString()) }, callback)
  19. popLastCompressedUpdate: (doc_id, callback = (error, update, version) ->) ->
  20. # under normal use we pass back the last update as
  21. # callback(null,update).
  22. #
  23. # when we have an existing last update but want to force a new one
  24. # to start, we pass it back as callback(null,null,version), just
  25. # giving the version so we can check consistency.
  26. MongoManager.getLastCompressedUpdate doc_id, (error, update) ->
  27. return callback(error) if error?
  28. if update?
  29. if update.inS3?
  30. # we want to force a new update, but ensure that it is
  31. # consistent with the version of the existing one in S3
  32. return callback null, null, update.v
  33. else
  34. MongoManager.deleteCompressedUpdate update._id, (error) ->
  35. return callback(error) if error?
  36. callback null, update
  37. else
  38. callback null, null
  39. insertCompressedUpdates: (project_id, doc_id, updates, temporary, callback = (error) ->) ->
  40. jobs = []
  41. for update in updates
  42. do (update) ->
  43. jobs.push (callback) -> MongoManager.insertCompressedUpdate project_id, doc_id, update, temporary, callback
  44. async.series jobs, (err, results) ->
  45. if not temporary
  46. # keep track of updates to be packed
  47. db.docHistoryStats.update {doc_id:ObjectId(doc_id)}, {
  48. $inc:{update_count:updates.length},
  49. $currentDate:{last_update:true}
  50. }, {upsert:true}, () ->
  51. callback(err,results)
  52. else
  53. callback(err,results)
  54. insertCompressedUpdate: (project_id, doc_id, update, temporary, callback = (error) ->) ->
  55. update = {
  56. doc_id: ObjectId(doc_id.toString())
  57. project_id: ObjectId(project_id.toString())
  58. op: update.op
  59. meta: update.meta
  60. v: update.v
  61. }
  62. if temporary
  63. seconds = 1000
  64. minutes = 60 * seconds
  65. hours = 60 * minutes
  66. days = 24 * hours
  67. update.expiresAt = new Date(Date.now() + 7 * days)
  68. # may need to roll over a pack here if we are inserting packs
  69. db.docHistory.insert update, callback
  70. getDocUpdates:(doc_id, options = {}, callback = (error, updates) ->) ->
  71. query =
  72. doc_id: ObjectId(doc_id.toString())
  73. if options.from?
  74. query["v"] ||= {}
  75. query["v"]["$gte"] = options.from
  76. if options.to?
  77. query["v"] ||= {}
  78. query["v"]["$lte"] = options.to
  79. PackManager.findDocResults(db.docHistory, query, options.limit, callback)
  80. getProjectUpdates: (project_id, options = {}, callback = (error, updates) ->) ->
  81. query =
  82. project_id: ObjectId(project_id.toString())
  83. if options.before?
  84. query["meta.end_ts"] = { $lt: options.before }
  85. PackManager.findProjectResults(db.docHistory, query, options.limit, callback)
  86. backportProjectId: (project_id, doc_id, callback = (error) ->) ->
  87. db.docHistory.update {
  88. doc_id: ObjectId(doc_id.toString())
  89. project_id: { $exists: false }
  90. }, {
  91. $set: { project_id: ObjectId(project_id.toString()) }
  92. }, {
  93. multi: true
  94. }, callback
  95. getProjectMetaData: (project_id, callback = (error, metadata) ->) ->
  96. db.projectHistoryMetaData.find {
  97. project_id: ObjectId(project_id.toString())
  98. }, (error, results) ->
  99. return callback(error) if error?
  100. callback null, results[0]
  101. setProjectMetaData: (project_id, metadata, callback = (error) ->) ->
  102. db.projectHistoryMetaData.update {
  103. project_id: ObjectId(project_id)
  104. }, {
  105. $set: metadata
  106. }, {
  107. upsert: true
  108. }, callback
  109. ensureIndices: () ->
  110. # For finding all updates that go into a diff for a doc
  111. db.docHistory.ensureIndex { doc_id: 1, v: 1 }, { background: true }
  112. # For finding all updates that affect a project
  113. db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1 }, { background: true }
  114. # For finding all packs that affect a project (use a sparse index so only packs are included)
  115. db.docHistory.ensureIndex { project_id: 1, "pack.0.meta.end_ts": 1, "meta.end_ts": 1}, { background: true, sparse: true }
  116. # For finding updates that don't yet have a project_id and need it inserting
  117. db.docHistory.ensureIndex { doc_id: 1, project_id: 1 }, { background: true }
  118. # For finding project meta-data
  119. db.projectHistoryMetaData.ensureIndex { project_id: 1 }, { background: true }
  120. # TTL index for auto deleting week old temporary ops
  121. db.docHistory.ensureIndex { expiresAt: 1 }, { expireAfterSeconds: 0, background: true }
  122. # For finding documents which need packing
  123. db.docHistoryStats.ensureIndex { doc_id: 1 }, { background: true }
  124. db.docHistoryStats.ensureIndex { updates: -1, doc_id: 1 }, { background: true }
  125. getDocChangesCount: (doc_id, callback)->
  126. db.docHistory.count { doc_id : ObjectId(doc_id.toString()), inS3 : { $exists : false }}, {}, callback
  127. getArchivedDocChanges: (doc_id, callback)->
  128. db.docHistory.count { doc_id: ObjectId(doc_id.toString()) , inS3: { $exists: true }}, {}, callback
  129. markDocHistoryAsArchiveInProgress: (doc_id, update, callback) ->
  130. db.docHistory.update { _id: update._id }, { $set : { inS3 : false } }, callback
  131. clearDocHistoryAsArchiveInProgress: (doc_id, update, callback) ->
  132. db.docHistory.update { _id: update._id }, { $unset : { inS3 : true } }, callback
  133. markDocHistoryAsArchived: (doc_id, update, callback)->
  134. db.docHistory.update { _id: update._id }, { $set : { inS3 : true } }, (error)->
  135. return callback(error) if error?
  136. db.docHistory.remove { doc_id : ObjectId(doc_id.toString()), inS3 : { $exists : false }, v: { $lt : update.v }, expiresAt: {$exists : false} }, (error)->
  137. return callback(error) if error?
  138. callback(error)
  139. markDocHistoryAsUnarchived: (doc_id, callback)->
  140. # note this removes any inS3 field, regardless of its value (true/false/null)
  141. db.docHistory.update { doc_id: ObjectId(doc_id.toString()) }, { $unset : { inS3 : true } }, { multi: true }, (error)->
  142. callback(error)