@fluidframework/container-runtime 2.0.0-internal.1.0.0.84253 → 2.0.0-internal.1.1.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 (91) hide show
  1. package/.mocharc.js +12 -0
  2. package/dist/blobManager.d.ts +7 -1
  3. package/dist/blobManager.d.ts.map +1 -1
  4. package/dist/blobManager.js +18 -1
  5. package/dist/blobManager.js.map +1 -1
  6. package/dist/containerRuntime.d.ts +2 -66
  7. package/dist/containerRuntime.d.ts.map +1 -1
  8. package/dist/containerRuntime.js +37 -295
  9. package/dist/containerRuntime.js.map +1 -1
  10. package/dist/dataStoreContext.d.ts +3 -5
  11. package/dist/dataStoreContext.d.ts.map +1 -1
  12. package/dist/dataStoreContext.js +13 -23
  13. package/dist/dataStoreContext.js.map +1 -1
  14. package/dist/dataStores.d.ts.map +1 -1
  15. package/dist/dataStores.js +1 -6
  16. package/dist/dataStores.js.map +1 -1
  17. package/dist/garbageCollection.d.ts +37 -6
  18. package/dist/garbageCollection.d.ts.map +1 -1
  19. package/dist/garbageCollection.js +61 -65
  20. package/dist/garbageCollection.js.map +1 -1
  21. package/dist/index.d.ts +2 -1
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +3 -2
  24. package/dist/index.js.map +1 -1
  25. package/dist/packageVersion.d.ts +1 -1
  26. package/dist/packageVersion.d.ts.map +1 -1
  27. package/dist/packageVersion.js +1 -1
  28. package/dist/packageVersion.js.map +1 -1
  29. package/dist/pendingStateManager.d.ts.map +1 -1
  30. package/dist/pendingStateManager.js +15 -2
  31. package/dist/pendingStateManager.js.map +1 -1
  32. package/dist/scheduleManager.d.ts +28 -0
  33. package/dist/scheduleManager.d.ts.map +1 -0
  34. package/dist/scheduleManager.js +235 -0
  35. package/dist/scheduleManager.js.map +1 -0
  36. package/dist/summaryCollection.js +1 -1
  37. package/dist/summaryCollection.js.map +1 -1
  38. package/dist/summaryManager.d.ts.map +1 -1
  39. package/dist/summaryManager.js +20 -5
  40. package/dist/summaryManager.js.map +1 -1
  41. package/lib/blobManager.d.ts +7 -1
  42. package/lib/blobManager.d.ts.map +1 -1
  43. package/lib/blobManager.js +19 -2
  44. package/lib/blobManager.js.map +1 -1
  45. package/lib/containerRuntime.d.ts +2 -66
  46. package/lib/containerRuntime.d.ts.map +1 -1
  47. package/lib/containerRuntime.js +38 -295
  48. package/lib/containerRuntime.js.map +1 -1
  49. package/lib/dataStoreContext.d.ts +3 -5
  50. package/lib/dataStoreContext.d.ts.map +1 -1
  51. package/lib/dataStoreContext.js +13 -23
  52. package/lib/dataStoreContext.js.map +1 -1
  53. package/lib/dataStores.d.ts.map +1 -1
  54. package/lib/dataStores.js +1 -6
  55. package/lib/dataStores.js.map +1 -1
  56. package/lib/garbageCollection.d.ts +37 -6
  57. package/lib/garbageCollection.d.ts.map +1 -1
  58. package/lib/garbageCollection.js +47 -52
  59. package/lib/garbageCollection.js.map +1 -1
  60. package/lib/index.d.ts +2 -1
  61. package/lib/index.d.ts.map +1 -1
  62. package/lib/index.js +2 -1
  63. package/lib/index.js.map +1 -1
  64. package/lib/packageVersion.d.ts +1 -1
  65. package/lib/packageVersion.d.ts.map +1 -1
  66. package/lib/packageVersion.js +1 -1
  67. package/lib/packageVersion.js.map +1 -1
  68. package/lib/pendingStateManager.d.ts.map +1 -1
  69. package/lib/pendingStateManager.js +15 -2
  70. package/lib/pendingStateManager.js.map +1 -1
  71. package/lib/scheduleManager.d.ts +28 -0
  72. package/lib/scheduleManager.d.ts.map +1 -0
  73. package/lib/scheduleManager.js +231 -0
  74. package/lib/scheduleManager.js.map +1 -0
  75. package/lib/summaryCollection.js +1 -1
  76. package/lib/summaryCollection.js.map +1 -1
  77. package/lib/summaryManager.d.ts.map +1 -1
  78. package/lib/summaryManager.js +20 -5
  79. package/lib/summaryManager.js.map +1 -1
  80. package/package.json +19 -15
  81. package/src/blobManager.ts +23 -1
  82. package/src/containerRuntime.ts +42 -392
  83. package/src/dataStoreContext.ts +10 -25
  84. package/src/dataStores.ts +0 -6
  85. package/src/garbageCollection.ts +64 -69
  86. package/src/index.ts +1 -2
  87. package/src/packageVersion.ts +1 -1
  88. package/src/pendingStateManager.ts +18 -2
  89. package/src/scheduleManager.ts +294 -0
  90. package/src/summaryCollection.ts +1 -1
  91. package/src/summaryManager.ts +20 -5
@@ -82,14 +82,9 @@ import {
82
82
  function createAttributes(
83
83
  pkg: readonly string[],
84
84
  isRootDataStore: boolean,
85
- disableIsolatedChannels: boolean,
86
85
  ): WriteFluidDataStoreAttributes {
87
86
  const stringifiedPkg = JSON.stringify(pkg);
88
- return disableIsolatedChannels ? {
89
- pkg: stringifiedPkg,
90
- snapshotFormatVersion: "0.1",
91
- isRootDataStore,
92
- } : {
87
+ return {
93
88
  pkg: stringifiedPkg,
94
89
  summaryFormatVersion: 2,
95
90
  isRootDataStore,
@@ -98,9 +93,8 @@ function createAttributes(
98
93
  export function createAttributesBlob(
99
94
  pkg: readonly string[],
100
95
  isRootDataStore: boolean,
101
- disableIsolatedChannels: boolean,
102
96
  ): ITreeEntry {
103
- const attributes = createAttributes(pkg, isRootDataStore, disableIsolatedChannels);
97
+ const attributes = createAttributes(pkg, isRootDataStore);
104
98
  return new BlobTreeEntry(dataStoreAttributesBlobName, JSON.stringify(attributes));
105
99
  }
106
100
 
@@ -123,7 +117,6 @@ export interface IFluidDataStoreContextProps {
123
117
  readonly scope: FluidObject;
124
118
  readonly createSummarizerNodeFn: CreateChildSummarizerNodeFn;
125
119
  readonly writeGCDataAtRoot: boolean;
126
- readonly disableIsolatedChannels: boolean;
127
120
  readonly pkg?: Readonly<string[]>;
128
121
  }
129
122
 
@@ -255,7 +248,6 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
255
248
  public readonly storage: IDocumentStorageService;
256
249
  public readonly scope: FluidObject;
257
250
  private readonly writeGCDataAtRoot: boolean;
258
- protected readonly disableIsolatedChannels: boolean;
259
251
  protected pkg?: readonly string[];
260
252
 
261
253
  constructor(
@@ -271,7 +263,6 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
271
263
  this.storage = props.storage;
272
264
  this.scope = props.scope;
273
265
  this.writeGCDataAtRoot = props.writeGCDataAtRoot;
274
- this.disableIsolatedChannels = props.disableIsolatedChannels;
275
266
  this.pkg = props.pkg;
276
267
 
277
268
  // URIs use slashes as delimiters. Handles use URIs.
@@ -387,8 +378,8 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
387
378
  /**
388
379
  * Notifies this object about changes in the connection state.
389
380
  * @param value - New connection state.
390
- * @param clientId - ID of the client. It's old ID when in disconnected state and
391
- * it's new client ID when we are connecting or connected.
381
+ * @param clientId - ID of the client. Its old ID when in disconnected state and
382
+ * its new client ID when we are connecting or connected.
392
383
  */
393
384
  public setConnectionState(connected: boolean, clientId?: string) {
394
385
  this.verifyNotClosed();
@@ -468,18 +459,15 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
468
459
 
469
460
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
470
461
  const summarizeResult = await this.channel!.summarize(fullTree, trackState, telemetryContext);
471
- let pathPartsForChildren: string[] | undefined;
472
462
 
473
- if (!this.disableIsolatedChannels) {
474
- // Wrap dds summaries in .channels subtree.
475
- wrapSummaryInChannelsTree(summarizeResult);
476
- pathPartsForChildren = [channelsTreeName];
477
- }
463
+ // Wrap dds summaries in .channels subtree.
464
+ wrapSummaryInChannelsTree(summarizeResult);
465
+ const pathPartsForChildren = [channelsTreeName];
478
466
 
479
467
  // Add data store's attributes to the summary.
480
468
  const { pkg } = await this.getInitialSnapshotDetails();
481
469
  const isRoot = await this.isRoot();
482
- const attributes = createAttributes(pkg, isRoot, this.disableIsolatedChannels);
470
+ const attributes = createAttributes(pkg, isRoot);
483
471
  addBlobToSummary(summarizeResult, dataStoreAttributesBlobName, JSON.stringify(attributes));
484
472
 
485
473
  // Add GC data to the summary if it's not written at the root.
@@ -921,16 +909,13 @@ export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
921
909
 
922
910
  const summarizeResult = this.channel.getAttachSummary();
923
911
 
924
- if (!this.disableIsolatedChannels) {
925
- // Wrap dds summaries in .channels subtree.
926
- wrapSummaryInChannelsTree(summarizeResult);
927
- }
912
+ // Wrap dds summaries in .channels subtree.
913
+ wrapSummaryInChannelsTree(summarizeResult);
928
914
 
929
915
  // Add data store's attributes to the summary.
930
916
  const attributes = createAttributes(
931
917
  this.pkg,
932
918
  this.isInMemoryRoot(),
933
- this.disableIsolatedChannels,
934
919
  );
935
920
  addBlobToSummary(summarizeResult, dataStoreAttributesBlobName, JSON.stringify(attributes));
936
921
 
package/src/dataStores.ts CHANGED
@@ -143,7 +143,6 @@ export class DataStores implements IDisposable {
143
143
  { type: CreateSummarizerNodeSource.FromSummary },
144
144
  ),
145
145
  writeGCDataAtRoot: this.writeGCDataAtRoot,
146
- disableIsolatedChannels: this.runtime.disableIsolatedChannels,
147
146
  });
148
147
  } else {
149
148
  if (typeof value !== "object") {
@@ -164,7 +163,6 @@ export class DataStores implements IDisposable {
164
163
  snapshotTree,
165
164
  isRootDataStore: undefined,
166
165
  writeGCDataAtRoot: this.writeGCDataAtRoot,
167
- disableIsolatedChannels: this.runtime.disableIsolatedChannels,
168
166
  });
169
167
  }
170
168
  this.contexts.addBoundOrRemoted(dataStoreContext);
@@ -245,13 +243,11 @@ export class DataStores implements IDisposable {
245
243
  entries: [createAttributesBlob(
246
244
  pkg,
247
245
  true /* isRootDataStore */,
248
- this.runtime.disableIsolatedChannels,
249
246
  )],
250
247
  },
251
248
  },
252
249
  ),
253
250
  writeGCDataAtRoot: this.writeGCDataAtRoot,
254
- disableIsolatedChannels: this.runtime.disableIsolatedChannels,
255
251
  pkg,
256
252
  });
257
253
 
@@ -356,7 +352,6 @@ export class DataStores implements IDisposable {
356
352
  snapshotTree: undefined,
357
353
  isRootDataStore: isRoot,
358
354
  writeGCDataAtRoot: this.writeGCDataAtRoot,
359
- disableIsolatedChannels: this.runtime.disableIsolatedChannels,
360
355
  });
361
356
  this.contexts.addUnbound(context);
362
357
  return context;
@@ -378,7 +373,6 @@ export class DataStores implements IDisposable {
378
373
  snapshotTree: undefined,
379
374
  isRootDataStore: false,
380
375
  writeGCDataAtRoot: this.writeGCDataAtRoot,
381
- disableIsolatedChannels: this.runtime.disableIsolatedChannels,
382
376
  createProps: props,
383
377
  });
384
378
  this.contexts.addUnbound(context);
@@ -61,11 +61,11 @@ export const gcTreeKey = "gc";
61
61
  export const gcBlobPrefix = "__gc";
62
62
 
63
63
  // Feature gate key to turn GC on / off.
64
- const runGCKey = "Fluid.GarbageCollection.RunGC";
64
+ export const runGCKey = "Fluid.GarbageCollection.RunGC";
65
65
  // Feature gate key to turn GC sweep on / off.
66
- // const runSweepKey = "Fluid.GarbageCollection.RunSweep";
66
+ export const runSweepKey = "Fluid.GarbageCollection.RunSweep";
67
67
  // Feature gate key to turn GC test mode on / off.
68
- const gcTestModeKey = "Fluid.GarbageCollection.GCTestMode";
68
+ export const gcTestModeKey = "Fluid.GarbageCollection.GCTestMode";
69
69
  // Feature gate key to write GC data at the root of the summary tree.
70
70
  const writeAtRootKey = "Fluid.GarbageCollection.WriteDataAtRoot";
71
71
  // Feature gate key to expire a session after a set period of time.
@@ -80,7 +80,7 @@ const disableSweepLogKey = "Fluid.GarbageCollection.DisableSweepLog";
80
80
  // One day in milliseconds.
81
81
  export const oneDayMs = 1 * 24 * 60 * 60 * 1000;
82
82
 
83
- const defaultInactiveTimeoutMs = 7 * oneDayMs; // 7 days
83
+ export const defaultInactiveTimeoutMs = 7 * oneDayMs; // 7 days
84
84
  export const defaultSessionExpiryDurationMs = 30 * oneDayMs; // 30 days
85
85
 
86
86
  /** The statistics of the system state after a garbage collection run. */
@@ -190,14 +190,14 @@ export interface IGarbageCollectorCreateParams {
190
190
  }
191
191
 
192
192
  /** The state of node that is unreferenced. */
193
- const UnreferencedState = {
193
+ export const UnreferencedState = {
194
194
  /** The node is active, i.e., it can become referenced again. */
195
195
  Active: "Active",
196
196
  /** The node is inactive, i.e., it should not become referenced. */
197
197
  Inactive: "Inactive",
198
198
  /** The node is ready to be deleted by the sweep phase. */
199
199
  SweepReady: "SweepReady",
200
- };
200
+ } as const;
201
201
  export type UnreferencedState = typeof UnreferencedState[keyof typeof UnreferencedState];
202
202
 
203
203
  /** The event that is logged when unreferenced node is used after a certain time. */
@@ -220,14 +220,16 @@ interface IUnreferencedEventProps {
220
220
  * Helper class that tracks the state of an unreferenced node such as the time it was unreferenced and if it can
221
221
  * be deleted by the sweep phase.
222
222
  */
223
- class UnreferencedStateTracker {
223
+ export class UnreferencedStateTracker {
224
224
  private _state: UnreferencedState = UnreferencedState.Active;
225
225
  public get state(): UnreferencedState {
226
226
  return this._state;
227
227
  }
228
228
 
229
- private inactiveTimer: Timer | undefined;
230
- private sweepTimer: ReturnType<typeof setTimeout> | undefined;
229
+ /** Timer to indicate when an unreferenced object is considered Inactive */
230
+ private readonly inactiveTimer: TimerWithNoDefaultTimeout;
231
+ /** Timer to indicate when an unreferenced object is Sweep-Ready */
232
+ private readonly sweepTimer: TimerWithNoDefaultTimeout;
231
233
 
232
234
  constructor(
233
235
  public readonly unreferencedTimestampMs: number,
@@ -238,8 +240,29 @@ class UnreferencedStateTracker {
238
240
  /** The current reference timestamp; undefined if no ops have ever been processed which can happen in tests. */
239
241
  currentReferenceTimestampMs?: number,
240
242
  ) {
241
- // If there is no current reference timestamp, don't track the node's unreferenced state. This will happen
242
- // later when updateTracking is called with a reference timestamp.
243
+ if (this.sweepTimeoutMs !== undefined) {
244
+ assert(this.inactiveTimeoutMs <= this.sweepTimeoutMs,
245
+ 0x3b0 /* inactive timeout must not be greater than the sweep timeout */);
246
+ }
247
+
248
+ this.sweepTimer = new TimerWithNoDefaultTimeout(
249
+ () => {
250
+ this._state = UnreferencedState.SweepReady;
251
+ assert(!this.inactiveTimer.hasTimer, 0x3b1 /* inactiveTimer still running after sweepTimer fired! */);
252
+ },
253
+ );
254
+
255
+ this.inactiveTimer = new TimerWithNoDefaultTimeout(() => {
256
+ this._state = UnreferencedState.Inactive;
257
+
258
+ // After the node becomes inactive, start the sweep timer after which the node will be ready for sweep.
259
+ if (this.sweepTimeoutMs !== undefined) {
260
+ this.sweepTimer.restart(this.sweepTimeoutMs - this.inactiveTimeoutMs);
261
+ }
262
+ });
263
+
264
+ // If there is no current reference timestamp, can't track the node's unreferenced state at this time.
265
+ // This will happen later when updateTracking is called with a reference timestamp.
243
266
  if (currentReferenceTimestampMs !== undefined) {
244
267
  this.updateTracking(currentReferenceTimestampMs);
245
268
  }
@@ -260,42 +283,21 @@ class UnreferencedStateTracker {
260
283
  // Also, start a timer for the sweep timeout.
261
284
  if (unreferencedDurationMs >= this.inactiveTimeoutMs) {
262
285
  this._state = UnreferencedState.Inactive;
263
- this.clearTimers();
286
+ this.inactiveTimer.clear();
264
287
 
265
288
  if (this.sweepTimeoutMs !== undefined) {
266
- setLongTimeout(
267
- this.sweepTimeoutMs - unreferencedDurationMs,
268
- () => { this._state = UnreferencedState.SweepReady; },
269
- (timer) => { this.sweepTimer = timer; },
270
- );
289
+ this.sweepTimer.restart(this.sweepTimeoutMs - unreferencedDurationMs);
271
290
  }
272
291
  return;
273
292
  }
274
293
 
275
- // The node is still active. Start the inactive timer for the remaining duration.
276
- const remainingDurationMs = this.inactiveTimeoutMs - unreferencedDurationMs;
277
- if (this.inactiveTimer === undefined) {
278
- const inactiveTimeoutHandler = () => {
279
- this._state = UnreferencedState.Inactive;
280
- // After the node becomes inactive, start the sweep timer after which the node will be ready for sweep.
281
- if (this.sweepTimeoutMs !== undefined) {
282
- setLongTimeout(
283
- this.sweepTimeoutMs - this.inactiveTimeoutMs,
284
- () => { this._state = UnreferencedState.SweepReady; },
285
- (timer) => { this.sweepTimer = timer; },
286
- );
287
- }
288
- };
289
- this.inactiveTimer = new Timer(remainingDurationMs, () => inactiveTimeoutHandler());
290
- }
291
- this.inactiveTimer.restart(remainingDurationMs);
294
+ // The node is still active. Ensure the inactive timer is running with the proper remaining duration.
295
+ this.inactiveTimer.restart(this.inactiveTimeoutMs - unreferencedDurationMs);
292
296
  }
293
297
 
294
298
  private clearTimers() {
295
- this.inactiveTimer?.clear();
296
- if (this.sweepTimer !== undefined) {
297
- clearTimeout(this.sweepTimer);
298
- }
299
+ this.inactiveTimer.clear();
300
+ this.sweepTimer.clear();
299
301
  }
300
302
 
301
303
  /** Stop tracking this node. Reset the unreferenced timers and state, if any. */
@@ -412,8 +414,8 @@ export class GarbageCollector implements IGarbageCollector {
412
414
  private readonly baseGCDetailsP: Promise<Map<string, IGarbageCollectionDetailsBase>>;
413
415
  // Map of node ids to their unreferenced state tracker.
414
416
  private readonly unreferencedNodesState: Map<string, UnreferencedStateTracker> = new Map();
415
- // The timeout responsible for closing the container when the session has expired
416
- private sessionExpiryTimer?: ReturnType<typeof setTimeout>;
417
+ // The Timer responsible for closing the container when the session has expired
418
+ private sessionExpiryTimer: Timer | undefined;
417
419
 
418
420
  // Keeps track of unreferenced events that are logged for a node. This is used to limit the log generation to one
419
421
  // per event per node.
@@ -484,7 +486,7 @@ export class GarbageCollector implements IGarbageCollector {
484
486
  // The sweep phase has to be explicitly enabled by setting the sweepAllowed flag in GC options to true.
485
487
  this.sweepEnabled = this.gcOptions.sweepAllowed === true;
486
488
 
487
- // Set the Session Expiry only if the flag is enabled or the test option is set.
489
+ // Set the Session Expiry only if the flag is enabled and GC is enabled.
488
490
  if (this.mc.config.getBoolean(runSessionExpiryKey) && this.gcEnabled) {
489
491
  this.sessionExpiryTimeoutMs = this.gcOptions.sessionExpiryTimeoutMs ?? defaultSessionExpiryDurationMs;
490
492
  }
@@ -497,11 +499,11 @@ export class GarbageCollector implements IGarbageCollector {
497
499
  this.mc.config.getNumber("Fluid.GarbageCollection.TestOverride.SessionExpiryMs");
498
500
  const timeoutMs = overrideSessionExpiryTimeoutMs ?? this.sessionExpiryTimeoutMs;
499
501
 
500
- setLongTimeout(
502
+ this.sessionExpiryTimer = new Timer(
501
503
  timeoutMs,
502
504
  () => { this.runtime.closeFn(new ClientSessionExpiredError(`Client session expired.`, timeoutMs)); },
503
- (timer) => { this.sessionExpiryTimer = timer; },
504
505
  );
506
+ this.sessionExpiryTimer.start();
505
507
 
506
508
  // TEMPORARY: Hardcode a default of 2 days which is the value used in the ODSP driver.
507
509
  // This unblocks the Sweep Log (see logSweepEvents function).
@@ -557,7 +559,7 @@ export class GarbageCollector implements IGarbageCollector {
557
559
 
558
560
  // Inactive timeout must be greater than sweep timeout since a node goes from active -> inactive -> sweep ready.
559
561
  if (this.sweepTimeoutMs !== undefined && this.inactiveTimeoutMs > this.sweepTimeoutMs) {
560
- throw new UsageError("inactive timeout should not be greated than the sweep timeout");
562
+ throw new UsageError("inactive timeout should not be greater than the sweep timeout");
561
563
  }
562
564
 
563
565
  // Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.
@@ -688,7 +690,7 @@ export class GarbageCollector implements IGarbageCollector {
688
690
  const usedRoutes = runGarbageCollection(gcNodes, ["/"]).referencedNodeIds;
689
691
 
690
692
  const baseGCDetailsMap = unpackChildNodesGCDetails({ gcData: { gcNodes }, usedRoutes });
691
- // Currently, the nodes may write the GC data. So, we need to update it's base GC details with the
693
+ // Currently, the nodes may write the GC data. So, we need to update its base GC details with the
692
694
  // unreferenced timestamp. Once we start writing the GC data here, we won't need to do this anymore.
693
695
  for (const [nodeId, nodeData] of Object.entries(baseState.gcNodes)) {
694
696
  if (nodeData.unreferencedTimestampMs !== undefined) {
@@ -986,10 +988,8 @@ export class GarbageCollector implements IGarbageCollector {
986
988
  }
987
989
 
988
990
  public dispose(): void {
989
- if (this.sessionExpiryTimer !== undefined) {
990
- clearTimeout(this.sessionExpiryTimer);
991
- this.sessionExpiryTimer = undefined;
992
- }
991
+ this.sessionExpiryTimer?.clear();
992
+ this.sessionExpiryTimer = undefined;
993
993
  }
994
994
 
995
995
  /**
@@ -1416,25 +1416,20 @@ function generateSortedGCState(gcState: IGarbageCollectionState): IGarbageCollec
1416
1416
  return sortedGCState;
1417
1417
  }
1418
1418
 
1419
- /**
1420
- * setLongTimeout is used for timeouts longer than setTimeout's ~24.8 day max
1421
- * @param timeoutMs - the total time the timeout needs to last in ms
1422
- * @param timeoutFn - the function to execute when the timer ends
1423
- * @param setTimerFn - the function used to update your timer variable
1424
- */
1425
- function setLongTimeout(
1426
- timeoutMs: number,
1427
- timeoutFn: () => void,
1428
- setTimerFn: (timer: ReturnType<typeof setTimeout>) => void,
1429
- ) {
1430
- // The setTimeout max is 24.8 days before looping occurs.
1431
- const maxTimeout = 2147483647;
1432
- let timer: ReturnType<typeof setTimeout>;
1433
- if (timeoutMs > maxTimeout) {
1434
- const newTimeoutMs = timeoutMs - maxTimeout;
1435
- timer = setTimeout(() => setLongTimeout(newTimeoutMs, timeoutFn, setTimerFn), maxTimeout);
1436
- } else {
1437
- timer = setTimeout(() => timeoutFn(), timeoutMs);
1419
+ /** A wrapper around common-utils Timer that requires the timeout when calling start/restart */
1420
+ class TimerWithNoDefaultTimeout extends Timer {
1421
+ constructor(
1422
+ private readonly callback: () => void,
1423
+ ) {
1424
+ // The default timeout/handlers will never be used since start/restart pass overrides below
1425
+ super(0, () => { throw new Error("DefaultHandler should not be used"); });
1426
+ }
1427
+
1428
+ start(timeoutMs: number) {
1429
+ super.start(timeoutMs, this.callback);
1430
+ }
1431
+
1432
+ restart(timeoutMs: number): void {
1433
+ super.restart(timeoutMs, this.callback);
1438
1434
  }
1439
- setTimerFn(timer);
1440
1435
  }
package/src/index.ts CHANGED
@@ -14,12 +14,10 @@ export {
14
14
  ISummaryConfigurationDisableSummarizer,
15
15
  ISummaryConfigurationDisableHeuristics,
16
16
  IContainerRuntimeOptions,
17
- IPendingRuntimeState,
18
17
  IRootSummaryTreeWithStats,
19
18
  isRuntimeMessage,
20
19
  RuntimeMessage,
21
20
  unpackRuntimeMessage,
22
- ScheduleManager,
23
21
  agentSchedulerId,
24
22
  ContainerRuntime,
25
23
  RuntimeHeaders,
@@ -41,6 +39,7 @@ export {
41
39
  IPendingMessage,
42
40
  IPendingState,
43
41
  } from "./pendingStateManager";
42
+ export { ScheduleManager } from "./scheduleManager";
44
43
  export { Summarizer } from "./summarizer";
45
44
  export {
46
45
  EnqueueSummarizeResult,
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-runtime";
9
- export const pkgVersion = "2.0.0-internal.1.0.0.84253";
9
+ export const pkgVersion = "2.0.0-internal.1.1.0";
@@ -14,6 +14,7 @@ import { FlushMode } from "@fluidframework/runtime-definitions";
14
14
  import { wrapError } from "@fluidframework/telemetry-utils";
15
15
  import Deque from "double-ended-queue";
16
16
  import { ContainerMessageType } from "./containerRuntime";
17
+ import { pkgVersion } from "./packageVersion";
17
18
 
18
19
  /**
19
20
  * This represents a message that has been submitted and is added to the pending queue when `submit` is called on the
@@ -368,8 +369,23 @@ export class PendingStateManager implements IDisposable {
368
369
  } else {
369
370
  // Get the batch metadata from the last message in the batch.
370
371
  const batchEndMetadata = message.metadata?.batch;
371
- assert(batchBeginMetadata === true, 0x16f /* "Did not receive batch begin metadata" */);
372
- assert(batchEndMetadata === false, 0x170 /* "Did not receive batch end metadata" */);
372
+ if (batchBeginMetadata !== true || batchEndMetadata !== false) {
373
+ this.stateHandler.close(DataProcessingError.create(
374
+ "Pending batch inconsistency", // Formerly known as asserts 0x16f and 0x170
375
+ "processPendingLocalMessage",
376
+ message,
377
+ {
378
+ runtimeVersion: pkgVersion,
379
+ batchClientId: this.pendingBatchBeginMessage.clientId,
380
+ clientId: this.stateHandler.clientId(),
381
+ hasBatchStart: batchBeginMetadata === true,
382
+ hasBatchEnd: batchEndMetadata === false,
383
+ messageType: message.type,
384
+ batchStartSequenceNumber: this.pendingBatchBeginMessage.clientSequenceNumber,
385
+ pendingMessagesCount: this.pendingMessagesCount,
386
+ nextPendingState: nextPendingState.type,
387
+ }));
388
+ }
373
389
  }
374
390
 
375
391
  // Clear the pending batch state now that we have processed the entire batch.