@fluidframework/routerlicious-driver 0.51.0 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/createNewUtils.js +4 -3
- package/dist/createNewUtils.js.map +1 -1
- package/dist/documentStorageService.d.ts.map +1 -1
- package/dist/documentStorageService.js +4 -261
- package/dist/documentStorageService.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/shreddedSummaryDocumentStorageService.d.ts +36 -0
- package/dist/shreddedSummaryDocumentStorageService.d.ts.map +1 -0
- package/dist/shreddedSummaryDocumentStorageService.js +143 -0
- package/dist/shreddedSummaryDocumentStorageService.js.map +1 -0
- package/dist/wholeSummaryDocumentStorageService.d.ts +31 -0
- package/dist/wholeSummaryDocumentStorageService.d.ts.map +1 -0
- package/dist/wholeSummaryDocumentStorageService.js +157 -0
- package/dist/wholeSummaryDocumentStorageService.js.map +1 -0
- package/lib/createNewUtils.js +4 -3
- package/lib/createNewUtils.js.map +1 -1
- package/lib/documentStorageService.d.ts.map +1 -1
- package/lib/documentStorageService.js +3 -260
- package/lib/documentStorageService.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/shreddedSummaryDocumentStorageService.d.ts +36 -0
- package/lib/shreddedSummaryDocumentStorageService.d.ts.map +1 -0
- package/lib/shreddedSummaryDocumentStorageService.js +139 -0
- package/lib/shreddedSummaryDocumentStorageService.js.map +1 -0
- package/lib/wholeSummaryDocumentStorageService.d.ts +31 -0
- package/lib/wholeSummaryDocumentStorageService.d.ts.map +1 -0
- package/lib/wholeSummaryDocumentStorageService.js +153 -0
- package/lib/wholeSummaryDocumentStorageService.js.map +1 -0
- package/package.json +11 -11
- package/src/documentStorageService.ts +20 -374
- package/src/packageVersion.ts +1 -1
- package/src/shreddedSummaryDocumentStorageService.ts +213 -0
- package/src/wholeSummaryDocumentStorageService.ts +231 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
7
|
+
import {
|
|
8
|
+
assert,
|
|
9
|
+
stringToBuffer,
|
|
10
|
+
Uint8ArrayToString,
|
|
11
|
+
} from "@fluidframework/common-utils";
|
|
12
|
+
import {
|
|
13
|
+
IDocumentStorageService,
|
|
14
|
+
ISummaryContext,
|
|
15
|
+
IDocumentStorageServicePolicies,
|
|
16
|
+
} from "@fluidframework/driver-definitions";
|
|
17
|
+
import {
|
|
18
|
+
ICreateBlobResponse,
|
|
19
|
+
ISnapshotTree,
|
|
20
|
+
ISummaryHandle,
|
|
21
|
+
ISummaryTree,
|
|
22
|
+
ITree,
|
|
23
|
+
IVersion,
|
|
24
|
+
} from "@fluidframework/protocol-definitions";
|
|
25
|
+
import {
|
|
26
|
+
convertWholeFlatSummaryToSnapshotTreeAndBlobs,
|
|
27
|
+
GitManager,
|
|
28
|
+
ISummaryUploadManager,
|
|
29
|
+
WholeSummaryUploadManager,
|
|
30
|
+
} from "@fluidframework/server-services-client";
|
|
31
|
+
import { PerformanceEvent } from "@fluidframework/telemetry-utils";
|
|
32
|
+
import { ICache, InMemoryCache } from "./cache";
|
|
33
|
+
|
|
34
|
+
const latestSnapshotId: string = "latest";
|
|
35
|
+
|
|
36
|
+
export class WholeSummaryDocumentStorageService implements IDocumentStorageService {
|
|
37
|
+
private readonly summaryUploadManager: ISummaryUploadManager;
|
|
38
|
+
private firstVersionsCall: boolean = true;
|
|
39
|
+
|
|
40
|
+
public get repositoryUrl(): string {
|
|
41
|
+
return "";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
constructor(
|
|
45
|
+
protected readonly id: string,
|
|
46
|
+
protected readonly manager: GitManager,
|
|
47
|
+
protected readonly logger: ITelemetryLogger,
|
|
48
|
+
public readonly policies: IDocumentStorageServicePolicies = {},
|
|
49
|
+
private readonly blobCache: ICache<ArrayBufferLike> = new InMemoryCache(),
|
|
50
|
+
private readonly snapshotTreeCache: ICache<ISnapshotTree> = new InMemoryCache()) {
|
|
51
|
+
this.summaryUploadManager = new WholeSummaryUploadManager(manager);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public async getVersions(versionId: string | null, count: number): Promise<IVersion[]> {
|
|
55
|
+
if (versionId !== this.id && versionId !== null) {
|
|
56
|
+
// Blobs in this scenario will never have multiple versions, so return blobId as is
|
|
57
|
+
return [{
|
|
58
|
+
id: versionId,
|
|
59
|
+
treeId: undefined!,
|
|
60
|
+
}];
|
|
61
|
+
}
|
|
62
|
+
// If this is the first versions call for the document, we know we will want the latest summary.
|
|
63
|
+
// Fetch latest summary, cache it, and return its id.
|
|
64
|
+
if (this.firstVersionsCall && count === 1) {
|
|
65
|
+
this.firstVersionsCall = false;
|
|
66
|
+
return [{
|
|
67
|
+
id: (await this.fetchAndCacheSnapshotTree(latestSnapshotId)).id,
|
|
68
|
+
treeId: undefined!,
|
|
69
|
+
}];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Otherwise, get the latest version of the document as normal.
|
|
73
|
+
const id = versionId ? versionId : this.id;
|
|
74
|
+
const commits = await PerformanceEvent.timedExecAsync(
|
|
75
|
+
this.logger,
|
|
76
|
+
{
|
|
77
|
+
eventName: "getVersions",
|
|
78
|
+
versionId: id,
|
|
79
|
+
count,
|
|
80
|
+
},
|
|
81
|
+
async () => this.manager.getCommits(id, count),
|
|
82
|
+
);
|
|
83
|
+
return commits.map((commit) => ({
|
|
84
|
+
date: commit.commit.author.date,
|
|
85
|
+
id: commit.sha,
|
|
86
|
+
treeId: undefined!,
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public async getSnapshotTree(version?: IVersion): Promise<ISnapshotTree | null> {
|
|
91
|
+
let requestVersion = version;
|
|
92
|
+
if (!requestVersion) {
|
|
93
|
+
const versions = await this.getVersions(this.id, 1);
|
|
94
|
+
if (versions.length === 0) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
requestVersion = versions[0];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return (await this.fetchAndCacheSnapshotTree(requestVersion.id)).snapshotTree;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public async readBlob(blobId: string): Promise<ArrayBufferLike> {
|
|
105
|
+
const cachedBlob = await this.blobCache.get(blobId);
|
|
106
|
+
if (cachedBlob !== undefined) {
|
|
107
|
+
return cachedBlob;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const blob = await PerformanceEvent.timedExecAsync(
|
|
111
|
+
this.logger,
|
|
112
|
+
{
|
|
113
|
+
eventName: "readBlob",
|
|
114
|
+
blobId,
|
|
115
|
+
},
|
|
116
|
+
async (event) => {
|
|
117
|
+
const response = await this.manager.getBlob(blobId);
|
|
118
|
+
event.end({
|
|
119
|
+
size: response.size,
|
|
120
|
+
});
|
|
121
|
+
return response;
|
|
122
|
+
},
|
|
123
|
+
);
|
|
124
|
+
const bufferValue = stringToBuffer(blob.content, blob.encoding);
|
|
125
|
+
|
|
126
|
+
await this.blobCache.put(blob.sha, bufferValue);
|
|
127
|
+
|
|
128
|
+
return bufferValue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
public async uploadSummaryWithContext(summary: ISummaryTree, context: ISummaryContext): Promise<string> {
|
|
132
|
+
const summaryHandle = await PerformanceEvent.timedExecAsync(
|
|
133
|
+
this.logger,
|
|
134
|
+
{
|
|
135
|
+
eventName: "uploadSummaryWithContext",
|
|
136
|
+
},
|
|
137
|
+
async () => this.summaryUploadManager.writeSummaryTree(summary, context.ackHandle ?? "", "channel"),
|
|
138
|
+
);
|
|
139
|
+
return summaryHandle;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
public async downloadSummary(handle: ISummaryHandle): Promise<ISummaryTree> {
|
|
143
|
+
throw new Error("NOT IMPLEMENTED!");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
public async write(tree: ITree, parents: string[], message: string, ref: string): Promise<IVersion> {
|
|
147
|
+
throw new Error("NOT IMPLEMENTED!");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
public async createBlob(file: ArrayBufferLike): Promise<ICreateBlobResponse> {
|
|
151
|
+
const uint8ArrayFile = new Uint8Array(file);
|
|
152
|
+
return PerformanceEvent.timedExecAsync(
|
|
153
|
+
this.logger,
|
|
154
|
+
{
|
|
155
|
+
eventName: "createBlob",
|
|
156
|
+
size: uint8ArrayFile.length,
|
|
157
|
+
},
|
|
158
|
+
async (event) => {
|
|
159
|
+
const response = await this.manager.createBlob(
|
|
160
|
+
Uint8ArrayToString(
|
|
161
|
+
uint8ArrayFile, "base64"),
|
|
162
|
+
"base64").then((r) => ({ id: r.sha, url: r.url }));
|
|
163
|
+
event.end({
|
|
164
|
+
blobId: response.id,
|
|
165
|
+
});
|
|
166
|
+
return response;
|
|
167
|
+
},
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private async fetchAndCacheSnapshotTree(versionId: string): Promise<{ id: string, snapshotTree: ISnapshotTree }> {
|
|
172
|
+
const cachedSnapshotTree = await this.snapshotTreeCache.get(versionId);
|
|
173
|
+
if (cachedSnapshotTree !== undefined) {
|
|
174
|
+
return { id: cachedSnapshotTree.id!, snapshotTree: cachedSnapshotTree };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const wholeFlatSummary = await PerformanceEvent.timedExecAsync(
|
|
178
|
+
this.logger,
|
|
179
|
+
{
|
|
180
|
+
eventName: "getWholeFlatSummary",
|
|
181
|
+
treeId: versionId,
|
|
182
|
+
},
|
|
183
|
+
async (event) => {
|
|
184
|
+
const response = await this.manager.getSummary(versionId);
|
|
185
|
+
event.end({
|
|
186
|
+
size: response.trees[0]?.entries.length,
|
|
187
|
+
});
|
|
188
|
+
return response;
|
|
189
|
+
},
|
|
190
|
+
);
|
|
191
|
+
const normalizedWholeSummary = convertWholeFlatSummaryToSnapshotTreeAndBlobs(wholeFlatSummary);
|
|
192
|
+
const snapshotId = normalizedWholeSummary.snapshotTree.id;
|
|
193
|
+
assert(snapshotId !== undefined, 0x275 /* "Root tree should contain the id" */);
|
|
194
|
+
|
|
195
|
+
const cachePs: Promise<any>[] = [
|
|
196
|
+
this.snapshotTreeCache.put(
|
|
197
|
+
snapshotId,
|
|
198
|
+
normalizedWholeSummary.snapshotTree,
|
|
199
|
+
),
|
|
200
|
+
this.initBlobCache(normalizedWholeSummary.blobs),
|
|
201
|
+
];
|
|
202
|
+
if (snapshotId !== versionId) {
|
|
203
|
+
// versionId could be "latest". When summarizer checks cache for "latest", we want it to be available.
|
|
204
|
+
// TODO: For in-memory cache, <latest,snapshotTree> will be a shared pointer with <snapshotId,snapshotTree>,
|
|
205
|
+
// However, for something like Redis, this will cache the same value twice. Alternatively, could we simply
|
|
206
|
+
// cache with versionId?
|
|
207
|
+
cachePs.push(this.snapshotTreeCache.put(
|
|
208
|
+
versionId,
|
|
209
|
+
normalizedWholeSummary.snapshotTree,
|
|
210
|
+
));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
await Promise.all([
|
|
214
|
+
this.snapshotTreeCache.put(
|
|
215
|
+
snapshotId,
|
|
216
|
+
normalizedWholeSummary.snapshotTree,
|
|
217
|
+
),
|
|
218
|
+
this.initBlobCache(normalizedWholeSummary.blobs),
|
|
219
|
+
]);
|
|
220
|
+
|
|
221
|
+
return { id: snapshotId, snapshotTree: normalizedWholeSummary.snapshotTree};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private async initBlobCache(blobs: Map<string, ArrayBuffer>): Promise<void> {
|
|
225
|
+
const blobCachePutPs: Promise<void>[] = [];
|
|
226
|
+
blobs.forEach((value, id) => {
|
|
227
|
+
blobCachePutPs.push(this.blobCache.put(id, value));
|
|
228
|
+
});
|
|
229
|
+
await Promise.all(blobCachePutPs);
|
|
230
|
+
}
|
|
231
|
+
}
|