mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-18 23:07:02 +08:00
feat(core): await sync doc (#4247)
This commit is contained in:
@@ -3,12 +3,14 @@ import type {
|
|||||||
LocalIndexedDBBackgroundProvider,
|
LocalIndexedDBBackgroundProvider,
|
||||||
SQLiteProvider,
|
SQLiteProvider,
|
||||||
} from '@affine/env/workspace';
|
} from '@affine/env/workspace';
|
||||||
|
import {
|
||||||
|
syncDataSourceFromDoc,
|
||||||
|
syncDocFromDataSource,
|
||||||
|
} from '@affine/y-provider';
|
||||||
import { assertExists } from '@blocksuite/global/utils';
|
import { assertExists } from '@blocksuite/global/utils';
|
||||||
import { Button } from '@toeverything/components/button';
|
import { Button } from '@toeverything/components/button';
|
||||||
import { forceUpgradePages } from '@toeverything/infra/blocksuite';
|
import { forceUpgradePages } from '@toeverything/infra/blocksuite';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import type { Doc as YDoc } from 'yjs';
|
|
||||||
import { applyUpdate, encodeStateAsUpdate, encodeStateVector } from 'yjs';
|
|
||||||
|
|
||||||
import { useCurrentWorkspace } from '../hooks/current/use-current-workspace';
|
import { useCurrentWorkspace } from '../hooks/current/use-current-workspace';
|
||||||
|
|
||||||
@@ -36,85 +38,31 @@ export const MigrationFallback = function MigrationFallback() {
|
|||||||
}, [providers]);
|
}, [providers]);
|
||||||
const handleClick = useCallback(async () => {
|
const handleClick = useCallback(async () => {
|
||||||
setDone(false);
|
setDone(false);
|
||||||
const downloadRecursively = async (doc: YDoc) => {
|
await syncDocFromDataSource(
|
||||||
{
|
workspace.blockSuiteWorkspace.doc,
|
||||||
const docState = await localProvider.datasource.queryDocState(
|
localProvider.datasource
|
||||||
doc.guid,
|
);
|
||||||
{
|
if (remoteProvider) {
|
||||||
stateVector: encodeStateVector(doc),
|
await syncDocFromDataSource(
|
||||||
}
|
workspace.blockSuiteWorkspace.doc,
|
||||||
);
|
remoteProvider.datasource
|
||||||
console.log('download indexeddb', doc.guid);
|
|
||||||
if (docState) {
|
|
||||||
applyUpdate(doc, docState.missing, 'migration');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (remoteProvider) {
|
|
||||||
{
|
|
||||||
const docState = await remoteProvider.datasource.queryDocState(
|
|
||||||
doc.guid,
|
|
||||||
{
|
|
||||||
stateVector: encodeStateVector(doc),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
console.log('download remote', doc.guid);
|
|
||||||
if (docState) {
|
|
||||||
applyUpdate(doc, docState.missing, 'migration');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await Promise.all(
|
|
||||||
[...doc.subdocs].map(async subdoc => {
|
|
||||||
await downloadRecursively(subdoc);
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
{
|
}
|
||||||
await localProvider.datasource.sendDocUpdate(
|
|
||||||
doc.guid,
|
|
||||||
encodeStateAsUpdate(doc)
|
|
||||||
);
|
|
||||||
console.log('upload indexeddb', doc.guid);
|
|
||||||
if (remoteProvider) {
|
|
||||||
await remoteProvider.datasource.sendDocUpdate(
|
|
||||||
doc.guid,
|
|
||||||
encodeStateAsUpdate(doc)
|
|
||||||
);
|
|
||||||
console.log('upload remote', doc.guid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const uploadRecursively = async (doc: YDoc) => {
|
|
||||||
{
|
|
||||||
await localProvider.datasource.sendDocUpdate(
|
|
||||||
doc.guid,
|
|
||||||
encodeStateAsUpdate(doc)
|
|
||||||
);
|
|
||||||
console.log('upload indexeddb', doc.guid);
|
|
||||||
if (remoteProvider) {
|
|
||||||
await remoteProvider.datasource.sendDocUpdate(
|
|
||||||
doc.guid,
|
|
||||||
encodeStateAsUpdate(doc)
|
|
||||||
);
|
|
||||||
console.log('upload remote', doc.guid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await Promise.all(
|
|
||||||
[...doc.subdocs].map(async subdoc => {
|
|
||||||
await uploadRecursively(subdoc);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
await downloadRecursively(workspace.blockSuiteWorkspace.doc);
|
|
||||||
console.log('download done');
|
|
||||||
|
|
||||||
console.log('start migration');
|
|
||||||
await forceUpgradePages({
|
await forceUpgradePages({
|
||||||
getCurrentRootDoc: async () => workspace.blockSuiteWorkspace.doc,
|
getCurrentRootDoc: async () => workspace.blockSuiteWorkspace.doc,
|
||||||
getSchema: () => workspace.blockSuiteWorkspace.schema,
|
getSchema: () => workspace.blockSuiteWorkspace.schema,
|
||||||
});
|
});
|
||||||
await uploadRecursively(workspace.blockSuiteWorkspace.doc);
|
await syncDataSourceFromDoc(
|
||||||
console.log('migration done');
|
workspace.blockSuiteWorkspace.doc,
|
||||||
|
localProvider.datasource
|
||||||
|
);
|
||||||
|
if (remoteProvider) {
|
||||||
|
await syncDataSourceFromDoc(
|
||||||
|
workspace.blockSuiteWorkspace.doc,
|
||||||
|
remoteProvider.datasource
|
||||||
|
);
|
||||||
|
}
|
||||||
setDone(true);
|
setDone(true);
|
||||||
}, [
|
}, [
|
||||||
localProvider.datasource,
|
localProvider.datasource,
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import {
|
|||||||
getWorkspaceQuery,
|
getWorkspaceQuery,
|
||||||
getWorkspacesQuery,
|
getWorkspacesQuery,
|
||||||
} from '@affine/graphql';
|
} from '@affine/graphql';
|
||||||
|
import { createAffineDataSource } from '@affine/workspace/affine/index';
|
||||||
|
import { syncDataSourceFromDoc } from '@affine/y-provider';
|
||||||
import { createIndexeddbStorage, Workspace } from '@blocksuite/store';
|
import { createIndexeddbStorage, Workspace } from '@blocksuite/store';
|
||||||
import { migrateLocalBlobStorage } from '@toeverything/infra/blocksuite';
|
import { migrateLocalBlobStorage } from '@toeverything/infra/blocksuite';
|
||||||
import {
|
import {
|
||||||
@@ -38,35 +40,43 @@ async function deleteLocalBlobStorage(id: string) {
|
|||||||
const createdWorkspaces = proxy<string[]>([]);
|
const createdWorkspaces = proxy<string[]>([]);
|
||||||
|
|
||||||
export const CRUD: WorkspaceCRUD<WorkspaceFlavour.AFFINE_CLOUD> = {
|
export const CRUD: WorkspaceCRUD<WorkspaceFlavour.AFFINE_CLOUD> = {
|
||||||
create: async blockSuiteWorkspace => {
|
create: async upstreamWorkspace => {
|
||||||
if (createdWorkspaces.some(id => id === blockSuiteWorkspace.id)) {
|
if (createdWorkspaces.some(id => id === upstreamWorkspace.id)) {
|
||||||
throw new Error('workspace already created');
|
throw new Error('workspace already created');
|
||||||
}
|
}
|
||||||
const { createWorkspace } = await fetcher({
|
const { createWorkspace } = await fetcher({
|
||||||
query: createWorkspaceMutation,
|
query: createWorkspaceMutation,
|
||||||
variables: {
|
variables: {
|
||||||
init: new File(
|
init: new File(
|
||||||
[Y.encodeStateAsUpdate(blockSuiteWorkspace.doc)],
|
[Y.encodeStateAsUpdate(upstreamWorkspace.doc)],
|
||||||
'initBinary.yDoc'
|
'initBinary.yDoc'
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
createdWorkspaces.push(blockSuiteWorkspace.id);
|
createdWorkspaces.push(upstreamWorkspace.id);
|
||||||
const newBLockSuiteWorkspace = getOrCreateWorkspace(
|
const newBlockSuiteWorkspace = getOrCreateWorkspace(
|
||||||
createWorkspace.id,
|
createWorkspace.id,
|
||||||
WorkspaceFlavour.AFFINE_CLOUD
|
WorkspaceFlavour.AFFINE_CLOUD
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const datasource = createAffineDataSource(
|
||||||
|
createWorkspace.id,
|
||||||
|
newBlockSuiteWorkspace.doc,
|
||||||
|
newBlockSuiteWorkspace.awarenessStore.awareness
|
||||||
|
);
|
||||||
|
|
||||||
|
await syncDataSourceFromDoc(upstreamWorkspace.doc, datasource);
|
||||||
|
|
||||||
Y.applyUpdate(
|
Y.applyUpdate(
|
||||||
newBLockSuiteWorkspace.doc,
|
newBlockSuiteWorkspace.doc,
|
||||||
Y.encodeStateAsUpdate(blockSuiteWorkspace.doc)
|
Y.encodeStateAsUpdate(upstreamWorkspace.doc)
|
||||||
);
|
);
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
[...blockSuiteWorkspace.doc.subdocs].map(async subdoc => {
|
[...upstreamWorkspace.doc.subdocs].map(async subdoc => {
|
||||||
subdoc.load();
|
subdoc.load();
|
||||||
return subdoc.whenLoaded.then(() => {
|
return subdoc.whenLoaded.then(() => {
|
||||||
newBLockSuiteWorkspace.doc.subdocs.forEach(newSubdoc => {
|
newBlockSuiteWorkspace.doc.subdocs.forEach(newSubdoc => {
|
||||||
if (newSubdoc.guid === subdoc.guid) {
|
if (newSubdoc.guid === subdoc.guid) {
|
||||||
Y.applyUpdate(newSubdoc, Y.encodeStateAsUpdate(subdoc));
|
Y.applyUpdate(newSubdoc, Y.encodeStateAsUpdate(subdoc));
|
||||||
}
|
}
|
||||||
@@ -76,12 +86,12 @@ export const CRUD: WorkspaceCRUD<WorkspaceFlavour.AFFINE_CLOUD> = {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const provider = createIndexedDBProvider(
|
const provider = createIndexedDBProvider(
|
||||||
newBLockSuiteWorkspace.doc,
|
newBlockSuiteWorkspace.doc,
|
||||||
DEFAULT_DB_NAME
|
DEFAULT_DB_NAME
|
||||||
);
|
);
|
||||||
provider.connect();
|
provider.connect();
|
||||||
migrateLocalBlobStorage(blockSuiteWorkspace.id, createWorkspace.id)
|
migrateLocalBlobStorage(upstreamWorkspace.id, createWorkspace.id)
|
||||||
.then(() => deleteLocalBlobStorage(blockSuiteWorkspace.id))
|
.then(() => deleteLocalBlobStorage(upstreamWorkspace.id))
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
console.error('error when moving blob storage:', e);
|
console.error('error when moving blob storage:', e);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import type { Doc as YDoc } from 'yjs';
|
||||||
|
import { applyUpdate, encodeStateAsUpdate } from 'yjs';
|
||||||
|
|
||||||
import type { DocState } from './types';
|
import type { DocState } from './types';
|
||||||
|
|
||||||
export interface DatasourceDocAdapter {
|
export interface DatasourceDocAdapter {
|
||||||
@@ -26,6 +29,40 @@ export interface DatasourceDocAdapter {
|
|||||||
): () => void;
|
): () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function syncDocFromDataSource(
|
||||||
|
rootDoc: YDoc,
|
||||||
|
datasource: DatasourceDocAdapter
|
||||||
|
) {
|
||||||
|
const downloadDocStateRecursively = async (doc: YDoc) => {
|
||||||
|
const docState = await datasource.queryDocState(doc.guid);
|
||||||
|
if (docState) {
|
||||||
|
applyUpdate(doc, docState.missing, 'sync-doc-from-datasource');
|
||||||
|
}
|
||||||
|
await Promise.all(
|
||||||
|
[...doc.subdocs].map(async subdoc => {
|
||||||
|
await downloadDocStateRecursively(subdoc);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
await downloadDocStateRecursively(rootDoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function syncDataSourceFromDoc(
|
||||||
|
rootDoc: YDoc,
|
||||||
|
datasource: DatasourceDocAdapter
|
||||||
|
) {
|
||||||
|
const uploadDocStateRecursively = async (doc: YDoc) => {
|
||||||
|
await datasource.sendDocUpdate(doc.guid, encodeStateAsUpdate(doc));
|
||||||
|
await Promise.all(
|
||||||
|
[...doc.subdocs].map(async subdoc => {
|
||||||
|
await uploadDocStateRecursively(subdoc);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
await uploadDocStateRecursively(rootDoc);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* query the datasource from source, and save the latest update to target
|
* query the datasource from source, and save the latest update to target
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user