Просмотр исходного кода

[web] Avoid double indexing of client side referencing (#28235)

* [web] Move chat client id to shared module

* [web] Avoid double indexing of client references

GitOrigin-RevId: 993930e66fdc9952649e3e8d345c70dd37516121
Mathias Jakobsen 11 месяцев назад
Родитель
Сommit
442c1952ab

+ 3 - 1
services/web/app/src/Features/LinkedFiles/LinkedFilesController.mjs

@@ -84,6 +84,7 @@ async function createLinkedFile(req, res, next) {
 
 
 async function refreshLinkedFile(req, res, next) {
 async function refreshLinkedFile(req, res, next) {
   const { project_id: projectId, file_id: fileId } = req.params
   const { project_id: projectId, file_id: fileId } = req.params
+  const { clientId } = req.body
   const userId = SessionManager.getLoggedInUserId(req.session)
   const userId = SessionManager.getLoggedInUserId(req.session)
 
 
   const { file, parentFolder } = await LinkedFilesHandler.promises.getFileById(
   const { file, parentFolder } = await LinkedFilesHandler.promises.getFileById(
@@ -138,7 +139,8 @@ async function refreshLinkedFile(req, res, next) {
       projectId,
       projectId,
       'references:keys:updated',
       'references:keys:updated',
       data.keys,
       data.keys,
-      true
+      true,
+      clientId
     )
     )
     res.json({ new_file_id: newFileId })
     res.json({ new_file_id: newFileId })
   } else {
   } else {

+ 14 - 4
services/web/app/src/Features/References/ReferencesController.mjs

@@ -19,7 +19,7 @@ let ReferencesController
 export default ReferencesController = {
 export default ReferencesController = {
   indexAll(req, res, next) {
   indexAll(req, res, next) {
     const projectId = req.params.Project_id
     const projectId = req.params.Project_id
-    const { shouldBroadcast } = req.body
+    const { shouldBroadcast, clientId } = req.body
     return ReferencesHandler.indexAll(projectId, function (error, data) {
     return ReferencesHandler.indexAll(projectId, function (error, data) {
       if (error) {
       if (error) {
         OError.tag(error, 'failed to index references', { projectId })
         OError.tag(error, 'failed to index references', { projectId })
@@ -31,12 +31,21 @@ export default ReferencesController = {
         projectId,
         projectId,
         shouldBroadcast,
         shouldBroadcast,
         true,
         true,
-        data
+        data,
+        clientId
       )
       )
     })
     })
   },
   },
 
 
-  _handleIndexResponse(req, res, projectId, shouldBroadcast, isAllDocs, data) {
+  _handleIndexResponse(
+    req,
+    res,
+    projectId,
+    shouldBroadcast,
+    isAllDocs,
+    data,
+    clientId
+  ) {
     if (data == null || data.keys == null) {
     if (data == null || data.keys == null) {
       return res.json({ projectId, keys: [] })
       return res.json({ projectId, keys: [] })
     }
     }
@@ -45,7 +54,8 @@ export default ReferencesController = {
         projectId,
         projectId,
         'references:keys:updated',
         'references:keys:updated',
         data.keys,
         data.keys,
-        isAllDocs
+        isAllDocs,
+        clientId
       )
       )
     }
     }
     return res.json(data)
     return res.json(data)

+ 2 - 8
services/web/frontend/js/features/chat/context/chat-context.tsx

@@ -8,8 +8,7 @@ import {
   useRef,
   useRef,
   FC,
   FC,
 } from 'react'
 } from 'react'
-import { v4 as uuid } from 'uuid'
-
+import clientIdGenerator from '@/utils/client-id'
 import { useUserContext } from '../../../shared/context/user-context'
 import { useUserContext } from '../../../shared/context/user-context'
 import { useProjectContext } from '../../../shared/context/project-context'
 import { useProjectContext } from '../../../shared/context/project-context'
 import { getJSON, postJSON } from '../../../infrastructure/fetch-json'
 import { getJSON, postJSON } from '../../../infrastructure/fetch-json'
@@ -78,11 +77,6 @@ type Action =
       error: any
       error: any
     }
     }
 
 
-// Wrap uuid in an object method so that it can be stubbed
-export const chatClientIdGenerator = {
-  generate: () => uuid(),
-}
-
 let nextChatMessageId = 1
 let nextChatMessageId = 1
 
 
 function generateChatMessageId() {
 function generateChatMessageId() {
@@ -201,7 +195,7 @@ export const ChatProvider: FC<React.PropsWithChildren> = ({ children }) => {
 
 
   const clientId = useRef<string>()
   const clientId = useRef<string>()
   if (clientId.current === undefined) {
   if (clientId.current === undefined) {
-    clientId.current = chatClientIdGenerator.generate()
+    clientId.current = clientIdGenerator.get()
   }
   }
   const user = useUserContext()
   const user = useUserContext()
   const { projectId } = useProjectContext()
   const { projectId } = useProjectContext()

+ 2 - 0
services/web/frontend/js/features/file-view/components/file-view-refresh-button.tsx

@@ -14,6 +14,7 @@ import importOverleafModules from '../../../../macros/import-overleaf-module.mac
 import OLButton from '@/shared/components/ol/ol-button'
 import OLButton from '@/shared/components/ol/ol-button'
 import { sendMB } from '@/infrastructure/event-tracking'
 import { sendMB } from '@/infrastructure/event-tracking'
 import useIsMounted from '@/shared/hooks/use-is-mounted'
 import useIsMounted from '@/shared/hooks/use-is-mounted'
+import clientId from '@/utils/client-id'
 
 
 type FileViewRefreshButtonProps = {
 type FileViewRefreshButtonProps = {
   setRefreshError: Dispatch<SetStateAction<Nullable<string>>>
   setRefreshError: Dispatch<SetStateAction<Nullable<string>>>
@@ -42,6 +43,7 @@ export default function FileViewRefreshButton({
       window.expectingLinkedFileRefreshedSocketFor = file.name
       window.expectingLinkedFileRefreshedSocketFor = file.name
       const body = {
       const body = {
         shouldReindexReferences: isTPR || /\.bib$/.test(file.name),
         shouldReindexReferences: isTPR || /\.bib$/.test(file.name),
+        clientId: clientId.get(),
       }
       }
       postJSON(`/project/${projectId}/linked_file/${file.id}/refresh`, {
       postJSON(`/project/${projectId}/linked_file/${file.id}/refresh`, {
         body,
         body,

+ 7 - 2
services/web/frontend/js/features/ide-react/context/references-context.tsx

@@ -24,6 +24,7 @@ import { debugConsole } from '@/utils/debugging'
 import { useFeatureFlag } from '@/shared/context/split-test-context'
 import { useFeatureFlag } from '@/shared/context/split-test-context'
 import type { ReferenceIndexer } from '../references/reference-indexer'
 import type { ReferenceIndexer } from '../references/reference-indexer'
 import { AdvancedReferenceSearchResult } from '@/features/ide-react/references/types'
 import { AdvancedReferenceSearchResult } from '@/features/ide-react/references/types'
+import clientId from '@/utils/client-id'
 
 
 export const ReferencesContext = createContext<
 export const ReferencesContext = createContext<
   | {
   | {
@@ -105,7 +106,7 @@ export const ReferencesProvider: FC<React.PropsWithChildren> = ({
       if (shouldBroadcast) {
       if (shouldBroadcast) {
         // Inform other clients about change in keys
         // Inform other clients about change in keys
         await postJSON(`/project/${projectId}/references/indexAll`, {
         await postJSON(`/project/${projectId}/references/indexAll`, {
-          body: { shouldBroadcast: true },
+          body: { shouldBroadcast: true, clientId: clientId.get() },
         }).catch(error => {
         }).catch(error => {
           // allow the request to fail
           // allow the request to fail
           debugConsole.error(error)
           debugConsole.error(error)
@@ -176,8 +177,12 @@ export const ReferencesProvider: FC<React.PropsWithChildren> = ({
     const handleProjectJoined = () => {
     const handleProjectJoined = () => {
       // We only need to grab the references when the editor first loads,
       // We only need to grab the references when the editor first loads,
       // not on every reconnect
       // not on every reconnect
-      socket.on('references:keys:updated', (keys, allDocs) => {
+      socket.on('references:keys:updated', (keys, allDocs, refresherId) => {
         if (clientSideReferences) {
         if (clientSideReferences) {
+          if (refresherId === clientId.get()) {
+            // We asked for this broadcast, so we must have already done the indexing
+            return
+          }
           indexAllReferences(false)
           indexAllReferences(false)
         } else {
         } else {
           setReferenceKeys(oldDocs =>
           setReferenceKeys(oldDocs =>

+ 7 - 0
services/web/frontend/js/utils/client-id.ts

@@ -0,0 +1,7 @@
+import { v4 as uuid } from 'uuid'
+
+// Wrap uuid in an object method so that it can be stubbed
+const clientId = uuid()
+export default {
+  get: () => clientId,
+}

+ 2 - 2
services/web/test/frontend/features/chat/context/chat-context.test.tsx

@@ -5,9 +5,9 @@ import { renderHook, act, waitFor } from '@testing-library/react'
 import { expect } from 'chai'
 import { expect } from 'chai'
 import sinon from 'sinon'
 import sinon from 'sinon'
 import fetchMock from 'fetch-mock'
 import fetchMock from 'fetch-mock'
+import clientIdGenerator from '@/utils/client-id'
 import {
 import {
   useChatContext,
   useChatContext,
-  chatClientIdGenerator,
   ServerMessageEntry,
   ServerMessageEntry,
 } from '@/features/chat/context/chat-context'
 } from '@/features/chat/context/chat-context'
 import { stubMathJax, tearDownMathJaxStubs } from '../components/stubs'
 import { stubMathJax, tearDownMathJaxStubs } from '../components/stubs'
@@ -35,7 +35,7 @@ describe('ChatContext', function () {
     window.metaAttributesCache.set('ol-user', user)
     window.metaAttributesCache.set('ol-user', user)
     window.metaAttributesCache.set('ol-preventCompileOnLoad', true)
     window.metaAttributesCache.set('ol-preventCompileOnLoad', true)
 
 
-    this.stub = sinon.stub(chatClientIdGenerator, 'generate').returns(uuidValue)
+    this.stub = sinon.stub(clientIdGenerator, 'get').returns(uuidValue)
   })
   })
 
 
   afterEach(function () {
   afterEach(function () {