@fluidframework/odsp-driver 1.1.0 → 1.2.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 (127) hide show
  1. package/dist/checkUrl.d.ts +1 -1
  2. package/dist/checkUrl.js +1 -1
  3. package/dist/checkUrl.js.map +1 -1
  4. package/dist/constants.d.ts +1 -1
  5. package/dist/constants.d.ts.map +1 -1
  6. package/dist/constants.js +1 -1
  7. package/dist/constants.js.map +1 -1
  8. package/dist/contractsPublic.d.ts +1 -0
  9. package/dist/contractsPublic.d.ts.map +1 -1
  10. package/dist/contractsPublic.js.map +1 -1
  11. package/dist/getFileLink.js +0 -4
  12. package/dist/getFileLink.js.map +1 -1
  13. package/dist/localOdspDriver/localOdspDocumentService.d.ts +26 -0
  14. package/dist/localOdspDriver/localOdspDocumentService.d.ts.map +1 -0
  15. package/dist/localOdspDriver/localOdspDocumentService.js +39 -0
  16. package/dist/localOdspDriver/localOdspDocumentService.js.map +1 -0
  17. package/dist/localOdspDriver/localOdspDocumentServiceFactory.d.ts +24 -0
  18. package/dist/localOdspDriver/localOdspDocumentServiceFactory.d.ts.map +1 -0
  19. package/dist/localOdspDriver/localOdspDocumentServiceFactory.js +45 -0
  20. package/dist/localOdspDriver/localOdspDocumentServiceFactory.js.map +1 -0
  21. package/dist/localOdspDriver/localOdspDocumentStorageManager.d.ts +27 -0
  22. package/dist/localOdspDriver/localOdspDocumentStorageManager.d.ts.map +1 -0
  23. package/dist/localOdspDriver/localOdspDocumentStorageManager.js +66 -0
  24. package/dist/localOdspDriver/localOdspDocumentStorageManager.js.map +1 -0
  25. package/dist/odspDeltaStorageService.js.map +1 -1
  26. package/dist/odspDocumentService.d.ts.map +1 -1
  27. package/dist/odspDocumentService.js.map +1 -1
  28. package/dist/odspDocumentServiceFactory.d.ts +2 -1
  29. package/dist/odspDocumentServiceFactory.d.ts.map +1 -1
  30. package/dist/odspDocumentServiceFactory.js +7 -1
  31. package/dist/odspDocumentServiceFactory.js.map +1 -1
  32. package/dist/odspDocumentServiceFactoryCore.d.ts +3 -1
  33. package/dist/odspDocumentServiceFactoryCore.d.ts.map +1 -1
  34. package/dist/odspDocumentServiceFactoryCore.js.map +1 -1
  35. package/dist/odspDocumentStorageManager.d.ts +5 -23
  36. package/dist/odspDocumentStorageManager.d.ts.map +1 -1
  37. package/dist/odspDocumentStorageManager.js +52 -245
  38. package/dist/odspDocumentStorageManager.js.map +1 -1
  39. package/dist/odspDocumentStorageServiceBase.d.ts +58 -0
  40. package/dist/odspDocumentStorageServiceBase.d.ts.map +1 -0
  41. package/dist/odspDocumentStorageServiceBase.js +216 -0
  42. package/dist/odspDocumentStorageServiceBase.js.map +1 -0
  43. package/dist/odspDriverUrlResolver.js +1 -1
  44. package/dist/odspDriverUrlResolver.js.map +1 -1
  45. package/dist/odspDriverUrlResolverForShareLink.d.ts +8 -2
  46. package/dist/odspDriverUrlResolverForShareLink.d.ts.map +1 -1
  47. package/dist/odspDriverUrlResolverForShareLink.js +11 -3
  48. package/dist/odspDriverUrlResolverForShareLink.js.map +1 -1
  49. package/dist/odspFluidFileLink.d.ts.map +1 -1
  50. package/dist/odspFluidFileLink.js +27 -21
  51. package/dist/odspFluidFileLink.js.map +1 -1
  52. package/dist/packageVersion.d.ts +1 -1
  53. package/dist/packageVersion.js +1 -1
  54. package/dist/packageVersion.js.map +1 -1
  55. package/lib/checkUrl.d.ts +1 -1
  56. package/lib/checkUrl.js +1 -1
  57. package/lib/checkUrl.js.map +1 -1
  58. package/lib/constants.d.ts +1 -1
  59. package/lib/constants.d.ts.map +1 -1
  60. package/lib/constants.js +1 -1
  61. package/lib/constants.js.map +1 -1
  62. package/lib/contractsPublic.d.ts +1 -0
  63. package/lib/contractsPublic.d.ts.map +1 -1
  64. package/lib/contractsPublic.js.map +1 -1
  65. package/lib/getFileLink.js +0 -4
  66. package/lib/getFileLink.js.map +1 -1
  67. package/lib/localOdspDriver/localOdspDocumentService.d.ts +26 -0
  68. package/lib/localOdspDriver/localOdspDocumentService.d.ts.map +1 -0
  69. package/lib/localOdspDriver/localOdspDocumentService.js +35 -0
  70. package/lib/localOdspDriver/localOdspDocumentService.js.map +1 -0
  71. package/lib/localOdspDriver/localOdspDocumentServiceFactory.d.ts +24 -0
  72. package/lib/localOdspDriver/localOdspDocumentServiceFactory.d.ts.map +1 -0
  73. package/lib/localOdspDriver/localOdspDocumentServiceFactory.js +41 -0
  74. package/lib/localOdspDriver/localOdspDocumentServiceFactory.js.map +1 -0
  75. package/lib/localOdspDriver/localOdspDocumentStorageManager.d.ts +27 -0
  76. package/lib/localOdspDriver/localOdspDocumentStorageManager.d.ts.map +1 -0
  77. package/lib/localOdspDriver/localOdspDocumentStorageManager.js +62 -0
  78. package/lib/localOdspDriver/localOdspDocumentStorageManager.js.map +1 -0
  79. package/lib/odspDeltaStorageService.js.map +1 -1
  80. package/lib/odspDocumentService.d.ts.map +1 -1
  81. package/lib/odspDocumentService.js +1 -1
  82. package/lib/odspDocumentService.js.map +1 -1
  83. package/lib/odspDocumentServiceFactory.d.ts +2 -1
  84. package/lib/odspDocumentServiceFactory.d.ts.map +1 -1
  85. package/lib/odspDocumentServiceFactory.js +5 -0
  86. package/lib/odspDocumentServiceFactory.js.map +1 -1
  87. package/lib/odspDocumentServiceFactoryCore.d.ts +3 -1
  88. package/lib/odspDocumentServiceFactoryCore.d.ts.map +1 -1
  89. package/lib/odspDocumentServiceFactoryCore.js.map +1 -1
  90. package/lib/odspDocumentStorageManager.d.ts +5 -23
  91. package/lib/odspDocumentStorageManager.d.ts.map +1 -1
  92. package/lib/odspDocumentStorageManager.js +53 -246
  93. package/lib/odspDocumentStorageManager.js.map +1 -1
  94. package/lib/odspDocumentStorageServiceBase.d.ts +58 -0
  95. package/lib/odspDocumentStorageServiceBase.d.ts.map +1 -0
  96. package/lib/odspDocumentStorageServiceBase.js +212 -0
  97. package/lib/odspDocumentStorageServiceBase.js.map +1 -0
  98. package/lib/odspDriverUrlResolver.js +1 -1
  99. package/lib/odspDriverUrlResolver.js.map +1 -1
  100. package/lib/odspDriverUrlResolverForShareLink.d.ts +8 -2
  101. package/lib/odspDriverUrlResolverForShareLink.d.ts.map +1 -1
  102. package/lib/odspDriverUrlResolverForShareLink.js +11 -3
  103. package/lib/odspDriverUrlResolverForShareLink.js.map +1 -1
  104. package/lib/odspFluidFileLink.d.ts.map +1 -1
  105. package/lib/odspFluidFileLink.js +27 -21
  106. package/lib/odspFluidFileLink.js.map +1 -1
  107. package/lib/packageVersion.d.ts +1 -1
  108. package/lib/packageVersion.js +1 -1
  109. package/lib/packageVersion.js.map +1 -1
  110. package/package.json +17 -12
  111. package/src/checkUrl.ts +1 -1
  112. package/src/constants.ts +1 -1
  113. package/src/contractsPublic.ts +1 -0
  114. package/src/getFileLink.ts +0 -5
  115. package/src/localOdspDriver/localOdspDocumentService.ts +54 -0
  116. package/src/localOdspDriver/localOdspDocumentServiceFactory.ts +67 -0
  117. package/src/localOdspDriver/localOdspDocumentStorageManager.ts +83 -0
  118. package/src/odspDeltaStorageService.ts +1 -1
  119. package/src/odspDocumentService.ts +5 -1
  120. package/src/odspDocumentServiceFactory.ts +7 -3
  121. package/src/odspDocumentServiceFactoryCore.ts +1 -1
  122. package/src/odspDocumentStorageManager.ts +81 -312
  123. package/src/odspDocumentStorageServiceBase.ts +268 -0
  124. package/src/odspDriverUrlResolver.ts +1 -1
  125. package/src/odspDriverUrlResolverForShareLink.ts +13 -1
  126. package/src/odspFluidFileLink.ts +26 -22
  127. package/src/packageVersion.ts +1 -1
@@ -15,8 +15,6 @@ import {
15
15
  import * as api from "@fluidframework/protocol-definitions";
16
16
  import {
17
17
  ISummaryContext,
18
- IDocumentStorageService,
19
- LoaderCachingPolicy,
20
18
  DriverErrorType,
21
19
  } from "@fluidframework/driver-definitions";
22
20
  import { RateLimiter, NonRetryableError } from "@fluidframework/driver-utils";
@@ -40,10 +38,11 @@ import {
40
38
  getWithRetryForTokenRefresh,
41
39
  } from "./odspUtils";
42
40
  import { ISnapshotContents } from "./odspPublicUtils";
43
- import { defaultCacheExpiryTimeoutMs, EpochTracker } from "./epochTracker";
41
+ import { EpochTracker } from "./epochTracker";
44
42
  import { OdspSummaryUploadManager } from "./odspSummaryUploadManager";
45
43
  import { FlushResult } from "./odspDocumentDeltaConnection";
46
44
  import { pkgVersion as driverVersion } from "./packageVersion";
45
+ import { OdspDocumentStorageServiceBase } from "./odspDocumentStorageServiceBase";
47
46
 
48
47
  export const defaultSummarizerCacheExpiryTimeout: number = 60 * 1000; // 60 seconds.
49
48
 
@@ -58,134 +57,15 @@ async function promiseRaceWithWinner<T>(promises: Promise<T>[]): Promise<{ index
58
57
  });
59
58
  }
60
59
 
61
- class BlobCache {
62
- // Save the timeout so we can cancel and reschedule it as needed
63
- private blobCacheTimeout: ReturnType<typeof setTimeout> | undefined;
64
- // If the defer flag is set when the timeout fires, we'll reschedule rather than clear immediately
65
- // This deferral approach is used (rather than clearing/resetting the timer) as current calling patterns trigger
66
- // too many calls to setTimeout/clearTimeout.
67
- private deferBlobCacheClear: boolean = false;
68
-
69
- private readonly _blobCache: Map<string, ArrayBuffer> = new Map();
70
-
71
- // Tracks all blob IDs evicted from cache
72
- private readonly blobsEvicted: Set<string> = new Set();
73
-
74
- // Initial time-out to purge data from cache
75
- // If this time out is very small, then we purge blobs from cache too soon and that results in a lot of
76
- // requests to storage, which brings down perf and may trip protection limits causing 429s
77
- private blobCacheTimeoutDuration = 2 * 60 * 1000;
78
-
79
- // SPO does not keep old snapshots around for long, so we are running chances of not
80
- // being able to rehydrate data store / DDS in the future if we purge anything (and with blob de-duping,
81
- // even if blob read by runtime, it could be read again in the future)
82
- // So for now, purging is disabled.
83
- private readonly purgeEnabled = false;
84
-
85
- public get value() {
86
- return this._blobCache;
87
- }
88
-
89
- public addBlobs(blobs: Map<string, ArrayBuffer>) {
90
- blobs.forEach((value, blobId) => {
91
- this._blobCache.set(blobId, value);
92
- });
93
- // Reset the timer on cache set
94
- this.scheduleClearBlobsCache();
95
- }
96
-
97
- /**
98
- * Schedule a timer for clearing the blob cache or defer the current one.
99
- */
100
- private scheduleClearBlobsCache() {
101
- if (this.blobCacheTimeout !== undefined) {
102
- // If we already have an outstanding timer, just signal that we should defer the clear
103
- this.deferBlobCacheClear = true;
104
- } else if (this.purgeEnabled) {
105
- // If we don't have an outstanding timer, set a timer
106
- // When the timer runs out, we'll decide whether to proceed with the cache clear or reset the timer
107
- const clearCacheOrDefer = () => {
108
- this.blobCacheTimeout = undefined;
109
- if (this.deferBlobCacheClear) {
110
- this.deferBlobCacheClear = false;
111
- this.scheduleClearBlobsCache();
112
- } else {
113
- // NOTE: Slightly better algorithm here would be to purge either only big blobs,
114
- // or sort them by size and purge enough big blobs to leave only 256Kb of small blobs in cache
115
- // Purging is optimizing memory footprint. But count controls potential number of storage requests
116
- // We want to optimize both - memory footprint and number of future requests to storage.
117
- // Note that Container can realize data store or DDS on-demand at any point in time, so we do not
118
- // control when blobs will be used.
119
- this._blobCache.forEach((_, blobId) => this.blobsEvicted.add(blobId));
120
- this._blobCache.clear();
121
- }
122
- };
123
- this.blobCacheTimeout = setTimeout(clearCacheOrDefer, this.blobCacheTimeoutDuration);
124
- // any future storage reads that get into the cache should be cleared from cache rather quickly -
125
- // there is not much value in keeping them longer
126
- this.blobCacheTimeoutDuration = 10 * 1000;
127
- }
128
- }
129
-
130
- public getBlob(blobId: string) {
131
- // Reset the timer on attempted cache read
132
- this.scheduleClearBlobsCache();
133
- const blobContent = this._blobCache.get(blobId);
134
- const evicted = this.blobsEvicted.has(blobId);
135
- return { blobContent, evicted };
136
- }
137
-
138
- public setBlob(blobId: string, blob: ArrayBuffer) {
139
- // This API is called as result of cache miss and reading blob from storage.
140
- // Runtime never reads same blob twice.
141
- // The only reason we may get read request for same blob is blob de-duping in summaries.
142
- // Note that the bigger the size, the less likely blobs are the same, so there is very little benefit of caching big blobs.
143
- // Images are the only exception - user may insert same image twice. But we currently do not de-dup them - only snapshot
144
- // blobs are de-duped.
145
- const size = blob.byteLength;
146
- if (size < 256 * 1024) {
147
- // Reset the timer on cache set
148
- this.scheduleClearBlobsCache();
149
- return this._blobCache.set(blobId, blob);
150
- } else {
151
- // we evicted it here by not caching.
152
- this.blobsEvicted.add(blobId);
153
- }
154
- }
155
- }
156
-
157
60
  interface GetVersionsTelemetryProps {
158
61
  cacheEntryAge?: number;
159
62
  cacheSummarizerExpired?: boolean;
160
63
  }
161
- export class OdspDocumentStorageService implements IDocumentStorageService {
162
- readonly policies = {
163
- // By default, ODSP tells the container not to prefetch/cache.
164
- caching: LoaderCachingPolicy.NoCaching,
165
-
166
- // ODSP storage works better if it has less number of blobs / edges
167
- // Runtime creating many small blobs results in sub-optimal perf.
168
- // 2K seems like the sweat spot:
169
- // The smaller the number, less blobs we aggregate. Most storages are very likely to have notion
170
- // of minimal "cluster" size, so having small blobs is wasteful
171
- // At the same time increasing the limit ensure that more blobs with user content are aggregated,
172
- // reducing possibility for de-duping of same blobs (i.e. .attributes rolled into aggregate blob
173
- // are not reused across data stores, or even within data store, resulting in duplication of content)
174
- // Note that duplication of content should not have significant impact for bytes over wire as
175
- // compression of http payload mostly takes care of it, but it does impact storage size and in-memory sizes.
176
- minBlobSize: 2048,
177
- maximumCacheDurationMs: defaultCacheExpiryTimeoutMs,
178
- };
179
-
180
- private readonly commitCache: Map<string, api.ISnapshotTree> = new Map();
181
-
182
- private readonly attributesBlobHandles: Set<string> = new Set();
183
64
 
65
+ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
184
66
  private readonly odspSummaryUploadManager: OdspSummaryUploadManager;
185
- private _ops: api.ISequencedDocumentMessage[] | undefined;
186
67
 
187
68
  private firstVersionCall = true;
188
- private _snapshotSequenceNumber: number | undefined;
189
69
 
190
70
  private readonly documentId: string;
191
71
  private readonly snapshotUrl: string | undefined;
@@ -203,21 +83,6 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
203
83
  // limits the amount of parallel "attachment" blob uploads
204
84
  private readonly createBlobRateLimiter = new RateLimiter(1);
205
85
 
206
- private readonly blobCache = new BlobCache();
207
-
208
- public set ops(ops: api.ISequencedDocumentMessage[] | undefined) {
209
- assert(this._ops === undefined, 0x0a5 /* "Trying to set ops when they are already set!" */);
210
- this._ops = ops;
211
- }
212
-
213
- public get ops(): api.ISequencedDocumentMessage[] | undefined {
214
- return this._ops;
215
- }
216
-
217
- public get snapshotSequenceNumber() {
218
- return this._snapshotSequenceNumber;
219
- }
220
-
221
86
  constructor(
222
87
  private readonly odspResolvedUrl: IOdspResolvedUrl,
223
88
  private readonly getStorageToken: InstrumentedStorageTokenFetcher,
@@ -229,10 +94,13 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
229
94
  private readonly flushCallback: () => Promise<FlushResult>,
230
95
  private readonly snapshotFormatFetchType?: SnapshotFormatSupportType,
231
96
  ) {
97
+ super();
98
+
232
99
  this.documentId = this.odspResolvedUrl.hashedDocumentId;
233
100
  this.snapshotUrl = this.odspResolvedUrl.endpoints.snapshotStorageUrl;
234
101
  this.attachmentPOSTUrl = this.odspResolvedUrl.endpoints.attachmentPOSTStorageUrl;
235
102
  this.attachmentGETUrl = this.odspResolvedUrl.endpoints.attachmentGETStorageUrl;
103
+
236
104
  this.odspSummaryUploadManager = new OdspSummaryUploadManager(
237
105
  this.snapshotUrl,
238
106
  getStorageToken,
@@ -242,10 +110,6 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
242
110
  );
243
111
  }
244
112
 
245
- public get repositoryUrl(): string {
246
- return "";
247
- }
248
-
249
113
  public async createBlob(file: ArrayBufferLike): Promise<api.ICreateBlobResponse> {
250
114
  this.checkAttachmentPOSTUrl();
251
115
 
@@ -288,95 +152,56 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
288
152
  return response.content;
289
153
  }
290
154
 
291
- private async readBlobCore(blobId: string): Promise<ArrayBuffer> {
292
- const { blobContent, evicted } = this.blobCache.getBlob(blobId);
293
- let blob = blobContent;
294
-
295
- if (blob === undefined) {
296
- this.checkAttachmentGETUrl();
155
+ protected async fetchBlobFromStorage(blobId: string, evicted: boolean): Promise<ArrayBuffer> {
156
+ this.checkAttachmentGETUrl();
297
157
 
298
- blob = await getWithRetryForTokenRefresh(async (options) => {
299
- const storageToken = await this.getStorageToken(options, "GetBlob");
300
- const unAuthedUrl = `${this.attachmentGETUrl}/${encodeURIComponent(blobId)}/content`;
301
- const { url, headers } = getUrlAndHeadersWithAuth(
302
- unAuthedUrl,
303
- storageToken,
304
- !!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
305
- );
158
+ const blob = await getWithRetryForTokenRefresh(async (options) => {
159
+ const storageToken = await this.getStorageToken(options, "GetBlob");
160
+ const unAuthedUrl = `${this.attachmentGETUrl}/${encodeURIComponent(blobId)}/content`;
161
+ const { url, headers } = getUrlAndHeadersWithAuth(
162
+ unAuthedUrl,
163
+ storageToken,
164
+ !!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
165
+ );
306
166
 
307
- return PerformanceEvent.timedExecAsync(
308
- this.logger,
309
- {
310
- eventName: "readDataBlob",
311
- blobId,
312
- evicted,
313
- headers: Object.keys(headers).length !== 0 ? true : undefined,
167
+ return PerformanceEvent.timedExecAsync(
168
+ this.logger,
169
+ {
170
+ eventName: "readDataBlob",
171
+ blobId,
172
+ evicted,
173
+ headers: Object.keys(headers).length !== 0 ? true : undefined,
174
+ waitQueueLength: this.epochTracker.rateLimiter.waitQueueLength,
175
+ },
176
+ async (event) => {
177
+ const res = await this.epochTracker.fetchArray(url, { headers }, "blob");
178
+ event.end({
314
179
  waitQueueLength: this.epochTracker.rateLimiter.waitQueueLength,
315
- },
316
- async (event) => {
317
- const res = await this.epochTracker.fetchArray(url, { headers }, "blob");
318
- event.end({
319
- waitQueueLength: this.epochTracker.rateLimiter.waitQueueLength,
180
+ ...res.propsToLog,
181
+ attempts: options.refresh ? 2 : 1,
182
+ });
183
+ const cacheControl = res.headers.get("cache-control");
184
+ if (cacheControl === undefined || !(cacheControl.includes("private") || cacheControl.includes("public"))) {
185
+ this.logger.sendErrorEvent({
186
+ eventName: "NonCacheableBlob",
187
+ cacheControl,
188
+ blobId,
320
189
  ...res.propsToLog,
321
- attempts: options.refresh ? 2 : 1,
322
190
  });
323
- const cacheControl = res.headers.get("cache-control");
324
- if (cacheControl === undefined || !(cacheControl.includes("private") || cacheControl.includes("public"))) {
325
- this.logger.sendErrorEvent({
326
- eventName: "NonCacheableBlob",
327
- cacheControl,
328
- blobId,
329
- ...res.propsToLog,
330
- });
331
- }
332
- return res.content;
333
- },
334
- );
335
- });
336
- this.blobCache.setBlob(blobId, blob);
337
- }
338
-
191
+ }
192
+ return res.content;
193
+ },
194
+ );
195
+ });
196
+ this.blobCache.setBlob(blobId, blob);
339
197
  return blob;
340
198
  }
341
199
 
342
- public async readBlob(blobId: string): Promise<ArrayBufferLike> {
343
- return this.readBlobCore(blobId);
344
- }
345
-
346
200
  public async getSnapshotTree(version?: api.IVersion, scenarioName?: string): Promise<api.ISnapshotTree | null> {
347
201
  if (!this.snapshotUrl) {
348
202
  return null;
349
203
  }
350
-
351
- let id: string;
352
- if (!version || !version.id) {
353
- const versions = await this.getVersions(null, 1, scenarioName);
354
- if (!versions || versions.length === 0) {
355
- return null;
356
- }
357
- id = versions[0].id;
358
- } else {
359
- id = version.id;
360
- }
361
-
362
- const snapshotTree = await this.readTree(id, scenarioName);
363
- if (!snapshotTree) {
364
- return null;
365
- }
366
-
367
- if (snapshotTree.blobs) {
368
- const attributesBlob = snapshotTree.blobs.attributes;
369
- if (attributesBlob) {
370
- this.attributesBlobHandles.add(attributesBlob);
371
- }
372
- }
373
-
374
- // When we upload the container snapshot, we upload appTree in ".app" and protocol tree in ".protocol"
375
- // So when we request the snapshot we get ".app" as tree and not as commit node as in the case just above.
376
- const appTree = snapshotTree.trees[".app"];
377
- const protocolTree = snapshotTree.trees[".protocol"];
378
-
379
- return this.combineProtocolAndAppSnapshotTree(appTree, protocolTree);
204
+ return super.getSnapshotTree(version, scenarioName);
380
205
  }
381
206
 
382
207
  public async getVersions(blobid: string | null, count: number, scenarioName?: string): Promise<api.IVersion[]> {
@@ -491,21 +316,8 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
491
316
 
492
317
  // Successful call, make network calls only
493
318
  this.firstVersionCall = false;
319
+ const id = this.initializeFromSnapshot(odspSnapshotCacheValue);
494
320
 
495
- this._snapshotSequenceNumber = odspSnapshotCacheValue.sequenceNumber;
496
- const { snapshotTree, blobs, ops } = odspSnapshotCacheValue;
497
- // id should be undefined in case of just ops in snapshot.
498
- let id: string | undefined;
499
- if (snapshotTree) {
500
- id = snapshotTree.id;
501
- assert(id !== undefined, 0x221 /* "Root tree should contain the id" */);
502
- this.setRootTree(id, snapshotTree);
503
- }
504
- if (blobs) {
505
- this.initBlobsCache(blobs);
506
- }
507
-
508
- this.ops = ops;
509
321
  return id ? [{ id, treeId: undefined! }] : [];
510
322
  }
511
323
 
@@ -648,15 +460,14 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
648
460
 
649
461
  // Enable flushing only if we have single commit summary and this is not the initial summary for an empty file
650
462
  if (".protocol" in summary.tree && context.ackHandle !== undefined) {
651
- let retry = 0;
652
- for (; ;) {
463
+ let retry = 1;
464
+ for (;;) {
653
465
  const result = await this.flushCallback();
654
466
  const seq = result.lastPersistedSequenceNumber;
655
467
  if (seq !== undefined && seq >= context.referenceSequenceNumber) {
656
468
  break;
657
469
  }
658
470
 
659
- retry++;
660
471
  if (retry > 3) {
661
472
  this.logger.sendErrorEvent({
662
473
  eventName: "FlushFailure",
@@ -674,6 +485,7 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
674
485
  referenceSequenceNumber: context.referenceSequenceNumber,
675
486
  });
676
487
 
488
+ retry++;
677
489
  await delay(1000 * (result.retryAfter ?? 1));
678
490
  }
679
491
  }
@@ -682,18 +494,6 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
682
494
  return id;
683
495
  }
684
496
 
685
- public async downloadSummary(commit: api.ISummaryHandle): Promise<api.ISummaryTree> {
686
- throw new Error("Not implemented yet");
687
- }
688
-
689
- private setRootTree(id: string, tree: api.ISnapshotTree) {
690
- this.commitCache.set(id, tree);
691
- }
692
-
693
- private initBlobsCache(blobs: Map<string, ArrayBuffer>) {
694
- this.blobCache.addBlobs(blobs);
695
- }
696
-
697
497
  private checkSnapshotUrl() {
698
498
  if (!this.snapshotUrl) {
699
499
  throw new NonRetryableError(
@@ -721,71 +521,40 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
721
521
  }
722
522
  }
723
523
 
724
- private async readTree(id: string, scenarioName?: string): Promise<api.ISnapshotTree | null> {
725
- if (!this.snapshotUrl) {
726
- return null;
727
- }
728
- let tree = this.commitCache.get(id);
729
- if (!tree) {
730
- tree = await getWithRetryForTokenRefresh(async (options) => {
731
- const storageToken = await this.getStorageToken(options, "ReadCommit");
732
- const snapshotDownloader = async (url: string, fetchOptions: { [index: string]: any; }) => {
733
- return this.epochTracker.fetchAndParseAsJSON(
734
- url,
735
- fetchOptions,
736
- "snapshotTree",
737
- undefined,
738
- scenarioName,
739
- );
740
- };
741
- const snapshot = await fetchSnapshot(
742
- this.snapshotUrl!,
743
- storageToken,
744
- id,
745
- this.fetchFullSnapshot,
746
- !!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
747
- this.logger,
748
- snapshotDownloader,
524
+ protected async fetchTreeFromSnapshot(id: string, scenarioName?: string): Promise<api.ISnapshotTree | undefined> {
525
+ return getWithRetryForTokenRefresh(async (options) => {
526
+ const storageToken = await this.getStorageToken(options, "ReadCommit");
527
+ const snapshotDownloader = async (url: string, fetchOptions: { [index: string]: any; }) => {
528
+ return this.epochTracker.fetchAndParseAsJSON(
529
+ url,
530
+ fetchOptions,
531
+ "snapshotTree",
532
+ undefined,
533
+ scenarioName,
749
534
  );
750
- let treeId = "";
751
- if (snapshot.snapshotTree) {
752
- assert(snapshot.snapshotTree.id !== undefined, 0x222 /* "Root tree should contain the id!!" */);
753
- treeId = snapshot.snapshotTree.id;
754
- this.setRootTree(treeId, snapshot.snapshotTree);
755
- }
756
- if (snapshot.blobs) {
757
- this.initBlobsCache(snapshot.blobs);
758
- }
759
- // If the version id doesn't match with the id of the tree, then use the id of first tree which in that case
760
- // will be the actual id of tree to be fetched.
761
- return this.commitCache.get(id) ?? this.commitCache.get(treeId);
762
- });
763
- }
764
-
765
- if (!tree) {
766
- return null;
767
- }
768
-
769
- return tree;
770
- }
771
-
772
- private combineProtocolAndAppSnapshotTree(
773
- hierarchicalAppTree: api.ISnapshotTree,
774
- hierarchicalProtocolTree: api.ISnapshotTree,
775
- ) {
776
- const summarySnapshotTree: api.ISnapshotTree = {
777
- blobs: {
778
- ...hierarchicalAppTree.blobs,
779
- },
780
- trees: {
781
- ...hierarchicalAppTree.trees,
782
- // the app tree could have a .protocol
783
- // in that case we want to server protocol to override it
784
- ".protocol": hierarchicalProtocolTree,
785
- },
786
- };
787
-
788
- return summarySnapshotTree;
535
+ };
536
+ const snapshot = await fetchSnapshot(
537
+ this.snapshotUrl!,
538
+ storageToken,
539
+ id,
540
+ this.fetchFullSnapshot,
541
+ !!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
542
+ this.logger,
543
+ snapshotDownloader,
544
+ );
545
+ let treeId = "";
546
+ if (snapshot.snapshotTree) {
547
+ assert(snapshot.snapshotTree.id !== undefined, 0x222 /* "Root tree should contain the id!!" */);
548
+ treeId = snapshot.snapshotTree.id;
549
+ this.setRootTree(treeId, snapshot.snapshotTree);
550
+ }
551
+ if (snapshot.blobs) {
552
+ this.initBlobsCache(snapshot.blobs);
553
+ }
554
+ // If the version id doesn't match with the id of the tree, then use the id of first tree which in that case
555
+ // will be the actual id of tree to be fetched.
556
+ return this.commitCache.get(id) ?? this.commitCache.get(treeId);
557
+ });
789
558
  }
790
559
  }
791
560