@fluidframework/container-runtime 2.0.0-dev.2.2.0.111723 → 2.0.0-dev.2.3.0.115467

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 (161) hide show
  1. package/dist/blobManager.d.ts +20 -5
  2. package/dist/blobManager.d.ts.map +1 -1
  3. package/dist/blobManager.js +57 -15
  4. package/dist/blobManager.js.map +1 -1
  5. package/dist/containerRuntime.d.ts +16 -33
  6. package/dist/containerRuntime.d.ts.map +1 -1
  7. package/dist/containerRuntime.js +71 -219
  8. package/dist/containerRuntime.js.map +1 -1
  9. package/dist/dataStoreContext.js +2 -2
  10. package/dist/dataStoreContext.js.map +1 -1
  11. package/dist/dataStores.d.ts.map +1 -1
  12. package/dist/dataStores.js +2 -1
  13. package/dist/dataStores.js.map +1 -1
  14. package/dist/garbageCollection.d.ts +7 -16
  15. package/dist/garbageCollection.d.ts.map +1 -1
  16. package/dist/garbageCollection.js +41 -61
  17. package/dist/garbageCollection.js.map +1 -1
  18. package/dist/garbageCollectionConstants.d.ts +19 -0
  19. package/dist/garbageCollectionConstants.d.ts.map +1 -0
  20. package/dist/garbageCollectionConstants.js +34 -0
  21. package/dist/garbageCollectionConstants.js.map +1 -0
  22. package/dist/gcSweepReadyUsageDetection.js +2 -2
  23. package/dist/gcSweepReadyUsageDetection.js.map +1 -1
  24. package/dist/index.d.ts +4 -2
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +7 -6
  27. package/dist/index.js.map +1 -1
  28. package/dist/opLifecycle/batchManager.d.ts +30 -0
  29. package/dist/opLifecycle/batchManager.d.ts.map +1 -0
  30. package/dist/{batchManager.js → opLifecycle/batchManager.js} +17 -17
  31. package/dist/opLifecycle/batchManager.js.map +1 -0
  32. package/dist/opLifecycle/definitions.d.ts +40 -0
  33. package/dist/opLifecycle/definitions.d.ts.map +1 -0
  34. package/dist/opLifecycle/definitions.js +7 -0
  35. package/dist/opLifecycle/definitions.js.map +1 -0
  36. package/dist/opLifecycle/index.d.ts +12 -0
  37. package/dist/opLifecycle/index.d.ts.map +1 -0
  38. package/dist/opLifecycle/index.js +21 -0
  39. package/dist/opLifecycle/index.js.map +1 -0
  40. package/dist/{opCompressor.d.ts → opLifecycle/opCompressor.d.ts} +2 -2
  41. package/dist/opLifecycle/opCompressor.d.ts.map +1 -0
  42. package/dist/{opCompressor.js → opLifecycle/opCompressor.js} +16 -13
  43. package/dist/opLifecycle/opCompressor.js.map +1 -0
  44. package/dist/{opDecompressor.d.ts → opLifecycle/opDecompressor.d.ts} +0 -0
  45. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -0
  46. package/dist/{opDecompressor.js → opLifecycle/opDecompressor.js} +5 -5
  47. package/dist/opLifecycle/opDecompressor.js.map +1 -0
  48. package/dist/opLifecycle/opSplitter.d.ts +17 -0
  49. package/dist/opLifecycle/opSplitter.d.ts.map +1 -0
  50. package/dist/opLifecycle/opSplitter.js +61 -0
  51. package/dist/opLifecycle/opSplitter.js.map +1 -0
  52. package/dist/opLifecycle/outbox.d.ts +47 -0
  53. package/dist/opLifecycle/outbox.d.ts.map +1 -0
  54. package/dist/opLifecycle/outbox.js +153 -0
  55. package/dist/opLifecycle/outbox.js.map +1 -0
  56. package/dist/opLifecycle/remoteMessageProcessor.d.ts +26 -0
  57. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
  58. package/dist/opLifecycle/remoteMessageProcessor.js +81 -0
  59. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -0
  60. package/dist/packageVersion.d.ts +1 -1
  61. package/dist/packageVersion.js +1 -1
  62. package/dist/packageVersion.js.map +1 -1
  63. package/dist/summaryFormat.js +2 -2
  64. package/dist/summaryFormat.js.map +1 -1
  65. package/lib/blobManager.d.ts +20 -5
  66. package/lib/blobManager.d.ts.map +1 -1
  67. package/lib/blobManager.js +59 -17
  68. package/lib/blobManager.js.map +1 -1
  69. package/lib/containerRuntime.d.ts +16 -33
  70. package/lib/containerRuntime.d.ts.map +1 -1
  71. package/lib/containerRuntime.js +68 -215
  72. package/lib/containerRuntime.js.map +1 -1
  73. package/lib/dataStoreContext.js +1 -1
  74. package/lib/dataStoreContext.js.map +1 -1
  75. package/lib/dataStores.d.ts.map +1 -1
  76. package/lib/dataStores.js +2 -1
  77. package/lib/dataStores.js.map +1 -1
  78. package/lib/garbageCollection.d.ts +7 -16
  79. package/lib/garbageCollection.d.ts.map +1 -1
  80. package/lib/garbageCollection.js +19 -39
  81. package/lib/garbageCollection.js.map +1 -1
  82. package/lib/garbageCollectionConstants.d.ts +19 -0
  83. package/lib/garbageCollectionConstants.d.ts.map +1 -0
  84. package/lib/garbageCollectionConstants.js +31 -0
  85. package/lib/garbageCollectionConstants.js.map +1 -0
  86. package/lib/gcSweepReadyUsageDetection.js +1 -1
  87. package/lib/gcSweepReadyUsageDetection.js.map +1 -1
  88. package/lib/index.d.ts +4 -2
  89. package/lib/index.d.ts.map +1 -1
  90. package/lib/index.js +3 -2
  91. package/lib/index.js.map +1 -1
  92. package/lib/opLifecycle/batchManager.d.ts +30 -0
  93. package/lib/opLifecycle/batchManager.d.ts.map +1 -0
  94. package/lib/{batchManager.js → opLifecycle/batchManager.js} +17 -17
  95. package/lib/opLifecycle/batchManager.js.map +1 -0
  96. package/lib/opLifecycle/definitions.d.ts +40 -0
  97. package/lib/opLifecycle/definitions.d.ts.map +1 -0
  98. package/lib/opLifecycle/definitions.js +6 -0
  99. package/lib/opLifecycle/definitions.js.map +1 -0
  100. package/lib/opLifecycle/index.d.ts +12 -0
  101. package/lib/opLifecycle/index.d.ts.map +1 -0
  102. package/lib/opLifecycle/index.js +11 -0
  103. package/lib/opLifecycle/index.js.map +1 -0
  104. package/lib/{opCompressor.d.ts → opLifecycle/opCompressor.d.ts} +2 -2
  105. package/lib/opLifecycle/opCompressor.d.ts.map +1 -0
  106. package/lib/{opCompressor.js → opLifecycle/opCompressor.js} +16 -13
  107. package/lib/opLifecycle/opCompressor.js.map +1 -0
  108. package/lib/{opDecompressor.d.ts → opLifecycle/opDecompressor.d.ts} +0 -0
  109. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -0
  110. package/lib/{opDecompressor.js → opLifecycle/opDecompressor.js} +4 -4
  111. package/lib/opLifecycle/opDecompressor.js.map +1 -0
  112. package/lib/opLifecycle/opSplitter.d.ts +17 -0
  113. package/lib/opLifecycle/opSplitter.d.ts.map +1 -0
  114. package/lib/opLifecycle/opSplitter.js +57 -0
  115. package/lib/opLifecycle/opSplitter.js.map +1 -0
  116. package/lib/opLifecycle/outbox.d.ts +47 -0
  117. package/lib/opLifecycle/outbox.d.ts.map +1 -0
  118. package/lib/opLifecycle/outbox.js +149 -0
  119. package/lib/opLifecycle/outbox.js.map +1 -0
  120. package/lib/opLifecycle/remoteMessageProcessor.d.ts +26 -0
  121. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
  122. package/lib/opLifecycle/remoteMessageProcessor.js +76 -0
  123. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -0
  124. package/lib/packageVersion.d.ts +1 -1
  125. package/lib/packageVersion.js +1 -1
  126. package/lib/packageVersion.js.map +1 -1
  127. package/lib/summaryFormat.js +1 -1
  128. package/lib/summaryFormat.js.map +1 -1
  129. package/package.json +21 -34
  130. package/src/blobManager.ts +74 -19
  131. package/src/containerRuntime.ts +91 -278
  132. package/src/dataStoreContext.ts +1 -1
  133. package/src/dataStores.ts +2 -1
  134. package/src/garbageCollection.ts +33 -43
  135. package/src/garbageCollectionConstants.ts +35 -0
  136. package/src/gcSweepReadyUsageDetection.ts +1 -1
  137. package/src/index.ts +5 -4
  138. package/src/{batchManager.ts → opLifecycle/batchManager.ts} +30 -33
  139. package/src/opLifecycle/definitions.ts +44 -0
  140. package/src/opLifecycle/index.ts +17 -0
  141. package/src/{opCompressor.ts → opLifecycle/opCompressor.ts} +21 -16
  142. package/src/{opDecompressor.ts → opLifecycle/opDecompressor.ts} +8 -6
  143. package/src/opLifecycle/opSplitter.ts +78 -0
  144. package/src/opLifecycle/outbox.ts +204 -0
  145. package/src/opLifecycle/remoteMessageProcessor.ts +90 -0
  146. package/src/packageVersion.ts +1 -1
  147. package/src/summaryFormat.ts +1 -1
  148. package/dist/batchManager.d.ts +0 -42
  149. package/dist/batchManager.d.ts.map +0 -1
  150. package/dist/batchManager.js.map +0 -1
  151. package/dist/opCompressor.d.ts.map +0 -1
  152. package/dist/opCompressor.js.map +0 -1
  153. package/dist/opDecompressor.d.ts.map +0 -1
  154. package/dist/opDecompressor.js.map +0 -1
  155. package/lib/batchManager.d.ts +0 -42
  156. package/lib/batchManager.d.ts.map +0 -1
  157. package/lib/batchManager.js.map +0 -1
  158. package/lib/opCompressor.d.ts.map +0 -1
  159. package/lib/opCompressor.js.map +0 -1
  160. package/lib/opDecompressor.d.ts.map +0 -1
  161. package/lib/opDecompressor.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"garbageCollectionConstants.js","sourceRoot":"","sources":["../src/garbageCollectionConstants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,sCAAsC;AACtC,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC;AAC9B,sDAAsD;AACtD,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC;AACnC,wDAAwD;AACxD,MAAM,CAAC,MAAM,kBAAkB,GAAG,cAAc,CAAC;AAEjD,wCAAwC;AACxC,MAAM,CAAC,MAAM,QAAQ,GAAG,+BAA+B,CAAC;AACxD,8CAA8C;AAC9C,MAAM,CAAC,MAAM,WAAW,GAAG,kCAAkC,CAAC;AAC9D,kDAAkD;AAClD,MAAM,CAAC,MAAM,aAAa,GAAG,oCAAoC,CAAC;AAClE,mEAAmE;AACnE,MAAM,CAAC,MAAM,mBAAmB,GAAG,0CAA0C,CAAC;AAC9E,6EAA6E;AAC7E,MAAM,CAAC,MAAM,eAAe,GAAG,sCAAsC,CAAC;AACtE,6CAA6C;AAC7C,MAAM,CAAC,MAAM,kBAAkB,GAAG,yCAAyC,CAAC;AAC5E,qHAAqH;AACrH,MAAM,CAAC,MAAM,mBAAmB,GAAG,0CAA0C,CAAC;AAC9E,0EAA0E;AAC1E,MAAM,CAAC,MAAM,wBAAwB,GAAG,+CAA+C,CAAC;AAExF,2BAA2B;AAC3B,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEhD,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,SAAS;AAC/D,MAAM,CAAC,MAAM,8BAA8B,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC,UAAU","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n\n// The key for the GC tree in summary.\nexport const gcTreeKey = \"gc\";\n// They prefix for GC blobs in the GC tree in summary.\nexport const gcBlobPrefix = \"__gc\";\n// The key for tombstone blob in the GC tree in summary.\nexport const gcTombstoneBlobKey = \"__tombstones\";\n\n// Feature gate key to turn GC on / off.\nexport const runGCKey = \"Fluid.GarbageCollection.RunGC\";\n// Feature gate key to turn GC sweep on / off.\nexport const runSweepKey = \"Fluid.GarbageCollection.RunSweep\";\n// Feature gate key to turn GC test mode on / off.\nexport const gcTestModeKey = \"Fluid.GarbageCollection.GCTestMode\";\n// Feature gate key to expire a session after a set period of time.\nexport const runSessionExpiryKey = \"Fluid.GarbageCollection.RunSessionExpiry\";\n// Feature gate key to write the gc blob as a handle if the data is the same.\nexport const trackGCStateKey = \"Fluid.GarbageCollection.TrackGCState\";\n// Feature gate key to turn GC sweep log off.\nexport const disableSweepLogKey = \"Fluid.GarbageCollection.DisableSweepLog\";\n// Feature gate key to disable the tombstone feature, i.e., tombstone information is not read / written into summary.\nexport const disableTombstoneKey = \"Fluid.GarbageCollection.DisableTombstone\";\n// Feature gate to enable throwing an error when tombstone object is used.\nexport const throwOnTombstoneUsageKey = \"Fluid.GarbageCollection.ThrowOnTombstoneUsage\";\n\n// One day in milliseconds.\nexport const oneDayMs = 1 * 24 * 60 * 60 * 1000;\n\nexport const defaultInactiveTimeoutMs = 7 * oneDayMs; // 7 days\nexport const defaultSessionExpiryDurationMs = 30 * oneDayMs; // 30 days\n"]}
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
  import { LoggingError, } from "@fluidframework/telemetry-utils";
6
- import { oneDayMs } from "./garbageCollection";
6
+ import { oneDayMs } from "./garbageCollectionConstants";
7
7
  /**
8
8
  * Feature Gate Key -
9
9
  * How many days between closing the container from this error (avoids locking user out of their file altogether)
@@ -1 +1 @@
1
- {"version":3,"file":"gcSweepReadyUsageDetection.js","sourceRoot":"","sources":["../src/gcSweepReadyUsageDetection.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 { ITelemetryProperties } from \"@fluidframework/common-definitions\";\nimport { ICriticalContainerError } from \"@fluidframework/container-definitions\";\nimport {\n IConfigProvider,\n IFluidErrorBase,\n LoggingError,\n MonitoringContext,\n} from \"@fluidframework/telemetry-utils\";\nimport { oneDayMs } from \"./garbageCollection\";\n\n/**\n * Feature Gate Key -\n * How many days between closing the container from this error (avoids locking user out of their file altogether)\n */\nexport const skipClosureForXDaysKey = \"Fluid.GarbageCollection.Dogfood.SweepReadyUsageDetection.SkipClosureForXDays\";\n\n/**\n * LocalStorage key (NOT via feature gate / monitoring context)\n * A map from docId to info about the last time we closed due to this error\n */\nexport const closuresMapLocalStorageKey = \"Fluid.GarbageCollection.Dogfood.SweepReadyUsageDetection.Closures\";\n\n/**\n * Feature gate key to enable closing the container if SweepReady objects are used.\n * Value should contain keywords \"interactiveClient\" and/or \"summarizer\" to enable detection in each container type\n */\nconst sweepReadyUsageDetectionSetting = {\n read(config: IConfigProvider) {\n const sweepReadyUsageDetectionKey = \"Fluid.GarbageCollection.Dogfood.SweepReadyUsageDetection\";\n const value = config.getString(sweepReadyUsageDetectionKey);\n if (value === undefined) {\n return { interactiveClient: false, summarizer: false };\n }\n return {\n interactiveClient: value.includes(\"interactiveClient\"),\n summarizer: value.includes(\"summarizer\"),\n };\n },\n};\n\n/**\n * Error class raised when a SweepReady object is used, indicating a bug in how\n * references are managed in the container by the application, or a bug in how\n * GC tracks those references.\n *\n * There's a chance for false positives when this error is raised by an Interactive Container,\n * since only the Summarizer has the latest truth about unreferenced node tracking\n */\nexport class SweepReadyUsageError extends LoggingError implements IFluidErrorBase {\n /** This errorType will be in temporary use (until Sweep is fully implemented) so don't add to any errorType type */\n public errorType: string = \"unreferencedObjectUsedAfterGarbageCollected\";\n}\n\n/**\n * This class encapsulates the logic around what to do when a SweepReady object is used.\n * There are several tactics we plan to use in Dogfood environments to aid diagnosis of these cases:\n * - Closing the interactive container when either the interactive or summarizer client detects this kind of violation\n * (via sweepReadyUsageDetectionSetting above)\n * - Throttling the frequency of these crashes via a \"Skip Closure Period\" per container per device\n * (via skipClosureForXDaysKey above. Uses localStorage and closuresMapLocalStorageKey to implement this behavior)\n */\nexport class SweepReadyUsageDetectionHandler {\n private readonly localStorage: Pick<Storage, \"getItem\" | \"setItem\">;\n\n constructor(\n private readonly uniqueContainerKey: string,\n private readonly mc: MonitoringContext,\n private readonly closeFn: (error?: ICriticalContainerError) => void,\n localStorageOverride?: Pick<Storage, \"getItem\" | \"setItem\">,\n ) {\n const noopStorage = { getItem: () => null, setItem: () => {} };\n // localStorage is not defined in Node environment, so fall back to noopStorage if needed.\n this.localStorage = localStorageOverride ?? globalThis.localStorage ?? noopStorage;\n\n if (this.localStorage === noopStorage) {\n // This means the Skip Closure Period logic will not work.\n this.mc.logger.sendTelemetryEvent({ eventName: \"SweepReadyUsageDetectionHandlerNoopStorage\" });\n }\n }\n\n /**\n * If SweepReady Usage Detection is enabled, close the interactive container.\n * If the SkipClosureForXDays setting is set, don't close the container more than once in that period.\n *\n * Once Sweep is fully implemented, this will be removed since the objects will be gone\n * and errors will arise elsewhere in the runtime\n */\n public usageDetectedInInteractiveClient(errorProps: ITelemetryProperties) {\n if (!sweepReadyUsageDetectionSetting.read(this.mc.config).interactiveClient) {\n return;\n }\n\n // Default stance is we close every time - this reflects the severity of SweepReady Object Usage.\n // However, we may choose to \"throttle\" the closures by setting the SkipClosureForXDays setting,\n // which will only allow the container to close once during that period, to avoid locking users out.\n let shouldClose: boolean = true;\n let pastClosuresMap: Record<string, { lastCloseTime: number; } | undefined> = {};\n let lastCloseTime: number | undefined;\n const skipClosureForXDays = this.mc.config.getNumber(skipClosureForXDaysKey);\n if (skipClosureForXDays !== undefined) {\n // Read pastClosuresMap from localStorage then extract the lastCloseTime from the map\n try {\n const rawValue = this.localStorage.getItem(closuresMapLocalStorageKey);\n const parsedValue = rawValue === null ? {} : JSON.parse(rawValue);\n if (typeof parsedValue === \"object\") {\n pastClosuresMap = parsedValue;\n }\n } catch (e) {\n }\n lastCloseTime = pastClosuresMap[this.uniqueContainerKey]?.lastCloseTime;\n\n // Don't close if we did already within the Skip Closure Period\n if (lastCloseTime !== undefined && Date.now() < lastCloseTime + skipClosureForXDays * oneDayMs) {\n shouldClose = false;\n }\n }\n\n const error = new SweepReadyUsageError(\n \"SweepReady object used in Non-Summarizer Client\",\n { errorDetails: JSON.stringify({ ...errorProps, lastCloseTime, skipClosureForXDays }) },\n );\n if (shouldClose) {\n // Update closures map in localStorage before closing\n // Note there is a race condition between different tabs updating localStorage and overwriting\n // each others' updates. If so, some tab will crash again. Just reload one at a time to get unstuck\n pastClosuresMap[this.uniqueContainerKey] = { lastCloseTime: Date.now() };\n this.localStorage.setItem(closuresMapLocalStorageKey, JSON.stringify(pastClosuresMap));\n\n this.closeFn(error);\n } else {\n this.mc.logger.sendErrorEvent({ eventName: \"SweepReadyObject_UsageAllowed\" }, error);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"gcSweepReadyUsageDetection.js","sourceRoot":"","sources":["../src/gcSweepReadyUsageDetection.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 { ITelemetryProperties } from \"@fluidframework/common-definitions\";\nimport { ICriticalContainerError } from \"@fluidframework/container-definitions\";\nimport {\n IConfigProvider,\n IFluidErrorBase,\n LoggingError,\n MonitoringContext,\n} from \"@fluidframework/telemetry-utils\";\nimport { oneDayMs } from \"./garbageCollectionConstants\";\n\n/**\n * Feature Gate Key -\n * How many days between closing the container from this error (avoids locking user out of their file altogether)\n */\nexport const skipClosureForXDaysKey = \"Fluid.GarbageCollection.Dogfood.SweepReadyUsageDetection.SkipClosureForXDays\";\n\n/**\n * LocalStorage key (NOT via feature gate / monitoring context)\n * A map from docId to info about the last time we closed due to this error\n */\nexport const closuresMapLocalStorageKey = \"Fluid.GarbageCollection.Dogfood.SweepReadyUsageDetection.Closures\";\n\n/**\n * Feature gate key to enable closing the container if SweepReady objects are used.\n * Value should contain keywords \"interactiveClient\" and/or \"summarizer\" to enable detection in each container type\n */\nconst sweepReadyUsageDetectionSetting = {\n read(config: IConfigProvider) {\n const sweepReadyUsageDetectionKey = \"Fluid.GarbageCollection.Dogfood.SweepReadyUsageDetection\";\n const value = config.getString(sweepReadyUsageDetectionKey);\n if (value === undefined) {\n return { interactiveClient: false, summarizer: false };\n }\n return {\n interactiveClient: value.includes(\"interactiveClient\"),\n summarizer: value.includes(\"summarizer\"),\n };\n },\n};\n\n/**\n * Error class raised when a SweepReady object is used, indicating a bug in how\n * references are managed in the container by the application, or a bug in how\n * GC tracks those references.\n *\n * There's a chance for false positives when this error is raised by an Interactive Container,\n * since only the Summarizer has the latest truth about unreferenced node tracking\n */\nexport class SweepReadyUsageError extends LoggingError implements IFluidErrorBase {\n /** This errorType will be in temporary use (until Sweep is fully implemented) so don't add to any errorType type */\n public errorType: string = \"unreferencedObjectUsedAfterGarbageCollected\";\n}\n\n/**\n * This class encapsulates the logic around what to do when a SweepReady object is used.\n * There are several tactics we plan to use in Dogfood environments to aid diagnosis of these cases:\n * - Closing the interactive container when either the interactive or summarizer client detects this kind of violation\n * (via sweepReadyUsageDetectionSetting above)\n * - Throttling the frequency of these crashes via a \"Skip Closure Period\" per container per device\n * (via skipClosureForXDaysKey above. Uses localStorage and closuresMapLocalStorageKey to implement this behavior)\n */\nexport class SweepReadyUsageDetectionHandler {\n private readonly localStorage: Pick<Storage, \"getItem\" | \"setItem\">;\n\n constructor(\n private readonly uniqueContainerKey: string,\n private readonly mc: MonitoringContext,\n private readonly closeFn: (error?: ICriticalContainerError) => void,\n localStorageOverride?: Pick<Storage, \"getItem\" | \"setItem\">,\n ) {\n const noopStorage = { getItem: () => null, setItem: () => {} };\n // localStorage is not defined in Node environment, so fall back to noopStorage if needed.\n this.localStorage = localStorageOverride ?? globalThis.localStorage ?? noopStorage;\n\n if (this.localStorage === noopStorage) {\n // This means the Skip Closure Period logic will not work.\n this.mc.logger.sendTelemetryEvent({ eventName: \"SweepReadyUsageDetectionHandlerNoopStorage\" });\n }\n }\n\n /**\n * If SweepReady Usage Detection is enabled, close the interactive container.\n * If the SkipClosureForXDays setting is set, don't close the container more than once in that period.\n *\n * Once Sweep is fully implemented, this will be removed since the objects will be gone\n * and errors will arise elsewhere in the runtime\n */\n public usageDetectedInInteractiveClient(errorProps: ITelemetryProperties) {\n if (!sweepReadyUsageDetectionSetting.read(this.mc.config).interactiveClient) {\n return;\n }\n\n // Default stance is we close every time - this reflects the severity of SweepReady Object Usage.\n // However, we may choose to \"throttle\" the closures by setting the SkipClosureForXDays setting,\n // which will only allow the container to close once during that period, to avoid locking users out.\n let shouldClose: boolean = true;\n let pastClosuresMap: Record<string, { lastCloseTime: number; } | undefined> = {};\n let lastCloseTime: number | undefined;\n const skipClosureForXDays = this.mc.config.getNumber(skipClosureForXDaysKey);\n if (skipClosureForXDays !== undefined) {\n // Read pastClosuresMap from localStorage then extract the lastCloseTime from the map\n try {\n const rawValue = this.localStorage.getItem(closuresMapLocalStorageKey);\n const parsedValue = rawValue === null ? {} : JSON.parse(rawValue);\n if (typeof parsedValue === \"object\") {\n pastClosuresMap = parsedValue;\n }\n } catch (e) {\n }\n lastCloseTime = pastClosuresMap[this.uniqueContainerKey]?.lastCloseTime;\n\n // Don't close if we did already within the Skip Closure Period\n if (lastCloseTime !== undefined && Date.now() < lastCloseTime + skipClosureForXDays * oneDayMs) {\n shouldClose = false;\n }\n }\n\n const error = new SweepReadyUsageError(\n \"SweepReady object used in Non-Summarizer Client\",\n { errorDetails: JSON.stringify({ ...errorProps, lastCloseTime, skipClosureForXDays }) },\n );\n if (shouldClose) {\n // Update closures map in localStorage before closing\n // Note there is a race condition between different tabs updating localStorage and overwriting\n // each others' updates. If so, some tab will crash again. Just reload one at a time to get unstuck\n pastClosuresMap[this.uniqueContainerKey] = { lastCloseTime: Date.now() };\n this.localStorage.setItem(closuresMapLocalStorageKey, JSON.stringify(pastClosuresMap));\n\n this.closeFn(error);\n } else {\n this.mc.logger.sendErrorEvent({ eventName: \"SweepReadyObject_UsageAllowed\" }, error);\n }\n }\n}\n"]}
package/lib/index.d.ts CHANGED
@@ -2,12 +2,14 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- export { ContainerMessageType, IChunkedOp, ContainerRuntimeMessage, IGCRuntimeOptions, ISummaryRuntimeOptions, ISummaryBaseConfiguration, ISummaryConfigurationHeuristics, ISummaryConfigurationDisableSummarizer, ISummaryConfigurationDisableHeuristics, IContainerRuntimeOptions, IRootSummaryTreeWithStats, isRuntimeMessage, RuntimeMessage, unpackRuntimeMessage, agentSchedulerId, ContainerRuntime, RuntimeHeaders, ISummaryConfiguration, DefaultSummaryConfiguration, ICompressionRuntimeOptions, CompressionAlgorithms, } from "./containerRuntime";
5
+ export { ContainerMessageType, ContainerRuntimeMessage, IGCRuntimeOptions, ISummaryRuntimeOptions, ISummaryBaseConfiguration, ISummaryConfigurationHeuristics, ISummaryConfigurationDisableSummarizer, ISummaryConfigurationDisableHeuristics, IContainerRuntimeOptions, IRootSummaryTreeWithStats, isRuntimeMessage, RuntimeMessage, agentSchedulerId, ContainerRuntime, RuntimeHeaders, ISummaryConfiguration, DefaultSummaryConfiguration, ICompressionRuntimeOptions, CompressionAlgorithms, } from "./containerRuntime";
6
6
  export { FluidDataStoreRegistry } from "./dataStoreRegistry";
7
- export { gcBlobPrefix, gcTombstoneBlobKey, gcTreeKey, IGCStats, } from "./garbageCollection";
7
+ export { IGCStats, } from "./garbageCollection";
8
+ export { gcBlobPrefix, gcTombstoneBlobKey, gcTreeKey, } from "./garbageCollectionConstants";
8
9
  export { IPendingFlush, IPendingLocalState, IPendingMessage, IPendingState, } from "./pendingStateManager";
9
10
  export { Summarizer } from "./summarizer";
10
11
  export { EnqueueSummarizeResult, IAckSummaryResult, IBaseSummarizeResult, IBroadcastSummaryResult, ICancellationToken, IConnectableRuntime, IEnqueueSummarizeOptions, IGenerateSummaryTreeResult, IGeneratedSummaryStats, INackSummaryResult, IOnDemandSummarizeOptions, IProvideSummarizer, IRefreshSummaryAckOptions, ISubmitSummaryOpResult, ISubmitSummaryOptions, ISummarizeOptions, ISummarizeResults, ISummarizer, ISummarizerEvents, ISummarizerInternalsProvider, ISummarizerRuntime, ISummarizingWarning, ISummaryCancellationToken, IUploadSummaryResult, SubmitSummaryResult, SummarizeResultPart, SummarizerStopReason, } from "./summarizerTypes";
11
12
  export { IAckedSummary, IClientSummaryWatcher, ISummary, ISummaryCollectionOpEvents, ISummaryAckMessage, ISummaryNackMessage, ISummaryOpMessage, OpActionEventListener, OpActionEventName, SummaryCollection, } from "./summaryCollection";
12
13
  export { ICancellableSummarizerController, neverCancelledSummaryToken } from "./runWhileConnectedCoordinator";
14
+ export { IChunkedOp, unpackRuntimeMessage } from "./opLifecycle";
13
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACH,oBAAoB,EACpB,UAAU,EACV,uBAAuB,EACvB,iBAAiB,EACjB,sBAAsB,EACtB,yBAAyB,EACzB,+BAA+B,EAC/B,sCAAsC,EACtC,sCAAsC,EACtC,wBAAwB,EACxB,yBAAyB,EACzB,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,qBAAqB,EACrB,2BAA2B,EAC3B,0BAA0B,EAC1B,qBAAqB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EACH,YAAY,EACZ,kBAAkB,EAClB,SAAS,EACT,QAAQ,GACX,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACH,aAAa,EACb,kBAAkB,EAClB,eAAe,EACf,aAAa,GAChB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EACH,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,0BAA0B,EAC1B,sBAAsB,EACtB,kBAAkB,EAClB,yBAAyB,EACzB,kBAAkB,EAClB,yBAAyB,EACzB,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,4BAA4B,EAC5B,kBAAkB,EAClB,mBAAmB,EACnB,yBAAyB,EACzB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACH,aAAa,EACb,qBAAqB,EACrB,QAAQ,EACR,0BAA0B,EAC1B,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,gCAAgC,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACH,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,sBAAsB,EACtB,yBAAyB,EACzB,+BAA+B,EAC/B,sCAAsC,EACtC,sCAAsC,EACtC,wBAAwB,EACxB,yBAAyB,EACzB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,qBAAqB,EACrB,2BAA2B,EAC3B,0BAA0B,EAC1B,qBAAqB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EACH,QAAQ,GACX,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACH,YAAY,EACZ,kBAAkB,EAClB,SAAS,GACZ,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACH,aAAa,EACb,kBAAkB,EAClB,eAAe,EACf,aAAa,GAChB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EACH,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,0BAA0B,EAC1B,sBAAsB,EACtB,kBAAkB,EAClB,yBAAyB,EACzB,kBAAkB,EAClB,yBAAyB,EACzB,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,4BAA4B,EAC5B,kBAAkB,EAClB,mBAAmB,EACnB,yBAAyB,EACzB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACH,aAAa,EACb,qBAAqB,EACrB,QAAQ,EACR,0BAA0B,EAC1B,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,gCAAgC,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AAC9G,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC"}
package/lib/index.js CHANGED
@@ -2,11 +2,12 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- export { ContainerMessageType, isRuntimeMessage, RuntimeMessage, unpackRuntimeMessage, agentSchedulerId, ContainerRuntime, RuntimeHeaders, DefaultSummaryConfiguration, CompressionAlgorithms, } from "./containerRuntime";
5
+ export { ContainerMessageType, isRuntimeMessage, RuntimeMessage, agentSchedulerId, ContainerRuntime, RuntimeHeaders, DefaultSummaryConfiguration, CompressionAlgorithms, } from "./containerRuntime";
6
6
  export { FluidDataStoreRegistry } from "./dataStoreRegistry";
7
- export { gcBlobPrefix, gcTombstoneBlobKey, gcTreeKey, } from "./garbageCollection";
7
+ export { gcBlobPrefix, gcTombstoneBlobKey, gcTreeKey, } from "./garbageCollectionConstants";
8
8
  export { Summarizer } from "./summarizer";
9
9
  export { ISummarizer, } from "./summarizerTypes";
10
10
  export { SummaryCollection, } from "./summaryCollection";
11
11
  export { neverCancelledSummaryToken } from "./runWhileConnectedCoordinator";
12
+ export { unpackRuntimeMessage } from "./opLifecycle";
12
13
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACH,oBAAoB,EAWpB,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EAEd,2BAA2B,EAE3B,qBAAqB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EACH,YAAY,EACZ,kBAAkB,EAClB,SAAS,GAEZ,MAAM,qBAAqB,CAAC;AAO7B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAkBH,WAAW,GAUd,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAUH,iBAAiB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAoC,0BAA0B,EAAE,MAAM,gCAAgC,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport {\n ContainerMessageType,\n IChunkedOp,\n ContainerRuntimeMessage,\n IGCRuntimeOptions,\n ISummaryRuntimeOptions,\n ISummaryBaseConfiguration,\n ISummaryConfigurationHeuristics,\n ISummaryConfigurationDisableSummarizer,\n ISummaryConfigurationDisableHeuristics,\n IContainerRuntimeOptions,\n IRootSummaryTreeWithStats,\n isRuntimeMessage,\n RuntimeMessage,\n unpackRuntimeMessage,\n agentSchedulerId,\n ContainerRuntime,\n RuntimeHeaders,\n ISummaryConfiguration,\n DefaultSummaryConfiguration,\n ICompressionRuntimeOptions,\n CompressionAlgorithms,\n} from \"./containerRuntime\";\nexport { FluidDataStoreRegistry } from \"./dataStoreRegistry\";\nexport {\n gcBlobPrefix,\n gcTombstoneBlobKey,\n gcTreeKey,\n IGCStats,\n} from \"./garbageCollection\";\nexport {\n IPendingFlush,\n IPendingLocalState,\n IPendingMessage,\n IPendingState,\n} from \"./pendingStateManager\";\nexport { Summarizer } from \"./summarizer\";\nexport {\n EnqueueSummarizeResult,\n IAckSummaryResult,\n IBaseSummarizeResult,\n IBroadcastSummaryResult,\n ICancellationToken,\n IConnectableRuntime,\n IEnqueueSummarizeOptions,\n IGenerateSummaryTreeResult,\n IGeneratedSummaryStats,\n INackSummaryResult,\n IOnDemandSummarizeOptions,\n IProvideSummarizer,\n IRefreshSummaryAckOptions,\n ISubmitSummaryOpResult,\n ISubmitSummaryOptions,\n ISummarizeOptions,\n ISummarizeResults,\n ISummarizer,\n ISummarizerEvents,\n ISummarizerInternalsProvider,\n ISummarizerRuntime,\n ISummarizingWarning,\n ISummaryCancellationToken,\n IUploadSummaryResult,\n SubmitSummaryResult,\n SummarizeResultPart,\n SummarizerStopReason,\n} from \"./summarizerTypes\";\nexport {\n IAckedSummary,\n IClientSummaryWatcher,\n ISummary,\n ISummaryCollectionOpEvents,\n ISummaryAckMessage,\n ISummaryNackMessage,\n ISummaryOpMessage,\n OpActionEventListener,\n OpActionEventName,\n SummaryCollection,\n} from \"./summaryCollection\";\nexport { ICancellableSummarizerController, neverCancelledSummaryToken } from \"./runWhileConnectedCoordinator\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACH,oBAAoB,EAUpB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EAEd,2BAA2B,EAE3B,qBAAqB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAI7D,OAAO,EACH,YAAY,EACZ,kBAAkB,EAClB,SAAS,GACZ,MAAM,8BAA8B,CAAC;AAOtC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAkBH,WAAW,GAUd,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAUH,iBAAiB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAoC,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AAC9G,OAAO,EAAc,oBAAoB,EAAE,MAAM,eAAe,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport {\n ContainerMessageType,\n ContainerRuntimeMessage,\n IGCRuntimeOptions,\n ISummaryRuntimeOptions,\n ISummaryBaseConfiguration,\n ISummaryConfigurationHeuristics,\n ISummaryConfigurationDisableSummarizer,\n ISummaryConfigurationDisableHeuristics,\n IContainerRuntimeOptions,\n IRootSummaryTreeWithStats,\n isRuntimeMessage,\n RuntimeMessage,\n agentSchedulerId,\n ContainerRuntime,\n RuntimeHeaders,\n ISummaryConfiguration,\n DefaultSummaryConfiguration,\n ICompressionRuntimeOptions,\n CompressionAlgorithms,\n} from \"./containerRuntime\";\nexport { FluidDataStoreRegistry } from \"./dataStoreRegistry\";\nexport {\n IGCStats,\n} from \"./garbageCollection\";\nexport {\n gcBlobPrefix,\n gcTombstoneBlobKey,\n gcTreeKey,\n} from \"./garbageCollectionConstants\";\nexport {\n IPendingFlush,\n IPendingLocalState,\n IPendingMessage,\n IPendingState,\n} from \"./pendingStateManager\";\nexport { Summarizer } from \"./summarizer\";\nexport {\n EnqueueSummarizeResult,\n IAckSummaryResult,\n IBaseSummarizeResult,\n IBroadcastSummaryResult,\n ICancellationToken,\n IConnectableRuntime,\n IEnqueueSummarizeOptions,\n IGenerateSummaryTreeResult,\n IGeneratedSummaryStats,\n INackSummaryResult,\n IOnDemandSummarizeOptions,\n IProvideSummarizer,\n IRefreshSummaryAckOptions,\n ISubmitSummaryOpResult,\n ISubmitSummaryOptions,\n ISummarizeOptions,\n ISummarizeResults,\n ISummarizer,\n ISummarizerEvents,\n ISummarizerInternalsProvider,\n ISummarizerRuntime,\n ISummarizingWarning,\n ISummaryCancellationToken,\n IUploadSummaryResult,\n SubmitSummaryResult,\n SummarizeResultPart,\n SummarizerStopReason,\n} from \"./summarizerTypes\";\nexport {\n IAckedSummary,\n IClientSummaryWatcher,\n ISummary,\n ISummaryCollectionOpEvents,\n ISummaryAckMessage,\n ISummaryNackMessage,\n ISummaryOpMessage,\n OpActionEventListener,\n OpActionEventName,\n SummaryCollection,\n} from \"./summaryCollection\";\nexport { ICancellableSummarizerController, neverCancelledSummaryToken } from \"./runWhileConnectedCoordinator\";\nexport { IChunkedOp, unpackRuntimeMessage } from \"./opLifecycle\";\n"]}
@@ -0,0 +1,30 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { ICompressionRuntimeOptions } from "../containerRuntime";
6
+ import { BatchMessage, IBatch, IBatchCheckpoint } from "./definitions";
7
+ export interface IBatchManagerOptions {
8
+ readonly hardLimit: number;
9
+ readonly softLimit?: number;
10
+ readonly compressionOptions?: ICompressionRuntimeOptions;
11
+ }
12
+ /**
13
+ * Helper class that manages partial batch & rollback.
14
+ */
15
+ export declare class BatchManager {
16
+ readonly options: IBatchManagerOptions;
17
+ private pendingBatch;
18
+ private batchContentSize;
19
+ get length(): number;
20
+ get contentSizeInBytes(): number;
21
+ constructor(options: IBatchManagerOptions);
22
+ push(message: BatchMessage): boolean;
23
+ get empty(): boolean;
24
+ popBatch(): IBatch;
25
+ /**
26
+ * Capture the pending state at this point
27
+ */
28
+ checkpoint(): IBatchCheckpoint;
29
+ }
30
+ //# sourceMappingURL=batchManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batchManager.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/batchManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEvE,MAAM,WAAW,oBAAoB;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,0BAA0B,CAAC;CAC5D;AAED;;GAEG;AACH,qBAAa,YAAY;aAOO,OAAO,EAAE,oBAAoB;IANzD,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,gBAAgB,CAAK;IAE7B,IAAW,MAAM,WAAuC;IACxD,IAAW,kBAAkB,WAAoC;gBAErC,OAAO,EAAE,oBAAoB;IAElD,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO;IA+B3C,IAAW,KAAK,YAA6C;IAEtD,QAAQ,IAAI,MAAM;IAYzB;;OAEG;IACI,UAAU,IAAI,gBAAgB;CAexC"}
@@ -2,21 +2,19 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { OpCompressor } from "./opCompressor";
6
5
  /**
7
6
  * Helper class that manages partial batch & rollback.
8
7
  */
9
8
  export class BatchManager {
10
- constructor(logger, options) {
11
- this.logger = logger;
9
+ constructor(options) {
12
10
  this.options = options;
13
11
  this.pendingBatch = [];
14
12
  this.batchContentSize = 0;
15
- this.opCompressor = new OpCompressor(logger);
16
13
  }
17
14
  get length() { return this.pendingBatch.length; }
15
+ get contentSizeInBytes() { return this.batchContentSize; }
18
16
  push(message) {
19
- var _a, _b, _c, _d, _e, _f;
17
+ var _a, _b;
20
18
  const contentSize = this.batchContentSize + ((_b = (_a = message.contents) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0);
21
19
  const opCount = this.pendingBatch.length;
22
20
  // Attempt to estimate batch size, aka socket message size.
@@ -32,12 +30,10 @@ export class BatchManager {
32
30
  // Cases where the message is still too large will be handled by the maxConsecutiveReconnects path.
33
31
  if (this.options.softLimit !== undefined
34
32
  && this.length > 0
35
- && socketMessageSize >= this.options.softLimit
36
- && Infinity === ((_d = (_c = this.options.compressionOptions) === null || _c === void 0 ? void 0 : _c.minimumBatchSizeInBytes) !== null && _d !== void 0 ? _d : Infinity)) {
33
+ && socketMessageSize >= this.options.softLimit) {
37
34
  return false;
38
35
  }
39
- if (socketMessageSize >= this.options.hardLimit
40
- && Infinity === ((_f = (_e = this.options.compressionOptions) === null || _e === void 0 ? void 0 : _e.minimumBatchSizeInBytes) !== null && _f !== void 0 ? _f : Infinity)) {
36
+ if (socketMessageSize >= this.options.hardLimit) {
41
37
  return false;
42
38
  }
43
39
  this.batchContentSize = contentSize;
@@ -46,16 +42,13 @@ export class BatchManager {
46
42
  }
47
43
  get empty() { return this.pendingBatch.length === 0; }
48
44
  popBatch() {
49
- const batch = this.pendingBatch;
50
- const size = this.batchContentSize;
45
+ const batch = {
46
+ content: this.pendingBatch,
47
+ contentSizeInBytes: this.batchContentSize,
48
+ };
51
49
  this.pendingBatch = [];
52
50
  this.batchContentSize = 0;
53
- if (batch.length > 0
54
- && this.options.compressionOptions !== undefined
55
- && this.options.compressionOptions.minimumBatchSizeInBytes < size) {
56
- return this.opCompressor.compressBatch(batch, size);
57
- }
58
- return batch;
51
+ return addBatchMetadata(batch);
59
52
  }
60
53
  /**
61
54
  * Capture the pending state at this point
@@ -76,4 +69,11 @@ export class BatchManager {
76
69
  };
77
70
  }
78
71
  }
72
+ const addBatchMetadata = (batch) => {
73
+ if (batch.content.length > 1) {
74
+ batch.content[0].metadata = Object.assign(Object.assign({}, batch.content[0].metadata), { batch: true });
75
+ batch.content[batch.content.length - 1].metadata = Object.assign(Object.assign({}, batch.content[batch.content.length - 1].metadata), { batch: false });
76
+ }
77
+ return batch;
78
+ };
79
79
  //# sourceMappingURL=batchManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batchManager.js","sourceRoot":"","sources":["../../src/opLifecycle/batchManager.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 { ICompressionRuntimeOptions } from \"../containerRuntime\";\nimport { BatchMessage, IBatch, IBatchCheckpoint } from \"./definitions\";\n\nexport interface IBatchManagerOptions {\n readonly hardLimit: number;\n readonly softLimit?: number;\n readonly compressionOptions?: ICompressionRuntimeOptions;\n}\n\n/**\n * Helper class that manages partial batch & rollback.\n */\nexport class BatchManager {\n private pendingBatch: BatchMessage[] = [];\n private batchContentSize = 0;\n\n public get length() { return this.pendingBatch.length; }\n public get contentSizeInBytes() { return this.batchContentSize; }\n\n constructor(public readonly options: IBatchManagerOptions) { }\n\n public push(message: BatchMessage): boolean {\n const contentSize = this.batchContentSize + (message.contents?.length ?? 0);\n const opCount = this.pendingBatch.length;\n\n // Attempt to estimate batch size, aka socket message size.\n // Each op has pretty large envelope, estimating to be 200 bytes.\n // Also content will be strigified, and that adds a lot of overhead due to a lot of escape characters.\n // Not taking it into account, as compression work should help there - compressed payload will be\n // initially stored as base64, and that requires only 2 extra escape characters.\n const socketMessageSize = contentSize + 200 * opCount;\n\n // If we were provided soft limit, check for exceeding it.\n // But only if we have any ops, as the intention here is to flush existing ops (on exceeding this limit)\n // and start over. That's not an option if we have no ops.\n // If compression is enabled, the soft and hard limit are ignored and the message will be pushed anyways.\n // Cases where the message is still too large will be handled by the maxConsecutiveReconnects path.\n if (this.options.softLimit !== undefined\n && this.length > 0\n && socketMessageSize >= this.options.softLimit) {\n return false;\n }\n\n if (socketMessageSize >= this.options.hardLimit) {\n return false;\n }\n\n this.batchContentSize = contentSize;\n this.pendingBatch.push(message);\n return true;\n }\n\n public get empty() { return this.pendingBatch.length === 0; }\n\n public popBatch(): IBatch {\n const batch: IBatch = {\n content: this.pendingBatch,\n contentSizeInBytes: this.batchContentSize,\n };\n\n this.pendingBatch = [];\n this.batchContentSize = 0;\n\n return addBatchMetadata(batch);\n }\n\n /**\n * Capture the pending state at this point\n */\n public checkpoint(): IBatchCheckpoint {\n const startPoint = this.pendingBatch.length;\n return {\n rollback: (process: (message: BatchMessage) => void) => {\n for (let i = this.pendingBatch.length; i > startPoint;) {\n i--;\n const message = this.pendingBatch[i];\n this.batchContentSize -= message.contents?.length ?? 0;\n process(message);\n }\n\n this.pendingBatch.length = startPoint;\n },\n };\n }\n}\n\nconst addBatchMetadata = (batch: IBatch): IBatch => {\n if (batch.content.length > 1) {\n batch.content[0].metadata = {\n ...batch.content[0].metadata,\n batch: true\n };\n batch.content[batch.content.length - 1].metadata = {\n ...batch.content[batch.content.length - 1].metadata,\n batch: false\n };\n }\n\n return batch;\n};\n"]}
@@ -0,0 +1,40 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { IBatchMessage } from "@fluidframework/container-definitions";
6
+ import { MessageType } from "@fluidframework/protocol-definitions";
7
+ import { CompressionAlgorithms, ContainerMessageType, ContainerRuntimeMessage } from "..";
8
+ /**
9
+ * Batch message type used internally by the runtime
10
+ */
11
+ export declare type BatchMessage = IBatchMessage & {
12
+ localOpMetadata: unknown;
13
+ deserializedContent: ContainerRuntimeMessage;
14
+ referenceSequenceNumber: number;
15
+ compression?: CompressionAlgorithms;
16
+ };
17
+ /**
18
+ * Batch interface used internally by the runtime.
19
+ */
20
+ export interface IBatch {
21
+ /**
22
+ * Sum of the in-memory content sizes of all messages in the batch.
23
+ * If the batch is compressed, this number reflects the post-compression size.
24
+ */
25
+ readonly contentSizeInBytes: number;
26
+ /**
27
+ * All the messages in the batch
28
+ */
29
+ readonly content: BatchMessage[];
30
+ }
31
+ export interface IBatchCheckpoint {
32
+ rollback: (action: (message: BatchMessage) => void) => void;
33
+ }
34
+ export interface IChunkedOp {
35
+ chunkId: number;
36
+ totalChunks: number;
37
+ contents: string;
38
+ originalType: MessageType | ContainerMessageType;
39
+ }
40
+ //# sourceMappingURL=definitions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/definitions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,IAAI,CAAC;AAE1F;;GAEG;AACH,oBAAY,YAAY,GAAG,aAAa,GAAG;IACvC,eAAe,EAAE,OAAO,CAAC;IACzB,mBAAmB,EAAE,uBAAuB,CAAC;IAC7C,uBAAuB,EAAE,MAAM,CAAC;IAChC,WAAW,CAAC,EAAE,qBAAqB,CAAC;CACvC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,MAAM;IACnB;;;OAGG;IACH,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,EAAE,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,KAAK,IAAI,CAAC;CAC/D;AAED,MAAM,WAAW,UAAU;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,WAAW,GAAG,oBAAoB,CAAC;CACpD"}
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=definitions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/opLifecycle/definitions.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IBatchMessage } from \"@fluidframework/container-definitions\";\nimport { MessageType } from \"@fluidframework/protocol-definitions\";\nimport { CompressionAlgorithms, ContainerMessageType, ContainerRuntimeMessage } from \"..\";\n\n/**\n * Batch message type used internally by the runtime\n */\nexport type BatchMessage = IBatchMessage & {\n localOpMetadata: unknown;\n deserializedContent: ContainerRuntimeMessage;\n referenceSequenceNumber: number;\n compression?: CompressionAlgorithms;\n};\n\n/**\n * Batch interface used internally by the runtime.\n */\nexport interface IBatch {\n /**\n * Sum of the in-memory content sizes of all messages in the batch.\n * If the batch is compressed, this number reflects the post-compression size.\n */\n readonly contentSizeInBytes: number;\n /**\n * All the messages in the batch\n */\n readonly content: BatchMessage[];\n}\n\nexport interface IBatchCheckpoint {\n rollback: (action: (message: BatchMessage) => void) => void;\n}\n\nexport interface IChunkedOp {\n chunkId: number;\n totalChunks: number;\n contents: string;\n originalType: MessageType | ContainerMessageType;\n}\n"]}
@@ -0,0 +1,12 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ export { BatchManager } from "./batchManager";
6
+ export { BatchMessage, IBatch, IBatchCheckpoint, IChunkedOp, } from "./definitions";
7
+ export { Outbox } from "./outbox";
8
+ export { OpCompressor } from "./opCompressor";
9
+ export { OpDecompressor } from "./opDecompressor";
10
+ export { OpSplitter } from "./opSplitter";
11
+ export { RemoteMessageProcessor, unpackRuntimeMessage } from "./remoteMessageProcessor";
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EACH,YAAY,EACZ,MAAM,EACN,gBAAgB,EAChB,UAAU,GACb,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,11 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ export { BatchManager } from "./batchManager";
6
+ export { Outbox } from "./outbox";
7
+ export { OpCompressor } from "./opCompressor";
8
+ export { OpDecompressor } from "./opDecompressor";
9
+ export { OpSplitter } from "./opSplitter";
10
+ export { RemoteMessageProcessor, unpackRuntimeMessage } from "./remoteMessageProcessor";
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/opLifecycle/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAO9C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport { BatchManager } from \"./batchManager\";\nexport {\n BatchMessage,\n IBatch,\n IBatchCheckpoint,\n IChunkedOp,\n} from \"./definitions\";\nexport { Outbox } from \"./outbox\";\nexport { OpCompressor } from \"./opCompressor\";\nexport { OpDecompressor } from \"./opDecompressor\";\nexport { OpSplitter } from \"./opSplitter\";\nexport { RemoteMessageProcessor, unpackRuntimeMessage } from \"./remoteMessageProcessor\";\n"]}
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
  import { ITelemetryLogger } from "@fluidframework/common-definitions";
6
- import { BatchMessage } from "./batchManager";
6
+ import { IBatch } from "./definitions";
7
7
  /**
8
8
  * Compresses batches of ops. It generates a single compressed op that contains
9
9
  * the contents of each op in the batch. It then submits empty ops for each original
@@ -13,6 +13,6 @@ export declare class OpCompressor {
13
13
  private readonly logger;
14
14
  private compressedBatchCount;
15
15
  constructor(logger: ITelemetryLogger);
16
- compressBatch(batch: BatchMessage[], originalLength: number): BatchMessage[];
16
+ compressBatch(batch: IBatch): IBatch;
17
17
  }
18
18
  //# sourceMappingURL=opCompressor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opCompressor.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opCompressor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAKtE,OAAO,EAAE,MAAM,EAAgB,MAAM,eAAe,CAAC;AAErD;;;;GAIG;AACH,qBAAa,YAAY;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IACxB,OAAO,CAAC,oBAAoB,CAAK;gBAErB,MAAM,EAAE,gBAAgB;IAI7B,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;CAsC9C"}
@@ -5,7 +5,7 @@
5
5
  import { IsoBuffer } from "@fluidframework/common-utils";
6
6
  import { ChildLogger } from "@fluidframework/telemetry-utils";
7
7
  import { compress } from "lz4js";
8
- import { CompressionAlgorithms } from "./containerRuntime";
8
+ import { CompressionAlgorithms } from "../containerRuntime";
9
9
  /**
10
10
  * Compresses batches of ops. It generates a single compressed op that contains
11
11
  * the contents of each op in the batch. It then submits empty ops for each original
@@ -16,31 +16,34 @@ export class OpCompressor {
16
16
  this.compressedBatchCount = 0;
17
17
  this.logger = ChildLogger.create(logger, "OpCompressor");
18
18
  }
19
- compressBatch(batch, originalLength) {
20
- const batchToSend = [];
19
+ compressBatch(batch) {
20
+ const messages = [];
21
21
  this.compressedBatchCount++;
22
- const batchedContents = [];
23
- for (const message of batch) {
24
- batchedContents.push(message.deserializedContent);
22
+ const contentToCompress = [];
23
+ for (const message of batch.content) {
24
+ contentToCompress.push(message.deserializedContent);
25
25
  }
26
26
  const compressionStart = Date.now();
27
- const contentsAsBuffer = new TextEncoder().encode(JSON.stringify(batchedContents));
27
+ const contentsAsBuffer = new TextEncoder().encode(JSON.stringify(contentToCompress));
28
28
  const compressedContents = compress(contentsAsBuffer);
29
29
  const compressedContent = IsoBuffer.from(compressedContents).toString("base64");
30
30
  const duration = Date.now() - compressionStart;
31
- if (originalLength > 200000 || this.compressedBatchCount % 100) {
31
+ if (batch.contentSizeInBytes > 200000 || this.compressedBatchCount % 25) {
32
32
  this.logger.sendPerformanceEvent({
33
33
  eventName: "CompressedBatch",
34
34
  duration,
35
- sizeBeforeCompression: originalLength,
35
+ sizeBeforeCompression: batch.contentSizeInBytes,
36
36
  sizeAfterCompression: compressedContent.length,
37
37
  });
38
38
  }
39
- batchToSend.push(Object.assign(Object.assign({}, batch[0]), { contents: JSON.stringify({ packedContents: compressedContent }), metadata: Object.assign(Object.assign({}, batch[0].metadata), { compressed: true }), compression: CompressionAlgorithms.lz4 }));
40
- for (const message of batch.slice(1)) {
41
- batchToSend.push(Object.assign(Object.assign({}, message), { contents: undefined }));
39
+ messages.push(Object.assign(Object.assign({}, batch.content[0]), { contents: JSON.stringify({ packedContents: compressedContent }), metadata: Object.assign(Object.assign({}, batch.content[0].metadata), { compressed: true }), compression: CompressionAlgorithms.lz4 }));
40
+ for (const message of batch.content.slice(1)) {
41
+ messages.push(Object.assign(Object.assign({}, message), { contents: undefined }));
42
42
  }
43
- return batchToSend;
43
+ return {
44
+ contentSizeInBytes: compressedContent.length,
45
+ content: messages,
46
+ };
44
47
  }
45
48
  }
46
49
  //# sourceMappingURL=opCompressor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opCompressor.js","sourceRoot":"","sources":["../../src/opLifecycle/opCompressor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,qBAAqB,EAA2B,MAAM,qBAAqB,CAAC;AAGrF;;;;GAIG;AACH,MAAM,OAAO,YAAY;IAIrB,YAAY,MAAwB;QAF5B,yBAAoB,GAAG,CAAC,CAAC;QAG7B,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC7D,CAAC;IAEM,aAAa,CAAC,KAAa;QAC9B,MAAM,QAAQ,GAAmB,EAAE,CAAC;QACpC,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC5B,MAAM,iBAAiB,GAA8B,EAAE,CAAC;QACxD,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE;YACjC,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;SACvD;QAED,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACpC,MAAM,gBAAgB,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC,CAAC;QACrF,MAAM,kBAAkB,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC;QACtD,MAAM,iBAAiB,GAAG,SAAS,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAChF,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,gBAAgB,CAAC;QAE/C,IAAI,KAAK,CAAC,kBAAkB,GAAG,MAAM,IAAI,IAAI,CAAC,oBAAoB,GAAG,EAAE,EAAE;YACrE,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;gBAC7B,SAAS,EAAE,iBAAiB;gBAC5B,QAAQ;gBACR,qBAAqB,EAAE,KAAK,CAAC,kBAAkB;gBAC/C,oBAAoB,EAAE,iBAAiB,CAAC,MAAM;aACjD,CAAC,CAAC;SACN;QAED,QAAQ,CAAC,IAAI,iCACN,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAE,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,cAAc,EAAE,iBAAiB,EAAE,CAAC,EACpF,QAAQ,kCAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAE,UAAU,EAAE,IAAI,KAC1D,WAAW,EAAE,qBAAqB,CAAC,GAAG,IACxC,CAAC;QAEH,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;YAC1C,QAAQ,CAAC,IAAI,iCAAM,OAAO,KAAE,QAAQ,EAAE,SAAS,IAAG,CAAC;SACtD;QAED,OAAO;YACH,kBAAkB,EAAE,iBAAiB,CAAC,MAAM;YAC5C,OAAO,EAAE,QAAQ;SACpB,CAAC;IACN,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 { IsoBuffer } from \"@fluidframework/common-utils\";\nimport { ChildLogger } from \"@fluidframework/telemetry-utils\";\nimport { compress } from \"lz4js\";\nimport { CompressionAlgorithms, ContainerRuntimeMessage } from \"../containerRuntime\";\nimport { IBatch, BatchMessage } from \"./definitions\";\n\n/**\n * Compresses batches of ops. It generates a single compressed op that contains\n * the contents of each op in the batch. It then submits empty ops for each original\n * op to reserve sequence numbers.\n */\nexport class OpCompressor {\n private readonly logger;\n private compressedBatchCount = 0;\n\n constructor(logger: ITelemetryLogger) {\n this.logger = ChildLogger.create(logger, \"OpCompressor\");\n }\n\n public compressBatch(batch: IBatch): IBatch {\n const messages: BatchMessage[] = [];\n this.compressedBatchCount++;\n const contentToCompress: ContainerRuntimeMessage[] = [];\n for (const message of batch.content) {\n contentToCompress.push(message.deserializedContent);\n }\n\n const compressionStart = Date.now();\n const contentsAsBuffer = new TextEncoder().encode(JSON.stringify(contentToCompress));\n const compressedContents = compress(contentsAsBuffer);\n const compressedContent = IsoBuffer.from(compressedContents).toString(\"base64\");\n const duration = Date.now() - compressionStart;\n\n if (batch.contentSizeInBytes > 200000 || this.compressedBatchCount % 25) {\n this.logger.sendPerformanceEvent({\n eventName: \"CompressedBatch\",\n duration,\n sizeBeforeCompression: batch.contentSizeInBytes,\n sizeAfterCompression: compressedContent.length,\n });\n }\n\n messages.push({\n ...batch.content[0], contents: JSON.stringify({ packedContents: compressedContent }),\n metadata: { ...batch.content[0].metadata, compressed: true },\n compression: CompressionAlgorithms.lz4,\n });\n\n for (const message of batch.content.slice(1)) {\n messages.push({ ...message, contents: undefined });\n }\n\n return {\n contentSizeInBytes: compressedContent.length,\n content: messages,\n };\n }\n}\n"]}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opDecompressor.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opDecompressor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAIjF;;;;;;;GAOG;AACH,qBAAa,cAAc;IACvB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,mBAAmB,CAAkB;IAC7C,OAAO,CAAC,cAAc,CAAK;IAEpB,cAAc,CAAC,OAAO,EAAE,yBAAyB,GAAG,yBAAyB;CA4DvF"}
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { decompress } from "lz4js";
6
6
  import { assert, IsoBuffer, Uint8ArrayToString } from "@fluidframework/common-utils";
7
- import { CompressionAlgorithms } from ".";
7
+ import { CompressionAlgorithms } from "../containerRuntime";
8
8
  /**
9
9
  * State machine that "unrolls" contents of compressed batches of ops after decompressing them.
10
10
  * This class relies on some implicit contracts defined below:
@@ -27,10 +27,10 @@ export class OpDecompressor {
27
27
  if (((_a = message.metadata) === null || _a === void 0 ? void 0 : _a.batch) === true
28
28
  && (((_b = message.metadata) === null || _b === void 0 ? void 0 : _b.compressed) || message.compression !== undefined)) {
29
29
  // Beginning of a compressed batch
30
- assert(this.activeBatch === false, "shouldn't have multiple active batches");
30
+ assert(this.activeBatch === false, 0x4b8 /* shouldn't have multiple active batches */);
31
31
  if (message.compression) {
32
32
  // lz4 is the only supported compression algorithm for now
33
- assert(message.compression === CompressionAlgorithms.lz4, "lz4 is currently the only supported compression algorithm");
33
+ assert(message.compression === CompressionAlgorithms.lz4, 0x4b9 /* lz4 is currently the only supported compression algorithm */);
34
34
  }
35
35
  this.activeBatch = true;
36
36
  const contents = IsoBuffer.from(message.contents.packedContents, "base64");
@@ -55,7 +55,7 @@ export class OpDecompressor {
55
55
  if (((_e = message.metadata) === null || _e === void 0 ? void 0 : _e.batch) === undefined &&
56
56
  (((_f = message.metadata) === null || _f === void 0 ? void 0 : _f.compressed) || message.compression === CompressionAlgorithms.lz4)) {
57
57
  // Single compressed message
58
- assert(this.activeBatch === false, "shouldn't receive compressed message in middle of a batch");
58
+ assert(this.activeBatch === false, 0x4ba /* shouldn't receive compressed message in middle of a batch */);
59
59
  const contents = IsoBuffer.from(message.contents.packedContents, "base64");
60
60
  const decompressedMessage = decompress(contents);
61
61
  const intoString = new TextDecoder().decode(decompressedMessage);
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opDecompressor.js","sourceRoot":"","sources":["../../src/opLifecycle/opDecompressor.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 { decompress } from \"lz4js\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\nimport { assert, IsoBuffer, Uint8ArrayToString } from \"@fluidframework/common-utils\";\nimport { CompressionAlgorithms } from \"../containerRuntime\";\n\n/**\n * State machine that \"unrolls\" contents of compressed batches of ops after decompressing them.\n * This class relies on some implicit contracts defined below:\n * 1. A compressed batch will have its first message with batch metadata set to true and compressed set to true\n * 2. Messages in the middle of a compressed batch will have neither batch metadata nor the compression property set\n * 3. The final message of a batch will have batch metadata set to false\n * 4. An individually compressed op will have undefined batch metadata and compression set to true\n */\nexport class OpDecompressor {\n private activeBatch = false;\n private rootMessageContents: any | undefined;\n private processedCount = 0;\n\n public processMessage(message: ISequencedDocumentMessage): ISequencedDocumentMessage {\n // We're checking for compression = true or top level compression property so\n // that we can enable compression without waiting on all ordering services\n // to pick up protocol change. Eventually only the top level property should\n // be used.\n if (message.metadata?.batch === true\n && (message.metadata?.compressed || message.compression !== undefined)) {\n // Beginning of a compressed batch\n assert(this.activeBatch === false, 0x4b8 /* shouldn't have multiple active batches */);\n if (message.compression) {\n // lz4 is the only supported compression algorithm for now\n assert(message.compression === CompressionAlgorithms.lz4,\n 0x4b9 /* lz4 is currently the only supported compression algorithm */);\n }\n\n this.activeBatch = true;\n\n const contents = IsoBuffer.from(message.contents.packedContents, \"base64\");\n const decompressedMessage = decompress(contents);\n const intoString = Uint8ArrayToString(decompressedMessage);\n const asObj = JSON.parse(intoString);\n this.rootMessageContents = asObj;\n\n return { ...message, contents: this.rootMessageContents[this.processedCount++] };\n }\n\n if (this.rootMessageContents !== undefined && message.metadata?.batch === undefined && this.activeBatch) {\n // Continuation of compressed batch\n return { ...message, contents: this.rootMessageContents[this.processedCount++] };\n }\n\n if (this.rootMessageContents !== undefined && message.metadata?.batch === false) {\n // End of compressed batch\n const returnMessage = {\n ...message,\n contents: this.rootMessageContents[this.processedCount++]\n };\n\n this.activeBatch = false;\n this.rootMessageContents = undefined;\n this.processedCount = 0;\n\n return returnMessage;\n }\n\n if (message.metadata?.batch === undefined &&\n (message.metadata?.compressed || message.compression === CompressionAlgorithms.lz4)) {\n // Single compressed message\n assert(this.activeBatch === false, 0x4ba /* shouldn't receive compressed message in middle of a batch */);\n\n const contents = IsoBuffer.from(message.contents.packedContents, \"base64\");\n const decompressedMessage = decompress(contents);\n const intoString = new TextDecoder().decode(decompressedMessage);\n const asObj = JSON.parse(intoString);\n\n return { ...message, contents: asObj[0] };\n }\n\n return message;\n }\n}\n"]}
@@ -0,0 +1,17 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
6
+ /**
7
+ * Responsible for creating and reconstructing chunked messages.
8
+ */
9
+ export declare class OpSplitter {
10
+ private readonly chunkMap;
11
+ constructor(chunks: [string, string[]][]);
12
+ get chunks(): ReadonlyMap<string, string[]>;
13
+ processRemoteMessage(message: ISequencedDocumentMessage): ISequencedDocumentMessage;
14
+ clearPartialChunks(clientId: string): void;
15
+ private addChunk;
16
+ }
17
+ //# sourceMappingURL=opSplitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opSplitter.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opSplitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAIjF;;GAEG;AACH,qBAAa,UAAU;IAEnB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;gBAErC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE;IAIxC,IAAW,MAAM,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAEjD;IAEM,oBAAoB,CAAC,OAAO,EAAE,yBAAyB,GAAG,yBAAyB;IAyBnF,kBAAkB,CAAC,QAAQ,EAAE,MAAM;IAM1C,OAAO,CAAC,QAAQ;CAqBnB"}
@@ -0,0 +1,57 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { DataCorruptionError, extractSafePropertiesFromMessage } from "@fluidframework/container-utils";
6
+ import { ContainerMessageType } from "../containerRuntime";
7
+ /**
8
+ * Responsible for creating and reconstructing chunked messages.
9
+ */
10
+ export class OpSplitter {
11
+ constructor(chunks) {
12
+ this.chunkMap = new Map(chunks);
13
+ }
14
+ get chunks() {
15
+ return this.chunkMap;
16
+ }
17
+ processRemoteMessage(message) {
18
+ if (message.type !== ContainerMessageType.ChunkedOp) {
19
+ return message;
20
+ }
21
+ const clientId = message.clientId;
22
+ const chunkedContent = message.contents;
23
+ this.addChunk(clientId, chunkedContent, message);
24
+ if (chunkedContent.chunkId < chunkedContent.totalChunks) {
25
+ // We are processing the op in chunks but haven't reached
26
+ // the last chunk yet in order to reconstruct the original op
27
+ return message;
28
+ }
29
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
30
+ const serializedContent = this.chunkMap.get(clientId).join("");
31
+ this.clearPartialChunks(clientId);
32
+ const newMessage = Object.assign({}, message);
33
+ newMessage.contents = serializedContent === "" ? undefined : JSON.parse(serializedContent);
34
+ newMessage.type = chunkedContent.originalType;
35
+ return newMessage;
36
+ }
37
+ clearPartialChunks(clientId) {
38
+ if (this.chunkMap.has(clientId)) {
39
+ this.chunkMap.delete(clientId);
40
+ }
41
+ }
42
+ addChunk(clientId, chunkedContent, originalMessage) {
43
+ let map = this.chunkMap.get(clientId);
44
+ if (map === undefined) {
45
+ map = [];
46
+ this.chunkMap.set(clientId, map);
47
+ }
48
+ if (chunkedContent.chunkId !== map.length + 1) {
49
+ // We are expecting the chunks to be processed sequentially, in the same order as they are sent.
50
+ // Therefore, the chunkId of the incoming op needs to match the length of the array (1-based indexing)
51
+ // holding the existing chunks for that particular clientId.
52
+ throw new DataCorruptionError("Chunk Id mismatch", Object.assign(Object.assign({}, extractSafePropertiesFromMessage(originalMessage)), { chunkMapLength: map.length, chunkId: chunkedContent.chunkId, totalChunks: chunkedContent.totalChunks }));
53
+ }
54
+ map.push(chunkedContent.contents);
55
+ }
56
+ }
57
+ //# sourceMappingURL=opSplitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opSplitter.js","sourceRoot":"","sources":["../../src/opLifecycle/opSplitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,mBAAmB,EAAE,gCAAgC,EAAE,MAAM,iCAAiC,CAAC;AAExG,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAG3D;;GAEG;AACH,MAAM,OAAO,UAAU;IAInB,YAAY,MAA4B;QACpC,IAAI,CAAC,QAAQ,GAAG,IAAI,GAAG,CAAmB,MAAM,CAAC,CAAC;IACtD,CAAC;IAED,IAAW,MAAM;QACb,OAAO,IAAI,CAAC,QAAQ,CAAC;IACzB,CAAC;IAEM,oBAAoB,CAAC,OAAkC;QAC1D,IAAI,OAAO,CAAC,IAAI,KAAK,oBAAoB,CAAC,SAAS,EAAE;YACjD,OAAO,OAAO,CAAC;SAClB;QAED,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QAClC,MAAM,cAAc,GAAG,OAAO,CAAC,QAAsB,CAAC;QACtD,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC;QAEjD,IAAI,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,WAAW,EAAE;YACrD,yDAAyD;YACzD,6DAA6D;YAC7D,OAAO,OAAO,CAAC;SAClB;QAED,oEAAoE;QACpE,MAAM,iBAAiB,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChE,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QAElC,MAAM,UAAU,qBAAQ,OAAO,CAAE,CAAC;QAClC,UAAU,CAAC,QAAQ,GAAG,iBAAiB,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAC3F,UAAU,CAAC,IAAI,GAAG,cAAc,CAAC,YAAY,CAAC;QAC9C,OAAO,UAAU,CAAC;IACtB,CAAC;IAEM,kBAAkB,CAAC,QAAgB;QACtC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;YAC7B,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;SAClC;IACL,CAAC;IAEO,QAAQ,CAAC,QAAgB,EAAE,cAA0B,EAAE,eAA0C;QACrG,IAAI,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACtC,IAAI,GAAG,KAAK,SAAS,EAAE;YACnB,GAAG,GAAG,EAAE,CAAC;YACT,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;SACpC;QAED,IAAI,cAAc,CAAC,OAAO,KAAK,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE;YAC3C,gGAAgG;YAChG,sGAAsG;YACtG,4DAA4D;YAC5D,MAAM,IAAI,mBAAmB,CAAC,mBAAmB,kCAC1C,gCAAgC,CAAC,eAAe,CAAC,KACpD,cAAc,EAAE,GAAG,CAAC,MAAM,EAC1B,OAAO,EAAE,cAAc,CAAC,OAAO,EAC/B,WAAW,EAAE,cAAc,CAAC,WAAW,IACzC,CAAC;SACN;QAED,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;CACJ","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { DataCorruptionError, extractSafePropertiesFromMessage } from \"@fluidframework/container-utils\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\nimport { ContainerMessageType } from \"../containerRuntime\";\nimport { IChunkedOp } from \"./definitions\";\n\n/**\n * Responsible for creating and reconstructing chunked messages.\n */\nexport class OpSplitter {\n // Local copy of incomplete received chunks.\n private readonly chunkMap: Map<string, string[]>;\n\n constructor(chunks: [string, string[]][]) {\n this.chunkMap = new Map<string, string[]>(chunks);\n }\n\n public get chunks(): ReadonlyMap<string, string[]> {\n return this.chunkMap;\n }\n\n public processRemoteMessage(message: ISequencedDocumentMessage): ISequencedDocumentMessage {\n if (message.type !== ContainerMessageType.ChunkedOp) {\n return message;\n }\n\n const clientId = message.clientId;\n const chunkedContent = message.contents as IChunkedOp;\n this.addChunk(clientId, chunkedContent, message);\n\n if (chunkedContent.chunkId < chunkedContent.totalChunks) {\n // We are processing the op in chunks but haven't reached\n // the last chunk yet in order to reconstruct the original op\n return message;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const serializedContent = this.chunkMap.get(clientId)!.join(\"\");\n this.clearPartialChunks(clientId);\n\n const newMessage = { ...message };\n newMessage.contents = serializedContent === \"\" ? undefined : JSON.parse(serializedContent);\n newMessage.type = chunkedContent.originalType;\n return newMessage;\n }\n\n public clearPartialChunks(clientId: string) {\n if (this.chunkMap.has(clientId)) {\n this.chunkMap.delete(clientId);\n }\n }\n\n private addChunk(clientId: string, chunkedContent: IChunkedOp, originalMessage: ISequencedDocumentMessage) {\n let map = this.chunkMap.get(clientId);\n if (map === undefined) {\n map = [];\n this.chunkMap.set(clientId, map);\n }\n\n if (chunkedContent.chunkId !== map.length + 1) {\n // We are expecting the chunks to be processed sequentially, in the same order as they are sent.\n // Therefore, the chunkId of the incoming op needs to match the length of the array (1-based indexing)\n // holding the existing chunks for that particular clientId.\n throw new DataCorruptionError(\"Chunk Id mismatch\", {\n ...extractSafePropertiesFromMessage(originalMessage),\n chunkMapLength: map.length,\n chunkId: chunkedContent.chunkId,\n totalChunks: chunkedContent.totalChunks,\n });\n }\n\n map.push(chunkedContent.contents);\n }\n}\n"]}