@fluidframework/container-loader 2.0.0-rc.4.0.3 → 2.0.0-rc.4.0.4

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.
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export declare const pkgName = "@fluidframework/container-loader";
8
- export declare const pkgVersion = "2.0.0-rc.4.0.3";
8
+ export declare const pkgVersion = "2.0.0-rc.4.0.4";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export const pkgName = "@fluidframework/container-loader";
8
- export const pkgVersion = "2.0.0-rc.4.0.3";
8
+ export const pkgVersion = "2.0.0-rc.4.0.4";
9
9
  //# sourceMappingURL=packageVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,kCAAkC,CAAC;AAC1D,MAAM,CAAC,MAAM,UAAU,GAAG,gBAAgB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/container-loader\";\nexport const pkgVersion = \"2.0.0-rc.4.0.3\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,kCAAkC,CAAC;AAC1D,MAAM,CAAC,MAAM,UAAU,GAAG,gBAAgB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/container-loader\";\nexport const pkgVersion = \"2.0.0-rc.4.0.4\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/container-loader",
3
- "version": "2.0.0-rc.4.0.3",
3
+ "version": "2.0.0-rc.4.0.4",
4
4
  "description": "Fluid container loader",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -117,15 +117,15 @@
117
117
  "temp-directory": "nyc/.nyc_output"
118
118
  },
119
119
  "dependencies": {
120
- "@fluid-internal/client-utils": ">=2.0.0-rc.4.0.3 <2.0.0-rc.4.1.0",
121
- "@fluidframework/container-definitions": ">=2.0.0-rc.4.0.3 <2.0.0-rc.4.1.0",
122
- "@fluidframework/core-interfaces": ">=2.0.0-rc.4.0.3 <2.0.0-rc.4.1.0",
123
- "@fluidframework/core-utils": ">=2.0.0-rc.4.0.3 <2.0.0-rc.4.1.0",
124
- "@fluidframework/driver-definitions": ">=2.0.0-rc.4.0.3 <2.0.0-rc.4.1.0",
125
- "@fluidframework/driver-utils": ">=2.0.0-rc.4.0.3 <2.0.0-rc.4.1.0",
120
+ "@fluid-internal/client-utils": ">=2.0.0-rc.4.0.4 <2.0.0-rc.4.1.0",
121
+ "@fluidframework/container-definitions": ">=2.0.0-rc.4.0.4 <2.0.0-rc.4.1.0",
122
+ "@fluidframework/core-interfaces": ">=2.0.0-rc.4.0.4 <2.0.0-rc.4.1.0",
123
+ "@fluidframework/core-utils": ">=2.0.0-rc.4.0.4 <2.0.0-rc.4.1.0",
124
+ "@fluidframework/driver-definitions": ">=2.0.0-rc.4.0.4 <2.0.0-rc.4.1.0",
125
+ "@fluidframework/driver-utils": ">=2.0.0-rc.4.0.4 <2.0.0-rc.4.1.0",
126
126
  "@fluidframework/protocol-base": "^4.0.0",
127
127
  "@fluidframework/protocol-definitions": "^3.2.0",
128
- "@fluidframework/telemetry-utils": ">=2.0.0-rc.4.0.3 <2.0.0-rc.4.1.0",
128
+ "@fluidframework/telemetry-utils": ">=2.0.0-rc.4.0.4 <2.0.0-rc.4.1.0",
129
129
  "@ungap/structured-clone": "^1.2.0",
130
130
  "debug": "^4.3.4",
131
131
  "double-ended-queue": "^2.1.0-0",
@@ -134,8 +134,8 @@
134
134
  "devDependencies": {
135
135
  "@arethetypeswrong/cli": "^0.15.2",
136
136
  "@biomejs/biome": "^1.6.2",
137
- "@fluid-internal/mocha-test-setup": ">=2.0.0-rc.4.0.3 <2.0.0-rc.4.1.0",
138
- "@fluid-private/test-loader-utils": ">=2.0.0-rc.4.0.3 <2.0.0-rc.4.1.0",
137
+ "@fluid-internal/mocha-test-setup": ">=2.0.0-rc.4.0.4 <2.0.0-rc.4.1.0",
138
+ "@fluid-private/test-loader-utils": ">=2.0.0-rc.4.0.4 <2.0.0-rc.4.1.0",
139
139
  "@fluid-tools/build-cli": "^0.38.0",
140
140
  "@fluidframework/build-common": "^2.0.3",
141
141
  "@fluidframework/build-tools": "^0.38.0",
@@ -11,8 +11,8 @@ import { IClient, ISequencedClient } from "@fluidframework/protocol-definitions"
11
11
  import {
12
12
  type TelemetryEventCategory,
13
13
  ITelemetryLoggerExt,
14
+ MonitoringContext,
14
15
  PerformanceEvent,
15
- loggerToMonitoringContext,
16
16
  } from "@fluidframework/telemetry-utils/internal";
17
17
 
18
18
  import { CatchUpMonitor, ICatchUpMonitor } from "./catchUpMonitor.js";
@@ -31,6 +31,7 @@ const JoinSignalTimeoutMs = 10000;
31
31
  /** Constructor parameter type for passing in dependencies needed by the ConnectionStateHandler */
32
32
  export interface IConnectionStateHandlerInputs {
33
33
  logger: ITelemetryLoggerExt;
34
+ mc: MonitoringContext;
34
35
  /** Log to telemetry any change in state, included to Connecting */
35
36
  connectionStateChanged: (
36
37
  value: ConnectionState,
@@ -92,10 +93,10 @@ export function createConnectionStateHandler(
92
93
  deltaManager: IDeltaManager<any, any>,
93
94
  clientId?: string,
94
95
  ) {
95
- const mc = loggerToMonitoringContext(inputs.logger);
96
+ const config = inputs.mc.config;
96
97
  return createConnectionStateHandlerCore(
97
- mc.config.getBoolean("Fluid.Container.DisableCatchUpBeforeDeclaringConnected") !== true, // connectedRaisedWhenCaughtUp
98
- mc.config.getBoolean("Fluid.Container.DisableJoinSignalWait") !== true, // readClientsWaitForJoinSignal
98
+ config.getBoolean("Fluid.Container.DisableCatchUpBeforeDeclaringConnected") !== true, // connectedRaisedWhenCaughtUp
99
+ config.getBoolean("Fluid.Container.DisableJoinSignalWait") !== true, // readClientsWaitForJoinSignal
99
100
  inputs,
100
101
  deltaManager,
101
102
  clientId,
@@ -189,6 +190,9 @@ class ConnectionStateHandlerPassThrough
189
190
  public get logger() {
190
191
  return this.inputs.logger;
191
192
  }
193
+ public get mc() {
194
+ return this.inputs.mc;
195
+ }
192
196
  public connectionStateChanged(
193
197
  value: ConnectionState,
194
198
  oldState: ConnectionState,
@@ -469,18 +473,6 @@ class ConnectionStateHandler implements IConnectionStateHandler {
469
473
  });
470
474
  }
471
475
  this.applyForConnectedState("addMemberEvent");
472
- } else if (clientId === this.clientId) {
473
- // If we see our clientId and it's not also our pending ID, it's our own join op
474
- // being replayed, so start the timer in case our previous client is still in quorum
475
- assert(
476
- !this.waitingForLeaveOp,
477
- 0x5d2 /* Unexpected join op with current clientId while waiting */,
478
- );
479
- assert(
480
- this.connectionState !== ConnectionState.Connected,
481
- 0x5d3 /* Unexpected join op with current clientId while connected */,
482
- );
483
- this.prevClientLeftTimer.restart();
484
476
  }
485
477
  }
486
478
 
@@ -528,7 +520,7 @@ class ConnectionStateHandler implements IConnectionStateHandler {
528
520
 
529
521
  private receivedRemoveMemberEvent(clientId: string) {
530
522
  // If the client which has left was us, then finish the timer.
531
- if (this.clientId === clientId) {
523
+ if (this.clientId === clientId && this.waitingForLeaveOp) {
532
524
  this.prevClientLeftTimer.clear();
533
525
  this.applyForConnectedState("removeMemberEvent");
534
526
  }
@@ -737,8 +729,24 @@ class ConnectionStateHandler implements IConnectionStateHandler {
737
729
  this.receivedAddMemberEvent(this.pendingClientId!);
738
730
  }
739
731
 
732
+ assert(
733
+ !this.waitingForLeaveOp,
734
+ "leave timer can't be set as we have not had access to quorum",
735
+ );
736
+
737
+ // This check is required for scenario of loading container from pending state, and ensuring there is no way
738
+ // old clientId is still in the quorum (very unlikely, but you never know)
740
739
  // if we have a clientId from a previous container we need to wait for its leave message
741
- if (this.clientId !== undefined && this.hasMember(this.clientId)) {
740
+ // This mimicks check in setConnectionState()
741
+ // Note that we are not consulting this.handler.shouldClientJoinWrite() here
742
+ // It could produce wrong results for stashed ops were never sent to Loader yet, and if this check
743
+ // makes determination only on that (and not uses "dirty" events), then it can produce wrong result.
744
+ // In most cases it does not matter, as this client already left quorum. But in really unfortunate case,
745
+ // we might wait even if we could avoid such wait.
746
+ if (
747
+ this._clientId !== undefined &&
748
+ protocol.quorum?.getMember(this._clientId) !== undefined
749
+ ) {
742
750
  this.prevClientLeftTimer.restart();
743
751
  }
744
752
  }
package/src/container.ts CHANGED
@@ -92,6 +92,7 @@ import {
92
92
  normalizeError,
93
93
  raiseConnectedEvent,
94
94
  wrapError,
95
+ loggerToMonitoringContext,
95
96
  } from "@fluidframework/telemetry-utils/internal";
96
97
  import structuredClone from "@ungap/structured-clone";
97
98
  import { v4 as uuid } from "uuid";
@@ -525,6 +526,15 @@ export class Container
525
526
  0x969 /* not connected yet */,
526
527
  );
527
528
 
529
+ // Track membership changes and update connection state accordingly
530
+ // We do this call here, instead of doing it in initializeProtocolState() due to pendingLocalState.
531
+ // When we load from stashed state, we let connectionStateHandler know about clientId from previous container instance.
532
+ // But we will play trailing ops from snapshot, including potentially playing join & leave ops for that same clientId!
533
+ // In other words, if connectionStateHandler has access to Quorum early in load sequence, it will see events (in stashed ops mode)
534
+ // in the order that is not possible in real life, that it may not expect.
535
+ // Ideally, we should supply pendingLocalState?.clientId here as well, not in constructor, but it does not matter (at least today)
536
+ this.connectionStateHandler.initProtocol(this.protocolHandler);
537
+
528
538
  // Propagate current connection state through the system.
529
539
  const readonly = this.readOnlyInfo.readonly ?? false;
530
540
  // This call does not look like needed any more, with delaying all connection-related events past loaded phase.
@@ -864,6 +874,9 @@ export class Container
864
874
  this.connectionStateHandler = createConnectionStateHandler(
865
875
  {
866
876
  logger: this.mc.logger,
877
+ // WARNING: logger on this context should not including getters like containerConnectionState above (on this.subLogger),
878
+ // as that will result in attempt to dereference this.connectionStateHandler from this call while it's still undefined.
879
+ mc: loggerToMonitoringContext(subLogger),
867
880
  connectionStateChanged: (value, oldState, reason) => {
868
881
  this.logConnectionStateChangeTelemetry(value, oldState, reason);
869
882
  if (this.loaded) {
@@ -1867,9 +1880,6 @@ export class Container
1867
1880
  protocolLogger.sendErrorEvent(error);
1868
1881
  });
1869
1882
 
1870
- // Track membership changes and update connection state accordingly
1871
- this.connectionStateHandler.initProtocol(protocol);
1872
-
1873
1883
  protocol.quorum.on("addProposal", (proposal: ISequencedProposal) => {
1874
1884
  if (proposal.key === "code" || proposal.key === "code2") {
1875
1885
  this.emit("codeDetailsProposed", proposal.value, proposal);
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-loader";
9
- export const pkgVersion = "2.0.0-rc.4.0.3";
9
+ export const pkgVersion = "2.0.0-rc.4.0.4";