@fluidframework/odsp-driver 0.47.0 → 0.48.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/createFile.js +3 -3
- package/dist/createFile.js.map +1 -1
- package/dist/epochTracker.js +1 -1
- package/dist/epochTracker.js.map +1 -1
- package/dist/odspDeltaStorageService.d.ts +1 -0
- package/dist/odspDeltaStorageService.d.ts.map +1 -1
- package/dist/odspDeltaStorageService.js +26 -1
- package/dist/odspDeltaStorageService.js.map +1 -1
- package/dist/odspDocumentDeltaConnection.d.ts.map +1 -1
- package/dist/odspDocumentDeltaConnection.js +11 -10
- package/dist/odspDocumentDeltaConnection.js.map +1 -1
- package/dist/odspDocumentService.js +4 -4
- package/dist/odspDocumentService.js.map +1 -1
- package/dist/odspDocumentStorageManager.js +5 -5
- package/dist/odspDocumentStorageManager.js.map +1 -1
- package/dist/odspDriverUrlResolverForShareLink.js +1 -1
- package/dist/odspDriverUrlResolverForShareLink.js.map +1 -1
- package/dist/odspUtils.d.ts.map +1 -1
- package/dist/odspUtils.js +6 -6
- package/dist/odspUtils.js.map +1 -1
- package/dist/opsCaching.d.ts +1 -0
- package/dist/opsCaching.d.ts.map +1 -1
- package/dist/opsCaching.js +12 -9
- package/dist/opsCaching.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/vroom.d.ts.map +1 -1
- package/dist/vroom.js +3 -0
- package/dist/vroom.js.map +1 -1
- package/lib/createFile.js +3 -3
- package/lib/createFile.js.map +1 -1
- package/lib/epochTracker.js +1 -1
- package/lib/epochTracker.js.map +1 -1
- package/lib/odspDeltaStorageService.d.ts +1 -0
- package/lib/odspDeltaStorageService.d.ts.map +1 -1
- package/lib/odspDeltaStorageService.js +26 -1
- package/lib/odspDeltaStorageService.js.map +1 -1
- package/lib/odspDocumentDeltaConnection.d.ts.map +1 -1
- package/lib/odspDocumentDeltaConnection.js +11 -10
- package/lib/odspDocumentDeltaConnection.js.map +1 -1
- package/lib/odspDocumentService.js +4 -4
- package/lib/odspDocumentService.js.map +1 -1
- package/lib/odspDocumentStorageManager.js +5 -5
- package/lib/odspDocumentStorageManager.js.map +1 -1
- package/lib/odspDriverUrlResolverForShareLink.js +1 -1
- package/lib/odspDriverUrlResolverForShareLink.js.map +1 -1
- package/lib/odspUtils.d.ts.map +1 -1
- package/lib/odspUtils.js +6 -6
- package/lib/odspUtils.js.map +1 -1
- package/lib/opsCaching.d.ts +1 -0
- package/lib/opsCaching.d.ts.map +1 -1
- package/lib/opsCaching.js +12 -9
- package/lib/opsCaching.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/vroom.d.ts.map +1 -1
- package/lib/vroom.js +3 -0
- package/lib/vroom.js.map +1 -1
- package/package.json +9 -9
- package/src/createFile.ts +3 -3
- package/src/epochTracker.ts +1 -1
- package/src/odspDeltaStorageService.ts +60 -33
- package/src/odspDocumentDeltaConnection.ts +13 -5
- package/src/odspDocumentService.ts +3 -3
- package/src/odspDocumentStorageManager.ts +5 -5
- package/src/odspDriverUrlResolverForShareLink.ts +1 -1
- package/src/odspUtils.ts +12 -6
- package/src/opsCaching.ts +14 -9
- package/src/packageVersion.ts +1 -1
- package/src/vroom.ts +3 -0
package/lib/opsCaching.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opsCaching.js","sourceRoot":"","sources":["../src/opsCaching.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAwB3D,MAAM,OAAO,QAAQ;IAInB,YACE,sBAA8B,EACb,MAAwB,EACxB,KAAa,EACb,SAAiB,EACjB,gBAAgB,EACzB,eAAe;QAJN,WAAM,GAAN,MAAM,CAAkB;QACxB,UAAK,GAAL,KAAK,CAAQ;QACb,cAAS,GAAT,SAAS,CAAQ;QACjB,qBAAgB,GAAhB,gBAAgB,CAAA;QACzB,oBAAe,GAAf,eAAe,CAAA;QATR,YAAO,GAA+B,IAAI,GAAG,EAAE,CAAC;QAW3D;;WAEG;QACH,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,uBAAuB,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC;QACjG,IAAI,cAAc,KAAK,CAAC,EAAE;YACtB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,sBAAsB,CAAC,EAAE;gBAC1D,cAAc;gBACd,SAAS,EAAG,IAAI,CAAC,2BAA2B,EAAE;gBAC9C,KAAK,EAAE,KAAK;aACf,CAAC,CAAC;SACN;IACL,CAAC;IAEM,OAAO;QACV,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE;YAC1B,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACzB,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;SAC1B;IACL,CAAC;IAEM,QAAQ;QACX,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE;YACrC,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE;gBAChC,SAAS;aACZ;YACD,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;YACpB,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;SAC1B;IACL,CAAC;IAEM,MAAM,CAAC,GAAe;QACzB,IAAI,IAAI,CAAC,eAAe,IAAI,CAAC,EAAE;YAC3B,OAAO;SACV;QAED,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE;YAClB,MAAM,WAAW,GAAG,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC;YAC3D,MAAM,eAAe,GAAG,IAAI,CAAC,uBAAuB,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC;YAExE,IAAI,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YAEjD,IAAI,YAAY,KAAK,SAAS,EAAE;gBAC5B,YAAY,GAAG;oBACX,cAAc,EAAE,IAAI,CAAC,SAAS,GAAG,CAAC;oBAClC,SAAS,EAAE,IAAI,CAAC,2BAA2B,EAAE;oBAC7C,KAAK,EAAE,IAAI;iBACd,CAAC;gBACF,YAAY,CAAC,SAAS,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC;gBAC7C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;aAC/C;iBAAM,IAAI,YAAY,KAAK,IAAI,IAAI,YAAY,CAAC,SAAS,CAAC,eAAe,CAAC,KAAK,SAAS,EAAE;gBACvF,YAAY,CAAC,SAAS,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC;gBAC7C,YAAY,CAAC,cAAc,EAAE,CAAC;gBAC9B,YAAY,CAAC,KAAK,GAAG,IAAI,CAAC;aAC7B;iBAAM;gBACH,yEAAyE;gBACzE,OAAO;aACV;YAED,IAAI,YAAY,CAAC,cAAc,KAAK,CAAC,EAAE;gBACnC,gCAAgC;gBAChC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;gBACtC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;aACvC;iBAAM;gBACH,IAAI,CAAC,aAAa,EAAE,CAAC;aACxB;YAED,IAAI,CAAC,eAAe,EAAE,CAAC;YACvB,IAAI,IAAI,CAAC,eAAe,KAAK,CAAC,EAAE;gBAC5B,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,EAAE,SAAS,EAAE,kBAAkB,EAAC,CAAC,CAAC;gBACnE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;gBACpB,IAAI,CAAC,OAAO,EAAE,CAAC;gBACf,MAAM;aACT;SACJ;IACL,CAAC;IAEM,KAAK,CAAC,GAAG,CAAC,IAAY,EAAE,EAAW;QACtC,MAAM,QAAQ,GAAe,EAAE,CAAC;QAChC,IAAI,WAAW,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QAChD,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAChC,iDAAiD;QACjD,OAAO,IAAI,EAAE;YACT,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,IAAI,WAAW,EAAE,CAAC,CAAC;YACtE,IAAI,GAAG,KAAK,SAAS,EAAE;gBACnB,MAAM;aACT;YACD,MAAM,MAAM,GAAe,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC3C,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE;gBACrB,mFAAmF;gBACnF,IAAI,EAAE,EAAE;oBACJ,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,CAAC,cAAc,IAAI,EAAE,EAAE;wBAC7C,MAAM;qBACT;oBACD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;wBACvB,IAAI,EAAE,CAAC,cAAc,GAAG,IAAI,GAAG,CAAC,EAAE;4BAC9B,MAAM;yBACT;6BAAM,IAAI,EAAE,CAAC,cAAc,IAAI,IAAI,EAAE;4BAClC,SAAS;yBACZ;qBACJ;oBACD,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;iBACrB;qBAAM,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;oBAC9B,MAAM;iBACT;aACJ;YAED,WAAW,EAAE,CAAC;SACjB;QAED,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;QAC3C,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,GAAG,IAAI,EAAE;YACxC,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;gBAC7B,SAAS,EAAE,cAAc;gBACzB,IAAI;gBACJ,EAAE;gBACF,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,QAAQ;aACX,CAAC,CAAC;SACN;QACD,OAAO,QAAQ,CAAC;IACpB,CAAC;IAES,KAAK,CAAC,WAAmB,EAAE,OAAe;QAChD,4EAA4E;QAC5E,yCAAyC;QACzC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,IAAI,WAAW,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YAC/F,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;IACP,CAAC;IAES,aAAa;QACnB,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,gBAAgB,GAAG,CAAC,EAAE;YAC1C,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBACzB,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;gBACvB,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpB,CAAC,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;SAC7B;IACL,CAAC;IAEO,cAAc,CAAC,cAAsB;QACzC,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;IACvD,CAAC;IAEO,uBAAuB,CAAC,cAAsB;QAClD,OAAO,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC;IAC3C,CAAC;IAEO,2BAA2B;QAC/B,MAAM,SAAS,GAAe,EAAE,CAAC;QACjC,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,sCAAsC;QACzE,OAAO,SAAS,CAAC;IACrB,CAAC;CACJ","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { performance } from \"@fluidframework/common-utils\";\n\n// ISequencedDocumentMessage\nexport interface IMessage {\n sequenceNumber: number;\n}\n\nexport type CacheEntry = (IMessage | undefined)[];\n\nexport interface IBatch {\n remainingSlots: number;\n batchData: CacheEntry;\n /**\n * Tells if this batch is dirty, i.e. it contains ops that were not flushed to cache\n */\n dirty: boolean;\n}\n\nexport interface ICache {\n write(batchNumber: string, data: string): Promise<void>;\n read(batchNumber: string): Promise<string | undefined>;\n remove(): void;\n}\n\nexport class OpsCache {\n private readonly batches: Map<number, null | IBatch> = new Map();\n private timer: ReturnType<typeof setTimeout> | undefined;\n\n constructor(\n startingSequenceNumber: number,\n private readonly logger: ITelemetryLogger,\n private readonly cache: ICache,\n private readonly batchSize: number,\n private readonly timerGranularity,\n private totalOpsToCache,\n ) {\n /** initial batch is a special case because it will never be full - all ops prior (inclusive) to\n * startingSequenceNumber are never going to show up (undefined)\n */\n const remainingSlots = this.batchSize - this.getPositionInBatchArray(startingSequenceNumber) - 1;\n if (remainingSlots !== 0) {\n this.batches.set(this.getBatchNumber(startingSequenceNumber), {\n remainingSlots,\n batchData : this.initializeNewBatchDataArray(),\n dirty: false,\n });\n }\n }\n\n public dispose() {\n this.batches.clear();\n if (this.timer !== undefined) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n }\n\n public flushOps() {\n for (const [key, value] of this.batches) {\n if (value === null || !value.dirty) {\n continue;\n }\n value.dirty = false;\n this.write(key, value);\n }\n }\n\n public addOps(ops: IMessage[]) {\n if (this.totalOpsToCache <= 0) {\n return;\n }\n\n for (const op of ops) {\n const batchNumber = this.getBatchNumber(op.sequenceNumber);\n const positionInBatch = this.getPositionInBatchArray(op.sequenceNumber);\n\n let currentBatch = this.batches.get(batchNumber);\n\n if (currentBatch === undefined) {\n currentBatch = {\n remainingSlots: this.batchSize - 1,\n batchData: this.initializeNewBatchDataArray(),\n dirty: true,\n };\n currentBatch.batchData[positionInBatch] = op;\n this.batches.set(batchNumber, currentBatch);\n } else if (currentBatch !== null && currentBatch.batchData[positionInBatch] === undefined) {\n currentBatch.batchData[positionInBatch] = op;\n currentBatch.remainingSlots--;\n currentBatch.dirty = true;\n } else {\n // Either batch was flushed or this op was already there - nothing to do!\n return;\n }\n\n if (currentBatch.remainingSlots === 0) {\n // batch is full, flush to cache\n this.write(batchNumber, currentBatch);\n this.batches.set(batchNumber, null);\n } else {\n this.scheduleTimer();\n }\n\n this.totalOpsToCache--;\n if (this.totalOpsToCache === 0) {\n this.logger.sendPerformanceEvent({ eventName: \"CacheOpsLimitHit\"});\n this.cache.remove();\n this.dispose();\n break;\n }\n }\n }\n\n public async get(from: number, to?: number): Promise<IMessage[]> {\n const messages: IMessage[] = [];\n let batchNumber = this.getBatchNumber(from + 1);\n const start = performance.now();\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const res = await this.cache.read(`${this.batchSize}_${batchNumber}`);\n if (res === undefined) {\n break;\n }\n const result: CacheEntry = JSON.parse(res);\n for (const op of result) {\n // Note that we write out undefined, but due to JSON.stringify, it turns into null!\n if (op) {\n if (to !== undefined && op.sequenceNumber >= to) {\n break;\n }\n if (messages.length === 0) {\n if (op.sequenceNumber > from + 1) {\n break;\n } else if (op.sequenceNumber <= from) {\n continue;\n }\n }\n messages.push(op);\n } else if (messages.length !== 0) {\n break;\n }\n }\n\n batchNumber++;\n }\n\n const duration = performance.now() - start;\n if (messages.length > 0 || duration > 1000) {\n this.logger.sendPerformanceEvent({\n eventName: \"CacheOpsUsed\",\n from,\n to,\n length: messages.length,\n duration,\n });\n }\n return messages;\n }\n\n protected write(batchNumber: number, payload: IBatch) {\n // Errors are caught and logged by PersistedCacheWithErrorHandling that sits\n // in the adapter chain of cache adapters\n this.cache.write(`${this.batchSize}_${batchNumber}`, JSON.stringify(payload.batchData)).catch(() => {\n this.totalOpsToCache = 0;\n });\n }\n\n protected scheduleTimer() {\n if (!this.timer && this.timerGranularity > 0) {\n this.timer = setTimeout(() => {\n this.timer = undefined;\n this.flushOps();\n }, this.timerGranularity);\n }\n }\n\n private getBatchNumber(sequenceNumber: number) {\n return Math.floor(sequenceNumber / this.batchSize);\n }\n\n private getPositionInBatchArray(sequenceNumber: number) {\n return sequenceNumber % this.batchSize;\n }\n\n private initializeNewBatchDataArray() {\n const tempArray: IMessage[] = [];\n tempArray.length = this.batchSize; // fill with empty, undefined elements\n return tempArray;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"opsCaching.js","sourceRoot":"","sources":["../src/opsCaching.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { performance } from \"@fluidframework/common-utils\";\n\n// ISequencedDocumentMessage\nexport interface IMessage {\n sequenceNumber: number;\n}\n\nexport type CacheEntry = (IMessage | undefined)[];\n\nexport interface IBatch {\n remainingSlots: number;\n batchData: CacheEntry;\n /**\n * Tells if this batch is dirty, i.e. it contains ops that were not flushed to cache\n */\n dirty: boolean;\n}\n\nexport interface ICache {\n write(batchNumber: string, data: string): Promise<void>;\n read(batchNumber: string): Promise<string | undefined>;\n remove(): void;\n}\n\nexport class OpsCache {\n private readonly batches: Map<number, null | IBatch> = new Map();\n private timer: ReturnType<typeof setTimeout> | undefined;\n\n constructor(\n startingSequenceNumber: number,\n private readonly logger: ITelemetryLogger,\n private readonly cache: ICache,\n private readonly batchSize: number,\n private readonly timerGranularity,\n private totalOpsToCache,\n ) {\n /** initial batch is a special case because it will never be full - all ops prior (inclusive) to\n * startingSequenceNumber are never going to show up (undefined)\n */\n const remainingSlots = this.batchSize - this.getPositionInBatchArray(startingSequenceNumber) - 1;\n if (remainingSlots !== 0) {\n this.batches.set(this.getBatchNumber(startingSequenceNumber), {\n remainingSlots,\n batchData : this.initializeNewBatchDataArray(),\n dirty: false,\n });\n }\n }\n\n public dispose() {\n this.batches.clear();\n if (this.timer !== undefined) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n }\n\n public flushOps() {\n for (const [key, value] of this.batches) {\n if (value === null || !value.dirty) {\n continue;\n }\n value.dirty = false;\n this.write(key, value);\n }\n }\n\n public addOps(ops: IMessage[]) {\n if (this.totalOpsToCache <= 0) {\n return;\n }\n\n for (const op of ops) {\n const batchNumber = this.getBatchNumber(op.sequenceNumber);\n const positionInBatch = this.getPositionInBatchArray(op.sequenceNumber);\n\n let currentBatch = this.batches.get(batchNumber);\n\n if (currentBatch === undefined) {\n currentBatch = {\n remainingSlots: this.batchSize - 1,\n batchData: this.initializeNewBatchDataArray(),\n dirty: true,\n };\n currentBatch.batchData[positionInBatch] = op;\n this.batches.set(batchNumber, currentBatch);\n } else if (currentBatch !== null && currentBatch.batchData[positionInBatch] === undefined) {\n currentBatch.batchData[positionInBatch] = op;\n currentBatch.remainingSlots--;\n currentBatch.dirty = true;\n } else {\n // Either batch was flushed or this op was already there - nothing to do!\n return;\n }\n\n if (currentBatch.remainingSlots === 0) {\n // batch is full, flush to cache\n this.write(batchNumber, currentBatch);\n this.batches.set(batchNumber, null);\n } else {\n this.scheduleTimer();\n }\n\n this.totalOpsToCache--;\n if (this.totalOpsToCache === 0) {\n this.logger.sendPerformanceEvent({ eventName: \"CacheOpsLimitHit\"});\n this.cache.remove();\n this.dispose();\n break;\n }\n }\n }\n\n private async getCore(from: number, to?: number): Promise<IMessage[]> {\n const messages: IMessage[] = [];\n let batchNumber = this.getBatchNumber(from);\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const res = await this.cache.read(`${this.batchSize}_${batchNumber}`);\n if (res === undefined) {\n return messages;\n }\n const result: CacheEntry = JSON.parse(res);\n for (const op of result) {\n // Note that we write out undefined, but due to JSON.stringify, it turns into null!\n if (op) {\n if (to !== undefined && op.sequenceNumber >= to) {\n return messages;\n }\n if (messages.length === 0) {\n if (op.sequenceNumber > from) {\n return messages;\n } else if (op.sequenceNumber < from) {\n continue;\n }\n }\n messages.push(op);\n } else if (messages.length !== 0) {\n return messages;\n }\n }\n\n batchNumber++;\n }\n }\n\n public async get(from: number, to?: number): Promise<IMessage[]> {\n const start = performance.now();\n\n const messages = await this.getCore(from, to);\n\n const duration = performance.now() - start;\n if (messages.length > 0 || duration > 1000) {\n this.logger.sendPerformanceEvent({\n eventName: \"CacheOpsUsed\",\n from,\n to,\n length: messages.length,\n duration,\n });\n }\n return messages;\n }\n\n protected write(batchNumber: number, payload: IBatch) {\n // Errors are caught and logged by PersistedCacheWithErrorHandling that sits\n // in the adapter chain of cache adapters\n this.cache.write(`${this.batchSize}_${batchNumber}`, JSON.stringify(payload.batchData)).catch(() => {\n this.totalOpsToCache = 0;\n });\n }\n\n protected scheduleTimer() {\n if (!this.timer && this.timerGranularity > 0) {\n this.timer = setTimeout(() => {\n this.timer = undefined;\n this.flushOps();\n }, this.timerGranularity);\n }\n }\n\n private getBatchNumber(sequenceNumber: number) {\n return Math.floor(sequenceNumber / this.batchSize);\n }\n\n private getPositionInBatchArray(sequenceNumber: number) {\n return sequenceNumber % this.batchSize;\n }\n\n private initializeNewBatchDataArray() {\n const tempArray: IMessage[] = [];\n tempArray.length = this.batchSize; // fill with empty, undefined elements\n return tempArray;\n }\n}\n"]}
|
package/lib/packageVersion.d.ts
CHANGED
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
|
|
6
6
|
*/
|
|
7
7
|
export declare const pkgName = "@fluidframework/odsp-driver";
|
|
8
|
-
export declare const pkgVersion = "0.
|
|
8
|
+
export declare const pkgVersion = "0.48.0";
|
|
9
9
|
//# sourceMappingURL=packageVersion.d.ts.map
|
package/lib/packageVersion.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,6BAA6B,CAAC;AACrD,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/odsp-driver\";\nexport const pkgVersion = \"0.
|
|
1
|
+
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,6BAA6B,CAAC;AACrD,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/odsp-driver\";\nexport const pkgVersion = \"0.48.0\";\n"]}
|
package/lib/vroom.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vroom.d.ts","sourceRoot":"","sources":["../src/vroom.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAEtE,OAAO,EAAE,+BAA+B,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AACzG,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAGtD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAQ9C;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CAClC,QAAQ,EAAE,aAAa,EACvB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,gBAAgB,EACxB,eAAe,EAAE,+BAA+B,EAChD,YAAY,EAAE,YAAY,EAC1B,kBAAkB,EAAE,OAAO,EAC3B,gBAAgB,CAAC,EAAE,MAAM,GAC1B,OAAO,CAAC,uBAAuB,CAAC,
|
|
1
|
+
{"version":3,"file":"vroom.d.ts","sourceRoot":"","sources":["../src/vroom.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAEtE,OAAO,EAAE,+BAA+B,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AACzG,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAGtD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAQ9C;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CAClC,QAAQ,EAAE,aAAa,EACvB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,gBAAgB,EACxB,eAAe,EAAE,+BAA+B,EAChD,YAAY,EAAE,YAAY,EAC1B,kBAAkB,EAAE,OAAO,EAC3B,gBAAgB,CAAC,EAAE,MAAM,GAC1B,OAAO,CAAC,uBAAuB,CAAC,CA8DlC"}
|
package/lib/vroom.js
CHANGED
|
@@ -45,6 +45,9 @@ export async function fetchJoinSession(urlParts, path, method, logger, getStorag
|
|
|
45
45
|
if (guestDisplayName) {
|
|
46
46
|
body.guestDisplayName = guestDisplayName;
|
|
47
47
|
}
|
|
48
|
+
// IMPORTANT: Must set content-type header explicitly to application/json when request has body.
|
|
49
|
+
// By default, request will use text/plain as content-type and will be rejected by backend.
|
|
50
|
+
headers["Content-Type"] = "application/json";
|
|
48
51
|
}
|
|
49
52
|
const response = await runWithRetry(async () => epochTracker.fetchAndParseAsJSON(`${getApiRoot(siteOrigin)}/drives/${urlParts.driveId}/items/${urlParts.itemId}/${path}?${queryParams}`, { method, headers, body: body ? JSON.stringify(body) : undefined }, "joinSession"), "joinSession", logger);
|
|
50
53
|
// TODO SPO-specific telemetry
|
package/lib/vroom.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vroom.js","sourceRoot":"","sources":["../src/vroom.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAGnE,OAAO,EAAE,2BAA2B,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAO5C;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAClC,QAAuB,EACvB,IAAY,EACZ,MAAc,EACd,MAAwB,EACxB,eAAgD,EAChD,YAA0B,EAC1B,kBAA2B,EAC3B,gBAAyB;IAEzB,OAAO,2BAA2B,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QACjD,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QAE5D,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO;YAC9B,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE;YAClE,CAAC,CAAC,EAAE,CAAC;QACT,OAAO,gBAAgB,CAAC,cAAc,CAClC,MAAM,kBACF,SAAS,EAAE,aAAa,EACxB,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAC9B,UAAU,GAEjB,KAAK,EAAE,KAAK,EAAE,EAAE;YACZ,kDAAkD;YAClD,MAAM,UAAU,GAAG,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAC/C,IAAI,WAAW,GAAG,gBAAgB,KAAK,EAAE,CAAC;YAC1C,IAAI,OAAO,GAAG,EAAE,CAAC;YACjB,IAAI,WAAW,CAAC,MAAM,GAAG,IAAI,EAAE;gBAC3B,WAAW,GAAG,EAAE,CAAC;gBACjB,OAAO,GAAG,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE,CAAC;aAClD;YACD,IAAI,IAAkC,CAAC;YACvC,IAAI,kBAAkB,IAAI,gBAAgB,EAAE;gBACxC,IAAI,GAAG,EAAE,CAAC;gBACV,IAAI,kBAAkB,EAAE;oBACpB,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;iBAClC;gBACD,IAAI,gBAAgB,EAAE;oBAClB,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;iBAC5C;
|
|
1
|
+
{"version":3,"file":"vroom.js","sourceRoot":"","sources":["../src/vroom.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { PerformanceEvent } from \"@fluidframework/telemetry-utils\";\nimport { InstrumentedStorageTokenFetcher, IOdspUrlParts } from \"@fluidframework/odsp-driver-definitions\";\nimport { ISocketStorageDiscovery } from \"./contracts\";\nimport { getWithRetryForTokenRefresh, getOrigin } from \"./odspUtils\";\nimport { getApiRoot } from \"./odspUrlHelper\";\nimport { EpochTracker } from \"./epochTracker\";\nimport { runWithRetry } from \"./retryUtils\";\n\ninterface IJoinSessionBody {\n requestSocketToken?: boolean;\n guestDisplayName?: string;\n}\n\n/**\n * Makes join session call on SPO to get information about the web socket for a document\n * @param driveId - The SPO drive id that this request should be made against\n * @param itemId -The SPO item id that this request should be made against\n * @param siteUrl - The SPO site that this request should be made against\n * @param path - The API path that is relevant to this request\n * @param method - The type of request, such as GET or POST\n * @param logger - A logger to use for this request\n * @param getStorageToken - A function that is able to provide the access token for this request\n * @param epochTracker - fetch wrapper which incorporates epoch logic around joinSession call\n * @param requestSocketToken - flag indicating whether joinSession is expected to return access token\n * which is used when establishing websocket connection with collab session backend service.\n * @param guestDisplayName - display name used to identify guest user joining a session.\n * This is optional and used only when collab session is being joined via invite.\n */\nexport async function fetchJoinSession(\n urlParts: IOdspUrlParts,\n path: string,\n method: string,\n logger: ITelemetryLogger,\n getStorageToken: InstrumentedStorageTokenFetcher,\n epochTracker: EpochTracker,\n requestSocketToken: boolean,\n guestDisplayName?: string,\n): Promise<ISocketStorageDiscovery> {\n return getWithRetryForTokenRefresh(async (options) => {\n const token = await getStorageToken(options, \"JoinSession\");\n\n const extraProps = options.refresh\n ? { hasClaims: !!options.claims, hasTenantId: !!options.tenantId }\n : {};\n return PerformanceEvent.timedExecAsync(\n logger, {\n eventName: \"JoinSession\",\n attempts: options.refresh ? 2 : 1,\n ...extraProps,\n },\n async (event) => {\n // TODO Extract the auth header-vs-query logic out\n const siteOrigin = getOrigin(urlParts.siteUrl);\n let queryParams = `access_token=${token}`;\n let headers = {};\n if (queryParams.length > 2048) {\n queryParams = \"\";\n headers = { Authorization: `Bearer ${token}` };\n }\n let body: IJoinSessionBody | undefined;\n if (requestSocketToken || guestDisplayName) {\n body = {};\n if (requestSocketToken) {\n body.requestSocketToken = true;\n }\n if (guestDisplayName) {\n body.guestDisplayName = guestDisplayName;\n }\n // IMPORTANT: Must set content-type header explicitly to application/json when request has body.\n // By default, request will use text/plain as content-type and will be rejected by backend.\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const response = await runWithRetry(\n async () => epochTracker.fetchAndParseAsJSON<ISocketStorageDiscovery>(\n `${getApiRoot(siteOrigin)}/drives/${\n urlParts.driveId\n }/items/${urlParts.itemId}/${path}?${queryParams}`,\n { method, headers, body: body ? JSON.stringify(body) : undefined },\n \"joinSession\",\n ),\n \"joinSession\",\n logger,\n );\n\n // TODO SPO-specific telemetry\n event.end({\n ...response.commonSpoHeaders,\n // pushV2 websocket urls will contain pushf\n pushv2: response.content.deltaStreamSocketUrl.includes(\"pushf\"),\n });\n\n if (response.content.runtimeTenantId && !response.content.tenantId) {\n response.content.tenantId = response.content.runtimeTenantId;\n }\n\n return response.content;\n });\n });\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/odsp-driver",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.48.0",
|
|
4
4
|
"description": "Socket storage implementation for SPO and ODC",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": "https://github.com/microsoft/FluidFramework",
|
|
@@ -58,15 +58,15 @@
|
|
|
58
58
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
59
59
|
"@fluidframework/common-utils": "^0.32.1",
|
|
60
60
|
"@fluidframework/core-interfaces": "^0.39.7",
|
|
61
|
-
"@fluidframework/driver-base": "^0.
|
|
61
|
+
"@fluidframework/driver-base": "^0.48.0",
|
|
62
62
|
"@fluidframework/driver-definitions": "^0.39.6",
|
|
63
|
-
"@fluidframework/driver-utils": "^0.
|
|
64
|
-
"@fluidframework/gitresources": "^0.
|
|
65
|
-
"@fluidframework/odsp-doclib-utils": "^0.
|
|
66
|
-
"@fluidframework/odsp-driver-definitions": "^0.
|
|
67
|
-
"@fluidframework/protocol-base": "^0.
|
|
63
|
+
"@fluidframework/driver-utils": "^0.48.0",
|
|
64
|
+
"@fluidframework/gitresources": "^0.1031.0-37526",
|
|
65
|
+
"@fluidframework/odsp-doclib-utils": "^0.48.0",
|
|
66
|
+
"@fluidframework/odsp-driver-definitions": "^0.48.0",
|
|
67
|
+
"@fluidframework/protocol-base": "^0.1031.0-37526",
|
|
68
68
|
"@fluidframework/protocol-definitions": "^0.1024.0",
|
|
69
|
-
"@fluidframework/telemetry-utils": "^0.
|
|
69
|
+
"@fluidframework/telemetry-utils": "^0.48.0",
|
|
70
70
|
"abort-controller": "^3.0.0",
|
|
71
71
|
"node-fetch": "^2.6.1",
|
|
72
72
|
"socket.io-client": "^2.4.0",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@fluidframework/build-common": "^0.23.0",
|
|
77
77
|
"@fluidframework/eslint-config-fluid": "^0.23.0",
|
|
78
|
-
"@fluidframework/mocha-test-setup": "^0.
|
|
78
|
+
"@fluidframework/mocha-test-setup": "^0.48.0",
|
|
79
79
|
"@microsoft/api-extractor": "^7.16.1",
|
|
80
80
|
"@types/mocha": "^8.2.2",
|
|
81
81
|
"@types/node-fetch": "^2.5.10",
|
package/src/createFile.ts
CHANGED
|
@@ -61,7 +61,7 @@ export async function createNewFluidFile(
|
|
|
61
61
|
): Promise<IOdspResolvedUrl> {
|
|
62
62
|
// Check for valid filename before the request to create file is actually made.
|
|
63
63
|
if (isInvalidFileName(newFileInfo.filename)) {
|
|
64
|
-
throwOdspNetworkError("
|
|
64
|
+
throwOdspNetworkError("invalidFilename", invalidFileNameStatusCode);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
let itemId: string;
|
|
@@ -132,7 +132,7 @@ export async function createNewEmptyFluidFile(
|
|
|
132
132
|
|
|
133
133
|
const content = fetchResponse.content;
|
|
134
134
|
if (!content || !content.id) {
|
|
135
|
-
throwOdspNetworkError("
|
|
135
|
+
throwOdspNetworkError("couldNotParseItemFromVroomResponse", fetchIncorrectResponse);
|
|
136
136
|
}
|
|
137
137
|
event.end({
|
|
138
138
|
headers: Object.keys(headers).length !== 0 ? true : undefined,
|
|
@@ -186,7 +186,7 @@ export async function createNewFluidFileFromSummary(
|
|
|
186
186
|
|
|
187
187
|
const content = fetchResponse.content;
|
|
188
188
|
if (!content || !content.itemId) {
|
|
189
|
-
throwOdspNetworkError("
|
|
189
|
+
throwOdspNetworkError("couldNotParseItemFromVroomResponse", fetchIncorrectResponse);
|
|
190
190
|
}
|
|
191
191
|
event.end({
|
|
192
192
|
headers: Object.keys(headers).length !== 0 ? true : undefined,
|
package/src/epochTracker.ts
CHANGED
|
@@ -282,7 +282,7 @@ export class EpochTracker implements IPersistedFileCache {
|
|
|
282
282
|
// initializes this value. Sometimes response does not contain epoch as it is still in
|
|
283
283
|
// implementation phase at server side. In that case also, don't compare it with our epoch value.
|
|
284
284
|
if (this.fluidEpoch && epochFromResponse && (this.fluidEpoch !== epochFromResponse)) {
|
|
285
|
-
throwOdspNetworkError(message ?? "
|
|
285
|
+
throwOdspNetworkError(message ?? "epochMismatch", fluidEpochMismatchError);
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
288
|
|
|
@@ -121,6 +121,23 @@ export class OdspDeltaStorageWithCache implements IDocumentDeltaStorageService {
|
|
|
121
121
|
) {
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
protected validateMessages(reason: string, messages: ISequencedDocumentMessage[], from: number) {
|
|
125
|
+
if (messages.length !== 0) {
|
|
126
|
+
const start = messages[0].sequenceNumber;
|
|
127
|
+
const length = messages.length;
|
|
128
|
+
const last = messages[length - 1].sequenceNumber;
|
|
129
|
+
if (start !== from) {
|
|
130
|
+
this.logger.sendErrorEvent({ eventName: "OpsFetchViolation", reason, from, start, last, length});
|
|
131
|
+
messages.length = 0;
|
|
132
|
+
}
|
|
133
|
+
if (last + 1 !== from + length) {
|
|
134
|
+
this.logger.sendErrorEvent({ eventName: "OpsFetchViolation", reason, from, start, last, length});
|
|
135
|
+
// we can do better here by finding consecutive sub-block and return it
|
|
136
|
+
messages.length = 0;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
124
141
|
public fetchMessages(
|
|
125
142
|
fromTotal: number,
|
|
126
143
|
toTotal: number | undefined,
|
|
@@ -137,44 +154,54 @@ export class OdspDeltaStorageWithCache implements IDocumentDeltaStorageService {
|
|
|
137
154
|
let opsFromCache = 0;
|
|
138
155
|
let opsFromStorage = 0;
|
|
139
156
|
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
this.snapshotOps = undefined;
|
|
157
|
+
const requestCallback = async (from: number, to: number, telemetryProps: ITelemetryProperties) => {
|
|
158
|
+
if (this.snapshotOps !== undefined && this.snapshotOps.length !== 0) {
|
|
159
|
+
const messages = this.snapshotOps.filter((op) =>
|
|
160
|
+
op.sequenceNumber >= from && op.sequenceNumber < to);
|
|
161
|
+
this.validateMessages("cached", messages, from);
|
|
162
|
+
if (messages.length > 0 && messages[0].sequenceNumber === from) {
|
|
163
|
+
this.snapshotOps = this.snapshotOps.filter((op) => op.sequenceNumber >= to);
|
|
164
|
+
opsFromSnapshot = messages.length;
|
|
165
|
+
return { messages, partialResult: true };
|
|
151
166
|
}
|
|
167
|
+
this.snapshotOps = undefined;
|
|
168
|
+
}
|
|
152
169
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
this.firstCacheMiss = Math.min(this.firstCacheMiss, from);
|
|
170
|
+
// Kick out request to PUSH for ops if it has them
|
|
171
|
+
this.requestFromSocket(from, to);
|
|
172
|
+
|
|
173
|
+
// Cache in normal flow is continuous. Once there is a miss, stop consulting cache.
|
|
174
|
+
// This saves a bit of processing time
|
|
175
|
+
if (from < this.firstCacheMiss) {
|
|
176
|
+
const messagesFromCache = await this.getCached(from, to);
|
|
177
|
+
this.validateMessages("cached", messagesFromCache, from);
|
|
178
|
+
if (messagesFromCache.length !== 0) {
|
|
179
|
+
opsFromCache += messagesFromCache.length;
|
|
180
|
+
return {
|
|
181
|
+
messages: messagesFromCache,
|
|
182
|
+
partialResult: true,
|
|
183
|
+
};
|
|
168
184
|
}
|
|
185
|
+
this.firstCacheMiss = Math.min(this.firstCacheMiss, from);
|
|
186
|
+
}
|
|
169
187
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
188
|
+
if (cachedOnly) {
|
|
189
|
+
return { messages: [], partialResult: false };
|
|
190
|
+
}
|
|
173
191
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
192
|
+
const ops = await this.getFromStorage(from, to, telemetryProps);
|
|
193
|
+
this.validateMessages("storage", ops.messages, from);
|
|
194
|
+
opsFromStorage += ops.messages.length;
|
|
195
|
+
this.opsReceived(ops.messages);
|
|
196
|
+
return ops;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const stream = requestOps(
|
|
200
|
+
async (from: number, to: number, telemetryProps: ITelemetryProperties) => {
|
|
201
|
+
const result = await requestCallback(from, to, telemetryProps);
|
|
202
|
+
// Catch all case, just in case
|
|
203
|
+
this.validateMessages("catch all", result.messages, from);
|
|
204
|
+
return result;
|
|
178
205
|
},
|
|
179
206
|
// Staging: starting with no concurrency, listening for feedback first.
|
|
180
207
|
// In future releases we will switch to actual concurrency
|
|
@@ -432,18 +432,26 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
|
|
|
432
432
|
// If so, there it most likely does not need these ops (otherwise it already asked for them)
|
|
433
433
|
if (data !== undefined) {
|
|
434
434
|
this.getOpsMap.delete(result.nonce);
|
|
435
|
+
const common = {
|
|
436
|
+
eventName: "GetOps",
|
|
437
|
+
code: result.code,
|
|
438
|
+
from: data.from,
|
|
439
|
+
to: data.to,
|
|
440
|
+
duration: performance.now() - data.start,
|
|
441
|
+
};
|
|
435
442
|
if (messages !== undefined && messages.length > 0) {
|
|
436
443
|
this.logger.sendPerformanceEvent({
|
|
437
|
-
|
|
444
|
+
...common,
|
|
438
445
|
first: messages[0].sequenceNumber,
|
|
439
446
|
last: messages[messages.length - 1].sequenceNumber,
|
|
440
|
-
code: result.code,
|
|
441
|
-
from: data.from,
|
|
442
|
-
to: data.to,
|
|
443
|
-
duration: performance.now() - data.start,
|
|
444
447
|
length: messages.length,
|
|
445
448
|
});
|
|
446
449
|
this.emit("op", this.documentId, messages);
|
|
450
|
+
} else {
|
|
451
|
+
this.logger.sendPerformanceEvent({
|
|
452
|
+
...common,
|
|
453
|
+
length: 0,
|
|
454
|
+
});
|
|
447
455
|
}
|
|
448
456
|
}
|
|
449
457
|
});
|
|
@@ -268,7 +268,7 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
268
268
|
|
|
269
269
|
const finalWebsocketToken = websocketToken ?? (websocketEndpoint.socketToken || null);
|
|
270
270
|
if (finalWebsocketToken === null) {
|
|
271
|
-
throwOdspNetworkError("
|
|
271
|
+
throwOdspNetworkError("pushTokenIsNull", fetchTokenErrorCode);
|
|
272
272
|
}
|
|
273
273
|
try {
|
|
274
274
|
const connection = await this.connectToDeltaStreamWithRetry(
|
|
@@ -391,10 +391,10 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
391
391
|
write: async (key: string, opsData: string) => {
|
|
392
392
|
return this.cache.persistedCache.put({...opsKey, key}, opsData);
|
|
393
393
|
},
|
|
394
|
-
read: async (
|
|
394
|
+
read: async (key: string) => this.cache.persistedCache.get({...opsKey, key}),
|
|
395
395
|
remove: () => { this.cache.persistedCache.removeEntries().catch(() => {}); },
|
|
396
396
|
},
|
|
397
|
-
|
|
397
|
+
batchSize,
|
|
398
398
|
this.hostPolicy.opsCaching?.timerGranularity ?? 5000,
|
|
399
399
|
this.hostPolicy.opsCaching?.totalOpsToCache ?? 5000,
|
|
400
400
|
);
|
|
@@ -485,10 +485,10 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
|
|
|
485
485
|
);
|
|
486
486
|
const versionsResponse = response.content;
|
|
487
487
|
if (!versionsResponse) {
|
|
488
|
-
throwOdspNetworkError("
|
|
488
|
+
throwOdspNetworkError("getVersionsReturnedNoResponse", 400);
|
|
489
489
|
}
|
|
490
490
|
if (!Array.isArray(versionsResponse.value)) {
|
|
491
|
-
throwOdspNetworkError("
|
|
491
|
+
throwOdspNetworkError("getVersionsReturnedNonArrayResponse", 400);
|
|
492
492
|
}
|
|
493
493
|
return versionsResponse.value.map((version) => {
|
|
494
494
|
// Parse the date from the message
|
|
@@ -660,19 +660,19 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
|
|
|
660
660
|
|
|
661
661
|
private checkSnapshotUrl() {
|
|
662
662
|
if (!this.snapshotUrl) {
|
|
663
|
-
throwOdspNetworkError("
|
|
663
|
+
throwOdspNetworkError("methodNotSupportedBecauseNoSnapshotUrlWasProvided", 400);
|
|
664
664
|
}
|
|
665
665
|
}
|
|
666
666
|
|
|
667
667
|
private checkAttachmentPOSTUrl() {
|
|
668
668
|
if (!this.attachmentPOSTUrl) {
|
|
669
|
-
throwOdspNetworkError("
|
|
669
|
+
throwOdspNetworkError("methodNotSupportedBecauseNoAttachmentPOSTUrlWasProvided", 400);
|
|
670
670
|
}
|
|
671
671
|
}
|
|
672
672
|
|
|
673
673
|
private checkAttachmentGETUrl() {
|
|
674
674
|
if (!this.attachmentGETUrl) {
|
|
675
|
-
throwOdspNetworkError("
|
|
675
|
+
throwOdspNetworkError("methodNotSupportedBecauseNoAttachmentGETUrlWasProvided", 400);
|
|
676
676
|
}
|
|
677
677
|
}
|
|
678
678
|
|
|
@@ -157,7 +157,7 @@ export class OdspDriverUrlResolverForShareLink implements IUrlResolver {
|
|
|
157
157
|
{ eventName: "GetSharingLinkToken" },
|
|
158
158
|
async (event) => tokenFetcher(options).then((tokenResponse) => {
|
|
159
159
|
if (tokenResponse === null) {
|
|
160
|
-
throwOdspNetworkError("
|
|
160
|
+
throwOdspNetworkError("shareLinkTokenIsNull", fetchTokenErrorCode);
|
|
161
161
|
}
|
|
162
162
|
event.end({ fromCache: isTokenFromCache(tokenResponse) });
|
|
163
163
|
return tokenResponse;
|
package/src/odspUtils.ts
CHANGED
|
@@ -105,7 +105,7 @@ export async function fetchHelper(
|
|
|
105
105
|
const response = fetchResponse as any as Response;
|
|
106
106
|
// Let's assume we can retry.
|
|
107
107
|
if (!response) {
|
|
108
|
-
throwOdspNetworkError(`
|
|
108
|
+
throwOdspNetworkError(`noResponseFromTheServer`, fetchIncorrectResponse);
|
|
109
109
|
}
|
|
110
110
|
if (!response.ok || response.status < 200 || response.status >= 300) {
|
|
111
111
|
throwOdspNetworkError(
|
|
@@ -129,10 +129,10 @@ export async function fetchHelper(
|
|
|
129
129
|
online = OnlineStatus.Offline;
|
|
130
130
|
}
|
|
131
131
|
if (error.name === "AbortError") {
|
|
132
|
-
throwOdspNetworkError("
|
|
132
|
+
throwOdspNetworkError("timeoutDuringFetch", fetchTimeoutStatusCode);
|
|
133
133
|
}
|
|
134
134
|
if (errorText.indexOf("ETIMEDOUT") !== -1) {
|
|
135
|
-
throwOdspNetworkError("
|
|
135
|
+
throwOdspNetworkError("timeoutDuringFetch(ETIMEDOUT)", fetchTimeoutStatusCode);
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
//
|
|
@@ -141,7 +141,7 @@ export async function fetchHelper(
|
|
|
141
141
|
// It is also non-serializable object due to circular references.
|
|
142
142
|
//
|
|
143
143
|
throwOdspNetworkError(
|
|
144
|
-
`
|
|
144
|
+
`fetchError`,
|
|
145
145
|
online === OnlineStatus.Offline ? offlineFetchFailureStatusCode : fetchFailureStatusCode,
|
|
146
146
|
undefined, // response
|
|
147
147
|
);
|
|
@@ -195,7 +195,13 @@ export async function fetchAndParseAsJSONHelper<T>(
|
|
|
195
195
|
};
|
|
196
196
|
return res;
|
|
197
197
|
} catch (e) {
|
|
198
|
-
throwOdspNetworkError(
|
|
198
|
+
throwOdspNetworkError(
|
|
199
|
+
"errorWhileParsingFetchResponse",
|
|
200
|
+
fetchIncorrectResponse,
|
|
201
|
+
content,
|
|
202
|
+
undefined,
|
|
203
|
+
{ error: Object(e) },
|
|
204
|
+
);
|
|
199
205
|
}
|
|
200
206
|
}
|
|
201
207
|
|
|
@@ -279,7 +285,7 @@ export function toInstrumentedOdspTokenFetcher(
|
|
|
279
285
|
event.end({ fromCache: isTokenFromCache(tokenResponse), isNull: token === null });
|
|
280
286
|
}
|
|
281
287
|
if (token === null && throwOnNullToken) {
|
|
282
|
-
throwOdspNetworkError(
|
|
288
|
+
throwOdspNetworkError("tokenIsNull", fetchTokenErrorCode, undefined, undefined, { method: name });
|
|
283
289
|
}
|
|
284
290
|
return token;
|
|
285
291
|
}),
|
package/src/opsCaching.ts
CHANGED
|
@@ -117,38 +117,43 @@ export class OpsCache {
|
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
private async getCore(from: number, to?: number): Promise<IMessage[]> {
|
|
121
121
|
const messages: IMessage[] = [];
|
|
122
|
-
let batchNumber = this.getBatchNumber(from
|
|
123
|
-
const start = performance.now();
|
|
122
|
+
let batchNumber = this.getBatchNumber(from);
|
|
124
123
|
// eslint-disable-next-line no-constant-condition
|
|
125
124
|
while (true) {
|
|
126
125
|
const res = await this.cache.read(`${this.batchSize}_${batchNumber}`);
|
|
127
126
|
if (res === undefined) {
|
|
128
|
-
|
|
127
|
+
return messages;
|
|
129
128
|
}
|
|
130
129
|
const result: CacheEntry = JSON.parse(res);
|
|
131
130
|
for (const op of result) {
|
|
132
131
|
// Note that we write out undefined, but due to JSON.stringify, it turns into null!
|
|
133
132
|
if (op) {
|
|
134
133
|
if (to !== undefined && op.sequenceNumber >= to) {
|
|
135
|
-
|
|
134
|
+
return messages;
|
|
136
135
|
}
|
|
137
136
|
if (messages.length === 0) {
|
|
138
|
-
if (op.sequenceNumber > from
|
|
139
|
-
|
|
140
|
-
} else if (op.sequenceNumber
|
|
137
|
+
if (op.sequenceNumber > from) {
|
|
138
|
+
return messages;
|
|
139
|
+
} else if (op.sequenceNumber < from) {
|
|
141
140
|
continue;
|
|
142
141
|
}
|
|
143
142
|
}
|
|
144
143
|
messages.push(op);
|
|
145
144
|
} else if (messages.length !== 0) {
|
|
146
|
-
|
|
145
|
+
return messages;
|
|
147
146
|
}
|
|
148
147
|
}
|
|
149
148
|
|
|
150
149
|
batchNumber++;
|
|
151
150
|
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
public async get(from: number, to?: number): Promise<IMessage[]> {
|
|
154
|
+
const start = performance.now();
|
|
155
|
+
|
|
156
|
+
const messages = await this.getCore(from, to);
|
|
152
157
|
|
|
153
158
|
const duration = performance.now() - start;
|
|
154
159
|
if (messages.length > 0 || duration > 1000) {
|
package/src/packageVersion.ts
CHANGED
package/src/vroom.ts
CHANGED
|
@@ -72,6 +72,9 @@ export async function fetchJoinSession(
|
|
|
72
72
|
if (guestDisplayName) {
|
|
73
73
|
body.guestDisplayName = guestDisplayName;
|
|
74
74
|
}
|
|
75
|
+
// IMPORTANT: Must set content-type header explicitly to application/json when request has body.
|
|
76
|
+
// By default, request will use text/plain as content-type and will be rejected by backend.
|
|
77
|
+
headers["Content-Type"] = "application/json";
|
|
75
78
|
}
|
|
76
79
|
|
|
77
80
|
const response = await runWithRetry(
|