From afa485deb1a0734344b74ff8ca87400d34faf891 Mon Sep 17 00:00:00 2001 From: Alexander Onnikov Date: Thu, 7 Nov 2024 13:18:53 +0700 Subject: [PATCH] UBERF-8547 Do not update collaborative doc in platform when content not changed (#7115) Signed-off-by: Alexander Onnikov --- server/collaborator/src/extensions/storage.ts | 46 +++++++++++++------ .../src/rpc/methods/updateContent.ts | 2 +- server/collaborator/src/storage/platform.ts | 13 +++++- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/server/collaborator/src/extensions/storage.ts b/server/collaborator/src/extensions/storage.ts index d4da1cb9be..3a8cf2ba43 100644 --- a/server/collaborator/src/extensions/storage.ts +++ b/server/collaborator/src/extensions/storage.ts @@ -14,7 +14,7 @@ // import { DocumentId } from '@hcengineering/collaborator-client' -import { MeasureContext } from '@hcengineering/core' +import { type Markup, MeasureContext } from '@hcengineering/core' import { Document, Extension, @@ -37,19 +37,35 @@ export interface StorageConfiguration { transformer: Transformer } +type DocumentName = string + +type ConnectionId = string + +interface DocumentUpdates { + context: Context + collaborators: Set +} + export class StorageExtension implements Extension { private readonly configuration: StorageConfiguration - private readonly collaborators = new Map>() - private readonly markups = new Map>() + private readonly updates = new Map() + private readonly markups = new Map>() constructor (configuration: StorageConfiguration) { this.configuration = configuration } async onChange ({ context, documentName }: withContext): Promise { - const collaborators = this.collaborators.get(documentName) ?? new Set() - collaborators.add(context.connectionId) - this.collaborators.set(documentName, collaborators) + const { connectionId } = context + + const updates = this.updates.get(documentName) + if (updates === undefined) { + const collaborators = new Set([connectionId]) + this.updates.set(documentName, { context, collaborators }) + } else { + updates.context = context + updates.collaborators.add(connectionId) + } } async onLoadDocument ({ context, documentName }: withContext): Promise { @@ -59,7 +75,7 @@ export class StorageExtension implements Extension { return await this.loadDocument(documentName, context) } - async afterLoadDocument ({ context, documentName, document }: withContext): Promise { + async afterLoadDocument ({ documentName, document }: withContext): Promise { // remember the markup for the document this.markups.set(documentName, this.configuration.transformer.fromYdoc(document)) } @@ -70,14 +86,14 @@ export class StorageExtension implements Extension { ctx.info('store document', { documentName, connectionId }) - const collaborators = this.collaborators.get(documentName) - if (collaborators === undefined || collaborators.size === 0) { + const updates = this.updates.get(documentName) + if (updates === undefined || updates.collaborators.size === 0) { ctx.info('no changes for document', { documentName, connectionId }) return } - this.collaborators.delete(documentName) - await this.storeDocument(documentName, document, context) + this.updates.delete(documentName) + await this.storeDocument(documentName, document, updates.context) } async onConnect ({ context, documentName, instance }: withContext): Promise { @@ -93,19 +109,19 @@ export class StorageExtension implements Extension { const params = { documentName, connectionId, connections: document.getConnectionsCount() } ctx.info('disconnect from document', params) - const collaborators = this.collaborators.get(documentName) - if (collaborators === undefined || !collaborators.has(connectionId)) { + const updates = this.updates.get(documentName) + if (updates === undefined || updates.collaborators.size === 0) { ctx.info('no changes for document', { documentName, connectionId }) return } - this.collaborators.delete(documentName) + this.updates.delete(documentName) await this.storeDocument(documentName, document, context) } async afterUnloadDocument ({ documentName }: afterUnloadDocumentPayload): Promise { this.configuration.ctx.info('unload document', { documentName }) - this.collaborators.delete(documentName) + this.updates.delete(documentName) this.markups.delete(documentName) } diff --git a/server/collaborator/src/rpc/methods/updateContent.ts b/server/collaborator/src/rpc/methods/updateContent.ts index ba4d7fe189..6544a22474 100644 --- a/server/collaborator/src/rpc/methods/updateContent.ts +++ b/server/collaborator/src/rpc/methods/updateContent.ts @@ -53,7 +53,7 @@ export async function updateContent ( fragment.delete(0, fragment.length) applyUpdate(document, update) }) - }) + }, connection) }) }) } finally { diff --git a/server/collaborator/src/storage/platform.ts b/server/collaborator/src/storage/platform.ts index 49ddea1973..bdc417cca5 100644 --- a/server/collaborator/src/storage/platform.ts +++ b/server/collaborator/src/storage/platform.ts @@ -34,6 +34,7 @@ import { Doc as YDoc } from 'yjs' import { Context } from '../context' import { CollabStorageAdapter } from './adapter' +import { areEqualMarkups } from '@hcengineering/text' export class PlatformStorageAdapter implements CollabStorageAdapter { constructor (private readonly storage: StorageAdapter) {} @@ -156,6 +157,14 @@ export class PlatformStorageAdapter implements CollabStorageAdapter { ): Promise { const { objectClass, objectId, objectAttr } = parsePlatformDocumentId(platformDocumentId) + const currMarkup = markup.curr[objectAttr] + const prevMarkup = markup.prev[objectAttr] + + if (areEqualMarkups(currMarkup, prevMarkup)) { + ctx.info('markup not changed, skip platform update', { documentName }) + return + } + const attribute = client.getHierarchy().findAttribute(objectClass, objectAttr) if (attribute === undefined) { ctx.warn('attribute not found', { documentName, objectClass, objectAttr }) @@ -192,8 +201,8 @@ export class PlatformStorageAdapter implements CollabStorageAdapter { attributeUpdates: { attrKey: objectAttr, attrClass: core.class.TypeMarkup, - prevValue: markup.prev[objectAttr], - set: [markup.curr[objectAttr]], + prevValue: prevMarkup, + set: [currMarkup], added: [], removed: [], isMixin: hierarchy.isMixin(objectClass)