feat(nbstore): add blob sync storage (#10752)

This commit is contained in:
EYHN
2025-03-14 18:05:54 +08:00
committed by GitHub
parent a2eb3fe1b2
commit 05200ad7b7
56 changed files with 1441 additions and 404 deletions

View File

@@ -176,6 +176,7 @@ class WorkerBlobStorage implements BlobStorage {
constructor(private readonly client: OpClient<WorkerOps>) {}
readonly storageType = 'blob';
readonly isReadonly = false;
get(key: string, _signal?: AbortSignal): Promise<BlobRecord | null> {
return this.client.call('blobStorage.getBlob', key);
@@ -233,47 +234,38 @@ class WorkerBlobSync implements BlobSync {
get state$() {
return this.client.ob$('blobSync.state');
}
setMaxBlobSize(size: number): void {
this.client.call('blobSync.setMaxBlobSize', size).catch(err => {
console.error('error setting max blob size', err);
});
blobState$(blobId: string) {
return this.client.ob$('blobSync.blobState', blobId);
}
onReachedMaxBlobSize(cb: (byteSize: number) => void): () => void {
const subscription = this.client
.ob$('blobSync.onReachedMaxBlobSize')
.subscribe(byteSize => {
cb(byteSize);
});
return () => {
subscription.unsubscribe();
};
}
downloadBlob(
blobId: string,
_signal?: AbortSignal
): Promise<BlobRecord | null> {
downloadBlob(blobId: string): Promise<void> {
return this.client.call('blobSync.downloadBlob', blobId);
}
uploadBlob(blob: BlobRecord, _signal?: AbortSignal): Promise<void> {
uploadBlob(blob: BlobRecord): Promise<void> {
return this.client.call('blobSync.uploadBlob', blob);
}
fullDownload(signal?: AbortSignal): Promise<void> {
const download = this.client.call('blobSync.fullDownload');
fullDownload(peerId?: string, signal?: AbortSignal): Promise<void> {
return new Promise((resolve, reject) => {
const abortListener = () => {
reject(signal?.reason);
subscription.unsubscribe();
};
signal?.addEventListener('abort', () => {
download.cancel();
signal?.addEventListener('abort', abortListener);
const subscription = this.client
.ob$('blobSync.fullDownload', peerId ?? null)
.subscribe({
next() {
signal?.removeEventListener('abort', abortListener);
resolve();
},
error(err) {
signal?.removeEventListener('abort', abortListener);
reject(err);
},
});
});
return download;
}
fullUpload(signal?: AbortSignal): Promise<void> {
const upload = this.client.call('blobSync.fullUpload');
signal?.addEventListener('abort', () => {
upload.cancel();
});
return upload;
}
}

View File

@@ -170,25 +170,6 @@ class StoreConsumer {
this.blobStorage.delete(key, permanently),
'blobStorage.releaseBlobs': () => this.blobStorage.release(),
'blobStorage.listBlobs': () => this.blobStorage.list(),
'docSyncStorage.clearClocks': () => this.docSyncStorage.clearClocks(),
'docSyncStorage.getPeerPulledRemoteClock': ({ peer, docId }) =>
this.docSyncStorage.getPeerPulledRemoteClock(peer, docId),
'docSyncStorage.getPeerPulledRemoteClocks': ({ peer }) =>
this.docSyncStorage.getPeerPulledRemoteClocks(peer),
'docSyncStorage.setPeerPulledRemoteClock': ({ peer, clock }) =>
this.docSyncStorage.setPeerPulledRemoteClock(peer, clock),
'docSyncStorage.getPeerRemoteClock': ({ peer, docId }) =>
this.docSyncStorage.getPeerRemoteClock(peer, docId),
'docSyncStorage.getPeerRemoteClocks': ({ peer }) =>
this.docSyncStorage.getPeerRemoteClocks(peer),
'docSyncStorage.setPeerRemoteClock': ({ peer, clock }) =>
this.docSyncStorage.setPeerRemoteClock(peer, clock),
'docSyncStorage.getPeerPushedClock': ({ peer, docId }) =>
this.docSyncStorage.getPeerPushedClock(peer, docId),
'docSyncStorage.getPeerPushedClocks': ({ peer }) =>
this.docSyncStorage.getPeerPushedClocks(peer),
'docSyncStorage.setPeerPushedClock': ({ peer, clock }) =>
this.docSyncStorage.setPeerPushedClock(peer, clock),
'awarenessStorage.update': ({ awareness, origin }) =>
this.awarenessStorage.update(awareness, origin),
'awarenessStorage.subscribeUpdate': docId =>
@@ -232,20 +213,23 @@ class StoreConsumer {
return () => undo();
}),
'docSync.resetSync': () => this.docSync.resetSync(),
'blobSync.state': () => this.blobSync.state$,
'blobSync.blobState': blobId => this.blobSync.blobState$(blobId),
'blobSync.downloadBlob': key => this.blobSync.downloadBlob(key),
'blobSync.uploadBlob': blob => this.blobSync.uploadBlob(blob),
'blobSync.fullDownload': (_, { signal }) =>
this.blobSync.fullDownload(signal),
'blobSync.fullUpload': (_, { signal }) =>
this.blobSync.fullUpload(signal),
'blobSync.state': () => this.blobSync.state$,
'blobSync.setMaxBlobSize': size => this.blobSync.setMaxBlobSize(size),
'blobSync.onReachedMaxBlobSize': () =>
'blobSync.fullDownload': peerId =>
new Observable(subscriber => {
const undo = this.blobSync.onReachedMaxBlobSize(byteSize => {
subscriber.next(byteSize);
});
return () => undo();
const abortController = new AbortController();
this.blobSync
.fullDownload(peerId ?? undefined, abortController.signal)
.then(() => {
subscriber.next();
subscriber.complete();
})
.catch(error => {
subscriber.error(error);
});
return () => abortController.abort(MANUALLY_STOP);
}),
'awarenessSync.update': ({ awareness, origin }) =>
this.awarenessSync.update(awareness, origin),

View File

@@ -10,7 +10,7 @@ import type {
StorageType,
} from '../storage';
import type { AwarenessRecord } from '../storage/awareness';
import type { BlobSyncState } from '../sync/blob';
import type { BlobSyncBlobState, BlobSyncState } from '../sync/blob';
import type { DocSyncDocState, DocSyncState } from '../sync/doc';
type StorageInitOptions = Values<{
@@ -45,22 +45,6 @@ interface GroupedWorkerOps {
listBlobs: [void, ListedBlobRecord[]];
};
docSyncStorage: {
getPeerPulledRemoteClocks: [{ peer: string }, DocClocks];
getPeerPulledRemoteClock: [
{ peer: string; docId: string },
DocClock | null,
];
setPeerPulledRemoteClock: [{ peer: string; clock: DocClock }, void];
getPeerRemoteClocks: [{ peer: string }, DocClocks];
getPeerRemoteClock: [{ peer: string; docId: string }, DocClock | null];
setPeerRemoteClock: [{ peer: string; clock: DocClock }, void];
getPeerPushedClocks: [{ peer: string }, DocClocks];
getPeerPushedClock: [{ peer: string; docId: string }, DocClock | null];
setPeerPushedClock: [{ peer: string; clock: DocClock }, void];
clearClocks: [void, void];
};
awarenessStorage: {
update: [{ awareness: AwarenessRecord; origin?: string }, void];
subscribeUpdate: [
@@ -85,13 +69,11 @@ interface GroupedWorkerOps {
};
blobSync: {
downloadBlob: [string, BlobRecord | null];
uploadBlob: [BlobRecord, void];
fullDownload: [void, void];
fullUpload: [void, void];
setMaxBlobSize: [number, void];
onReachedMaxBlobSize: [void, number];
state: [void, BlobSyncState];
blobState: [string, BlobSyncBlobState];
downloadBlob: [string, void];
uploadBlob: [BlobRecord, void];
fullDownload: [string | null, void];
};
awarenessSync: {