2019-09-25 11:42:49 -04:00
|
|
|
RedisManager = require "./RedisManager"
|
|
|
|
ProjectManager = require "./ProjectManager"
|
|
|
|
logger = require "logger-sharelatex"
|
|
|
|
metrics = require "./Metrics"
|
|
|
|
async = require "async"
|
|
|
|
|
2019-09-26 05:14:49 -04:00
|
|
|
# Maintain a sorted set of project flushAndDelete requests, ordered by timestamp
|
|
|
|
# (ZADD), and process them from oldest to newest. A flushAndDelete request comes
|
|
|
|
# from real-time and is triggered when a user leaves a project.
|
|
|
|
#
|
|
|
|
# The aim is to remove the project from redis 5 minutes after the last request
|
|
|
|
# if there has been no activity (document updates) in that time. If there is
|
|
|
|
# activity we can expect a further flushAndDelete request when the editing user
|
|
|
|
# leaves the project.
|
|
|
|
#
|
|
|
|
# If a new flushAndDelete request comes in while an existing request is already
|
|
|
|
# in the queue we update the timestamp as we can postpone flushing further.
|
|
|
|
#
|
|
|
|
# Documents are processed by checking the queue, seeing if the first entry is
|
|
|
|
# older than 5 minutes, and popping it from the queue in that case.
|
|
|
|
|
2019-09-25 11:42:49 -04:00
|
|
|
module.exports = DeleteQueueManager =
|
|
|
|
flushAndDeleteOldProjects: (options, callback) ->
|
|
|
|
startTime = Date.now()
|
2019-09-26 10:05:38 -04:00
|
|
|
cutoffTime = startTime - options.min_delete_age
|
2019-09-25 11:42:49 -04:00
|
|
|
count = 0
|
|
|
|
|
|
|
|
flushProjectIfNotModified = (project_id, flushTimestamp, cb) ->
|
|
|
|
ProjectManager.getProjectDocsTimestamps project_id, (err, timestamps) ->
|
|
|
|
return callback(err) if err?
|
|
|
|
if !timestamps?
|
|
|
|
logger.log {project_id}, "skipping flush of queued project - no timestamps"
|
|
|
|
return cb()
|
|
|
|
# are any of the timestamps newer than the time the project was flushed?
|
2019-09-26 09:59:03 -04:00
|
|
|
for timestamp in timestamps when timestamp > flushTimestamp
|
2019-09-25 11:42:49 -04:00
|
|
|
metrics.inc "queued-delete-skipped"
|
|
|
|
logger.debug {project_id, timestamps, flushTimestamp}, "found newer timestamp, will skip delete"
|
|
|
|
return cb()
|
|
|
|
logger.log {project_id, flushTimestamp}, "flushing queued project"
|
|
|
|
ProjectManager.flushAndDeleteProjectWithLocks project_id, {skip_history_flush: true}, (err) ->
|
2019-09-26 10:46:45 -04:00
|
|
|
if err?
|
|
|
|
logger.err {project_id, err}, "error flushing queued project"
|
2019-09-25 11:42:49 -04:00
|
|
|
metrics.inc "queued-delete-completed"
|
|
|
|
return cb(null, true)
|
|
|
|
|
|
|
|
flushNextProject = () ->
|
|
|
|
now = Date.now()
|
|
|
|
if now - startTime > options.timeout
|
|
|
|
logger.log "hit time limit on flushing old projects"
|
2019-09-26 10:46:14 -04:00
|
|
|
return callback(null, count)
|
2019-09-25 11:42:49 -04:00
|
|
|
if count > options.limit
|
|
|
|
logger.log "hit count limit on flushing old projects"
|
2019-09-26 10:46:14 -04:00
|
|
|
return callback(null, count)
|
2019-09-25 12:04:36 -04:00
|
|
|
RedisManager.getNextProjectToFlushAndDelete cutoffTime, (err, project_id, flushTimestamp, queueLength) ->
|
2019-09-25 11:42:49 -04:00
|
|
|
return callback(err) if err?
|
2019-09-26 10:46:14 -04:00
|
|
|
return callback(null, count) if !project_id?
|
2019-09-25 12:04:36 -04:00
|
|
|
logger.log {project_id, queueLength: queueLength}, "flushing queued project"
|
|
|
|
metrics.globalGauge "queued-flush-backlog", queueLength
|
2019-09-25 11:42:49 -04:00
|
|
|
flushProjectIfNotModified project_id, flushTimestamp, (err, flushed) ->
|
|
|
|
count++ if flushed
|
|
|
|
flushNextProject()
|
|
|
|
|
|
|
|
flushNextProject()
|