فهرست منبع

[web] shard compile server persistence by compileBackendClass (#29156)

* [web] shard compile server persistence by compileBackendClass

* [web] make prettier happy

GitOrigin-RevId: d7cf8bde07e7110053d9d7531c007111af5cc46b
Jakob Ackermann 10 ماه پیش
والد
کامیت
706971ef41

+ 40 - 6
services/web/app/src/Features/Compile/ClsiCookieManager.js

@@ -20,7 +20,21 @@ if (Settings.redis.clsi_cookie_secondary != null) {
 }
 
 const ClsiCookieManagerFactory = function (backendGroup) {
-  function buildKey(projectId, userId) {
+  /**
+   * @param {string} projectId
+   * @param {string | null} userId
+   * @param {string} compileBackendClass
+   * @return {string}
+   */
+  function buildKey(projectId, userId, compileBackendClass) {
+    if (backendGroup != null) {
+      return `clsiserver:${backendGroup}:${compileBackendClass}:${projectId}:${userId}`
+    } else {
+      return `clsiserver:${compileBackendClass}:${projectId}:${userId}`
+    }
+  }
+
+  function buildOldKey(projectId, userId) {
     if (backendGroup != null) {
       return `clsiserver:${backendGroup}:${projectId}:${userId}`
     } else {
@@ -37,7 +51,14 @@ const ClsiCookieManagerFactory = function (backendGroup) {
     if (!clsiCookiesEnabled) {
       return
     }
-    const serverId = await rclient.get(buildKey(projectId, userId))
+    let serverId = await rclient.get(
+      buildKey(projectId, userId, compileBackendClass)
+    )
+    if (!serverId) {
+      // Fallback to the old key.
+      // TODO(das7pad): remove this in 24h.
+      serverId = await rclient.get(buildOldKey(projectId, userId))
+    }
 
     if (!serverId) {
       return await cookieManager.promises._populateServerIdViaRequest(
@@ -157,7 +178,7 @@ const ClsiCookieManagerFactory = function (backendGroup) {
     if (serverId == null) {
       // We don't get a cookie back if it hasn't changed
       return await rclient.expire(
-        buildKey(projectId, userId),
+        buildKey(projectId, userId, compileBackendClass),
         _getTTLInSeconds(previous)
       )
     }
@@ -176,15 +197,28 @@ const ClsiCookieManagerFactory = function (backendGroup) {
         rclientSecondary,
         projectId,
         userId,
+        compileBackendClass,
         serverId
       ).catch(() => {})
     }
-    await _setServerIdInRedis(rclient, projectId, userId, serverId)
+    await _setServerIdInRedis(
+      rclient,
+      projectId,
+      userId,
+      compileBackendClass,
+      serverId
+    )
   }
 
-  async function _setServerIdInRedis(rclient, projectId, userId, serverId) {
+  async function _setServerIdInRedis(
+    rclient,
+    projectId,
+    userId,
+    compileBackendClass,
+    serverId
+  ) {
     await rclient.setex(
-      buildKey(projectId, userId),
+      buildKey(projectId, userId, compileBackendClass),
       _getTTLInSeconds(serverId),
       serverId
     )

+ 52 - 25
services/web/app/src/Features/Compile/ClsiManager.mjs

@@ -38,13 +38,53 @@ const CLSI_COOKIES_ENABLED = (Settings.clsiCookie?.key ?? '') !== ''
 // The timeout in services/clsi/app.js is 10 minutes, so we'll be on the safe side with 12 minutes
 const COMPILE_REQUEST_TIMEOUT_MS = 12 * 60 * 1000
 
-async function clearClsiServerId(projectId, userId) {
-  const jobs = [ClsiCookieManager.promises.clearServerId(projectId, userId)]
+function getNewCompileBackendClass(projectId, compileBackendClass) {
+  // Sample x% of projects to move up one bracket.
+  if (
+    SplitTestHandler.getPercentile(projectId, 'double-compile', 'release') >=
+    Settings.apis.clsi_new.sample
+  ) {
+    return null
+  }
+
+  switch (compileBackendClass) {
+    case 'n2d':
+      return 'n4'
+    case 'c2d':
+      return 'n4'
+    default:
+      throw new Error('unknown ?compileBackendClass')
+  }
+}
+
+/**
+ * @param {string} projectId
+ * @param {string | null} userId
+ * @param {string} compileBackendClass
+ * @return {Promise<void>}
+ */
+async function clearClsiServerId(projectId, userId, compileBackendClass) {
+  const jobs = [
+    ClsiCookieManager.promises.clearServerId(
+      projectId,
+      userId,
+      compileBackendClass
+    ),
+  ]
   if (Settings.apis.clsi_new?.url) {
     // Mirror resetting the clsiserverid in both backends.
-    jobs.push(
-      NewBackendCloudClsiCookieManager.promises.clearServerId(projectId, userId)
+    const newCompileBackendClass = getNewCompileBackendClass(
+      projectId,
+      compileBackendClass
     )
+    if (newCompileBackendClass) {
+      jobs.push(
+        NewBackendCloudClsiCookieManager.promises.clearServerId(
+          projectId,
+          userId
+        )
+      )
+    }
   }
   await Promise.all(jobs)
 }
@@ -176,14 +216,14 @@ async function deleteAuxFiles(projectId, userId, options, clsiserverid) {
       await DocumentUpdaterHandler.promises.clearProjectState(projectId)
     } finally {
       // always clear the clsi server id, even if prior actions failed
-      await clearClsiServerId(projectId, userId)
+      await clearClsiServerId(projectId, userId, compileBackendClass)
     }
   }
 }
 
 async function _sendBuiltRequest(projectId, userId, req, options) {
   if (options.forceNewClsiServer) {
-    await clearClsiServerId(projectId, userId)
+    await clearClsiServerId(projectId, userId, options.compileBackendClass)
   }
   const validationProblems = ClsiFormatChecker.checkRecoursesForProblems(
     req.compile?.resources
@@ -436,28 +476,15 @@ async function _makeNewBackendRequest(
   if (Settings.apis.clsi_new?.url == null) {
     return null
   }
-  url = new URL(
-    url.toString().replace(Settings.apis.clsi.url, Settings.apis.clsi_new.url)
+  const newCompileBackendClass = getNewCompileBackendClass(
+    projectId,
+    currentCompileBackendClass
   )
+  if (!newCompileBackendClass) return null
 
-  // Sample x% of projects to move up one bracket.
-  if (
-    SplitTestHandler.getPercentile(projectId, 'double-compile', 'release') >=
-    Settings.apis.clsi_new.sample
+  url = new URL(
+    url.toString().replace(Settings.apis.clsi.url, Settings.apis.clsi_new.url)
   )
-    return null
-
-  let newCompileBackendClass
-  switch (currentCompileBackendClass) {
-    case 'n2d':
-      newCompileBackendClass = 'n4'
-      break
-    case 'c2d':
-      newCompileBackendClass = 'n4'
-      break
-    default:
-      throw new Error('unknown ?compileBackendClass')
-  }
   url.searchParams.set('compileBackendClass', newCompileBackendClass)
 
   const clsiServerId =

+ 25 - 3
services/web/test/unit/src/Compile/ClsiCookieManagerTests.js

@@ -54,6 +54,28 @@ describe('ClsiCookieManager', function () {
         '',
         'n2d'
       )
+      this.redis.get
+        .calledWith(`clsiserver:n2d:${this.project_id}:${this.user_id}`)
+        .should.equal(true)
+      serverId.should.equal('clsi-7')
+    })
+
+    it('should fallback to old key', async function () {
+      this.redis.get
+        .withArgs(`clsiserver:n2d:${this.project_id}:${this.user_id}`)
+        .resolves(null)
+      this.redis.get
+        .withArgs(`clsiserver:${this.project_id}:${this.user_id}`)
+        .resolves('clsi-7')
+      const serverId = await this.ClsiCookieManager.promises.getServerId(
+        this.project_id,
+        this.user_id,
+        '',
+        'n2d'
+      )
+      this.redis.get
+        .calledWith(`clsiserver:n2d:${this.project_id}:${this.user_id}`)
+        .should.equal(true)
       this.redis.get
         .calledWith(`clsiserver:${this.project_id}:${this.user_id}`)
         .should.equal(true)
@@ -177,7 +199,7 @@ describe('ClsiCookieManager', function () {
         null
       )
       this.redis.setex.should.have.been.calledWith(
-        `clsiserver:${this.project_id}:${this.user_id}`,
+        `clsiserver:n2d:${this.project_id}:${this.user_id}`,
         this.settings.clsiCookie.ttlInSeconds,
         this.clsiServerId
       )
@@ -194,7 +216,7 @@ describe('ClsiCookieManager', function () {
         null
       )
       expect(this.redis.setex).to.have.been.calledWith(
-        `clsiserver:${this.project_id}:${this.user_id}`,
+        `clsiserver:n2d:${this.project_id}:${this.user_id}`,
         this.settings.clsiCookie.ttlInSecondsRegular,
         this.clsiServerId
       )
@@ -245,7 +267,7 @@ describe('ClsiCookieManager', function () {
         null
       )
       this.redis_secondary.setex.should.have.been.calledWith(
-        `clsiserver:${this.project_id}:${this.user_id}`,
+        `clsiserver:n2d:${this.project_id}:${this.user_id}`,
         this.settings.clsiCookie.ttlInSeconds,
         this.clsiServerId
       )