fix(workspace): should avoid sending providers' update back (#3384)

This commit is contained in:
liuyi
2023-07-26 17:47:24 +08:00
committed by GitHub
parent 2c249781a2
commit 6bafa83cef
4 changed files with 33 additions and 11 deletions

View File

@@ -53,7 +53,7 @@ export const createSQLiteProvider: DocProviderCreator = (
passive: true, passive: true,
connect: () => { connect: () => {
datasource = createDatasource(id); datasource = createDatasource(id);
provider = createLazyProvider(rootDoc, datasource); provider = createLazyProvider(rootDoc, datasource, { origin: 'sqlite' });
provider.connect(); provider.connect();
connected = true; connected = true;
}, },

View File

@@ -114,7 +114,7 @@ export const createIndexedDBProvider = (
return { return {
connect: () => { connect: () => {
datasource = createDatasource({ dbName, mergeCount }); datasource = createDatasource({ dbName, mergeCount });
provider = createLazyProvider(doc, datasource); provider = createLazyProvider(doc, datasource, { origin: 'idb' });
provider.connect(); provider.connect();
}, },
disconnect: () => { disconnect: () => {

View File

@@ -1,6 +1,6 @@
import { setTimeout } from 'node:timers/promises'; import { setTimeout } from 'node:timers/promises';
import { describe, expect, test } from 'vitest'; import { describe, expect, test, vi } from 'vitest';
import { applyUpdate, Doc, encodeStateAsUpdate } from 'yjs'; import { applyUpdate, Doc, encodeStateAsUpdate } from 'yjs';
import { createLazyProvider } from '../lazy-provider'; import { createLazyProvider } from '../lazy-provider';
@@ -178,4 +178,19 @@ describe('y-provider', () => {
expect(provider.connected).toBe(false); expect(provider.connected).toBe(false);
}); });
test('should not send remote update back', async () => {
const remoteRootDoc = new Doc(); // this is the remote doc lives in remote
const datasource = createMemoryDatasource(remoteRootDoc);
const spy = vi.spyOn(datasource, 'sendDocUpdate');
const rootDoc = new Doc({ guid: remoteRootDoc.guid }); // this is the doc that we want to sync
const provider = createLazyProvider(rootDoc, datasource);
provider.connect();
remoteRootDoc.getText('text').insert(0, 'test-value');
expect(spy).not.toBeCalled();
});
}); });

View File

@@ -9,8 +9,6 @@ import {
import type { DatasourceDocAdapter } from './types'; import type { DatasourceDocAdapter } from './types';
const selfUpdateOrigin = 'lazy-provider-self-origin';
function getDoc(doc: Doc, guid: string): Doc | undefined { function getDoc(doc: Doc, guid: string): Doc | undefined {
if (doc.guid === guid) { if (doc.guid === guid) {
return doc; return doc;
@@ -24,12 +22,17 @@ function getDoc(doc: Doc, guid: string): Doc | undefined {
return undefined; return undefined;
} }
interface LazyProviderOptions {
origin?: string;
}
/** /**
* Creates a lazy provider that connects to a datasource and synchronizes a root document. * Creates a lazy provider that connects to a datasource and synchronizes a root document.
*/ */
export const createLazyProvider = ( export const createLazyProvider = (
rootDoc: Doc, rootDoc: Doc,
datasource: DatasourceDocAdapter datasource: DatasourceDocAdapter,
options: LazyProviderOptions = {}
): Omit<PassiveDocProvider, 'flavour'> => { ): Omit<PassiveDocProvider, 'flavour'> => {
let connected = false; let connected = false;
const pendingMap = new Map<string, Uint8Array[]>(); // guid -> pending-updates const pendingMap = new Map<string, Uint8Array[]>(); // guid -> pending-updates
@@ -37,6 +40,8 @@ export const createLazyProvider = (
const connectedDocs = new Set<string>(); const connectedDocs = new Set<string>();
let datasourceUnsub: (() => void) | undefined; let datasourceUnsub: (() => void) | undefined;
const { origin = 'lazy-provider' } = options;
async function syncDoc(doc: Doc) { async function syncDoc(doc: Doc) {
const guid = doc.guid; const guid = doc.guid;
@@ -47,7 +52,7 @@ export const createLazyProvider = (
pendingMap.set(guid, []); pendingMap.set(guid, []);
if (remoteUpdate) { if (remoteUpdate) {
applyUpdate(doc, remoteUpdate, selfUpdateOrigin); applyUpdate(doc, remoteUpdate, origin);
} }
const sv = remoteUpdate const sv = remoteUpdate
@@ -67,8 +72,8 @@ export const createLazyProvider = (
function setupDocListener(doc: Doc) { function setupDocListener(doc: Doc) {
const disposables = new Set<() => void>(); const disposables = new Set<() => void>();
disposableMap.set(doc.guid, disposables); disposableMap.set(doc.guid, disposables);
const updateHandler = async (update: Uint8Array, origin: unknown) => { const updateHandler = async (update: Uint8Array, updateOrigin: unknown) => {
if (origin === selfUpdateOrigin) { if (origin === updateOrigin) {
return; return;
} }
datasource.sendDocUpdate(doc.guid, update).catch(console.error); datasource.sendDocUpdate(doc.guid, update).catch(console.error);
@@ -100,10 +105,12 @@ export const createLazyProvider = (
datasourceUnsub = datasource.onDocUpdate?.((guid, update) => { datasourceUnsub = datasource.onDocUpdate?.((guid, update) => {
const doc = getDoc(rootDoc, guid); const doc = getDoc(rootDoc, guid);
if (doc) { if (doc) {
applyUpdate(doc, update); applyUpdate(doc, update, origin);
// //
if (pendingMap.has(guid)) { if (pendingMap.has(guid)) {
pendingMap.get(guid)?.forEach(update => applyUpdate(doc, update)); pendingMap
.get(guid)
?.forEach(update => applyUpdate(doc, update, origin));
pendingMap.delete(guid); pendingMap.delete(guid);
} }
} else { } else {