@fluidframework/container-runtime 2.0.0-internal.2.4.0 → 2.0.0-internal.3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/containerRuntime.d.ts +45 -42
  2. package/dist/containerRuntime.d.ts.map +1 -1
  3. package/dist/containerRuntime.js +87 -38
  4. package/dist/containerRuntime.js.map +1 -1
  5. package/dist/dataStoreContext.d.ts +1 -0
  6. package/dist/dataStoreContext.d.ts.map +1 -1
  7. package/dist/dataStoreContext.js +7 -2
  8. package/dist/dataStoreContext.js.map +1 -1
  9. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  10. package/dist/opLifecycle/outbox.js +0 -1
  11. package/dist/opLifecycle/outbox.js.map +1 -1
  12. package/dist/packageVersion.d.ts +1 -1
  13. package/dist/packageVersion.js +1 -1
  14. package/dist/packageVersion.js.map +1 -1
  15. package/dist/pendingStateManager.d.ts +4 -13
  16. package/dist/pendingStateManager.d.ts.map +1 -1
  17. package/dist/pendingStateManager.js +130 -160
  18. package/dist/pendingStateManager.js.map +1 -1
  19. package/dist/summarizerClientElection.d.ts +1 -2
  20. package/dist/summarizerClientElection.d.ts.map +1 -1
  21. package/dist/summarizerClientElection.js +3 -30
  22. package/dist/summarizerClientElection.js.map +1 -1
  23. package/dist/summarizerTypes.d.ts +0 -4
  24. package/dist/summarizerTypes.d.ts.map +1 -1
  25. package/dist/summarizerTypes.js.map +1 -1
  26. package/lib/containerRuntime.d.ts +45 -42
  27. package/lib/containerRuntime.d.ts.map +1 -1
  28. package/lib/containerRuntime.js +87 -38
  29. package/lib/containerRuntime.js.map +1 -1
  30. package/lib/dataStoreContext.d.ts +1 -0
  31. package/lib/dataStoreContext.d.ts.map +1 -1
  32. package/lib/dataStoreContext.js +7 -2
  33. package/lib/dataStoreContext.js.map +1 -1
  34. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  35. package/lib/opLifecycle/outbox.js +0 -1
  36. package/lib/opLifecycle/outbox.js.map +1 -1
  37. package/lib/packageVersion.d.ts +1 -1
  38. package/lib/packageVersion.js +1 -1
  39. package/lib/packageVersion.js.map +1 -1
  40. package/lib/pendingStateManager.d.ts +4 -13
  41. package/lib/pendingStateManager.d.ts.map +1 -1
  42. package/lib/pendingStateManager.js +130 -160
  43. package/lib/pendingStateManager.js.map +1 -1
  44. package/lib/summarizerClientElection.d.ts +1 -2
  45. package/lib/summarizerClientElection.d.ts.map +1 -1
  46. package/lib/summarizerClientElection.js +3 -30
  47. package/lib/summarizerClientElection.js.map +1 -1
  48. package/lib/summarizerTypes.d.ts +0 -4
  49. package/lib/summarizerTypes.d.ts.map +1 -1
  50. package/lib/summarizerTypes.js.map +1 -1
  51. package/package.json +34 -16
  52. package/src/containerRuntime.ts +116 -84
  53. package/src/dataStoreContext.ts +12 -6
  54. package/src/opLifecycle/outbox.ts +0 -2
  55. package/src/packageVersion.ts +1 -1
  56. package/src/pendingStateManager.ts +146 -187
  57. package/src/summarizerClientElection.ts +1 -30
  58. package/src/summarizerTypes.ts +0 -4
@@ -55,7 +55,6 @@ export const DefaultSummaryConfiguration = {
55
55
  maxAckWaitTime: 10 * 60 * 1000,
56
56
  maxOpsSinceLastSummary: 7000,
57
57
  initialSummarizerDelayMs: 5 * 1000,
58
- summarizerClientElection: false,
59
58
  nonRuntimeOpWeight: 0.1,
60
59
  runtimeOpWeight: 1.0,
61
60
  nonRuntimeHeuristicThreshold: 20,
@@ -164,6 +163,14 @@ export class ContainerRuntime extends TypedEventEmitter {
164
163
  this.flushMicroTaskExists = false;
165
164
  this.savedOps = [];
166
165
  this.consecutiveReconnects = 0;
166
+ this.ensureNoDataModelChangesCalls = 0;
167
+ /**
168
+ * Tracks the number of detected reentrant ops to report,
169
+ * in order to self-throttle the telemetry events.
170
+ *
171
+ * This should be removed as part of ADO:2322
172
+ */
173
+ this.opReentryCallsToReport = 5;
167
174
  this._disposed = false;
168
175
  this.emitDirtyDocumentEvent = true;
169
176
  this.defaultTelemetrySignalSampleCount = 100;
@@ -236,7 +243,6 @@ export class ContainerRuntime extends TypedEventEmitter {
236
243
  && this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableOpReentryCheck") !== true;
237
244
  this.summariesDisabled = this.isSummariesDisabled();
238
245
  this.heuristicsDisabled = this.isHeuristicsDisabled();
239
- this.summarizerClientElectionEnabled = this.isSummarizerClientElectionEnabled();
240
246
  this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
241
247
  this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
242
248
  this.maxConsecutiveReconnects =
@@ -302,7 +308,6 @@ export class ContainerRuntime extends TypedEventEmitter {
302
308
  clientId: () => this.clientId,
303
309
  close: this.closeFn,
304
310
  connected: () => this.connected,
305
- flush: this.flush.bind(this),
306
311
  reSubmit: this.reSubmit.bind(this),
307
312
  rollback: this.rollback.bind(this),
308
313
  orderSequentially: this.orderSequentially.bind(this),
@@ -339,7 +344,7 @@ export class ContainerRuntime extends TypedEventEmitter {
339
344
  const orderedClientLogger = ChildLogger.create(this.logger, "OrderedClientElection");
340
345
  const orderedClientCollection = new OrderedClientCollection(orderedClientLogger, this.context.deltaManager, this.context.quorum);
341
346
  const orderedClientElectionForSummarizer = new OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData !== null && electedSummarizerData !== void 0 ? electedSummarizerData : this.context.deltaManager.lastSequenceNumber, SummarizerClientElection.isClientEligible);
342
- this.summarizerClientElection = new SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, this.maxOpsSinceLastSummary, this.summarizerClientElectionEnabled);
347
+ this.summarizerClientElection = new SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, this.maxOpsSinceLastSummary);
343
348
  if (this.context.clientDetails.type === summarizerClientType) {
344
349
  this._summarizer = new Summarizer("/_summarizer", this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.handleContext, this.summaryCollection, async (runtime) => RunWhileConnectedCoordinator.create(runtime));
345
350
  }
@@ -401,6 +406,7 @@ export class ContainerRuntime extends TypedEventEmitter {
401
406
  get IContainerRuntime() { return this; }
402
407
  get IFluidRouter() { return this; }
403
408
  /**
409
+ * @deprecated - use loadRuntime instead.
404
410
  * Load the stores from a snapshot and returns the runtime.
405
411
  * @param context - Context of the container.
406
412
  * @param registryEntries - Mapping to the stores.
@@ -411,7 +417,35 @@ export class ContainerRuntime extends TypedEventEmitter {
411
417
  * allows mixin classes to leverage this method to define their own async initializer.
412
418
  */
413
419
  static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing, containerRuntimeCtor = ContainerRuntime) {
420
+ let existingFlag = true;
421
+ if (!existing) {
422
+ existingFlag = false;
423
+ }
424
+ return this.loadRuntime({
425
+ context,
426
+ registryEntries,
427
+ existing: existingFlag,
428
+ requestHandler,
429
+ runtimeOptions,
430
+ containerScope,
431
+ containerRuntimeCtor,
432
+ });
433
+ }
434
+ /**
435
+ * Load the stores from a snapshot and returns the runtime.
436
+ * @param params - An object housing the runtime properties:
437
+ * - context - Context of the container.
438
+ * - registryEntries - Mapping to the stores.
439
+ * - existing - When loading from an existing snapshot
440
+ * - requestHandler - Request handlers for the container runtime
441
+ * - runtimeOptions - Additional options to be passed to the runtime
442
+ * - containerScope - runtime services provided with context
443
+ * - containerRuntimeCtor - Constructor to use to create the ContainerRuntime instance.
444
+ * This allows mixin classes to leverage this method to define their own async initializer.
445
+ */
446
+ static async loadRuntime(params) {
414
447
  var _a, _b, _c, _d;
448
+ const { context, registryEntries, existing, requestHandler, runtimeOptions = {}, containerScope = {}, containerRuntimeCtor = ContainerRuntime } = params;
415
449
  // If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
416
450
  // back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
417
451
  const backCompatContext = context;
@@ -542,6 +576,23 @@ export class ContainerRuntime extends TypedEventEmitter {
542
576
  get IFluidHandleContext() {
543
577
  return this.handleContext;
544
578
  }
579
+ /**
580
+ * Invokes the given callback and expects that no ops are submitted
581
+ * until execution finishes. If an op is submitted, an error will be raised.
582
+ *
583
+ * Can be disabled by feature gate `Fluid.ContainerRuntime.DisableOpReentryCheck`
584
+ *
585
+ * @param callback - the callback to be invoked
586
+ */
587
+ ensureNoDataModelChanges(callback) {
588
+ this.ensureNoDataModelChangesCalls++;
589
+ try {
590
+ return callback();
591
+ }
592
+ finally {
593
+ this.ensureNoDataModelChangesCalls--;
594
+ }
595
+ }
545
596
  get connected() {
546
597
  return this._connected;
547
598
  }
@@ -556,42 +607,12 @@ export class ContainerRuntime extends TypedEventEmitter {
556
607
  return this._summarizer;
557
608
  }
558
609
  isSummariesDisabled() {
559
- // back-compat: disableSummaries was moved from ISummaryRuntimeOptions
560
- // to ISummaryConfiguration in 0.60.
561
- if (this.runtimeOptions.summaryOptions.disableSummaries === true) {
562
- return true;
563
- }
564
610
  return this.summaryConfiguration.state === "disabled";
565
611
  }
566
612
  isHeuristicsDisabled() {
567
- var _a;
568
- // back-compat: disableHeuristics was moved from ISummarizerOptions
569
- // to ISummaryConfiguration in 0.60.
570
- if (((_a = this.runtimeOptions.summaryOptions.summarizerOptions) === null || _a === void 0 ? void 0 : _a.disableHeuristics) === true) {
571
- return true;
572
- }
573
613
  return this.summaryConfiguration.state === "disableHeuristics";
574
614
  }
575
- isSummarizerClientElectionEnabled() {
576
- var _a;
577
- if (this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) {
578
- return (_a = this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) !== null && _a !== void 0 ? _a : true;
579
- }
580
- // back-compat: summarizerClientElection was moved from ISummaryRuntimeOptions
581
- // to ISummaryConfiguration in 0.60.
582
- if (this.runtimeOptions.summaryOptions.summarizerClientElection === true) {
583
- return true;
584
- }
585
- return this.summaryConfiguration.state !== "disabled"
586
- ? this.summaryConfiguration.summarizerClientElection === true
587
- : false;
588
- }
589
615
  getMaxOpsSinceLastSummary() {
590
- // back-compat: maxOpsSinceLastSummary was moved from ISummaryRuntimeOptions
591
- // to ISummaryConfiguration in 0.60.
592
- if (this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary !== undefined) {
593
- return this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary;
594
- }
595
616
  return this.summaryConfiguration.state !== "disabled"
596
617
  ? this.summaryConfiguration.maxOpsSinceLastSummary
597
618
  : 0;
@@ -1092,7 +1113,8 @@ export class ContainerRuntime extends TypedEventEmitter {
1092
1113
  finally {
1093
1114
  this._orderSequentiallyCalls--;
1094
1115
  }
1095
- if (this.flushMode === FlushMode.Immediate && this._orderSequentiallyCalls === 0) {
1116
+ // We don't flush on TurnBased since we expect all messages in the same JS turn to be part of the same batch
1117
+ if (this.flushMode !== FlushMode.TurnBased && this._orderSequentiallyCalls === 0) {
1096
1118
  this.flush();
1097
1119
  }
1098
1120
  return result;
@@ -1616,6 +1638,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1616
1638
  }
1617
1639
  submit(type, contents, localOpMetadata = undefined, metadata = undefined) {
1618
1640
  this.verifyNotClosed();
1641
+ this.verifyCanSubmitOps();
1619
1642
  // There should be no ops in detached container state!
1620
1643
  assert(this.attachState !== AttachState.Detached, 0x132 /* "sending ops in detached container" */);
1621
1644
  const deserializedContent = { type, contents };
@@ -1697,6 +1720,32 @@ export class ContainerRuntime extends TypedEventEmitter {
1697
1720
  throw new Error("Runtime is closed");
1698
1721
  }
1699
1722
  }
1723
+ verifyCanSubmitOps() {
1724
+ if (this.ensureNoDataModelChangesCalls > 0) {
1725
+ const errorMessage = "Op was submitted from within a `ensureNoDataModelChanges` callback";
1726
+ if (this.opReentryCallsToReport > 0) {
1727
+ this.mc.logger.sendTelemetryEvent({ eventName: "OpReentry" },
1728
+ // We need to capture the call stack in order to inspect the source of this usage pattern
1729
+ new UsageError(errorMessage));
1730
+ this.opReentryCallsToReport--;
1731
+ }
1732
+ // Creating ops while processing ops can lead
1733
+ // to undefined behavior and events observed in the wrong order.
1734
+ // For example, we have two callbacks registered for a DDS, A and B.
1735
+ // Then if on change #1 callback A creates change #2, the invocation flow will be:
1736
+ //
1737
+ // A because of #1
1738
+ // A because of #2
1739
+ // B because of #2
1740
+ // B because of #1
1741
+ //
1742
+ // The runtime must enforce op coherence by not allowing ops to be submitted
1743
+ // while ops are being processed.
1744
+ if (this.enableOpReentryCheck) {
1745
+ throw new UsageError(errorMessage);
1746
+ }
1747
+ }
1748
+ }
1700
1749
  /**
1701
1750
  * Finds the right store and asks it to resubmit the message. This typically happens when we
1702
1751
  * reconnect and there are pending messages.
@@ -1849,13 +1898,13 @@ export class ContainerRuntime extends TypedEventEmitter {
1849
1898
  if (!((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad)) {
1850
1899
  throw new UsageError("can't get state when offline load disabled");
1851
1900
  }
1901
+ if (this._orderSequentiallyCalls !== 0) {
1902
+ throw new UsageError("can't get state during orderSequentially");
1903
+ }
1852
1904
  // Flush pending batch.
1853
1905
  // getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
1854
1906
  // to close current batch.
1855
1907
  this.flush();
1856
- if (this._orderSequentiallyCalls !== 0) {
1857
- throw new UsageError("can't get state during orderSequentially");
1858
- }
1859
1908
  const previousPendingState = this.context.pendingLocalState;
1860
1909
  if (previousPendingState) {
1861
1910
  return {