@fluidframework/container-runtime 2.0.0-internal.2.3.1 → 2.0.0-internal.3.0.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 (79) hide show
  1. package/dist/blobManager.d.ts +3 -1
  2. package/dist/blobManager.d.ts.map +1 -1
  3. package/dist/blobManager.js +35 -2
  4. package/dist/blobManager.js.map +1 -1
  5. package/dist/containerRuntime.d.ts +45 -42
  6. package/dist/containerRuntime.d.ts.map +1 -1
  7. package/dist/containerRuntime.js +89 -40
  8. package/dist/containerRuntime.js.map +1 -1
  9. package/dist/dataStoreContext.d.ts +1 -0
  10. package/dist/dataStoreContext.d.ts.map +1 -1
  11. package/dist/dataStoreContext.js +7 -2
  12. package/dist/dataStoreContext.js.map +1 -1
  13. package/dist/garbageCollection.d.ts +15 -7
  14. package/dist/garbageCollection.d.ts.map +1 -1
  15. package/dist/garbageCollection.js +96 -36
  16. package/dist/garbageCollection.js.map +1 -1
  17. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  18. package/dist/opLifecycle/outbox.js +0 -1
  19. package/dist/opLifecycle/outbox.js.map +1 -1
  20. package/dist/packageVersion.d.ts +1 -1
  21. package/dist/packageVersion.js +1 -1
  22. package/dist/packageVersion.js.map +1 -1
  23. package/dist/pendingStateManager.d.ts +4 -13
  24. package/dist/pendingStateManager.d.ts.map +1 -1
  25. package/dist/pendingStateManager.js +130 -160
  26. package/dist/pendingStateManager.js.map +1 -1
  27. package/dist/summarizer.js.map +1 -1
  28. package/dist/summarizerClientElection.d.ts +1 -2
  29. package/dist/summarizerClientElection.d.ts.map +1 -1
  30. package/dist/summarizerClientElection.js +3 -30
  31. package/dist/summarizerClientElection.js.map +1 -1
  32. package/dist/summarizerTypes.d.ts +0 -4
  33. package/dist/summarizerTypes.d.ts.map +1 -1
  34. package/dist/summarizerTypes.js.map +1 -1
  35. package/lib/blobManager.d.ts +3 -1
  36. package/lib/blobManager.d.ts.map +1 -1
  37. package/lib/blobManager.js +35 -2
  38. package/lib/blobManager.js.map +1 -1
  39. package/lib/containerRuntime.d.ts +45 -42
  40. package/lib/containerRuntime.d.ts.map +1 -1
  41. package/lib/containerRuntime.js +89 -40
  42. package/lib/containerRuntime.js.map +1 -1
  43. package/lib/dataStoreContext.d.ts +1 -0
  44. package/lib/dataStoreContext.d.ts.map +1 -1
  45. package/lib/dataStoreContext.js +7 -2
  46. package/lib/dataStoreContext.js.map +1 -1
  47. package/lib/garbageCollection.d.ts +15 -7
  48. package/lib/garbageCollection.d.ts.map +1 -1
  49. package/lib/garbageCollection.js +97 -37
  50. package/lib/garbageCollection.js.map +1 -1
  51. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  52. package/lib/opLifecycle/outbox.js +0 -1
  53. package/lib/opLifecycle/outbox.js.map +1 -1
  54. package/lib/packageVersion.d.ts +1 -1
  55. package/lib/packageVersion.js +1 -1
  56. package/lib/packageVersion.js.map +1 -1
  57. package/lib/pendingStateManager.d.ts +4 -13
  58. package/lib/pendingStateManager.d.ts.map +1 -1
  59. package/lib/pendingStateManager.js +130 -160
  60. package/lib/pendingStateManager.js.map +1 -1
  61. package/lib/summarizer.js.map +1 -1
  62. package/lib/summarizerClientElection.d.ts +1 -2
  63. package/lib/summarizerClientElection.d.ts.map +1 -1
  64. package/lib/summarizerClientElection.js +3 -30
  65. package/lib/summarizerClientElection.js.map +1 -1
  66. package/lib/summarizerTypes.d.ts +0 -4
  67. package/lib/summarizerTypes.d.ts.map +1 -1
  68. package/lib/summarizerTypes.js.map +1 -1
  69. package/package.json +55 -20
  70. package/src/blobManager.ts +41 -2
  71. package/src/containerRuntime.ts +118 -85
  72. package/src/dataStoreContext.ts +12 -6
  73. package/src/garbageCollection.ts +103 -34
  74. package/src/opLifecycle/outbox.ts +0 -2
  75. package/src/packageVersion.ts +1 -1
  76. package/src/pendingStateManager.ts +146 -187
  77. package/src/summarizer.ts +1 -1
  78. package/src/summarizerClientElection.ts +1 -30
  79. package/src/summarizerTypes.ts +0 -4
@@ -1 +1 @@
1
- {"version":3,"file":"outbox.js","sourceRoot":"","sources":["../../src/opLifecycle/outbox.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 { assert } from \"@fluidframework/common-utils\";\nimport { IContainerContext } from \"@fluidframework/container-definitions\";\nimport { GenericError } from \"@fluidframework/container-utils\";\nimport { MessageType } from \"@fluidframework/protocol-definitions\";\nimport { ICompressionRuntimeOptions } from \"../containerRuntime\";\nimport { PendingStateManager } from \"../pendingStateManager\";\nimport { BatchManager } from \"./batchManager\";\nimport { BatchMessage, IBatch } from \"./definitions\";\nimport { OpCompressor } from \"./opCompressor\";\nimport { OpSplitter } from \"./opSplitter\";\n\nexport interface IOutboxConfig {\n readonly compressionOptions: ICompressionRuntimeOptions;\n // The maximum size of a batch that we can send over the wire.\n readonly maxBatchSizeInBytes: number;\n readonly enableOpReentryCheck?: boolean;\n};\n\nexport interface IOutboxParameters {\n readonly shouldSend: () => boolean,\n readonly pendingStateManager: PendingStateManager,\n readonly containerContext: IContainerContext,\n readonly config: IOutboxConfig,\n readonly compressor: OpCompressor;\n readonly splitter: OpSplitter;\n readonly logger: ITelemetryLogger;\n}\n\nexport class Outbox {\n private readonly attachFlowBatch: BatchManager;\n private readonly mainBatch: BatchManager;\n private readonly defaultAttachFlowSoftLimitInBytes = 64 * 1024;\n\n constructor(private readonly params: IOutboxParameters) {\n const isCompressionEnabled = this.params.config.compressionOptions.minimumBatchSizeInBytes !== Number.POSITIVE_INFINITY;\n // We need to allow infinite size batches if we enable compression\n const hardLimit = isCompressionEnabled ? Infinity : this.params.config.maxBatchSizeInBytes;\n const softLimit = isCompressionEnabled ? Infinity : this.defaultAttachFlowSoftLimitInBytes;\n\n this.attachFlowBatch = new BatchManager({\n hardLimit,\n softLimit,\n enableOpReentryCheck: params.config.enableOpReentryCheck,\n }, params.logger);\n this.mainBatch = new BatchManager({\n hardLimit,\n enableOpReentryCheck: params.config.enableOpReentryCheck,\n }, params.logger);\n }\n\n public get isEmpty(): boolean {\n return this.attachFlowBatch.length === 0 && this.mainBatch.length === 0;\n }\n\n public submit(message: BatchMessage) {\n if (!this.mainBatch.push(message)) {\n throw new GenericError(\n \"BatchTooLarge\",\n /* error */ undefined,\n {\n opSize: (message.contents?.length) ?? 0,\n count: this.mainBatch.length,\n limit: this.mainBatch.options.hardLimit,\n });\n }\n }\n\n public submitAttach(message: BatchMessage) {\n if (!this.attachFlowBatch.push(message)) {\n // BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged\n // when queue is not empty.\n // Flush queue & retry. Failure on retry would mean - single message is bigger than hard limit\n this.flushInternal(this.attachFlowBatch.popBatch());\n if (!this.attachFlowBatch.push(message)) {\n throw new GenericError(\n \"BatchTooLarge\",\n /* error */ undefined,\n {\n opSize: (message.contents?.length) ?? 0,\n count: this.attachFlowBatch.length,\n limit: this.attachFlowBatch.options.hardLimit,\n });\n }\n }\n\n // If compression is enabled, we will always successfully receive\n // attach ops and compress then send them at the next JS turn, regardless\n // of the overall size of the accumulated ops in the batch.\n // However, it is more efficient to flush these ops faster, preferably\n // after they reach a size which would benefit from compression.\n if (this.attachFlowBatch.contentSizeInBytes >= this.params.config.compressionOptions.minimumBatchSizeInBytes) {\n this.flushInternal(this.attachFlowBatch.popBatch());\n }\n }\n\n public flush() {\n this.flushInternal(this.attachFlowBatch.popBatch());\n this.flushInternal(this.mainBatch.popBatch());\n }\n\n private flushInternal(rawBatch: IBatch) {\n const processedBatch = this.compressBatch(rawBatch);\n const clientSequenceNumber = this.sendBatch(processedBatch);\n\n this.persistBatch(clientSequenceNumber, rawBatch.content);\n }\n\n private compressBatch(batch: IBatch): IBatch {\n if (batch.content.length === 0\n || this.params.config.compressionOptions === undefined\n || this.params.config.compressionOptions.minimumBatchSizeInBytes > batch.contentSizeInBytes) {\n // Nothing to do if the batch is empty or if compression is disabled or if we don't need to compress\n return batch;\n }\n\n const compressedBatch = this.params.compressor.compressBatch(batch);\n if (compressedBatch.contentSizeInBytes <= this.params.config.maxBatchSizeInBytes) {\n // If we don't reach the maximum supported size of a batch, it can safely be sent as is\n return compressedBatch;\n }\n\n if (this.params.splitter.isBatchChunkingEnabled) {\n return this.params.splitter.splitCompressedBatch(compressedBatch);\n }\n\n // If we've reached this point, the runtime would attempt to send a batch larger than the allowed size\n throw new GenericError(\n \"BatchTooLarge\",\n /* error */ undefined,\n {\n opSize: batch.contentSizeInBytes,\n count: batch.content.length,\n limit: this.params.config.maxBatchSizeInBytes,\n compressed: true,\n });\n }\n\n /**\n * Sends the batch object to the container context to be sent over the wire.\n *\n * @param batch - batch to be sent\n * @returns the client sequence number of the last batched op which was sent and\n * -1 if there are no ops or the container cannot send ops.\n */\n private sendBatch(batch: IBatch): number {\n let clientSequenceNumber: number = -1;\n const length = batch.content.length;\n\n // Did we disconnect in the middle of turn-based batch?\n // If so, do nothing, as pending state manager will resubmit it correctly on reconnect.\n if (length === 0 || !this.params.shouldSend()) {\n return clientSequenceNumber;\n }\n\n if (this.params.containerContext.submitBatchFn === undefined) {\n // Legacy path - supporting old loader versions. Can be removed only when LTS moves above\n // version that has support for batches (submitBatchFn)\n for (const message of batch.content) {\n // Legacy path doesn't support compressed payloads and will submit uncompressed payload anyways\n if (message.metadata?.compressed) {\n delete message.metadata.compressed;\n }\n\n clientSequenceNumber = this.params.containerContext.submitFn(\n MessageType.Operation,\n message.deserializedContent,\n true, // batch\n message.metadata);\n }\n\n this.params.containerContext.deltaManager.flush();\n } else {\n // returns clientSequenceNumber of last message in a batch\n clientSequenceNumber = this.params.containerContext.submitBatchFn(\n batch.content.map((message) => ({\n contents: message.contents,\n metadata: message.metadata,\n compression: message.compression,\n })));\n }\n\n // Convert from clientSequenceNumber of last message in the batch to clientSequenceNumber of first message.\n clientSequenceNumber -= length - 1;\n assert(clientSequenceNumber >= 0, 0x3d0 /* clientSequenceNumber can't be negative */);\n return clientSequenceNumber;\n }\n\n private persistBatch(initialClientSequenceNumber: number, batch: BatchMessage[]) {\n let clientSequenceNumber = initialClientSequenceNumber;\n // Let the PendingStateManager know that a message was submitted.\n // In future, need to shift toward keeping batch as a whole!\n for (const message of batch) {\n this.params.pendingStateManager.onSubmitMessage(\n message.deserializedContent.type,\n clientSequenceNumber,\n message.referenceSequenceNumber,\n message.deserializedContent.contents,\n message.localOpMetadata,\n message.metadata,\n );\n\n clientSequenceNumber++;\n }\n\n this.params.pendingStateManager.onFlush();\n }\n\n public checkpoint() {\n return {\n mainBatch: this.mainBatch.checkpoint(),\n attachFlowBatch: this.attachFlowBatch.checkpoint(),\n };\n }\n}\n"]}
1
+ {"version":3,"file":"outbox.js","sourceRoot":"","sources":["../../src/opLifecycle/outbox.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAGH,+DAAsD;AAEtD,qEAA+D;AAC/D,+EAAmE;AAGnE,iDAA8C;AAU7C,CAAC;AAYF,MAAa,MAAM;IAKf,YAA6B,MAAyB;QAAzB,WAAM,GAAN,MAAM,CAAmB;QAFrC,sCAAiC,GAAG,EAAE,GAAG,IAAI,CAAC;QAG3D,MAAM,oBAAoB,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,uBAAuB,KAAK,MAAM,CAAC,iBAAiB,CAAC;QACxH,kEAAkE;QAClE,MAAM,SAAS,GAAG,oBAAoB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC;QAC3F,MAAM,SAAS,GAAG,oBAAoB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,iCAAiC,CAAC;QAE3F,IAAI,CAAC,eAAe,GAAG,IAAI,2BAAY,CAAC;YACpC,SAAS;YACT,SAAS;YACT,oBAAoB,EAAE,MAAM,CAAC,MAAM,CAAC,oBAAoB;SAC3D,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QAClB,IAAI,CAAC,SAAS,GAAG,IAAI,2BAAY,CAAC;YAC9B,SAAS;YACT,oBAAoB,EAAE,MAAM,CAAC,MAAM,CAAC,oBAAoB;SAC3D,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACtB,CAAC;IAED,IAAW,OAAO;QACd,OAAO,IAAI,CAAC,eAAe,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC;IAC5E,CAAC;IAEM,MAAM,CAAC,OAAqB;;QAC/B,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;YAC/B,MAAM,IAAI,8BAAY,CAClB,eAAe;YACf,WAAW,CAAC,SAAS,EACrB;gBACI,MAAM,EAAE,MAAA,CAAC,MAAA,OAAO,CAAC,QAAQ,0CAAE,MAAM,CAAC,mCAAI,CAAC;gBACvC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM;gBAC5B,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS;aAC1C,CAAC,CAAC;SACV;IACL,CAAC;IAEM,YAAY,CAAC,OAAqB;;QACrC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;YACrC,oFAAoF;YACpF,2BAA2B;YAC3B,8FAA8F;YAC9F,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC,CAAC;YACpD,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;gBACrC,MAAM,IAAI,8BAAY,CAClB,eAAe;gBACf,WAAW,CAAC,SAAS,EACrB;oBACI,MAAM,EAAE,MAAA,CAAC,MAAA,OAAO,CAAC,QAAQ,0CAAE,MAAM,CAAC,mCAAI,CAAC;oBACvC,KAAK,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM;oBAClC,KAAK,EAAE,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,SAAS;iBAChD,CAAC,CAAC;aACV;SACJ;QAED,iEAAiE;QACjE,yEAAyE;QACzE,2DAA2D;QAC3D,sEAAsE;QACtE,gEAAgE;QAChE,IAAI,IAAI,CAAC,eAAe,CAAC,kBAAkB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,uBAAuB,EAAE;YAC1G,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC,CAAC;SACvD;IACL,CAAC;IAEM,KAAK;QACR,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC;IAClD,CAAC;IAEO,aAAa,CAAC,QAAgB;QAClC,MAAM,cAAc,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QACpD,MAAM,oBAAoB,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QAE5D,IAAI,CAAC,YAAY,CAAC,oBAAoB,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC9D,CAAC;IAEO,aAAa,CAAC,KAAa;QAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;eACvB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,kBAAkB,KAAK,SAAS;eACnD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,uBAAuB,GAAG,KAAK,CAAC,kBAAkB,EAAE;YAC7F,oGAAoG;YACpG,OAAO,KAAK,CAAC;SAChB;QAED,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACpE,IAAI,eAAe,CAAC,kBAAkB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,mBAAmB,EAAE;YAC9E,uFAAuF;YACvF,OAAO,eAAe,CAAC;SAC1B;QAED,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,EAAE;YAC7C,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,oBAAoB,CAAC,eAAe,CAAC,CAAC;SACrE;QAED,sGAAsG;QACtG,MAAM,IAAI,8BAAY,CAClB,eAAe;QACf,WAAW,CAAC,SAAS,EACrB;YACI,MAAM,EAAE,KAAK,CAAC,kBAAkB;YAChC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM;YAC3B,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,mBAAmB;YAC7C,UAAU,EAAE,IAAI;SACnB,CAAC,CAAC;IACX,CAAC;IAED;;;;;;OAMG;IACK,SAAS,CAAC,KAAa;;QAC3B,IAAI,oBAAoB,GAAW,CAAC,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAEpC,uDAAuD;QACvD,uFAAuF;QACvF,IAAI,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE;YAC3C,OAAO,oBAAoB,CAAC;SAC/B;QAED,IAAI,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,aAAa,KAAK,SAAS,EAAE;YAC1D,yFAAyF;YACzF,uDAAuD;YACvD,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE;gBACjC,+FAA+F;gBAC/F,IAAI,MAAA,OAAO,CAAC,QAAQ,0CAAE,UAAU,EAAE;oBAC9B,OAAO,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;iBACtC;gBAED,oBAAoB,GAAG,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,QAAQ,CACxD,kCAAW,CAAC,SAAS,EACrB,OAAO,CAAC,mBAAmB,EAC3B,IAAI,EAAE,QAAQ;gBACd,OAAO,CAAC,QAAQ,CAAC,CAAC;aACzB;YAED,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;SACrD;aAAM;YACH,0DAA0D;YAC1D,oBAAoB,GAAG,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,aAAa,CAC7D,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;gBAC5B,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,WAAW,EAAE,OAAO,CAAC,WAAW;aACnC,CAAC,CAAC,CAAC,CAAC;SACZ;QAED,2GAA2G;QAC3G,oBAAoB,IAAI,MAAM,GAAG,CAAC,CAAC;QACnC,IAAA,qBAAM,EAAC,oBAAoB,IAAI,CAAC,EAAE,KAAK,CAAC,4CAA4C,CAAC,CAAC;QACtF,OAAO,oBAAoB,CAAC;IAChC,CAAC;IAEO,YAAY,CAAC,2BAAmC,EAAE,KAAqB;QAC3E,IAAI,oBAAoB,GAAG,2BAA2B,CAAC;QACvD,iEAAiE;QACjE,4DAA4D;QAC5D,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE;YACzB,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,eAAe,CAC3C,OAAO,CAAC,mBAAmB,CAAC,IAAI,EAChC,oBAAoB,EACpB,OAAO,CAAC,uBAAuB,EAC/B,OAAO,CAAC,mBAAmB,CAAC,QAAQ,EACpC,OAAO,CAAC,eAAe,EACvB,OAAO,CAAC,QAAQ,CACnB,CAAC;YAEF,oBAAoB,EAAE,CAAC;SAC1B;IACL,CAAC;IAEM,UAAU;QACb,OAAO;YACH,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE;YACtC,eAAe,EAAE,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE;SACrD,CAAC;IACN,CAAC;CACJ;AAvLD,wBAuLC","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 { assert } from \"@fluidframework/common-utils\";\nimport { IContainerContext } from \"@fluidframework/container-definitions\";\nimport { GenericError } from \"@fluidframework/container-utils\";\nimport { MessageType } from \"@fluidframework/protocol-definitions\";\nimport { ICompressionRuntimeOptions } from \"../containerRuntime\";\nimport { PendingStateManager } from \"../pendingStateManager\";\nimport { BatchManager } from \"./batchManager\";\nimport { BatchMessage, IBatch } from \"./definitions\";\nimport { OpCompressor } from \"./opCompressor\";\nimport { OpSplitter } from \"./opSplitter\";\n\nexport interface IOutboxConfig {\n readonly compressionOptions: ICompressionRuntimeOptions;\n // The maximum size of a batch that we can send over the wire.\n readonly maxBatchSizeInBytes: number;\n readonly enableOpReentryCheck?: boolean;\n};\n\nexport interface IOutboxParameters {\n readonly shouldSend: () => boolean,\n readonly pendingStateManager: PendingStateManager,\n readonly containerContext: IContainerContext,\n readonly config: IOutboxConfig,\n readonly compressor: OpCompressor;\n readonly splitter: OpSplitter;\n readonly logger: ITelemetryLogger;\n}\n\nexport class Outbox {\n private readonly attachFlowBatch: BatchManager;\n private readonly mainBatch: BatchManager;\n private readonly defaultAttachFlowSoftLimitInBytes = 64 * 1024;\n\n constructor(private readonly params: IOutboxParameters) {\n const isCompressionEnabled = this.params.config.compressionOptions.minimumBatchSizeInBytes !== Number.POSITIVE_INFINITY;\n // We need to allow infinite size batches if we enable compression\n const hardLimit = isCompressionEnabled ? Infinity : this.params.config.maxBatchSizeInBytes;\n const softLimit = isCompressionEnabled ? Infinity : this.defaultAttachFlowSoftLimitInBytes;\n\n this.attachFlowBatch = new BatchManager({\n hardLimit,\n softLimit,\n enableOpReentryCheck: params.config.enableOpReentryCheck,\n }, params.logger);\n this.mainBatch = new BatchManager({\n hardLimit,\n enableOpReentryCheck: params.config.enableOpReentryCheck,\n }, params.logger);\n }\n\n public get isEmpty(): boolean {\n return this.attachFlowBatch.length === 0 && this.mainBatch.length === 0;\n }\n\n public submit(message: BatchMessage) {\n if (!this.mainBatch.push(message)) {\n throw new GenericError(\n \"BatchTooLarge\",\n /* error */ undefined,\n {\n opSize: (message.contents?.length) ?? 0,\n count: this.mainBatch.length,\n limit: this.mainBatch.options.hardLimit,\n });\n }\n }\n\n public submitAttach(message: BatchMessage) {\n if (!this.attachFlowBatch.push(message)) {\n // BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged\n // when queue is not empty.\n // Flush queue & retry. Failure on retry would mean - single message is bigger than hard limit\n this.flushInternal(this.attachFlowBatch.popBatch());\n if (!this.attachFlowBatch.push(message)) {\n throw new GenericError(\n \"BatchTooLarge\",\n /* error */ undefined,\n {\n opSize: (message.contents?.length) ?? 0,\n count: this.attachFlowBatch.length,\n limit: this.attachFlowBatch.options.hardLimit,\n });\n }\n }\n\n // If compression is enabled, we will always successfully receive\n // attach ops and compress then send them at the next JS turn, regardless\n // of the overall size of the accumulated ops in the batch.\n // However, it is more efficient to flush these ops faster, preferably\n // after they reach a size which would benefit from compression.\n if (this.attachFlowBatch.contentSizeInBytes >= this.params.config.compressionOptions.minimumBatchSizeInBytes) {\n this.flushInternal(this.attachFlowBatch.popBatch());\n }\n }\n\n public flush() {\n this.flushInternal(this.attachFlowBatch.popBatch());\n this.flushInternal(this.mainBatch.popBatch());\n }\n\n private flushInternal(rawBatch: IBatch) {\n const processedBatch = this.compressBatch(rawBatch);\n const clientSequenceNumber = this.sendBatch(processedBatch);\n\n this.persistBatch(clientSequenceNumber, rawBatch.content);\n }\n\n private compressBatch(batch: IBatch): IBatch {\n if (batch.content.length === 0\n || this.params.config.compressionOptions === undefined\n || this.params.config.compressionOptions.minimumBatchSizeInBytes > batch.contentSizeInBytes) {\n // Nothing to do if the batch is empty or if compression is disabled or if we don't need to compress\n return batch;\n }\n\n const compressedBatch = this.params.compressor.compressBatch(batch);\n if (compressedBatch.contentSizeInBytes <= this.params.config.maxBatchSizeInBytes) {\n // If we don't reach the maximum supported size of a batch, it can safely be sent as is\n return compressedBatch;\n }\n\n if (this.params.splitter.isBatchChunkingEnabled) {\n return this.params.splitter.splitCompressedBatch(compressedBatch);\n }\n\n // If we've reached this point, the runtime would attempt to send a batch larger than the allowed size\n throw new GenericError(\n \"BatchTooLarge\",\n /* error */ undefined,\n {\n opSize: batch.contentSizeInBytes,\n count: batch.content.length,\n limit: this.params.config.maxBatchSizeInBytes,\n compressed: true,\n });\n }\n\n /**\n * Sends the batch object to the container context to be sent over the wire.\n *\n * @param batch - batch to be sent\n * @returns the client sequence number of the last batched op which was sent and\n * -1 if there are no ops or the container cannot send ops.\n */\n private sendBatch(batch: IBatch): number {\n let clientSequenceNumber: number = -1;\n const length = batch.content.length;\n\n // Did we disconnect in the middle of turn-based batch?\n // If so, do nothing, as pending state manager will resubmit it correctly on reconnect.\n if (length === 0 || !this.params.shouldSend()) {\n return clientSequenceNumber;\n }\n\n if (this.params.containerContext.submitBatchFn === undefined) {\n // Legacy path - supporting old loader versions. Can be removed only when LTS moves above\n // version that has support for batches (submitBatchFn)\n for (const message of batch.content) {\n // Legacy path doesn't support compressed payloads and will submit uncompressed payload anyways\n if (message.metadata?.compressed) {\n delete message.metadata.compressed;\n }\n\n clientSequenceNumber = this.params.containerContext.submitFn(\n MessageType.Operation,\n message.deserializedContent,\n true, // batch\n message.metadata);\n }\n\n this.params.containerContext.deltaManager.flush();\n } else {\n // returns clientSequenceNumber of last message in a batch\n clientSequenceNumber = this.params.containerContext.submitBatchFn(\n batch.content.map((message) => ({\n contents: message.contents,\n metadata: message.metadata,\n compression: message.compression,\n })));\n }\n\n // Convert from clientSequenceNumber of last message in the batch to clientSequenceNumber of first message.\n clientSequenceNumber -= length - 1;\n assert(clientSequenceNumber >= 0, 0x3d0 /* clientSequenceNumber can't be negative */);\n return clientSequenceNumber;\n }\n\n private persistBatch(initialClientSequenceNumber: number, batch: BatchMessage[]) {\n let clientSequenceNumber = initialClientSequenceNumber;\n // Let the PendingStateManager know that a message was submitted.\n // In future, need to shift toward keeping batch as a whole!\n for (const message of batch) {\n this.params.pendingStateManager.onSubmitMessage(\n message.deserializedContent.type,\n clientSequenceNumber,\n message.referenceSequenceNumber,\n message.deserializedContent.contents,\n message.localOpMetadata,\n message.metadata,\n );\n\n clientSequenceNumber++;\n }\n }\n\n public checkpoint() {\n return {\n mainBatch: this.mainBatch.checkpoint(),\n attachFlowBatch: this.attachFlowBatch.checkpoint(),\n };\n }\n}\n"]}
@@ -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/container-runtime";
8
- export declare const pkgVersion = "2.0.0-internal.2.3.1";
8
+ export declare const pkgVersion = "2.0.0-internal.3.0.0";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -8,5 +8,5 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.pkgVersion = exports.pkgName = void 0;
10
10
  exports.pkgName = "@fluidframework/container-runtime";
11
- exports.pkgVersion = "2.0.0-internal.2.3.1";
11
+ exports.pkgVersion = "2.0.0-internal.3.0.0";
12
12
  //# sourceMappingURL=packageVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,mCAAmC,CAAC;AAC9C,QAAA,UAAU,GAAG,sBAAsB,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/container-runtime\";\nexport const pkgVersion = \"2.0.0-internal.2.3.1\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,mCAAmC,CAAC;AAC9C,QAAA,UAAU,GAAG,sBAAsB,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/container-runtime\";\nexport const pkgVersion = \"2.0.0-internal.3.0.0\";\n"]}
@@ -22,6 +22,7 @@ export interface IPendingMessage {
22
22
  /**
23
23
  * This represents an explicit flush call and is added to the pending queue when flush is called on the ContainerRuntime
24
24
  * to flush pending messages.
25
+ * @deprecated Use batch metadata on IPendingMessage instead. To be removed in 2.0.0-internal.4.0.0 (AB#2496)
25
26
  */
26
27
  export interface IPendingFlush {
27
28
  type: "flush";
@@ -38,7 +39,6 @@ export interface IRuntimeStateHandler {
38
39
  clientId(): string | undefined;
39
40
  close(error?: ICriticalContainerError): void;
40
41
  applyStashedOp: (type: ContainerMessageType, content: ISequencedDocumentMessage) => Promise<unknown>;
41
- flush(): void;
42
42
  reSubmit(type: ContainerMessageType, content: any, localOpMetadata: unknown, opMetadata: Record<string, unknown> | undefined): void;
43
43
  rollback(type: ContainerMessageType, content: any, localOpMetadata: unknown): void;
44
44
  orderSequentially(callback: () => void): void;
@@ -54,16 +54,15 @@ export interface IRuntimeStateHandler {
54
54
  */
55
55
  export declare class PendingStateManager implements IDisposable {
56
56
  private readonly stateHandler;
57
- private readonly pendingStates;
58
- private readonly initialStates;
57
+ private readonly pendingMessages;
58
+ private readonly initialMessages;
59
59
  private readonly disposeOnce;
60
- private _pendingMessagesCount;
61
60
  get pendingMessagesCount(): number;
62
61
  private isProcessingBatch;
63
62
  private pendingBatchBeginMessage;
64
63
  private clientId;
65
64
  /**
66
- * Called to check if there are any pending messages in the pending state queue.
65
+ * Called to check if there are any pending messages in the pending message queue.
67
66
  * @returns A boolean indicating whether there are messages or not.
68
67
  */
69
68
  hasPendingMessages(): boolean;
@@ -80,10 +79,6 @@ export declare class PendingStateManager implements IDisposable {
80
79
  * @param localOpMetadata - The local metadata associated with the message.
81
80
  */
82
81
  onSubmitMessage(type: ContainerMessageType, clientSequenceNumber: number, referenceSequenceNumber: number, content: any, localOpMetadata: unknown, opMetadata: Record<string, unknown> | undefined): void;
83
- /**
84
- * Called when flush() is called on the ContainerRuntime to manually flush messages.
85
- */
86
- onFlush(): void;
87
82
  /**
88
83
  * Applies stashed ops at their reference sequence number so they are ready to be ACKed or resubmitted
89
84
  * @param seqNum - Sequence number at which to apply ops. Will apply all ops if seqNum is undefined.
@@ -105,10 +100,6 @@ export declare class PendingStateManager implements IDisposable {
105
100
  * @param message - The message that is being processed.
106
101
  */
107
102
  private maybeProcessBatchEnd;
108
- /**
109
- * Returns the next pending state from the pending state queue.
110
- */
111
- private peekNextPendingState;
112
103
  /**
113
104
  * Called when the Container's connection state changes. If the Container gets connected, it replays all the pending
114
105
  * states in its queue. This includes triggering resubmission of unacked ops.
@@ -1 +1 @@
1
- {"version":3,"file":"pendingStateManager.d.ts","sourceRoot":"","sources":["../src/pendingStateManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pendingStateManager.d.ts","sourceRoot":"","sources":["../src/pendingStateManager.ts"],"names":[],"mappings":"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"}
@@ -23,38 +23,74 @@ const packageVersion_1 = require("./packageVersion");
23
23
  */
24
24
  class PendingStateManager {
25
25
  constructor(stateHandler, initialLocalState) {
26
- var _a;
26
+ var _a, _b;
27
27
  this.stateHandler = stateHandler;
28
- this.pendingStates = new double_ended_queue_1.default();
28
+ this.pendingMessages = new double_ended_queue_1.default();
29
+ this.initialMessages = new double_ended_queue_1.default();
29
30
  this.disposeOnce = new common_utils_1.Lazy(() => {
30
- this.initialStates.clear();
31
- this.pendingStates.clear();
31
+ this.initialMessages.clear();
32
+ this.pendingMessages.clear();
32
33
  });
33
- // Maintains the count of messages that are currently unacked.
34
- this._pendingMessagesCount = 0;
35
34
  // Indicates whether we are processing a batch.
36
35
  this.isProcessingBatch = false;
37
36
  this.dispose = () => this.disposeOnce.value;
38
- this.initialStates = new double_ended_queue_1.default((_a = initialLocalState === null || initialLocalState === void 0 ? void 0 : initialLocalState.pendingStates) !== null && _a !== void 0 ? _a : []);
37
+ /**
38
+ * Convert old local state format to the new format
39
+ * The old format contained "flush" messages as the indicator of batch ends
40
+ * The new format instead uses batch metadata on the last message to indicate batch ends
41
+ * ! TODO: Remove this conversion in "2.0.0-internal.4.0.0" as rollback from future version will be new format
42
+ * AB#2496 tracks removal
43
+ */
44
+ if (initialLocalState === null || initialLocalState === void 0 ? void 0 : initialLocalState.pendingStates) {
45
+ const pendingStates = initialLocalState === null || initialLocalState === void 0 ? void 0 : initialLocalState.pendingStates;
46
+ let currentlyBatching = false;
47
+ for (let i = 0; i < pendingStates.length; i++) {
48
+ const initialState = pendingStates[i];
49
+ // Skip over "flush" messages
50
+ if (initialState.type === "message") {
51
+ if ((_a = initialState.opMetadata) === null || _a === void 0 ? void 0 : _a.batch) {
52
+ currentlyBatching = true;
53
+ }
54
+ else if (((_b = initialState.opMetadata) === null || _b === void 0 ? void 0 : _b.batch) === false) {
55
+ currentlyBatching = false;
56
+ }
57
+ else if (
58
+ // End of batch if we are currently batching and this is last message or next message is flush
59
+ currentlyBatching
60
+ && (i === pendingStates.length - 1 || pendingStates[i + 1].type === "flush")) {
61
+ currentlyBatching = false;
62
+ initialState.opMetadata = Object.assign(Object.assign({}, initialState.opMetadata), { batch: false });
63
+ }
64
+ this.initialMessages.push(initialState);
65
+ }
66
+ }
67
+ }
39
68
  }
40
69
  get pendingMessagesCount() {
41
- return this._pendingMessagesCount;
70
+ return this.pendingMessages.length;
42
71
  }
43
72
  /**
44
- * Called to check if there are any pending messages in the pending state queue.
73
+ * Called to check if there are any pending messages in the pending message queue.
45
74
  * @returns A boolean indicating whether there are messages or not.
46
75
  */
47
76
  hasPendingMessages() {
48
- return this._pendingMessagesCount !== 0 || !this.initialStates.isEmpty();
77
+ return !this.pendingMessages.isEmpty() || !this.initialMessages.isEmpty();
49
78
  }
50
79
  getLocalState() {
51
- (0, common_utils_1.assert)(this.initialStates.isEmpty(), 0x2e9 /* "Must call getLocalState() after applying initial states" */);
52
- if (this.hasPendingMessages()) {
80
+ (0, common_utils_1.assert)(this.initialMessages.isEmpty(), 0x2e9 /* "Must call getLocalState() after applying initial states" */);
81
+ if (!this.pendingMessages.isEmpty()) {
53
82
  return {
54
- pendingStates: this.pendingStates.toArray().map(
55
- // delete localOpMetadata since it may not be serializable
56
- // and will be regenerated by applyStashedOp()
57
- (state) => state.type === "message" ? Object.assign(Object.assign({}, state), { localOpMetadata: undefined }) : state),
83
+ pendingStates: this.pendingMessages.toArray().reduce((arr, message) => {
84
+ var _a;
85
+ // delete localOpMetadata since it may not be serializable
86
+ // and will be regenerated by applyStashedOp()
87
+ arr.push(Object.assign(Object.assign({}, message), { localOpMetadata: undefined }));
88
+ // TODO: Remove in 2.0.0-internal.4.0.0 (AB#2496)
89
+ if (((_a = message.opMetadata) === null || _a === void 0 ? void 0 : _a.batch) === false) {
90
+ arr.push({ type: "flush" });
91
+ }
92
+ return arr;
93
+ }, new Array()),
58
94
  };
59
95
  }
60
96
  }
@@ -77,20 +113,7 @@ class PendingStateManager {
77
113
  localOpMetadata,
78
114
  opMetadata,
79
115
  };
80
- this.pendingStates.push(pendingMessage);
81
- this._pendingMessagesCount++;
82
- }
83
- /**
84
- * Called when flush() is called on the ContainerRuntime to manually flush messages.
85
- */
86
- onFlush() {
87
- // If the previous state is not a message, flush is a no-op.
88
- const previousState = this.pendingStates.peekBack();
89
- if ((previousState === null || previousState === void 0 ? void 0 : previousState.type) !== "message") {
90
- return;
91
- }
92
- // An explicit flush is interesting and is tracked only if there are messages sent in TurnBased mode.
93
- this.pendingStates.push({ type: "flush" });
116
+ this.pendingMessages.push(pendingMessage);
94
117
  }
95
118
  /**
96
119
  * Applies stashed ops at their reference sequence number so they are ready to be ACKed or resubmitted
@@ -98,29 +121,23 @@ class PendingStateManager {
98
121
  */
99
122
  async applyStashedOpsAt(seqNum) {
100
123
  // apply stashed ops at sequence number
101
- while (!this.initialStates.isEmpty()) {
124
+ while (!this.initialMessages.isEmpty()) {
102
125
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
103
- const nextState = this.initialStates.peekFront();
104
- if (nextState.type === "message") {
105
- if (seqNum !== undefined) {
106
- if (nextState.referenceSequenceNumber > seqNum) {
107
- break; // nothing left to do at this sequence number
108
- }
109
- else if (nextState.referenceSequenceNumber < seqNum) {
110
- throw new Error("loaded from snapshot too recent to apply stashed ops");
111
- }
126
+ const nextMessage = this.initialMessages.peekFront();
127
+ if (seqNum !== undefined) {
128
+ if (nextMessage.referenceSequenceNumber > seqNum) {
129
+ break; // nothing left to do at this sequence number
130
+ }
131
+ if (nextMessage.referenceSequenceNumber < seqNum) {
132
+ throw new Error("loaded from snapshot too recent to apply stashed ops");
112
133
  }
113
- // applyStashedOp will cause the DDS to behave as if it has sent the op but not actually send it
114
- const localOpMetadata = await this.stateHandler.applyStashedOp(nextState.messageType, nextState.content);
115
- nextState.localOpMetadata = localOpMetadata;
116
134
  }
117
- // then we push onto pendingStates which will cause PendingStateManager to resubmit when we connect
135
+ // applyStashedOp will cause the DDS to behave as if it has sent the op but not actually send it
136
+ const localOpMetadata = await this.stateHandler.applyStashedOp(nextMessage.messageType, nextMessage.content);
137
+ nextMessage.localOpMetadata = localOpMetadata;
138
+ // then we push onto pendingMessages which will cause PendingStateManager to resubmit when we connect
118
139
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
119
- const firstPendingState = this.initialStates.shift();
120
- this.pendingStates.push(firstPendingState);
121
- if (firstPendingState.type === "message") {
122
- this._pendingMessagesCount++;
123
- }
140
+ this.pendingMessages.push(this.initialMessages.shift());
124
141
  }
125
142
  }
126
143
  /**
@@ -131,23 +148,21 @@ class PendingStateManager {
131
148
  processPendingLocalMessage(message) {
132
149
  // Pre-processing part - This may be the start of a batch.
133
150
  this.maybeProcessBatchBegin(message);
134
- // Get the next state from the pending queue and verify that it is of type "message".
135
- const pendingState = this.peekNextPendingState();
136
- (0, common_utils_1.assert)(pendingState.type === "message", 0x169 /* "No pending message found for this remote message" */);
137
- this.pendingStates.shift();
151
+ // Get the next message from the pending queue. Verify a message exists.
152
+ const pendingMessage = this.pendingMessages.peekFront();
153
+ (0, common_utils_1.assert)(pendingMessage !== undefined, 0x169 /* "No pending message found for this remote message" */);
154
+ this.pendingMessages.shift();
138
155
  // Processing part - Verify that there has been no data corruption.
139
156
  // The clientSequenceNumber of the incoming message must match that of the pending message.
140
- if (pendingState.clientSequenceNumber !== message.clientSequenceNumber) {
157
+ if (pendingMessage.clientSequenceNumber !== message.clientSequenceNumber) {
141
158
  // Close the container because this could indicate data corruption.
142
- const error = container_utils_1.DataProcessingError.create("pending local message clientSequenceNumber mismatch", "unexpectedAckReceived", message, { expectedClientSequenceNumber: pendingState.clientSequenceNumber });
159
+ const error = container_utils_1.DataProcessingError.create("pending local message clientSequenceNumber mismatch", "unexpectedAckReceived", message, { expectedClientSequenceNumber: pendingMessage.clientSequenceNumber });
143
160
  this.stateHandler.close(error);
144
161
  return;
145
162
  }
146
- this._pendingMessagesCount--;
147
- (0, common_utils_1.assert)(this._pendingMessagesCount >= 0, 0x3d6 /* positive */);
148
163
  // Post-processing part - If we are processing a batch then this could be the last message in the batch.
149
164
  this.maybeProcessBatchEnd(message);
150
- return pendingState.localOpMetadata;
165
+ return pendingMessage.localOpMetadata;
151
166
  }
152
167
  /**
153
168
  * This message could be the first message in batch. If so, set batch state marking the beginning of a batch.
@@ -155,20 +170,6 @@ class PendingStateManager {
155
170
  */
156
171
  maybeProcessBatchBegin(message) {
157
172
  var _a;
158
- /**
159
- * We are checking if the next message is the start of a batch. It can happen in the following scenarios:
160
- *
161
- * 1. The FlushMode was set to TurnBased before this message was sent.
162
- *
163
- * 2. The FlushMode was already TurnBased and a flush was called before this message was sent. This essentially
164
- * means that the flush marked the end of a previous batch and beginning of a new batch.
165
- *
166
- * Keep reading pending states from the queue until we encounter a message. It's possible that the FlushMode was
167
- * updated a bunch of times without sending any messages.
168
- */
169
- while (this.peekNextPendingState().type !== "message") {
170
- this.pendingStates.shift();
171
- }
172
173
  // This message is the first in a batch if the "batch" property on the metadata is set to true
173
174
  if ((_a = message.metadata) === null || _a === void 0 ? void 0 : _a.batch) {
174
175
  // We should not already be processing a batch and there should be no pending batch begin message.
@@ -187,48 +188,36 @@ class PendingStateManager {
187
188
  if (!this.isProcessingBatch) {
188
189
  return;
189
190
  }
190
- const nextPendingState = this.peekNextPendingState();
191
- if (nextPendingState.type === "message") {
192
- return;
193
- }
194
191
  // There should be a pending batch begin message.
195
192
  (0, common_utils_1.assert)(this.pendingBatchBeginMessage !== undefined, 0x16d /* "There is no pending batch begin message" */);
196
- // Get the batch begin metadata from the first message in the batch.
197
- const batchBeginMetadata = (_a = this.pendingBatchBeginMessage.metadata) === null || _a === void 0 ? void 0 : _a.batch;
198
- // There could be just a single message in the batch. If so, it should not have any batch metadata. If there
199
- // are multiple messages in the batch, verify that we got the correct batch begin and end metadata.
200
- if (this.pendingBatchBeginMessage === message) {
201
- (0, common_utils_1.assert)(batchBeginMetadata === undefined, 0x16e /* "Batch with single message should not have batch metadata" */);
202
- }
203
- else {
204
- // Get the batch metadata from the last message in the batch.
205
- const batchEndMetadata = (_b = message.metadata) === null || _b === void 0 ? void 0 : _b.batch;
206
- if (batchBeginMetadata !== true || batchEndMetadata !== false) {
207
- this.stateHandler.close(container_utils_1.DataProcessingError.create("Pending batch inconsistency", // Formerly known as asserts 0x16f and 0x170
208
- "processPendingLocalMessage", message, {
209
- runtimeVersion: packageVersion_1.pkgVersion,
210
- batchClientId: this.pendingBatchBeginMessage.clientId,
211
- clientId: this.stateHandler.clientId(),
212
- hasBatchStart: batchBeginMetadata === true,
213
- hasBatchEnd: batchEndMetadata === false,
214
- messageType: message.type,
215
- batchStartSequenceNumber: this.pendingBatchBeginMessage.clientSequenceNumber,
216
- pendingMessagesCount: this.pendingMessagesCount,
217
- nextPendingState: nextPendingState.type,
218
- }));
193
+ const batchEndMetadata = (_a = message.metadata) === null || _a === void 0 ? void 0 : _a.batch;
194
+ if (this.pendingMessages.isEmpty() || batchEndMetadata === false) {
195
+ // Get the batch begin metadata from the first message in the batch.
196
+ const batchBeginMetadata = (_b = this.pendingBatchBeginMessage.metadata) === null || _b === void 0 ? void 0 : _b.batch;
197
+ // There could be just a single message in the batch. If so, it should not have any batch metadata. If there
198
+ // are multiple messages in the batch, verify that we got the correct batch begin and end metadata.
199
+ if (this.pendingBatchBeginMessage === message) {
200
+ (0, common_utils_1.assert)(batchBeginMetadata === undefined, 0x16e /* "Batch with single message should not have batch metadata" */);
201
+ }
202
+ else {
203
+ if (batchBeginMetadata !== true || batchEndMetadata !== false) {
204
+ this.stateHandler.close(container_utils_1.DataProcessingError.create("Pending batch inconsistency", // Formerly known as asserts 0x16f and 0x170
205
+ "processPendingLocalMessage", message, {
206
+ runtimeVersion: packageVersion_1.pkgVersion,
207
+ batchClientId: this.pendingBatchBeginMessage.clientId,
208
+ clientId: this.stateHandler.clientId(),
209
+ hasBatchStart: batchBeginMetadata === true,
210
+ hasBatchEnd: batchEndMetadata === false,
211
+ messageType: message.type,
212
+ batchStartSequenceNumber: this.pendingBatchBeginMessage.clientSequenceNumber,
213
+ pendingMessagesCount: this.pendingMessagesCount,
214
+ }));
215
+ }
219
216
  }
217
+ // Clear the pending batch state now that we have processed the entire batch.
218
+ this.pendingBatchBeginMessage = undefined;
219
+ this.isProcessingBatch = false;
220
220
  }
221
- // Clear the pending batch state now that we have processed the entire batch.
222
- this.pendingBatchBeginMessage = undefined;
223
- this.isProcessingBatch = false;
224
- }
225
- /**
226
- * Returns the next pending state from the pending state queue.
227
- */
228
- peekNextPendingState() {
229
- const nextPendingState = this.pendingStates.peekFront();
230
- (0, common_utils_1.assert)(!!nextPendingState, 0x171 /* "No pending state found for the remote message" */);
231
- return nextPendingState;
232
221
  }
233
222
  /**
234
223
  * Called when the Container's connection state changes. If the Container gets connected, it replays all the pending
@@ -240,63 +229,44 @@ class PendingStateManager {
240
229
  // This assert suggests we are about to send same ops twice, which will result in data loss.
241
230
  (0, common_utils_1.assert)(this.clientId !== this.stateHandler.clientId(), 0x173 /* "replayPendingStates called twice for same clientId!" */);
242
231
  this.clientId = this.stateHandler.clientId();
243
- (0, common_utils_1.assert)(this.initialStates.isEmpty(), 0x174 /* "initial states should be empty before replaying pending" */);
244
- let pendingStatesCount = this.pendingStates.length;
245
- if (pendingStatesCount === 0) {
232
+ (0, common_utils_1.assert)(this.initialMessages.isEmpty(), 0x174 /* "initial states should be empty before replaying pending" */);
233
+ let pendingMessagesCount = this.pendingMessages.length;
234
+ if (pendingMessagesCount === 0) {
246
235
  return;
247
236
  }
248
- // Reset the pending message count because all these messages will be removed from the queue.
249
- this._pendingMessagesCount = 0;
250
- const messageBatchQueue = new double_ended_queue_1.default();
251
- // Process exactly `pendingStatesCount` items in the queue as it represents the number of states that were
237
+ // Process exactly `pendingMessagesCount` items in the queue as it represents the number of messages that were
252
238
  // pending when we connected. This is important because the `reSubmitFn` might add more items in the queue
253
239
  // which must not be replayed.
254
- while (pendingStatesCount > 0) {
240
+ while (pendingMessagesCount > 0) {
255
241
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
256
- const pendingState = this.pendingStates.shift();
257
- switch (pendingState.type) {
258
- case "message":
259
- (0, common_utils_1.assert)(((_a = pendingState.opMetadata) === null || _a === void 0 ? void 0 : _a.batch) !== false || messageBatchQueue.length > 0, 0x41b /* We cannot process batches in chunks */);
260
- /**
261
- * We want to ensure grouped messages get processed in a batch.
262
- * Note: It is not possible for the PendingStateManager to receive a partially acked batch. It will
263
- * either receive the whole batch ack or nothing at all.
264
- */
265
- if (messageBatchQueue.length > 0 || ((_b = pendingState.opMetadata) === null || _b === void 0 ? void 0 : _b.batch)) {
266
- messageBatchQueue.enqueue(pendingState);
267
- }
268
- else {
269
- this.stateHandler.reSubmit(pendingState.messageType, pendingState.content, pendingState.localOpMetadata, pendingState.opMetadata);
242
+ let pendingMessage = this.pendingMessages.shift();
243
+ pendingMessagesCount--;
244
+ (0, common_utils_1.assert)(((_a = pendingMessage.opMetadata) === null || _a === void 0 ? void 0 : _a.batch) !== false, 0x41b /* We cannot process batches in chunks */);
245
+ /**
246
+ * We want to ensure grouped messages get processed in a batch.
247
+ * Note: It is not possible for the PendingStateManager to receive a partially acked batch. It will
248
+ * either receive the whole batch ack or nothing at all.
249
+ */
250
+ if ((_b = pendingMessage.opMetadata) === null || _b === void 0 ? void 0 : _b.batch) {
251
+ (0, common_utils_1.assert)(pendingMessagesCount > 0, 0x554 /* Last pending message cannot be a batch begin */);
252
+ this.stateHandler.orderSequentially(() => {
253
+ var _a, _b;
254
+ while (pendingMessagesCount >= 0) { // check is >= because batch end may be last pending message
255
+ this.stateHandler.reSubmit(pendingMessage.messageType, pendingMessage.content, pendingMessage.localOpMetadata, pendingMessage.opMetadata);
256
+ if (((_a = pendingMessage.opMetadata) === null || _a === void 0 ? void 0 : _a.batch) === false) {
257
+ break;
258
+ }
259
+ (0, common_utils_1.assert)(pendingMessagesCount > 0, 0x555 /* No batch end found */);
260
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
261
+ pendingMessage = this.pendingMessages.shift();
262
+ pendingMessagesCount--;
263
+ (0, common_utils_1.assert)(((_b = pendingMessage.opMetadata) === null || _b === void 0 ? void 0 : _b.batch) !== true, 0x556 /* Batch start needs a corresponding batch end */);
270
264
  }
271
- break;
272
- case "flush":
273
- /**
274
- * A "flush" call can indicate the end of a batch.
275
- * We can't rely on the "batch" property in the message metadata as it gets
276
- * updated elsewhere and it is not the same object instance that gets updated.
277
- */
278
- if (messageBatchQueue.length > 0) {
279
- this.stateHandler.orderSequentially(() => {
280
- while (messageBatchQueue.length > 0) {
281
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
282
- const message = messageBatchQueue.dequeue();
283
- this.stateHandler.reSubmit(message.messageType, message.content, message.localOpMetadata, message.opMetadata);
284
- }
285
- });
286
- }
287
- (0, common_utils_1.assert)(messageBatchQueue.length === 0, 0x41c /* cannot flush in the middle of a batch */);
288
- this.stateHandler.flush();
289
- break;
290
- default:
291
- break;
265
+ });
266
+ }
267
+ else {
268
+ this.stateHandler.reSubmit(pendingMessage.messageType, pendingMessage.content, pendingMessage.localOpMetadata, pendingMessage.opMetadata);
292
269
  }
293
- pendingStatesCount--;
294
- }
295
- // There are some cases where ops are stashed but not flushed. We need to ensure they are resubmitted
296
- while (messageBatchQueue.length > 0) {
297
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
298
- const message = messageBatchQueue.dequeue();
299
- this.stateHandler.reSubmit(message.messageType, message.content, message.localOpMetadata, message.opMetadata);
300
270
  }
301
271
  }
302
272
  }