@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.
Files changed (37) hide show
  1. package/dist/createNewUtils.js +4 -3
  2. package/dist/createNewUtils.js.map +1 -1
  3. package/dist/documentStorageService.d.ts.map +1 -1
  4. package/dist/documentStorageService.js +4 -261
  5. package/dist/documentStorageService.js.map +1 -1
  6. package/dist/packageVersion.d.ts +1 -1
  7. package/dist/packageVersion.js +1 -1
  8. package/dist/packageVersion.js.map +1 -1
  9. package/dist/shreddedSummaryDocumentStorageService.d.ts +36 -0
  10. package/dist/shreddedSummaryDocumentStorageService.d.ts.map +1 -0
  11. package/dist/shreddedSummaryDocumentStorageService.js +143 -0
  12. package/dist/shreddedSummaryDocumentStorageService.js.map +1 -0
  13. package/dist/wholeSummaryDocumentStorageService.d.ts +31 -0
  14. package/dist/wholeSummaryDocumentStorageService.d.ts.map +1 -0
  15. package/dist/wholeSummaryDocumentStorageService.js +157 -0
  16. package/dist/wholeSummaryDocumentStorageService.js.map +1 -0
  17. package/lib/createNewUtils.js +4 -3
  18. package/lib/createNewUtils.js.map +1 -1
  19. package/lib/documentStorageService.d.ts.map +1 -1
  20. package/lib/documentStorageService.js +3 -260
  21. package/lib/documentStorageService.js.map +1 -1
  22. package/lib/packageVersion.d.ts +1 -1
  23. package/lib/packageVersion.js +1 -1
  24. package/lib/packageVersion.js.map +1 -1
  25. package/lib/shreddedSummaryDocumentStorageService.d.ts +36 -0
  26. package/lib/shreddedSummaryDocumentStorageService.d.ts.map +1 -0
  27. package/lib/shreddedSummaryDocumentStorageService.js +139 -0
  28. package/lib/shreddedSummaryDocumentStorageService.js.map +1 -0
  29. package/lib/wholeSummaryDocumentStorageService.d.ts +31 -0
  30. package/lib/wholeSummaryDocumentStorageService.d.ts.map +1 -0
  31. package/lib/wholeSummaryDocumentStorageService.js +153 -0
  32. package/lib/wholeSummaryDocumentStorageService.js.map +1 -0
  33. package/package.json +11 -11
  34. package/src/documentStorageService.ts +20 -374
  35. package/src/packageVersion.ts +1 -1
  36. package/src/shreddedSummaryDocumentStorageService.ts +213 -0
  37. package/src/wholeSummaryDocumentStorageService.ts +231 -0
@@ -4,392 +4,23 @@
4
4
  */
5
5
 
6
6
  import type { ITelemetryLogger } from "@fluidframework/common-definitions";
7
- import {
8
- assert,
9
- stringToBuffer,
10
- Uint8ArrayToString,
11
- } from "@fluidframework/common-utils";
12
7
  import {
13
8
  IDocumentStorageService,
14
- ISummaryContext,
15
9
  IDocumentStorageServicePolicies,
16
10
  LoaderCachingPolicy,
17
11
  } from "@fluidframework/driver-definitions";
18
- import { buildHierarchy } from "@fluidframework/protocol-base";
19
12
  import {
20
- ICreateBlobResponse,
21
13
  ISnapshotTree,
22
- ISnapshotTreeEx,
23
- ISummaryHandle,
24
- ISummaryTree,
25
- ITree,
26
14
  IVersion,
27
15
  } from "@fluidframework/protocol-definitions";
28
16
  import {
29
- convertWholeFlatSummaryToSnapshotTreeAndBlobs,
30
17
  GitManager,
31
- ISummaryUploadManager,
32
- SummaryTreeUploadManager,
33
- WholeSummaryUploadManager,
34
18
  } from "@fluidframework/server-services-client";
35
- import { PerformanceEvent } from "@fluidframework/telemetry-utils";
36
19
  import { DocumentStorageServiceProxy, PrefetchDocumentStorageService } from "@fluidframework/driver-utils";
37
- import { RetriableGitManager } from "./retriableGitManager";
38
20
  import { IRouterliciousDriverPolicies } from "./policies";
39
- import { ICache, InMemoryCache } from "./cache";
40
-
41
- /**
42
- * Document access to underlying storage for routerlicious driver.
43
- * Uploads summaries piece-by-piece traversing the tree recursively.
44
- * Downloads summaries
45
- */
46
- class ShreddedSummaryDocumentStorageService implements IDocumentStorageService {
47
- // The values of this cache is useless. We only need the keys. So we are always putting
48
- // empty strings as values.
49
- protected readonly blobsShaCache = new Map<string, string>();
50
- private readonly summaryUploadManager: ISummaryUploadManager;
51
-
52
- public get repositoryUrl(): string {
53
- return "";
54
- }
55
-
56
- constructor(
57
- protected readonly id: string,
58
- protected readonly manager: GitManager,
59
- protected readonly logger: ITelemetryLogger,
60
- public readonly policies: IDocumentStorageServicePolicies = {}) {
61
- this.summaryUploadManager = new SummaryTreeUploadManager(
62
- new RetriableGitManager(manager, logger),
63
- this.blobsShaCache,
64
- this.getPreviousFullSnapshot.bind(this),
65
- );
66
- }
67
-
68
- public async getVersions(versionId: string, count: number): Promise<IVersion[]> {
69
- const id = versionId ? versionId : this.id;
70
- const commits = await PerformanceEvent.timedExecAsync(
71
- this.logger,
72
- {
73
- eventName: "getVersions",
74
- versionId: id,
75
- count,
76
- },
77
- async () => this.manager.getCommits(id, count),
78
- );
79
- return commits.map((commit) => ({
80
- date: commit.commit.author.date,
81
- id: commit.sha,
82
- treeId: commit.commit.tree.sha,
83
- }));
84
- }
85
-
86
- public async getSnapshotTree(version?: IVersion): Promise<ISnapshotTreeEx | null> {
87
- let requestVersion = version;
88
- if (!requestVersion) {
89
- const versions = await this.getVersions(this.id, 1);
90
- if (versions.length === 0) {
91
- return null;
92
- }
93
-
94
- requestVersion = versions[0];
95
- }
96
-
97
- const rawTree = await PerformanceEvent.timedExecAsync(
98
- this.logger,
99
- {
100
- eventName: "getSnapshotTree",
101
- treeId: requestVersion.treeId,
102
- },
103
- async (event) => {
104
- const response = await this.manager.getTree(requestVersion!.treeId);
105
- event.end({
106
- size: response.tree.length,
107
- });
108
- return response;
109
- },
110
- );
111
- const tree = buildHierarchy(rawTree, this.blobsShaCache, true);
112
- return tree;
113
- }
114
-
115
- public async readBlob(blobId: string): Promise<ArrayBufferLike> {
116
- const value = await PerformanceEvent.timedExecAsync(
117
- this.logger,
118
- {
119
- eventName: "readBlob",
120
- blobId,
121
- },
122
- async (event) => {
123
- const response = await this.manager.getBlob(blobId);
124
- event.end({
125
- size: response.size,
126
- });
127
- return response;
128
- },
129
- );
130
- this.blobsShaCache.set(value.sha, "");
131
- return stringToBuffer(value.content, value.encoding);
132
- }
133
-
134
- public async write(tree: ITree, parents: string[], message: string, ref: string): Promise<IVersion> {
135
- const branch = ref ? `datastores/${this.id}/${ref}` : this.id;
136
- const commit = await PerformanceEvent.timedExecAsync(
137
- this.logger,
138
- {
139
- eventName: "write",
140
- id: branch,
141
- },
142
- async () => this.manager.write(branch, tree, parents, message),
143
- );
144
- return { date: commit.committer.date, id: commit.sha, treeId: commit.tree.sha };
145
- }
146
-
147
- public async uploadSummaryWithContext(summary: ISummaryTree, context: ISummaryContext): Promise<string> {
148
- const summaryHandle = await PerformanceEvent.timedExecAsync(
149
- this.logger,
150
- {
151
- eventName: "uploadSummaryWithContext",
152
- },
153
- async () => this.summaryUploadManager.writeSummaryTree(summary, context.ackHandle ?? "", "channel"),
154
- );
155
- return summaryHandle;
156
- }
157
-
158
- public async downloadSummary(handle: ISummaryHandle): Promise<ISummaryTree> {
159
- throw new Error("NOT IMPLEMENTED!");
160
- }
161
-
162
- public async createBlob(file: ArrayBufferLike): Promise<ICreateBlobResponse> {
163
- const uint8ArrayFile = new Uint8Array(file);
164
- return PerformanceEvent.timedExecAsync(
165
- this.logger,
166
- {
167
- eventName: "createBlob",
168
- size: uint8ArrayFile.length,
169
- },
170
- async (event) => {
171
- const response = await this.manager.createBlob(
172
- Uint8ArrayToString(
173
- uint8ArrayFile, "base64"),
174
- "base64").then((r) => ({ id: r.sha, url: r.url }));
175
- event.end({
176
- blobId: response.id,
177
- });
178
- return response;
179
- },
180
- );
181
- }
182
-
183
- private async getPreviousFullSnapshot(parentHandle: string): Promise<ISnapshotTreeEx | null | undefined> {
184
- return parentHandle
185
- ? this.getVersions(parentHandle, 1)
186
- .then(async (versions) => {
187
- // Clear the cache as the getSnapshotTree call will fill the cache.
188
- this.blobsShaCache.clear();
189
- return this.getSnapshotTree(versions[0]);
190
- })
191
- : undefined;
192
- }
193
- }
194
-
195
- const latestSnapshotId: string = "latest";
196
-
197
- class WholeSummaryDocumentStorageService implements IDocumentStorageService {
198
- private readonly summaryUploadManager: ISummaryUploadManager;
199
- private firstVersionsCall: boolean = true;
200
-
201
- public get repositoryUrl(): string {
202
- return "";
203
- }
204
-
205
- constructor(
206
- protected readonly id: string,
207
- protected readonly manager: GitManager,
208
- protected readonly logger: ITelemetryLogger,
209
- public readonly policies: IDocumentStorageServicePolicies = {},
210
- private readonly blobCache: ICache<ArrayBufferLike> = new InMemoryCache(),
211
- private readonly snapshotTreeCache: ICache<ISnapshotTree> = new InMemoryCache()) {
212
- this.summaryUploadManager = new WholeSummaryUploadManager(manager);
213
- }
214
-
215
- public async getVersions(versionId: string | null, count: number): Promise<IVersion[]> {
216
- if (versionId !== this.id && versionId !== null) {
217
- // Blobs in this scenario will never have multiple versions, so return blobId as is
218
- return [{
219
- id: versionId,
220
- treeId: undefined!,
221
- }];
222
- }
223
- // If this is the first versions call for the document, we know we will want the latest summary.
224
- // Fetch latest summary, cache it, and return its id.
225
- if (this.firstVersionsCall && count === 1) {
226
- this.firstVersionsCall = false;
227
- return [{
228
- id: (await this.fetchAndCacheSnapshotTree(latestSnapshotId)).id,
229
- treeId: undefined!,
230
- }];
231
- }
232
-
233
- // Otherwise, get the latest version of the document as normal.
234
- const id = versionId ? versionId : this.id;
235
- const commits = await PerformanceEvent.timedExecAsync(
236
- this.logger,
237
- {
238
- eventName: "getVersions",
239
- versionId: id,
240
- count,
241
- },
242
- async () => this.manager.getCommits(id, count),
243
- );
244
- return commits.map((commit) => ({
245
- date: commit.commit.author.date,
246
- id: commit.sha,
247
- treeId: undefined!,
248
- }));
249
- }
250
-
251
- public async getSnapshotTree(version?: IVersion): Promise<ISnapshotTree | null> {
252
- let requestVersion = version;
253
- if (!requestVersion) {
254
- const versions = await this.getVersions(this.id, 1);
255
- if (versions.length === 0) {
256
- return null;
257
- }
258
-
259
- requestVersion = versions[0];
260
- }
261
-
262
- return (await this.fetchAndCacheSnapshotTree(requestVersion.id)).snapshotTree;
263
- }
264
-
265
- public async readBlob(blobId: string): Promise<ArrayBufferLike> {
266
- const cachedBlob = await this.blobCache.get(blobId);
267
- if (cachedBlob !== undefined) {
268
- return cachedBlob;
269
- }
270
-
271
- const blob = await PerformanceEvent.timedExecAsync(
272
- this.logger,
273
- {
274
- eventName: "readBlob",
275
- blobId,
276
- },
277
- async (event) => {
278
- const response = await this.manager.getBlob(blobId);
279
- event.end({
280
- size: response.size,
281
- });
282
- return response;
283
- },
284
- );
285
- const bufferValue = stringToBuffer(blob.content, blob.encoding);
286
-
287
- await this.blobCache.put(blob.sha, bufferValue);
288
-
289
- return bufferValue;
290
- }
291
-
292
- public async uploadSummaryWithContext(summary: ISummaryTree, context: ISummaryContext): Promise<string> {
293
- const summaryHandle = await PerformanceEvent.timedExecAsync(
294
- this.logger,
295
- {
296
- eventName: "uploadSummaryWithContext",
297
- },
298
- async () => this.summaryUploadManager.writeSummaryTree(summary, context.ackHandle ?? "", "channel"),
299
- );
300
- return summaryHandle;
301
- }
302
-
303
- public async downloadSummary(handle: ISummaryHandle): Promise<ISummaryTree> {
304
- throw new Error("NOT IMPLEMENTED!");
305
- }
306
-
307
- public async write(tree: ITree, parents: string[], message: string, ref: string): Promise<IVersion> {
308
- throw new Error("NOT IMPLEMENTED!");
309
- }
310
-
311
- public async createBlob(file: ArrayBufferLike): Promise<ICreateBlobResponse> {
312
- const uint8ArrayFile = new Uint8Array(file);
313
- return PerformanceEvent.timedExecAsync(
314
- this.logger,
315
- {
316
- eventName: "createBlob",
317
- size: uint8ArrayFile.length,
318
- },
319
- async (event) => {
320
- const response = await this.manager.createBlob(
321
- Uint8ArrayToString(
322
- uint8ArrayFile, "base64"),
323
- "base64").then((r) => ({ id: r.sha, url: r.url }));
324
- event.end({
325
- blobId: response.id,
326
- });
327
- return response;
328
- },
329
- );
330
- }
331
-
332
- private async fetchAndCacheSnapshotTree(versionId: string): Promise<{ id: string, snapshotTree: ISnapshotTree }> {
333
- const cachedSnapshotTree = await this.snapshotTreeCache.get(versionId);
334
- if (cachedSnapshotTree !== undefined) {
335
- return { id: versionId, snapshotTree: cachedSnapshotTree };
336
- }
337
-
338
- const wholeFlatSummary = await PerformanceEvent.timedExecAsync(
339
- this.logger,
340
- {
341
- eventName: "getWholeFlatSummary",
342
- treeId: versionId,
343
- },
344
- async (event) => {
345
- const response = await this.manager.getSummary(versionId);
346
- event.end({
347
- size: response.trees[0]?.entries.length,
348
- });
349
- return response;
350
- },
351
- );
352
- const normalizedWholeSummary = convertWholeFlatSummaryToSnapshotTreeAndBlobs(wholeFlatSummary);
353
- const snapshotId = normalizedWholeSummary.snapshotTree.id;
354
- assert(snapshotId !== undefined, 0x275 /* "Root tree should contain the id" */);
355
-
356
- const cachePs: Promise<any>[] = [
357
- this.snapshotTreeCache.put(
358
- snapshotId,
359
- normalizedWholeSummary.snapshotTree,
360
- ),
361
- this.initBlobCache(normalizedWholeSummary.blobs),
362
- ];
363
- if (snapshotId !== versionId) {
364
- // versionId could be "latest". When summarizer checks cache for "latest", we want it to be available.
365
- // TODO: For in-memory cache, <latest,snapshotTree> will be a shared pointer with <snapshotId,snapshotTree>,
366
- // However, for something like Redis, this will cache the same value twice. Alternatively, could we simply
367
- // cache with versionId?
368
- cachePs.push(this.snapshotTreeCache.put(
369
- versionId,
370
- normalizedWholeSummary.snapshotTree,
371
- ));
372
- }
373
-
374
- await Promise.all([
375
- this.snapshotTreeCache.put(
376
- snapshotId,
377
- normalizedWholeSummary.snapshotTree,
378
- ),
379
- this.initBlobCache(normalizedWholeSummary.blobs),
380
- ]);
381
-
382
- return { id: snapshotId, snapshotTree: normalizedWholeSummary.snapshotTree};
383
- }
384
-
385
- private async initBlobCache(blobs: Map<string, ArrayBuffer>): Promise<void> {
386
- const blobCachePutPs: Promise<void>[] = [];
387
- blobs.forEach((value, id) => {
388
- blobCachePutPs.push(this.blobCache.put(id, value));
389
- });
390
- await Promise.all(blobCachePutPs);
391
- }
392
- }
21
+ import { ICache } from "./cache";
22
+ import { WholeSummaryDocumentStorageService } from "./wholeSummaryDocumentStorageService";
23
+ import { ShreddedSummaryDocumentStorageService } from "./shreddedSummaryDocumentStorageService";
393
24
 
394
25
  export class DocumentStorageService extends DocumentStorageServiceProxy {
395
26
  private _logTailSha: string | undefined = undefined;
@@ -407,8 +38,23 @@ export class DocumentStorageService extends DocumentStorageServiceProxy {
407
38
  blobCache?: ICache<ArrayBufferLike>,
408
39
  snapshotTreeCache?: ICache<ISnapshotTree>): IDocumentStorageService {
409
40
  const storageService = driverPolicies?.enableWholeSummaryUpload ?
410
- new WholeSummaryDocumentStorageService(id, manager, logger, policies, blobCache, snapshotTreeCache) :
411
- new ShreddedSummaryDocumentStorageService(id, manager, logger, policies);
41
+ new WholeSummaryDocumentStorageService(
42
+ id,
43
+ manager,
44
+ logger,
45
+ policies,
46
+ blobCache,
47
+ snapshotTreeCache,
48
+ ) :
49
+ new ShreddedSummaryDocumentStorageService(
50
+ id,
51
+ manager,
52
+ logger,
53
+ policies,
54
+ driverPolicies,
55
+ blobCache,
56
+ snapshotTreeCache,
57
+ );
412
58
  // TODO: worth prefetching latest summary making version + snapshot call with WholeSummary storage?
413
59
  if (!driverPolicies?.enableWholeSummaryUpload && policies.caching === LoaderCachingPolicy.Prefetch) {
414
60
  return new PrefetchDocumentStorageService(storageService);
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/routerlicious-driver";
9
- export const pkgVersion = "0.51.0";
9
+ export const pkgVersion = "0.52.0";
@@ -0,0 +1,213 @@
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
+ stringToBuffer,
9
+ Uint8ArrayToString,
10
+ } from "@fluidframework/common-utils";
11
+ import {
12
+ IDocumentStorageService,
13
+ ISummaryContext,
14
+ IDocumentStorageServicePolicies,
15
+ } from "@fluidframework/driver-definitions";
16
+ import { buildHierarchy } from "@fluidframework/protocol-base";
17
+ import {
18
+ ICreateBlobResponse,
19
+ ISnapshotTree,
20
+ ISnapshotTreeEx,
21
+ ISummaryHandle,
22
+ ISummaryTree,
23
+ ITree,
24
+ IVersion,
25
+ } from "@fluidframework/protocol-definitions";
26
+ import {
27
+ GitManager,
28
+ ISummaryUploadManager,
29
+ SummaryTreeUploadManager,
30
+ } from "@fluidframework/server-services-client";
31
+ import { PerformanceEvent } from "@fluidframework/telemetry-utils";
32
+ import { IRouterliciousDriverPolicies } from "./policies";
33
+ import { ICache, InMemoryCache } from "./cache";
34
+ import { RetriableGitManager } from "./retriableGitManager";
35
+
36
+ // eslint-disable-next-line no-new-func,@typescript-eslint/no-implied-eval
37
+ const isNode = (new Function("try {return this===global;}catch(e){ return false;}"))();
38
+
39
+ /**
40
+ * Document access to underlying storage for routerlicious driver.
41
+ * Uploads summaries piece-by-piece traversing the tree recursively.
42
+ * Downloads summaries piece-by-piece on-demand, or up-front when prefetch is enabled.
43
+ */
44
+ export class ShreddedSummaryDocumentStorageService implements IDocumentStorageService {
45
+ // The values of this cache is useless. We only need the keys. So we are always putting
46
+ // empty strings as values.
47
+ protected readonly blobsShaCache = new Map<string, string>();
48
+ private readonly blobCache: ICache<ArrayBufferLike> | undefined;
49
+ private readonly snapshotTreeCache: ICache<ISnapshotTreeEx> | undefined;
50
+ private readonly summaryUploadManager: ISummaryUploadManager;
51
+
52
+ public get repositoryUrl(): string {
53
+ return "";
54
+ }
55
+
56
+ constructor(
57
+ protected readonly id: string,
58
+ protected readonly manager: GitManager,
59
+ protected readonly logger: ITelemetryLogger,
60
+ public readonly policies: IDocumentStorageServicePolicies = {},
61
+ driverPolicies?: IRouterliciousDriverPolicies,
62
+ blobCache?: ICache<ArrayBufferLike>,
63
+ snapshotTreeCache?: ICache<ISnapshotTree>) {
64
+ this.summaryUploadManager = new SummaryTreeUploadManager(
65
+ new RetriableGitManager(manager, logger),
66
+ this.blobsShaCache,
67
+ this.getPreviousFullSnapshot.bind(this),
68
+ );
69
+ if (driverPolicies?.enableRestLess === true || isNode) {
70
+ this.blobCache = blobCache ?? new InMemoryCache();
71
+ this.snapshotTreeCache = (snapshotTreeCache ?? new InMemoryCache()) as ICache<ISnapshotTreeEx>;
72
+ }
73
+ }
74
+
75
+ public async getVersions(versionId: string, count: number): Promise<IVersion[]> {
76
+ const id = versionId ? versionId : this.id;
77
+ const commits = await PerformanceEvent.timedExecAsync(
78
+ this.logger,
79
+ {
80
+ eventName: "getVersions",
81
+ versionId: id,
82
+ count,
83
+ },
84
+ async () => this.manager.getCommits(id, count),
85
+ );
86
+ return commits.map((commit) => ({
87
+ date: commit.commit.author.date,
88
+ id: commit.sha,
89
+ treeId: commit.commit.tree.sha,
90
+ }));
91
+ }
92
+
93
+ public async getSnapshotTree(version?: IVersion): Promise<ISnapshotTreeEx | null> {
94
+ let requestVersion = version;
95
+ if (!requestVersion) {
96
+ const versions = await this.getVersions(this.id, 1);
97
+ if (versions.length === 0) {
98
+ return null;
99
+ }
100
+
101
+ requestVersion = versions[0];
102
+ }
103
+
104
+ const cachedSnapshotTree = await this.snapshotTreeCache?.get(requestVersion.treeId);
105
+ if (cachedSnapshotTree) {
106
+ return cachedSnapshotTree;
107
+ }
108
+
109
+ const rawTree = await PerformanceEvent.timedExecAsync(
110
+ this.logger,
111
+ {
112
+ eventName: "getSnapshotTree",
113
+ treeId: requestVersion.treeId,
114
+ },
115
+ async (event) => {
116
+ const response = await this.manager.getTree(requestVersion!.treeId);
117
+ event.end({
118
+ size: response.tree.length,
119
+ });
120
+ return response;
121
+ },
122
+ );
123
+ const tree = buildHierarchy(rawTree, this.blobsShaCache, true);
124
+ await this.snapshotTreeCache?.put(tree.id, tree);
125
+ return tree;
126
+ }
127
+
128
+ public async readBlob(blobId: string): Promise<ArrayBufferLike> {
129
+ const cachedBlob = await this.blobCache?.get(blobId);
130
+ if (cachedBlob) {
131
+ return cachedBlob;
132
+ }
133
+
134
+ const value = await PerformanceEvent.timedExecAsync(
135
+ this.logger,
136
+ {
137
+ eventName: "readBlob",
138
+ blobId,
139
+ },
140
+ async (event) => {
141
+ const response = await this.manager.getBlob(blobId);
142
+ event.end({
143
+ size: response.size,
144
+ });
145
+ return response;
146
+ },
147
+ );
148
+ this.blobsShaCache.set(value.sha, "");
149
+ const bufferContent = stringToBuffer(value.content, value.encoding);
150
+ await this.blobCache?.put(value.sha, bufferContent);
151
+ return bufferContent;
152
+ }
153
+
154
+ public async write(tree: ITree, parents: string[], message: string, ref: string): Promise<IVersion> {
155
+ const branch = ref ? `datastores/${this.id}/${ref}` : this.id;
156
+ const commit = await PerformanceEvent.timedExecAsync(
157
+ this.logger,
158
+ {
159
+ eventName: "write",
160
+ id: branch,
161
+ },
162
+ async () => this.manager.write(branch, tree, parents, message),
163
+ );
164
+ return { date: commit.committer.date, id: commit.sha, treeId: commit.tree.sha };
165
+ }
166
+
167
+ public async uploadSummaryWithContext(summary: ISummaryTree, context: ISummaryContext): Promise<string> {
168
+ const summaryHandle = await PerformanceEvent.timedExecAsync(
169
+ this.logger,
170
+ {
171
+ eventName: "uploadSummaryWithContext",
172
+ },
173
+ async () => this.summaryUploadManager.writeSummaryTree(summary, context.ackHandle ?? "", "channel"),
174
+ );
175
+ return summaryHandle;
176
+ }
177
+
178
+ public async downloadSummary(handle: ISummaryHandle): Promise<ISummaryTree> {
179
+ throw new Error("NOT IMPLEMENTED!");
180
+ }
181
+
182
+ public async createBlob(file: ArrayBufferLike): Promise<ICreateBlobResponse> {
183
+ const uint8ArrayFile = new Uint8Array(file);
184
+ return PerformanceEvent.timedExecAsync(
185
+ this.logger,
186
+ {
187
+ eventName: "createBlob",
188
+ size: uint8ArrayFile.length,
189
+ },
190
+ async (event) => {
191
+ const response = await this.manager.createBlob(
192
+ Uint8ArrayToString(
193
+ uint8ArrayFile, "base64"),
194
+ "base64").then((r) => ({ id: r.sha, url: r.url }));
195
+ event.end({
196
+ blobId: response.id,
197
+ });
198
+ return response;
199
+ },
200
+ );
201
+ }
202
+
203
+ private async getPreviousFullSnapshot(parentHandle: string): Promise<ISnapshotTreeEx | null | undefined> {
204
+ return parentHandle
205
+ ? this.getVersions(parentHandle, 1)
206
+ .then(async (versions) => {
207
+ // Clear the cache as the getSnapshotTree call will fill the cache.
208
+ this.blobsShaCache.clear();
209
+ return this.getSnapshotTree(versions[0]);
210
+ })
211
+ : undefined;
212
+ }
213
+ }