@fluidframework/odsp-driver 1.1.0 → 1.2.0-77818
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/contractsPublic.d.ts +1 -0
- package/dist/contractsPublic.d.ts.map +1 -1
- package/dist/contractsPublic.js.map +1 -1
- package/dist/localOdspDriver/localOdspDocumentService.d.ts +26 -0
- package/dist/localOdspDriver/localOdspDocumentService.d.ts.map +1 -0
- package/dist/localOdspDriver/localOdspDocumentService.js +39 -0
- package/dist/localOdspDriver/localOdspDocumentService.js.map +1 -0
- package/dist/localOdspDriver/localOdspDocumentServiceFactory.d.ts +24 -0
- package/dist/localOdspDriver/localOdspDocumentServiceFactory.d.ts.map +1 -0
- package/dist/localOdspDriver/localOdspDocumentServiceFactory.js +45 -0
- package/dist/localOdspDriver/localOdspDocumentServiceFactory.js.map +1 -0
- package/dist/localOdspDriver/localOdspDocumentStorageManager.d.ts +27 -0
- package/dist/localOdspDriver/localOdspDocumentStorageManager.d.ts.map +1 -0
- package/dist/localOdspDriver/localOdspDocumentStorageManager.js +66 -0
- package/dist/localOdspDriver/localOdspDocumentStorageManager.js.map +1 -0
- package/dist/odspDeltaStorageService.js.map +1 -1
- package/dist/odspDocumentService.d.ts.map +1 -1
- package/dist/odspDocumentService.js.map +1 -1
- package/dist/odspDocumentServiceFactory.d.ts +2 -1
- package/dist/odspDocumentServiceFactory.d.ts.map +1 -1
- package/dist/odspDocumentServiceFactory.js +7 -1
- package/dist/odspDocumentServiceFactory.js.map +1 -1
- package/dist/odspDocumentServiceFactoryCore.d.ts +3 -1
- package/dist/odspDocumentServiceFactoryCore.d.ts.map +1 -1
- package/dist/odspDocumentServiceFactoryCore.js.map +1 -1
- package/dist/odspDocumentStorageManager.d.ts +5 -23
- package/dist/odspDocumentStorageManager.d.ts.map +1 -1
- package/dist/odspDocumentStorageManager.js +52 -245
- package/dist/odspDocumentStorageManager.js.map +1 -1
- package/dist/odspDocumentStorageServiceBase.d.ts +58 -0
- package/dist/odspDocumentStorageServiceBase.d.ts.map +1 -0
- package/dist/odspDocumentStorageServiceBase.js +216 -0
- package/dist/odspDocumentStorageServiceBase.js.map +1 -0
- package/dist/odspDriverUrlResolver.js +1 -1
- package/dist/odspDriverUrlResolver.js.map +1 -1
- package/dist/odspDriverUrlResolverForShareLink.d.ts +8 -2
- package/dist/odspDriverUrlResolverForShareLink.d.ts.map +1 -1
- package/dist/odspDriverUrlResolverForShareLink.js +11 -3
- package/dist/odspDriverUrlResolverForShareLink.js.map +1 -1
- package/dist/odspFluidFileLink.d.ts.map +1 -1
- package/dist/odspFluidFileLink.js +27 -21
- package/dist/odspFluidFileLink.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/lib/contractsPublic.d.ts +1 -0
- package/lib/contractsPublic.d.ts.map +1 -1
- package/lib/contractsPublic.js.map +1 -1
- package/lib/localOdspDriver/localOdspDocumentService.d.ts +26 -0
- package/lib/localOdspDriver/localOdspDocumentService.d.ts.map +1 -0
- package/lib/localOdspDriver/localOdspDocumentService.js +35 -0
- package/lib/localOdspDriver/localOdspDocumentService.js.map +1 -0
- package/lib/localOdspDriver/localOdspDocumentServiceFactory.d.ts +24 -0
- package/lib/localOdspDriver/localOdspDocumentServiceFactory.d.ts.map +1 -0
- package/lib/localOdspDriver/localOdspDocumentServiceFactory.js +41 -0
- package/lib/localOdspDriver/localOdspDocumentServiceFactory.js.map +1 -0
- package/lib/localOdspDriver/localOdspDocumentStorageManager.d.ts +27 -0
- package/lib/localOdspDriver/localOdspDocumentStorageManager.d.ts.map +1 -0
- package/lib/localOdspDriver/localOdspDocumentStorageManager.js +62 -0
- package/lib/localOdspDriver/localOdspDocumentStorageManager.js.map +1 -0
- package/lib/odspDeltaStorageService.js.map +1 -1
- package/lib/odspDocumentService.d.ts.map +1 -1
- package/lib/odspDocumentService.js +1 -1
- package/lib/odspDocumentService.js.map +1 -1
- package/lib/odspDocumentServiceFactory.d.ts +2 -1
- package/lib/odspDocumentServiceFactory.d.ts.map +1 -1
- package/lib/odspDocumentServiceFactory.js +5 -0
- package/lib/odspDocumentServiceFactory.js.map +1 -1
- package/lib/odspDocumentServiceFactoryCore.d.ts +3 -1
- package/lib/odspDocumentServiceFactoryCore.d.ts.map +1 -1
- package/lib/odspDocumentServiceFactoryCore.js.map +1 -1
- package/lib/odspDocumentStorageManager.d.ts +5 -23
- package/lib/odspDocumentStorageManager.d.ts.map +1 -1
- package/lib/odspDocumentStorageManager.js +53 -246
- package/lib/odspDocumentStorageManager.js.map +1 -1
- package/lib/odspDocumentStorageServiceBase.d.ts +58 -0
- package/lib/odspDocumentStorageServiceBase.d.ts.map +1 -0
- package/lib/odspDocumentStorageServiceBase.js +212 -0
- package/lib/odspDocumentStorageServiceBase.js.map +1 -0
- package/lib/odspDriverUrlResolver.js +1 -1
- package/lib/odspDriverUrlResolver.js.map +1 -1
- package/lib/odspDriverUrlResolverForShareLink.d.ts +8 -2
- package/lib/odspDriverUrlResolverForShareLink.d.ts.map +1 -1
- package/lib/odspDriverUrlResolverForShareLink.js +11 -3
- package/lib/odspDriverUrlResolverForShareLink.js.map +1 -1
- package/lib/odspFluidFileLink.d.ts.map +1 -1
- package/lib/odspFluidFileLink.js +27 -21
- package/lib/odspFluidFileLink.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/package.json +11 -11
- package/src/contractsPublic.ts +1 -0
- package/src/localOdspDriver/localOdspDocumentService.ts +54 -0
- package/src/localOdspDriver/localOdspDocumentServiceFactory.ts +67 -0
- package/src/localOdspDriver/localOdspDocumentStorageManager.ts +83 -0
- package/src/odspDeltaStorageService.ts +1 -1
- package/src/odspDocumentService.ts +5 -1
- package/src/odspDocumentServiceFactory.ts +7 -3
- package/src/odspDocumentServiceFactoryCore.ts +1 -1
- package/src/odspDocumentStorageManager.ts +81 -312
- package/src/odspDocumentStorageServiceBase.ts +268 -0
- package/src/odspDriverUrlResolver.ts +1 -1
- package/src/odspDriverUrlResolverForShareLink.ts +13 -1
- package/src/odspFluidFileLink.ts +26 -22
- package/src/packageVersion.ts +1 -1
|
@@ -4,15 +4,16 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
6
6
|
import * as api from "@fluidframework/protocol-definitions";
|
|
7
|
-
import { ISummaryContext
|
|
7
|
+
import { ISummaryContext } from "@fluidframework/driver-definitions";
|
|
8
8
|
import { IOdspResolvedUrl, InstrumentedStorageTokenFetcher } from "@fluidframework/odsp-driver-definitions";
|
|
9
9
|
import { HostStoragePolicyInternal } from "./contracts";
|
|
10
10
|
import { SnapshotFormatSupportType } from "./fetchSnapshot";
|
|
11
11
|
import { IOdspCache } from "./odspCache";
|
|
12
12
|
import { EpochTracker } from "./epochTracker";
|
|
13
13
|
import { FlushResult } from "./odspDocumentDeltaConnection";
|
|
14
|
+
import { OdspDocumentStorageServiceBase } from "./odspDocumentStorageServiceBase";
|
|
14
15
|
export declare const defaultSummarizerCacheExpiryTimeout: number;
|
|
15
|
-
export declare class OdspDocumentStorageService
|
|
16
|
+
export declare class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
16
17
|
private readonly odspResolvedUrl;
|
|
17
18
|
private readonly getStorageToken;
|
|
18
19
|
private readonly logger;
|
|
@@ -22,17 +23,8 @@ export declare class OdspDocumentStorageService implements IDocumentStorageServi
|
|
|
22
23
|
private readonly epochTracker;
|
|
23
24
|
private readonly flushCallback;
|
|
24
25
|
private readonly snapshotFormatFetchType?;
|
|
25
|
-
readonly policies: {
|
|
26
|
-
caching: LoaderCachingPolicy;
|
|
27
|
-
minBlobSize: number;
|
|
28
|
-
maximumCacheDurationMs: number;
|
|
29
|
-
};
|
|
30
|
-
private readonly commitCache;
|
|
31
|
-
private readonly attributesBlobHandles;
|
|
32
26
|
private readonly odspSummaryUploadManager;
|
|
33
|
-
private _ops;
|
|
34
27
|
private firstVersionCall;
|
|
35
|
-
private _snapshotSequenceNumber;
|
|
36
28
|
private readonly documentId;
|
|
37
29
|
private readonly snapshotUrl;
|
|
38
30
|
private readonly attachmentPOSTUrl;
|
|
@@ -45,27 +37,17 @@ export declare class OdspDocumentStorageService implements IDocumentStorageServi
|
|
|
45
37
|
private readonly maxSnapshotSizeLimit;
|
|
46
38
|
private readonly maxSnapshotFetchTimeout;
|
|
47
39
|
private readonly createBlobRateLimiter;
|
|
48
|
-
private readonly blobCache;
|
|
49
|
-
set ops(ops: api.ISequencedDocumentMessage[] | undefined);
|
|
50
|
-
get ops(): api.ISequencedDocumentMessage[] | undefined;
|
|
51
|
-
get snapshotSequenceNumber(): number | undefined;
|
|
52
40
|
constructor(odspResolvedUrl: IOdspResolvedUrl, getStorageToken: InstrumentedStorageTokenFetcher, logger: ITelemetryLogger, fetchFullSnapshot: boolean, cache: IOdspCache, hostPolicy: HostStoragePolicyInternal, epochTracker: EpochTracker, flushCallback: () => Promise<FlushResult>, snapshotFormatFetchType?: SnapshotFormatSupportType | undefined);
|
|
53
|
-
get repositoryUrl(): string;
|
|
54
41
|
createBlob(file: ArrayBufferLike): Promise<api.ICreateBlobResponse>;
|
|
55
|
-
|
|
56
|
-
readBlob(blobId: string): Promise<ArrayBufferLike>;
|
|
42
|
+
protected fetchBlobFromStorage(blobId: string, evicted: boolean): Promise<ArrayBuffer>;
|
|
57
43
|
getSnapshotTree(version?: api.IVersion, scenarioName?: string): Promise<api.ISnapshotTree | null>;
|
|
58
44
|
getVersions(blobid: string | null, count: number, scenarioName?: string): Promise<api.IVersion[]>;
|
|
59
45
|
private fetchSnapshot;
|
|
60
46
|
private fetchSnapshotCore;
|
|
61
47
|
uploadSummaryWithContext(summary: api.ISummaryTree, context: ISummaryContext): Promise<string>;
|
|
62
|
-
downloadSummary(commit: api.ISummaryHandle): Promise<api.ISummaryTree>;
|
|
63
|
-
private setRootTree;
|
|
64
|
-
private initBlobsCache;
|
|
65
48
|
private checkSnapshotUrl;
|
|
66
49
|
private checkAttachmentPOSTUrl;
|
|
67
50
|
private checkAttachmentGETUrl;
|
|
68
|
-
|
|
69
|
-
private combineProtocolAndAppSnapshotTree;
|
|
51
|
+
protected fetchTreeFromSnapshot(id: string, scenarioName?: string): Promise<api.ISnapshotTree | undefined>;
|
|
70
52
|
}
|
|
71
53
|
//# sourceMappingURL=odspDocumentStorageManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"odspDocumentStorageManager.d.ts","sourceRoot":"","sources":["../src/odspDocumentStorageManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAQtE,OAAO,KAAK,GAAG,MAAM,sCAAsC,CAAC;AAC5D,OAAO,EACH,eAAe,
|
|
1
|
+
{"version":3,"file":"odspDocumentStorageManager.d.ts","sourceRoot":"","sources":["../src/odspDocumentStorageManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { assert, delay, } from "@fluidframework/common-utils";
|
|
6
6
|
import { PerformanceEvent, } from "@fluidframework/telemetry-utils";
|
|
7
|
-
import {
|
|
7
|
+
import { DriverErrorType, } from "@fluidframework/driver-definitions";
|
|
8
8
|
import { RateLimiter, NonRetryableError } from "@fluidframework/driver-utils";
|
|
9
9
|
import { OdspErrorType, } from "@fluidframework/odsp-driver-definitions";
|
|
10
10
|
import { downloadSnapshot, fetchSnapshot, fetchSnapshotWithRedeem } from "./fetchSnapshot";
|
|
11
11
|
import { getUrlAndHeadersWithAuth } from "./getUrlAndHeadersWithAuth";
|
|
12
12
|
import { createCacheSnapshotKey, getWithRetryForTokenRefresh, } from "./odspUtils";
|
|
13
|
-
import { defaultCacheExpiryTimeoutMs } from "./epochTracker";
|
|
14
13
|
import { OdspSummaryUploadManager } from "./odspSummaryUploadManager";
|
|
15
14
|
import { pkgVersion as driverVersion } from "./packageVersion";
|
|
15
|
+
import { OdspDocumentStorageServiceBase } from "./odspDocumentStorageServiceBase";
|
|
16
16
|
export const defaultSummarizerCacheExpiryTimeout = 60 * 1000; // 60 seconds.
|
|
17
17
|
/* eslint-disable max-len */
|
|
18
18
|
// An implementation of Promise.race that gives you the winner of the promise race
|
|
@@ -23,98 +23,10 @@ async function promiseRaceWithWinner(promises) {
|
|
|
23
23
|
});
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
|
-
class
|
|
27
|
-
constructor() {
|
|
28
|
-
// If the defer flag is set when the timeout fires, we'll reschedule rather than clear immediately
|
|
29
|
-
// This deferral approach is used (rather than clearing/resetting the timer) as current calling patterns trigger
|
|
30
|
-
// too many calls to setTimeout/clearTimeout.
|
|
31
|
-
this.deferBlobCacheClear = false;
|
|
32
|
-
this._blobCache = new Map();
|
|
33
|
-
// Tracks all blob IDs evicted from cache
|
|
34
|
-
this.blobsEvicted = new Set();
|
|
35
|
-
// Initial time-out to purge data from cache
|
|
36
|
-
// If this time out is very small, then we purge blobs from cache too soon and that results in a lot of
|
|
37
|
-
// requests to storage, which brings down perf and may trip protection limits causing 429s
|
|
38
|
-
this.blobCacheTimeoutDuration = 2 * 60 * 1000;
|
|
39
|
-
// SPO does not keep old snapshots around for long, so we are running chances of not
|
|
40
|
-
// being able to rehydrate data store / DDS in the future if we purge anything (and with blob de-duping,
|
|
41
|
-
// even if blob read by runtime, it could be read again in the future)
|
|
42
|
-
// So for now, purging is disabled.
|
|
43
|
-
this.purgeEnabled = false;
|
|
44
|
-
}
|
|
45
|
-
get value() {
|
|
46
|
-
return this._blobCache;
|
|
47
|
-
}
|
|
48
|
-
addBlobs(blobs) {
|
|
49
|
-
blobs.forEach((value, blobId) => {
|
|
50
|
-
this._blobCache.set(blobId, value);
|
|
51
|
-
});
|
|
52
|
-
// Reset the timer on cache set
|
|
53
|
-
this.scheduleClearBlobsCache();
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Schedule a timer for clearing the blob cache or defer the current one.
|
|
57
|
-
*/
|
|
58
|
-
scheduleClearBlobsCache() {
|
|
59
|
-
if (this.blobCacheTimeout !== undefined) {
|
|
60
|
-
// If we already have an outstanding timer, just signal that we should defer the clear
|
|
61
|
-
this.deferBlobCacheClear = true;
|
|
62
|
-
}
|
|
63
|
-
else if (this.purgeEnabled) {
|
|
64
|
-
// If we don't have an outstanding timer, set a timer
|
|
65
|
-
// When the timer runs out, we'll decide whether to proceed with the cache clear or reset the timer
|
|
66
|
-
const clearCacheOrDefer = () => {
|
|
67
|
-
this.blobCacheTimeout = undefined;
|
|
68
|
-
if (this.deferBlobCacheClear) {
|
|
69
|
-
this.deferBlobCacheClear = false;
|
|
70
|
-
this.scheduleClearBlobsCache();
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
// NOTE: Slightly better algorithm here would be to purge either only big blobs,
|
|
74
|
-
// or sort them by size and purge enough big blobs to leave only 256Kb of small blobs in cache
|
|
75
|
-
// Purging is optimizing memory footprint. But count controls potential number of storage requests
|
|
76
|
-
// We want to optimize both - memory footprint and number of future requests to storage.
|
|
77
|
-
// Note that Container can realize data store or DDS on-demand at any point in time, so we do not
|
|
78
|
-
// control when blobs will be used.
|
|
79
|
-
this._blobCache.forEach((_, blobId) => this.blobsEvicted.add(blobId));
|
|
80
|
-
this._blobCache.clear();
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
this.blobCacheTimeout = setTimeout(clearCacheOrDefer, this.blobCacheTimeoutDuration);
|
|
84
|
-
// any future storage reads that get into the cache should be cleared from cache rather quickly -
|
|
85
|
-
// there is not much value in keeping them longer
|
|
86
|
-
this.blobCacheTimeoutDuration = 10 * 1000;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
getBlob(blobId) {
|
|
90
|
-
// Reset the timer on attempted cache read
|
|
91
|
-
this.scheduleClearBlobsCache();
|
|
92
|
-
const blobContent = this._blobCache.get(blobId);
|
|
93
|
-
const evicted = this.blobsEvicted.has(blobId);
|
|
94
|
-
return { blobContent, evicted };
|
|
95
|
-
}
|
|
96
|
-
setBlob(blobId, blob) {
|
|
97
|
-
// This API is called as result of cache miss and reading blob from storage.
|
|
98
|
-
// Runtime never reads same blob twice.
|
|
99
|
-
// The only reason we may get read request for same blob is blob de-duping in summaries.
|
|
100
|
-
// Note that the bigger the size, the less likely blobs are the same, so there is very little benefit of caching big blobs.
|
|
101
|
-
// Images are the only exception - user may insert same image twice. But we currently do not de-dup them - only snapshot
|
|
102
|
-
// blobs are de-duped.
|
|
103
|
-
const size = blob.byteLength;
|
|
104
|
-
if (size < 256 * 1024) {
|
|
105
|
-
// Reset the timer on cache set
|
|
106
|
-
this.scheduleClearBlobsCache();
|
|
107
|
-
return this._blobCache.set(blobId, blob);
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
// we evicted it here by not caching.
|
|
111
|
-
this.blobsEvicted.add(blobId);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
export class OdspDocumentStorageService {
|
|
26
|
+
export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
116
27
|
constructor(odspResolvedUrl, getStorageToken, logger, fetchFullSnapshot, cache, hostPolicy, epochTracker, flushCallback, snapshotFormatFetchType) {
|
|
117
28
|
var _a;
|
|
29
|
+
super();
|
|
118
30
|
this.odspResolvedUrl = odspResolvedUrl;
|
|
119
31
|
this.getStorageToken = getStorageToken;
|
|
120
32
|
this.logger = logger;
|
|
@@ -124,24 +36,6 @@ export class OdspDocumentStorageService {
|
|
|
124
36
|
this.epochTracker = epochTracker;
|
|
125
37
|
this.flushCallback = flushCallback;
|
|
126
38
|
this.snapshotFormatFetchType = snapshotFormatFetchType;
|
|
127
|
-
this.policies = {
|
|
128
|
-
// By default, ODSP tells the container not to prefetch/cache.
|
|
129
|
-
caching: LoaderCachingPolicy.NoCaching,
|
|
130
|
-
// ODSP storage works better if it has less number of blobs / edges
|
|
131
|
-
// Runtime creating many small blobs results in sub-optimal perf.
|
|
132
|
-
// 2K seems like the sweat spot:
|
|
133
|
-
// The smaller the number, less blobs we aggregate. Most storages are very likely to have notion
|
|
134
|
-
// of minimal "cluster" size, so having small blobs is wasteful
|
|
135
|
-
// At the same time increasing the limit ensure that more blobs with user content are aggregated,
|
|
136
|
-
// reducing possibility for de-duping of same blobs (i.e. .attributes rolled into aggregate blob
|
|
137
|
-
// are not reused across data stores, or even within data store, resulting in duplication of content)
|
|
138
|
-
// Note that duplication of content should not have significant impact for bytes over wire as
|
|
139
|
-
// compression of http payload mostly takes care of it, but it does impact storage size and in-memory sizes.
|
|
140
|
-
minBlobSize: 2048,
|
|
141
|
-
maximumCacheDurationMs: defaultCacheExpiryTimeoutMs,
|
|
142
|
-
};
|
|
143
|
-
this.commitCache = new Map();
|
|
144
|
-
this.attributesBlobHandles = new Set();
|
|
145
39
|
this.firstVersionCall = true;
|
|
146
40
|
// Driver specified limits for snapshot size and time.
|
|
147
41
|
/**
|
|
@@ -153,26 +47,12 @@ export class OdspDocumentStorageService {
|
|
|
153
47
|
this.maxSnapshotFetchTimeout = 120000; // 2 min
|
|
154
48
|
// limits the amount of parallel "attachment" blob uploads
|
|
155
49
|
this.createBlobRateLimiter = new RateLimiter(1);
|
|
156
|
-
this.blobCache = new BlobCache();
|
|
157
50
|
this.documentId = this.odspResolvedUrl.hashedDocumentId;
|
|
158
51
|
this.snapshotUrl = this.odspResolvedUrl.endpoints.snapshotStorageUrl;
|
|
159
52
|
this.attachmentPOSTUrl = this.odspResolvedUrl.endpoints.attachmentPOSTStorageUrl;
|
|
160
53
|
this.attachmentGETUrl = this.odspResolvedUrl.endpoints.attachmentGETStorageUrl;
|
|
161
54
|
this.odspSummaryUploadManager = new OdspSummaryUploadManager(this.snapshotUrl, getStorageToken, logger, epochTracker, !!((_a = this.hostPolicy.sessionOptions) === null || _a === void 0 ? void 0 : _a.forceAccessTokenViaAuthorizationHeader));
|
|
162
55
|
}
|
|
163
|
-
set ops(ops) {
|
|
164
|
-
assert(this._ops === undefined, 0x0a5 /* "Trying to set ops when they are already set!" */);
|
|
165
|
-
this._ops = ops;
|
|
166
|
-
}
|
|
167
|
-
get ops() {
|
|
168
|
-
return this._ops;
|
|
169
|
-
}
|
|
170
|
-
get snapshotSequenceNumber() {
|
|
171
|
-
return this._snapshotSequenceNumber;
|
|
172
|
-
}
|
|
173
|
-
get repositoryUrl() {
|
|
174
|
-
return "";
|
|
175
|
-
}
|
|
176
56
|
async createBlob(file) {
|
|
177
57
|
this.checkAttachmentPOSTUrl();
|
|
178
58
|
const response = await getWithRetryForTokenRefresh(async (options) => {
|
|
@@ -196,70 +76,38 @@ export class OdspDocumentStorageService {
|
|
|
196
76
|
});
|
|
197
77
|
return response.content;
|
|
198
78
|
}
|
|
199
|
-
async
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
this.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
blobId }, res.propsToLog));
|
|
222
|
-
}
|
|
223
|
-
return res.content;
|
|
224
|
-
});
|
|
79
|
+
async fetchBlobFromStorage(blobId, evicted) {
|
|
80
|
+
this.checkAttachmentGETUrl();
|
|
81
|
+
const blob = await getWithRetryForTokenRefresh(async (options) => {
|
|
82
|
+
var _a;
|
|
83
|
+
const storageToken = await this.getStorageToken(options, "GetBlob");
|
|
84
|
+
const unAuthedUrl = `${this.attachmentGETUrl}/${encodeURIComponent(blobId)}/content`;
|
|
85
|
+
const { url, headers } = getUrlAndHeadersWithAuth(unAuthedUrl, storageToken, !!((_a = this.hostPolicy.sessionOptions) === null || _a === void 0 ? void 0 : _a.forceAccessTokenViaAuthorizationHeader));
|
|
86
|
+
return PerformanceEvent.timedExecAsync(this.logger, {
|
|
87
|
+
eventName: "readDataBlob",
|
|
88
|
+
blobId,
|
|
89
|
+
evicted,
|
|
90
|
+
headers: Object.keys(headers).length !== 0 ? true : undefined,
|
|
91
|
+
waitQueueLength: this.epochTracker.rateLimiter.waitQueueLength,
|
|
92
|
+
}, async (event) => {
|
|
93
|
+
const res = await this.epochTracker.fetchArray(url, { headers }, "blob");
|
|
94
|
+
event.end(Object.assign(Object.assign({ waitQueueLength: this.epochTracker.rateLimiter.waitQueueLength }, res.propsToLog), { attempts: options.refresh ? 2 : 1 }));
|
|
95
|
+
const cacheControl = res.headers.get("cache-control");
|
|
96
|
+
if (cacheControl === undefined || !(cacheControl.includes("private") || cacheControl.includes("public"))) {
|
|
97
|
+
this.logger.sendErrorEvent(Object.assign({ eventName: "NonCacheableBlob", cacheControl,
|
|
98
|
+
blobId }, res.propsToLog));
|
|
99
|
+
}
|
|
100
|
+
return res.content;
|
|
225
101
|
});
|
|
226
|
-
|
|
227
|
-
|
|
102
|
+
});
|
|
103
|
+
this.blobCache.setBlob(blobId, blob);
|
|
228
104
|
return blob;
|
|
229
105
|
}
|
|
230
|
-
async readBlob(blobId) {
|
|
231
|
-
return this.readBlobCore(blobId);
|
|
232
|
-
}
|
|
233
106
|
async getSnapshotTree(version, scenarioName) {
|
|
234
107
|
if (!this.snapshotUrl) {
|
|
235
108
|
return null;
|
|
236
109
|
}
|
|
237
|
-
|
|
238
|
-
if (!version || !version.id) {
|
|
239
|
-
const versions = await this.getVersions(null, 1, scenarioName);
|
|
240
|
-
if (!versions || versions.length === 0) {
|
|
241
|
-
return null;
|
|
242
|
-
}
|
|
243
|
-
id = versions[0].id;
|
|
244
|
-
}
|
|
245
|
-
else {
|
|
246
|
-
id = version.id;
|
|
247
|
-
}
|
|
248
|
-
const snapshotTree = await this.readTree(id, scenarioName);
|
|
249
|
-
if (!snapshotTree) {
|
|
250
|
-
return null;
|
|
251
|
-
}
|
|
252
|
-
if (snapshotTree.blobs) {
|
|
253
|
-
const attributesBlob = snapshotTree.blobs.attributes;
|
|
254
|
-
if (attributesBlob) {
|
|
255
|
-
this.attributesBlobHandles.add(attributesBlob);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
// When we upload the container snapshot, we upload appTree in ".app" and protocol tree in ".protocol"
|
|
259
|
-
// So when we request the snapshot we get ".app" as tree and not as commit node as in the case just above.
|
|
260
|
-
const appTree = snapshotTree.trees[".app"];
|
|
261
|
-
const protocolTree = snapshotTree.trees[".protocol"];
|
|
262
|
-
return this.combineProtocolAndAppSnapshotTree(appTree, protocolTree);
|
|
110
|
+
return super.getSnapshotTree(version, scenarioName);
|
|
263
111
|
}
|
|
264
112
|
async getVersions(blobid, count, scenarioName) {
|
|
265
113
|
// Regular load workflow uses blobId === documentID to indicate "latest".
|
|
@@ -358,19 +206,7 @@ export class OdspDocumentStorageService {
|
|
|
358
206
|
});
|
|
359
207
|
// Successful call, make network calls only
|
|
360
208
|
this.firstVersionCall = false;
|
|
361
|
-
|
|
362
|
-
const { snapshotTree, blobs, ops } = odspSnapshotCacheValue;
|
|
363
|
-
// id should be undefined in case of just ops in snapshot.
|
|
364
|
-
let id;
|
|
365
|
-
if (snapshotTree) {
|
|
366
|
-
id = snapshotTree.id;
|
|
367
|
-
assert(id !== undefined, 0x221 /* "Root tree should contain the id" */);
|
|
368
|
-
this.setRootTree(id, snapshotTree);
|
|
369
|
-
}
|
|
370
|
-
if (blobs) {
|
|
371
|
-
this.initBlobsCache(blobs);
|
|
372
|
-
}
|
|
373
|
-
this.ops = ops;
|
|
209
|
+
const id = this.initializeFromSnapshot(odspSnapshotCacheValue);
|
|
374
210
|
return id ? [{ id, treeId: undefined }] : [];
|
|
375
211
|
}
|
|
376
212
|
return getWithRetryForTokenRefresh(async (options) => {
|
|
@@ -455,34 +291,25 @@ export class OdspDocumentStorageService {
|
|
|
455
291
|
this.checkSnapshotUrl();
|
|
456
292
|
// Enable flushing only if we have single commit summary and this is not the initial summary for an empty file
|
|
457
293
|
if (".protocol" in summary.tree && context.ackHandle !== undefined) {
|
|
458
|
-
let retry =
|
|
294
|
+
let retry = 1;
|
|
459
295
|
for (;;) {
|
|
460
296
|
const result = await this.flushCallback();
|
|
461
297
|
const seq = result.lastPersistedSequenceNumber;
|
|
462
298
|
if (seq !== undefined && seq >= context.referenceSequenceNumber) {
|
|
463
299
|
break;
|
|
464
300
|
}
|
|
465
|
-
retry++;
|
|
466
301
|
if (retry > 3) {
|
|
467
302
|
this.logger.sendErrorEvent(Object.assign(Object.assign({ eventName: "FlushFailure" }, result), { retry, referenceSequenceNumber: context.referenceSequenceNumber }));
|
|
468
303
|
break;
|
|
469
304
|
}
|
|
470
305
|
this.logger.sendPerformanceEvent(Object.assign(Object.assign({ eventName: "FlushExtraCall" }, result), { retry, referenceSequenceNumber: context.referenceSequenceNumber }));
|
|
306
|
+
retry++;
|
|
471
307
|
await delay(1000 * ((_a = result.retryAfter) !== null && _a !== void 0 ? _a : 1));
|
|
472
308
|
}
|
|
473
309
|
}
|
|
474
310
|
const id = await this.odspSummaryUploadManager.writeSummaryTree(summary, context);
|
|
475
311
|
return id;
|
|
476
312
|
}
|
|
477
|
-
async downloadSummary(commit) {
|
|
478
|
-
throw new Error("Not implemented yet");
|
|
479
|
-
}
|
|
480
|
-
setRootTree(id, tree) {
|
|
481
|
-
this.commitCache.set(id, tree);
|
|
482
|
-
}
|
|
483
|
-
initBlobsCache(blobs) {
|
|
484
|
-
this.blobCache.addBlobs(blobs);
|
|
485
|
-
}
|
|
486
313
|
checkSnapshotUrl() {
|
|
487
314
|
if (!this.snapshotUrl) {
|
|
488
315
|
throw new NonRetryableError("Method failed because no snapshot url was available", DriverErrorType.genericError, { driverVersion });
|
|
@@ -498,47 +325,27 @@ export class OdspDocumentStorageService {
|
|
|
498
325
|
throw new NonRetryableError("Method failed because no attachment GET url was available", DriverErrorType.genericError, { driverVersion });
|
|
499
326
|
}
|
|
500
327
|
}
|
|
501
|
-
async
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
}
|
|
523
|
-
// If the version id doesn't match with the id of the tree, then use the id of first tree which in that case
|
|
524
|
-
// will be the actual id of tree to be fetched.
|
|
525
|
-
return (_b = this.commitCache.get(id)) !== null && _b !== void 0 ? _b : this.commitCache.get(treeId);
|
|
526
|
-
});
|
|
527
|
-
}
|
|
528
|
-
if (!tree) {
|
|
529
|
-
return null;
|
|
530
|
-
}
|
|
531
|
-
return tree;
|
|
532
|
-
}
|
|
533
|
-
combineProtocolAndAppSnapshotTree(hierarchicalAppTree, hierarchicalProtocolTree) {
|
|
534
|
-
const summarySnapshotTree = {
|
|
535
|
-
blobs: Object.assign({}, hierarchicalAppTree.blobs),
|
|
536
|
-
trees: Object.assign(Object.assign({}, hierarchicalAppTree.trees), {
|
|
537
|
-
// the app tree could have a .protocol
|
|
538
|
-
// in that case we want to server protocol to override it
|
|
539
|
-
".protocol": hierarchicalProtocolTree }),
|
|
540
|
-
};
|
|
541
|
-
return summarySnapshotTree;
|
|
328
|
+
async fetchTreeFromSnapshot(id, scenarioName) {
|
|
329
|
+
return getWithRetryForTokenRefresh(async (options) => {
|
|
330
|
+
var _a, _b;
|
|
331
|
+
const storageToken = await this.getStorageToken(options, "ReadCommit");
|
|
332
|
+
const snapshotDownloader = async (url, fetchOptions) => {
|
|
333
|
+
return this.epochTracker.fetchAndParseAsJSON(url, fetchOptions, "snapshotTree", undefined, scenarioName);
|
|
334
|
+
};
|
|
335
|
+
const snapshot = await fetchSnapshot(this.snapshotUrl, storageToken, id, this.fetchFullSnapshot, !!((_a = this.hostPolicy.sessionOptions) === null || _a === void 0 ? void 0 : _a.forceAccessTokenViaAuthorizationHeader), this.logger, snapshotDownloader);
|
|
336
|
+
let treeId = "";
|
|
337
|
+
if (snapshot.snapshotTree) {
|
|
338
|
+
assert(snapshot.snapshotTree.id !== undefined, 0x222 /* "Root tree should contain the id!!" */);
|
|
339
|
+
treeId = snapshot.snapshotTree.id;
|
|
340
|
+
this.setRootTree(treeId, snapshot.snapshotTree);
|
|
341
|
+
}
|
|
342
|
+
if (snapshot.blobs) {
|
|
343
|
+
this.initBlobsCache(snapshot.blobs);
|
|
344
|
+
}
|
|
345
|
+
// If the version id doesn't match with the id of the tree, then use the id of first tree which in that case
|
|
346
|
+
// will be the actual id of tree to be fetched.
|
|
347
|
+
return (_b = this.commitCache.get(id)) !== null && _b !== void 0 ? _b : this.commitCache.get(treeId);
|
|
348
|
+
});
|
|
542
349
|
}
|
|
543
350
|
}
|
|
544
351
|
/* eslint-enable max-len */
|