Explorar o código

Merge pull request #26477 from overleaf/em-redis-buffer-resync

Handle invalid content hash when persisting changes

GitOrigin-RevId: 5259190396c8c261cad1abcd5de66314c1e871fb
Eric Mc Sween hai 1 ano
pai
achega
f68bf5a69f

+ 1 - 0
services/history-v1/api/controllers/project_import.js

@@ -163,6 +163,7 @@ async function flushChanges(req, res, next) {
     maxChanges: 0,
     minChangeTimestamp: farFuture,
     maxChangeTimestamp: farFuture,
+    autoResync: true,
   }
   try {
     await persistBuffer(projectId, limits)

+ 4 - 0
services/history-v1/config/custom-environment-variables.json

@@ -104,5 +104,9 @@
       "password": "REDIS_PASSWORD",
       "port": "REDIS_PORT"
     }
+  },
+  "projectHistory": {
+    "host": "PROJECT_HISTORY_HOST",
+    "port": "PROJECT_HISTORY_PORT"
   }
 }

+ 4 - 1
services/history-v1/config/default.json

@@ -39,5 +39,8 @@
   "databasePoolMin": "2",
   "databasePoolMax": "10",
   "httpsOnly": "false",
-  "httpRequestTimeout": "300000"
+  "httpRequestTimeout": "300000",
+  "projectHistory": {
+    "port": "3054"
+  }
 }

+ 19 - 0
services/history-v1/storage/lib/persist_buffer.js

@@ -9,6 +9,7 @@ const chunkStore = require('./chunk_store')
 const { BlobStore } = require('./blob_store')
 const BatchBlobStore = require('./batch_blob_store')
 const persistChanges = require('./persist_changes')
+const resyncProject = require('./resync_project')
 const redisBackend = require('./chunk_store/redis')
 
 /**
@@ -171,6 +172,24 @@ async function persistBuffer(projectId, limits) {
     'updated persisted version in Redis'
   )
 
+  // 7. Resync the project if content hash validation failed
+  if (limits.autoResync && persistResult.resyncNeeded) {
+    if (
+      changesToPersist.some(
+        change => change.getOrigin()?.getKind() === 'history-resync'
+      )
+    ) {
+      // To avoid an infinite loop, do not resync if the current batch of
+      // changes contains a history resync.
+      logger.warn(
+        { projectId },
+        'content hash validation failed while persisting a history resync, skipping additional resync'
+      )
+    } else {
+      await resyncProject(projectId)
+    }
+  }
+
   logger.debug(
     { projectId, finalPersistedVersion: newEndVersion },
     'persistBuffer operation completed successfully'

+ 14 - 0
services/history-v1/storage/lib/resync_project.js

@@ -0,0 +1,14 @@
+// @ts-check
+
+const config = require('config')
+const { fetchNothing } = require('@overleaf/fetch-utils')
+
+const PROJECT_HISTORY_URL = `http://${config.projectHistory.host}:${config.projectHistory.port}`
+
+async function resyncProject(projectId) {
+  await fetchNothing(`${PROJECT_HISTORY_URL}/project/${projectId}/resync`, {
+    method: 'POST',
+  })
+}
+
+module.exports = resyncProject

+ 1 - 0
services/history-v1/storage/scripts/persist_redis_chunks.mjs

@@ -45,6 +45,7 @@ async function persistProjectAction(projectId) {
     maxChanges: 0,
     minChangeTimestamp: farFuture,
     maxChangeTimestamp: farFuture,
+    autoResync: true,
   }
   await persistBuffer(projectId, limits)
   if (job && job.close) {