perf: use lazy load provider for IDB and SQLITE (#3351)

This commit is contained in:
Peng Xiao
2023-07-26 00:56:48 +08:00
committed by GitHub
parent e3f66d7e22
commit 20ee9d485d
25 changed files with 481 additions and 758 deletions

View File

@@ -3,6 +3,7 @@ import {
applyUpdate,
type Doc,
encodeStateAsUpdate,
encodeStateVector,
encodeStateVectorFromUpdate,
} from 'yjs';
@@ -33,30 +34,30 @@ export const createLazyProvider = (
let connected = false;
const pendingMap = new Map<string, Uint8Array[]>(); // guid -> pending-updates
const disposableMap = new Map<string, Set<() => void>>();
const connectedDocs = new Set();
const connectedDocs = new Set<string>();
let datasourceUnsub: (() => void) | undefined;
async function syncDoc(doc: Doc) {
const guid = doc.guid;
// perf: optimize me
const currentUpdate = encodeStateAsUpdate(doc);
const remoteUpdate = await datasource.queryDocState(guid, {
stateVector: encodeStateVectorFromUpdate(currentUpdate),
stateVector: encodeStateVector(doc),
});
const updates = [currentUpdate];
pendingMap.set(guid, []);
if (remoteUpdate) {
applyUpdate(doc, remoteUpdate, selfUpdateOrigin);
const newUpdate = encodeStateAsUpdate(
doc,
encodeStateVectorFromUpdate(remoteUpdate)
);
updates.push(newUpdate);
await datasource.sendDocUpdate(guid, newUpdate);
}
const sv = remoteUpdate
? encodeStateVectorFromUpdate(remoteUpdate)
: undefined;
// perf: optimize me
// it is possible the doc is only in memory but not yet in the datasource
// we need to send the whole update to the datasource
await datasource.sendDocUpdate(guid, encodeStateAsUpdate(doc, sv));
}
/**
@@ -73,10 +74,7 @@ export const createLazyProvider = (
datasource.sendDocUpdate(doc.guid, update).catch(console.error);
};
const subdocLoadHandler = (event: {
loaded: Set<Doc>;
removed: Set<Doc>;
}) => {
const subdocsHandler = (event: { loaded: Set<Doc>; removed: Set<Doc> }) => {
event.loaded.forEach(subdoc => {
connectDoc(subdoc).catch(console.error);
});
@@ -86,11 +84,11 @@ export const createLazyProvider = (
};
doc.on('update', updateHandler);
doc.on('subdocs', subdocLoadHandler);
doc.on('subdocs', subdocsHandler);
// todo: handle destroy?
disposables.add(() => {
doc.off('update', updateHandler);
doc.off('subdocs', subdocLoadHandler);
doc.off('subdocs', subdocsHandler);
});
}
@@ -127,6 +125,7 @@ export const createLazyProvider = (
connectedDocs.add(doc.guid);
setupDocListener(doc);
await syncDoc(doc);
await Promise.all(
[...doc.subdocs]
.filter(subdoc => subdoc.shouldLoad)
@@ -150,6 +149,7 @@ export const createLazyProvider = (
disposables.forEach(dispose => dispose());
});
disposableMap.clear();
connectedDocs.clear();
}
/**

View File

@@ -12,3 +12,19 @@ export function getDoc(doc: Doc, guid: string): Doc | undefined {
}
return undefined;
}
const saveAlert = (event: BeforeUnloadEvent) => {
event.preventDefault();
return (event.returnValue =
'Data is not saved. Are you sure you want to leave?');
};
export const writeOperation = async (op: Promise<unknown>) => {
window.addEventListener('beforeunload', saveAlert, {
capture: true,
});
await op;
window.removeEventListener('beforeunload', saveAlert, {
capture: true,
});
};