@fluidframework/container-loader 0.52.1 → 0.53.0-46105
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.
- package/dist/connectionStateHandler.d.ts +1 -0
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +6 -0
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +0 -21
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +96 -123
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +1 -0
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +4 -0
- package/dist/containerContext.js.map +1 -1
- package/dist/deltaManager.d.ts +0 -7
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +31 -32
- package/dist/deltaManager.js.map +1 -1
- package/dist/loader.d.ts +5 -0
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +6 -1
- package/dist/loader.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +1 -0
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +6 -0
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +0 -21
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +97 -124
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +1 -0
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +4 -0
- package/lib/containerContext.js.map +1 -1
- package/lib/deltaManager.d.ts +0 -7
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +31 -32
- package/lib/deltaManager.js.map +1 -1
- package/lib/loader.d.ts +5 -0
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +6 -1
- package/lib/loader.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/package.json +8 -8
- package/src/connectionStateHandler.ts +8 -0
- package/src/container.ts +126 -148
- package/src/containerContext.ts +4 -0
- package/src/deltaManager.ts +35 -34
- package/src/loader.ts +29 -19
- package/src/packageVersion.ts +1 -1
|
@@ -34,6 +34,7 @@ export declare class ConnectionStateHandler {
|
|
|
34
34
|
constructor(handler: IConnectionStateHandler, logger: ITelemetryLogger);
|
|
35
35
|
private startJoinOpTimer;
|
|
36
36
|
private stopJoinOpTimer;
|
|
37
|
+
dispose(): void;
|
|
37
38
|
receivedAddMemberEvent(clientId: string): void;
|
|
38
39
|
private applyForConnectedState;
|
|
39
40
|
receivedRemoveMemberEvent(clientId: string): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connectionStateHandler.d.ts","sourceRoot":"","sources":["../src/connectionStateHandler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAGxF,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,uBAAuB;IACpC,eAAe,EAAE,MAAM,iBAAiB,GAAG,SAAS,CAAC;IACrD,iCAAiC,EAC7B,CAAC,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IAC7F,qBAAqB,EAAE,MAAM,OAAO,CAAC;IACrC,sBAAsB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,sBAAsB,EAAE,MAAM,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC3D,cAAc,CAAC,EAAE,OAAO,CAAC;CAC5B;AAID,qBAAa,sBAAsB;IA0B3B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IA1B3B,OAAO,CAAC,gBAAgB,CAAgC;IACxD,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAQ;IAC5C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IAEpC,OAAO,CAAC,SAAS,CAA+B;IAEhD,IAAW,eAAe,IAAI,eAAe,CAE5C;IAED,IAAW,SAAS,IAAI,OAAO,CAE9B;IAED,IAAW,QAAQ,IAAI,MAAM,GAAG,SAAS,CAExC;IAED,IAAW,eAAe,IAAI,MAAM,GAAG,SAAS,CAE/C;gBAGoB,OAAO,EAAE,uBAAuB,EAChC,MAAM,EAAE,gBAAgB;
|
|
1
|
+
{"version":3,"file":"connectionStateHandler.d.ts","sourceRoot":"","sources":["../src/connectionStateHandler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAGxF,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,uBAAuB;IACpC,eAAe,EAAE,MAAM,iBAAiB,GAAG,SAAS,CAAC;IACrD,iCAAiC,EAC7B,CAAC,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IAC7F,qBAAqB,EAAE,MAAM,OAAO,CAAC;IACrC,sBAAsB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,sBAAsB,EAAE,MAAM,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC3D,cAAc,CAAC,EAAE,OAAO,CAAC;CAC5B;AAID,qBAAa,sBAAsB;IA0B3B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IA1B3B,OAAO,CAAC,gBAAgB,CAAgC;IACxD,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAQ;IAC5C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IAEpC,OAAO,CAAC,SAAS,CAA+B;IAEhD,IAAW,eAAe,IAAI,eAAe,CAE5C;IAED,IAAW,SAAS,IAAI,OAAO,CAE9B;IAED,IAAW,QAAQ,IAAI,MAAM,GAAG,SAAS,CAExC;IAED,IAAW,eAAe,IAAI,MAAM,GAAG,SAAS,CAE/C;gBAGoB,OAAO,EAAE,uBAAuB,EAChC,MAAM,EAAE,gBAAgB;IA2B7C,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,eAAe;IAKhB,OAAO;IAKP,sBAAsB,CAAC,QAAQ,EAAE,MAAM;IAsB9C,OAAO,CAAC,sBAAsB;IA4BvB,yBAAyB,CAAC,QAAQ,EAAE,MAAM;IAQ1C,uBAAuB,CAAC,MAAM,EAAE,MAAM;IAOtC,oBAAoB,CACvB,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,kBAAkB;IAgC/B,OAAO,CAAC,kBAAkB;CAuD7B"}
|
|
@@ -18,6 +18,7 @@ class ConnectionStateHandler {
|
|
|
18
18
|
this.prevClientLeftTimer = new common_utils_1.Timer((_a =
|
|
19
19
|
// Default is 90 sec for which we are going to wait for its own "leave" message.
|
|
20
20
|
this.handler.maxClientLeaveWaitTime) !== null && _a !== void 0 ? _a : 90000, () => {
|
|
21
|
+
common_utils_1.assert(!this.connected, 0x2ac /* "Connected when timeout waiting for leave from previous session fired!" */);
|
|
21
22
|
this.applyForConnectedState("timeout");
|
|
22
23
|
});
|
|
23
24
|
// Based on recent data, it looks like majority of cases where we get stuck are due to really slow or
|
|
@@ -51,6 +52,10 @@ class ConnectionStateHandler {
|
|
|
51
52
|
common_utils_1.assert(this.joinOpTimer.hasTimer, 0x235 /* "no joinOpTimer" */);
|
|
52
53
|
this.joinOpTimer.clear();
|
|
53
54
|
}
|
|
55
|
+
dispose() {
|
|
56
|
+
common_utils_1.assert(!this.joinOpTimer.hasTimer, 0x2a5 /* "join timer" */);
|
|
57
|
+
this.prevClientLeftTimer.clear();
|
|
58
|
+
}
|
|
54
59
|
receivedAddMemberEvent(clientId) {
|
|
55
60
|
// This is the only one that requires the pending client ID
|
|
56
61
|
if (clientId === this.pendingClientId) {
|
|
@@ -134,6 +139,7 @@ class ConnectionStateHandler {
|
|
|
134
139
|
// Note that we might be still initializing quorum - connection is established proactively on load!
|
|
135
140
|
if ((protocolHandler !== undefined && protocolHandler.quorum.getMember(details.clientId) !== undefined)
|
|
136
141
|
|| connectionMode === "read") {
|
|
142
|
+
common_utils_1.assert(!this.prevClientLeftTimer.hasTimer, 0x2a6 /* "there should be no timer for 'read' connections" */);
|
|
137
143
|
this.setConnectionState(container_1.ConnectionState.Connected);
|
|
138
144
|
}
|
|
139
145
|
else if (connectionMode === "write") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connectionStateHandler.js","sourceRoot":"","sources":["../src/connectionStateHandler.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { IConnectionDetails } from \"@fluidframework/container-definitions\";\nimport { ProtocolOpHandler } from \"@fluidframework/protocol-base\";\nimport { ConnectionMode, ISequencedClient } from \"@fluidframework/protocol-definitions\";\nimport { PerformanceEvent } from \"@fluidframework/telemetry-utils\";\nimport { assert, Timer } from \"@fluidframework/common-utils\";\nimport { ConnectionState } from \"./container\";\n\nexport interface IConnectionStateHandler {\n protocolHandler: () => ProtocolOpHandler | undefined,\n logConnectionStateChangeTelemetry:\n (value: ConnectionState, oldState: ConnectionState, reason?: string | undefined) => void,\n shouldClientJoinWrite: () => boolean,\n maxClientLeaveWaitTime: number | undefined,\n logConnectionIssue: (eventName: string) => void,\n connectionStateChanged: () => void,\n}\n\nexport interface ILocalSequencedClient extends ISequencedClient {\n shouldHaveLeft?: boolean;\n}\n\nconst JoinOpTimer = 45000;\n\nexport class ConnectionStateHandler {\n private _connectionState = ConnectionState.Disconnected;\n private _pendingClientId: string | undefined;\n private _clientId: string | undefined;\n private readonly prevClientLeftTimer: Timer;\n private readonly joinOpTimer: Timer;\n\n private waitEvent: PerformanceEvent | undefined;\n\n public get connectionState(): ConnectionState {\n return this._connectionState;\n }\n\n public get connected(): boolean {\n return this.connectionState === ConnectionState.Connected;\n }\n\n public get clientId(): string | undefined {\n return this._clientId;\n }\n\n public get pendingClientId(): string | undefined {\n return this._pendingClientId;\n }\n\n constructor(\n private readonly handler: IConnectionStateHandler,\n private readonly logger: ITelemetryLogger,\n ) {\n this.prevClientLeftTimer = new Timer(\n // Default is 90 sec for which we are going to wait for its own \"leave\" message.\n this.handler.maxClientLeaveWaitTime ?? 90000,\n () => {\n this.applyForConnectedState(\"timeout\");\n },\n );\n\n // Based on recent data, it looks like majority of cases where we get stuck are due to really slow or\n // timing out ops fetches. So attempt recovery infrequently. Also fetch uses 30 second timeout, so\n // if retrying fixes the problem, we should not see these events.\n this.joinOpTimer = new Timer(\n JoinOpTimer,\n () => {\n // I've observed timer firing within couple ms from disconnect event, looks like\n // queued timer callback is not cancelled if timer is cancelled while callback sits in the queue.\n if (this.connectionState === ConnectionState.Connecting) {\n this.handler.logConnectionIssue(\"NoJoinOp\");\n }\n },\n );\n }\n\n private startJoinOpTimer() {\n assert(!this.joinOpTimer.hasTimer, 0x234 /* \"has joinOpTimer\" */);\n this.joinOpTimer.start();\n }\n\n private stopJoinOpTimer() {\n assert(this.joinOpTimer.hasTimer, 0x235 /* \"no joinOpTimer\" */);\n this.joinOpTimer.clear();\n }\n\n public receivedAddMemberEvent(clientId: string) {\n // This is the only one that requires the pending client ID\n if (clientId === this.pendingClientId) {\n if (this.joinOpTimer.hasTimer) {\n this.stopJoinOpTimer();\n } else {\n // timer has already fired, meaning it took too long to get join on.\n // Record how long it actually took to recover.\n this.handler.logConnectionIssue(\"ReceivedJoinOp\");\n }\n // Start the event in case we are waiting for leave or timeout.\n if (this.prevClientLeftTimer.hasTimer) {\n this.waitEvent = PerformanceEvent.start(this.logger, {\n eventName: \"WaitBeforeClientLeave\",\n waitOnClientId: this._clientId,\n hadOutstandingOps: this.handler.shouldClientJoinWrite(),\n });\n }\n this.applyForConnectedState(\"addMemberEvent\");\n }\n }\n\n private applyForConnectedState(source: \"removeMemberEvent\" | \"addMemberEvent\" | \"timeout\") {\n const protocolHandler = this.handler.protocolHandler();\n assert(protocolHandler !== undefined, 0x236 /* \"In all cases it should be already installed\" */);\n // Move to connected state only if we are in Connecting state, we have seen our join op\n // and there is no timer running which means we are not waiting for previous client to leave\n // or timeout has occured while doing so.\n if (this.pendingClientId !== this.clientId\n && this.pendingClientId !== undefined\n && protocolHandler.quorum.getMember(this.pendingClientId) !== undefined\n && !this.prevClientLeftTimer.hasTimer\n ) {\n this.waitEvent?.end({ source });\n this.setConnectionState(ConnectionState.Connected);\n } else {\n // Adding this event temporarily so that we can get help debugging if something goes wrong.\n this.logger.sendTelemetryEvent({\n eventName: \"connectedStateRejected\",\n category: source === \"timeout\" ? \"error\" : \"generic\",\n source,\n pendingClientId: this.pendingClientId,\n clientId: this.clientId,\n hasTimer: this.prevClientLeftTimer.hasTimer,\n inQuorum: protocolHandler !== undefined && this.pendingClientId !== undefined\n && protocolHandler.quorum.getMember(this.pendingClientId) !== undefined,\n });\n }\n }\n\n public receivedRemoveMemberEvent(clientId: string) {\n // If the client which has left was us, then finish the timer.\n if (this.clientId === clientId) {\n this.prevClientLeftTimer.clear();\n this.applyForConnectedState(\"removeMemberEvent\");\n }\n }\n\n public receivedDisconnectEvent(reason: string) {\n if (this.joinOpTimer.hasTimer) {\n this.stopJoinOpTimer();\n }\n this.setConnectionState(ConnectionState.Disconnected, reason);\n }\n\n public receivedConnectEvent(\n connectionMode: ConnectionMode,\n details: IConnectionDetails,\n ) {\n const oldState = this._connectionState;\n this._connectionState = ConnectionState.Connecting;\n\n // Stash the clientID to detect when transitioning from connecting (socket.io channel open) to connected\n // (have received the join message for the client ID)\n // This is especially important in the reconnect case. It's possible there could be outstanding\n // ops sent by this client, so we should keep the old client id until we see our own client's\n // join message. after we see the join message for out new connection with our new client id,\n // we know there can no longer be outstanding ops that we sent with the previous client id.\n this._pendingClientId = details.clientId;\n\n // Report telemetry after we set client id, but before transitioning to Connected state below!\n this.handler.logConnectionStateChangeTelemetry(ConnectionState.Connecting, oldState);\n\n const protocolHandler = this.handler.protocolHandler();\n // Check if we already processed our own join op through delta storage!\n // we are fetching ops from storage in parallel to connecting to ordering service\n // Given async processes, it's possible that we have already processed our own join message before\n // connection was fully established.\n // Note that we might be still initializing quorum - connection is established proactively on load!\n if ((protocolHandler !== undefined && protocolHandler.quorum.getMember(details.clientId) !== undefined)\n || connectionMode === \"read\"\n ) {\n this.setConnectionState(ConnectionState.Connected);\n } else if (connectionMode === \"write\") {\n this.startJoinOpTimer();\n }\n }\n\n private setConnectionState(value: ConnectionState.Disconnected, reason: string);\n private setConnectionState(value: ConnectionState.Connected);\n private setConnectionState(value: ConnectionState, reason?: string) {\n if (this.connectionState === value) {\n // Already in the desired state - exit early\n this.logger.sendErrorEvent({ eventName: \"setConnectionStateSame\", value });\n return;\n }\n\n const oldState = this._connectionState;\n this._connectionState = value;\n const quorum = this.handler.protocolHandler()?.quorum;\n let client: ILocalSequencedClient | undefined;\n if (this._clientId !== undefined) {\n client = quorum?.getMember(this._clientId);\n }\n if (value === ConnectionState.Connected) {\n assert(oldState === ConnectionState.Connecting,\n 0x1d8 /* \"Should only transition from Connecting state\" */);\n // Mark our old client should have left in the quorum if it's still there\n if (client !== undefined) {\n client.shouldHaveLeft = true;\n }\n this._clientId = this.pendingClientId;\n } else if (value === ConnectionState.Disconnected) {\n // Important as we process our own joinSession message through delta request\n this._pendingClientId = undefined;\n // Only wait for \"leave\" message if the connected client exists in the quorum because only the write\n // client will exist in the quorum and only for those clients we will receive \"removeMember\" event and\n // the client has some unacked ops.\n // Also server would not accept ops from read client. Also check if the timer is not already running as\n // we could receive \"Disconnected\" event multiple times without getting connected and in that case we\n // don't want to reset the timer as we still want to wait on original client which started this timer.\n if (client !== undefined\n && this.handler.shouldClientJoinWrite()\n && this.prevClientLeftTimer.hasTimer === false\n ) {\n this.prevClientLeftTimer.restart();\n } else {\n // Adding this event temporarily so that we can get help debugging if something goes wrong.\n this.logger.sendTelemetryEvent({\n eventName: \"noWaitOnDisconnected\",\n inQuorum: client !== undefined,\n hasTimer: this.prevClientLeftTimer.hasTimer,\n shouldClientJoinWrite: this.handler.shouldClientJoinWrite(),\n });\n }\n }\n\n // Report transition before we propagate event across layers\n this.handler.logConnectionStateChangeTelemetry(this._connectionState, oldState, reason);\n\n // Propagate event across layers\n this.handler.connectionStateChanged();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"connectionStateHandler.js","sourceRoot":"","sources":["../src/connectionStateHandler.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { IConnectionDetails } from \"@fluidframework/container-definitions\";\nimport { ProtocolOpHandler } from \"@fluidframework/protocol-base\";\nimport { ConnectionMode, ISequencedClient } from \"@fluidframework/protocol-definitions\";\nimport { PerformanceEvent } from \"@fluidframework/telemetry-utils\";\nimport { assert, Timer } from \"@fluidframework/common-utils\";\nimport { ConnectionState } from \"./container\";\n\nexport interface IConnectionStateHandler {\n protocolHandler: () => ProtocolOpHandler | undefined,\n logConnectionStateChangeTelemetry:\n (value: ConnectionState, oldState: ConnectionState, reason?: string | undefined) => void,\n shouldClientJoinWrite: () => boolean,\n maxClientLeaveWaitTime: number | undefined,\n logConnectionIssue: (eventName: string) => void,\n connectionStateChanged: () => void,\n}\n\nexport interface ILocalSequencedClient extends ISequencedClient {\n shouldHaveLeft?: boolean;\n}\n\nconst JoinOpTimer = 45000;\n\nexport class ConnectionStateHandler {\n private _connectionState = ConnectionState.Disconnected;\n private _pendingClientId: string | undefined;\n private _clientId: string | undefined;\n private readonly prevClientLeftTimer: Timer;\n private readonly joinOpTimer: Timer;\n\n private waitEvent: PerformanceEvent | undefined;\n\n public get connectionState(): ConnectionState {\n return this._connectionState;\n }\n\n public get connected(): boolean {\n return this.connectionState === ConnectionState.Connected;\n }\n\n public get clientId(): string | undefined {\n return this._clientId;\n }\n\n public get pendingClientId(): string | undefined {\n return this._pendingClientId;\n }\n\n constructor(\n private readonly handler: IConnectionStateHandler,\n private readonly logger: ITelemetryLogger,\n ) {\n this.prevClientLeftTimer = new Timer(\n // Default is 90 sec for which we are going to wait for its own \"leave\" message.\n this.handler.maxClientLeaveWaitTime ?? 90000,\n () => {\n assert(!this.connected,\n 0x2ac /* \"Connected when timeout waiting for leave from previous session fired!\" */);\n this.applyForConnectedState(\"timeout\");\n },\n );\n\n // Based on recent data, it looks like majority of cases where we get stuck are due to really slow or\n // timing out ops fetches. So attempt recovery infrequently. Also fetch uses 30 second timeout, so\n // if retrying fixes the problem, we should not see these events.\n this.joinOpTimer = new Timer(\n JoinOpTimer,\n () => {\n // I've observed timer firing within couple ms from disconnect event, looks like\n // queued timer callback is not cancelled if timer is cancelled while callback sits in the queue.\n if (this.connectionState === ConnectionState.Connecting) {\n this.handler.logConnectionIssue(\"NoJoinOp\");\n }\n },\n );\n }\n\n private startJoinOpTimer() {\n assert(!this.joinOpTimer.hasTimer, 0x234 /* \"has joinOpTimer\" */);\n this.joinOpTimer.start();\n }\n\n private stopJoinOpTimer() {\n assert(this.joinOpTimer.hasTimer, 0x235 /* \"no joinOpTimer\" */);\n this.joinOpTimer.clear();\n }\n\n public dispose() {\n assert(!this.joinOpTimer.hasTimer, 0x2a5 /* \"join timer\" */);\n this.prevClientLeftTimer.clear();\n }\n\n public receivedAddMemberEvent(clientId: string) {\n // This is the only one that requires the pending client ID\n if (clientId === this.pendingClientId) {\n if (this.joinOpTimer.hasTimer) {\n this.stopJoinOpTimer();\n } else {\n // timer has already fired, meaning it took too long to get join on.\n // Record how long it actually took to recover.\n this.handler.logConnectionIssue(\"ReceivedJoinOp\");\n }\n // Start the event in case we are waiting for leave or timeout.\n if (this.prevClientLeftTimer.hasTimer) {\n this.waitEvent = PerformanceEvent.start(this.logger, {\n eventName: \"WaitBeforeClientLeave\",\n waitOnClientId: this._clientId,\n hadOutstandingOps: this.handler.shouldClientJoinWrite(),\n });\n }\n this.applyForConnectedState(\"addMemberEvent\");\n }\n }\n\n private applyForConnectedState(source: \"removeMemberEvent\" | \"addMemberEvent\" | \"timeout\") {\n const protocolHandler = this.handler.protocolHandler();\n assert(protocolHandler !== undefined, 0x236 /* \"In all cases it should be already installed\" */);\n // Move to connected state only if we are in Connecting state, we have seen our join op\n // and there is no timer running which means we are not waiting for previous client to leave\n // or timeout has occured while doing so.\n if (this.pendingClientId !== this.clientId\n && this.pendingClientId !== undefined\n && protocolHandler.quorum.getMember(this.pendingClientId) !== undefined\n && !this.prevClientLeftTimer.hasTimer\n ) {\n this.waitEvent?.end({ source });\n this.setConnectionState(ConnectionState.Connected);\n } else {\n // Adding this event temporarily so that we can get help debugging if something goes wrong.\n this.logger.sendTelemetryEvent({\n eventName: \"connectedStateRejected\",\n category: source === \"timeout\" ? \"error\" : \"generic\",\n source,\n pendingClientId: this.pendingClientId,\n clientId: this.clientId,\n hasTimer: this.prevClientLeftTimer.hasTimer,\n inQuorum: protocolHandler !== undefined && this.pendingClientId !== undefined\n && protocolHandler.quorum.getMember(this.pendingClientId) !== undefined,\n });\n }\n }\n\n public receivedRemoveMemberEvent(clientId: string) {\n // If the client which has left was us, then finish the timer.\n if (this.clientId === clientId) {\n this.prevClientLeftTimer.clear();\n this.applyForConnectedState(\"removeMemberEvent\");\n }\n }\n\n public receivedDisconnectEvent(reason: string) {\n if (this.joinOpTimer.hasTimer) {\n this.stopJoinOpTimer();\n }\n this.setConnectionState(ConnectionState.Disconnected, reason);\n }\n\n public receivedConnectEvent(\n connectionMode: ConnectionMode,\n details: IConnectionDetails,\n ) {\n const oldState = this._connectionState;\n this._connectionState = ConnectionState.Connecting;\n\n // Stash the clientID to detect when transitioning from connecting (socket.io channel open) to connected\n // (have received the join message for the client ID)\n // This is especially important in the reconnect case. It's possible there could be outstanding\n // ops sent by this client, so we should keep the old client id until we see our own client's\n // join message. after we see the join message for out new connection with our new client id,\n // we know there can no longer be outstanding ops that we sent with the previous client id.\n this._pendingClientId = details.clientId;\n\n // Report telemetry after we set client id, but before transitioning to Connected state below!\n this.handler.logConnectionStateChangeTelemetry(ConnectionState.Connecting, oldState);\n\n const protocolHandler = this.handler.protocolHandler();\n // Check if we already processed our own join op through delta storage!\n // we are fetching ops from storage in parallel to connecting to ordering service\n // Given async processes, it's possible that we have already processed our own join message before\n // connection was fully established.\n // Note that we might be still initializing quorum - connection is established proactively on load!\n if ((protocolHandler !== undefined && protocolHandler.quorum.getMember(details.clientId) !== undefined)\n || connectionMode === \"read\"\n ) {\n assert(!this.prevClientLeftTimer.hasTimer, 0x2a6 /* \"there should be no timer for 'read' connections\" */);\n this.setConnectionState(ConnectionState.Connected);\n } else if (connectionMode === \"write\") {\n this.startJoinOpTimer();\n }\n }\n\n private setConnectionState(value: ConnectionState.Disconnected, reason: string);\n private setConnectionState(value: ConnectionState.Connected);\n private setConnectionState(value: ConnectionState, reason?: string) {\n if (this.connectionState === value) {\n // Already in the desired state - exit early\n this.logger.sendErrorEvent({ eventName: \"setConnectionStateSame\", value });\n return;\n }\n\n const oldState = this._connectionState;\n this._connectionState = value;\n const quorum = this.handler.protocolHandler()?.quorum;\n let client: ILocalSequencedClient | undefined;\n if (this._clientId !== undefined) {\n client = quorum?.getMember(this._clientId);\n }\n if (value === ConnectionState.Connected) {\n assert(oldState === ConnectionState.Connecting,\n 0x1d8 /* \"Should only transition from Connecting state\" */);\n // Mark our old client should have left in the quorum if it's still there\n if (client !== undefined) {\n client.shouldHaveLeft = true;\n }\n this._clientId = this.pendingClientId;\n } else if (value === ConnectionState.Disconnected) {\n // Important as we process our own joinSession message through delta request\n this._pendingClientId = undefined;\n // Only wait for \"leave\" message if the connected client exists in the quorum because only the write\n // client will exist in the quorum and only for those clients we will receive \"removeMember\" event and\n // the client has some unacked ops.\n // Also server would not accept ops from read client. Also check if the timer is not already running as\n // we could receive \"Disconnected\" event multiple times without getting connected and in that case we\n // don't want to reset the timer as we still want to wait on original client which started this timer.\n if (client !== undefined\n && this.handler.shouldClientJoinWrite()\n && this.prevClientLeftTimer.hasTimer === false\n ) {\n this.prevClientLeftTimer.restart();\n } else {\n // Adding this event temporarily so that we can get help debugging if something goes wrong.\n this.logger.sendTelemetryEvent({\n eventName: \"noWaitOnDisconnected\",\n inQuorum: client !== undefined,\n hasTimer: this.prevClientLeftTimer.hasTimer,\n shouldClientJoinWrite: this.handler.shouldClientJoinWrite(),\n });\n }\n }\n\n // Report transition before we propagate event across layers\n this.handler.logConnectionStateChangeTelemetry(this._connectionState, oldState, reason);\n\n // Propagate event across layers\n this.handler.connectionStateChanged();\n }\n}\n"]}
|
package/dist/container.d.ts
CHANGED
|
@@ -112,27 +112,6 @@ export declare class Container extends EventEmitterWithErrorHandling<IContainerE
|
|
|
112
112
|
get IFluidRouter(): IFluidRouter;
|
|
113
113
|
get resolvedUrl(): IResolvedUrl | undefined;
|
|
114
114
|
get loadedFromVersion(): IVersion | undefined;
|
|
115
|
-
/**
|
|
116
|
-
* Tells if container is in read-only mode.
|
|
117
|
-
* Data stores should listen for "readonly" notifications and disallow user making changes to data stores.
|
|
118
|
-
* Readonly state can be because of no storage write permission,
|
|
119
|
-
* or due to host forcing readonly mode for container.
|
|
120
|
-
*
|
|
121
|
-
* We do not differentiate here between no write access to storage vs. host disallowing changes to container -
|
|
122
|
-
* in all cases container runtime and data stores should respect readonly state and not allow local changes.
|
|
123
|
-
*
|
|
124
|
-
* It is undefined if we have not yet established websocket connection
|
|
125
|
-
* and do not know if user has write access to a file.
|
|
126
|
-
* @deprecated - use readOnlyInfo
|
|
127
|
-
*/
|
|
128
|
-
get readonly(): boolean | undefined;
|
|
129
|
-
/**
|
|
130
|
-
* Tells if user has no write permissions for file in storage
|
|
131
|
-
* It is undefined if we have not yet established websocket connection
|
|
132
|
-
* and do not know if user has write access to a file.
|
|
133
|
-
* @deprecated - use readOnlyInfo
|
|
134
|
-
*/
|
|
135
|
-
get readonlyPermissions(): boolean | undefined;
|
|
136
115
|
get readOnlyInfo(): ReadOnlyInfo;
|
|
137
116
|
/**
|
|
138
117
|
* Tracks host requiring read-only mode.
|
package/dist/container.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,OAAO,EACH,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,iBAAiB,EAEpB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACH,SAAS,EAET,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,uBAAuB,EACvB,gBAAgB,EAChB,WAAW,EAGX,YAAY,EACZ,kBAAkB,EACrB,MAAM,uCAAuC,CAAC;AAO/C,OAAO,EAEH,uBAAuB,EACvB,iBAAiB,EACjB,YAAY,EACf,MAAM,oCAAoC,CAAC;AAc5C,OAAO,EAGH,oBAAoB,EACpB,cAAc,EAGd,gBAAgB,EAEhB,OAAO,EAEP,yBAAyB,EAOzB,QAAQ,EAQX,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAEH,6BAA6B,EAG7B,eAAe,EAIlB,MAAM,iCAAiC,CAAC;AAKzC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAkB,MAAM,UAAU,CAAC;AAelE,MAAM,WAAW,qBAAqB;IAClC;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;OAEG;IACH,qBAAqB,CAAC,EAAE,cAAc,CAAC;IACvC,WAAW,EAAE,iBAAiB,CAAC;IAC/B;;OAEG;IACH,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B;;OAEG;IACH,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CACjC;AAED,MAAM,WAAW,gBAAgB;IAC7B,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;OAEG;IACH,qBAAqB,CAAC,EAAE,cAAc,CAAC;CAC1C;AAED,oBAAY,eAAe;IACvB;;OAEG;IACH,YAAY,IAAA;IAEZ;;OAEG;IACH,UAAU,IAAA;IAEV;;OAEG;IACH,SAAS,IAAA;CACZ;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,SAAS,oBAiDhE;AAMD,qBAAa,SAAU,SAAQ,6BAA6B,CAAC,gBAAgB,CAAE,YAAW,UAAU;
|
|
1
|
+
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"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"}
|
package/dist/container.js
CHANGED
|
@@ -144,7 +144,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
144
144
|
this.subLogger = telemetry_utils_1.ChildLogger.create(loader.services.subLogger, undefined, {
|
|
145
145
|
all: {
|
|
146
146
|
clientType,
|
|
147
|
-
loaderVersion: packageVersion_1.pkgVersion,
|
|
148
147
|
containerId: uuid_1.v4(),
|
|
149
148
|
docId: () => this.id,
|
|
150
149
|
containerAttachState: () => this._attachState,
|
|
@@ -229,22 +228,22 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
229
228
|
switch (event) {
|
|
230
229
|
case dirtyContainerEvent:
|
|
231
230
|
if (this._dirtyContainer) {
|
|
232
|
-
listener(
|
|
231
|
+
listener();
|
|
233
232
|
}
|
|
234
233
|
break;
|
|
235
234
|
case savedContainerEvent:
|
|
236
235
|
if (!this._dirtyContainer) {
|
|
237
|
-
listener(
|
|
236
|
+
listener();
|
|
238
237
|
}
|
|
239
238
|
break;
|
|
240
239
|
case telemetry_utils_1.connectedEventName:
|
|
241
240
|
if (this.connected) {
|
|
242
|
-
listener(
|
|
241
|
+
listener(this.clientId);
|
|
243
242
|
}
|
|
244
243
|
break;
|
|
245
244
|
case telemetry_utils_1.disconnectedEventName:
|
|
246
245
|
if (!this.connected) {
|
|
247
|
-
listener(
|
|
246
|
+
listener();
|
|
248
247
|
}
|
|
249
248
|
break;
|
|
250
249
|
default:
|
|
@@ -291,16 +290,18 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
291
290
|
container.close(err);
|
|
292
291
|
onClosed(err);
|
|
293
292
|
});
|
|
294
|
-
}), { start: true, end: true, cancel: "
|
|
293
|
+
}), { start: true, end: true, cancel: "generic" });
|
|
295
294
|
}
|
|
296
295
|
/**
|
|
297
296
|
* Create a new container in a detached state.
|
|
298
297
|
*/
|
|
299
298
|
static async createDetached(loader, codeDetails) {
|
|
300
299
|
const container = new Container(loader, {});
|
|
301
|
-
container.
|
|
302
|
-
|
|
303
|
-
|
|
300
|
+
return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.logger, { eventName: "CreateDetached" }, async (_event) => {
|
|
301
|
+
container._lifecycleState = "loading";
|
|
302
|
+
await container.createDetached(codeDetails);
|
|
303
|
+
return container;
|
|
304
|
+
}, { start: true, end: true, cancel: "generic" });
|
|
304
305
|
}
|
|
305
306
|
/**
|
|
306
307
|
* Create a new container in a detached state that is initialized with a
|
|
@@ -308,10 +309,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
308
309
|
*/
|
|
309
310
|
static async rehydrateDetachedFromSnapshot(loader, snapshot) {
|
|
310
311
|
const container = new Container(loader, {});
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
312
|
+
return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.logger, { eventName: "RehydrateDetachedFromSnapshot" }, async (_event) => {
|
|
313
|
+
const deserializedSummary = JSON.parse(snapshot);
|
|
314
|
+
container._lifecycleState = "loading";
|
|
315
|
+
await container.rehydrateDetachedFromSnapshot(deserializedSummary);
|
|
316
|
+
return container;
|
|
317
|
+
}, { start: true, end: true, cancel: "generic" });
|
|
315
318
|
}
|
|
316
319
|
get loaded() {
|
|
317
320
|
return (this._lifecycleState !== "created" && this._lifecycleState !== "loading");
|
|
@@ -356,31 +359,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
356
359
|
get loadedFromVersion() {
|
|
357
360
|
return this._loadedFromVersion;
|
|
358
361
|
}
|
|
359
|
-
/**
|
|
360
|
-
* Tells if container is in read-only mode.
|
|
361
|
-
* Data stores should listen for "readonly" notifications and disallow user making changes to data stores.
|
|
362
|
-
* Readonly state can be because of no storage write permission,
|
|
363
|
-
* or due to host forcing readonly mode for container.
|
|
364
|
-
*
|
|
365
|
-
* We do not differentiate here between no write access to storage vs. host disallowing changes to container -
|
|
366
|
-
* in all cases container runtime and data stores should respect readonly state and not allow local changes.
|
|
367
|
-
*
|
|
368
|
-
* It is undefined if we have not yet established websocket connection
|
|
369
|
-
* and do not know if user has write access to a file.
|
|
370
|
-
* @deprecated - use readOnlyInfo
|
|
371
|
-
*/
|
|
372
|
-
get readonly() {
|
|
373
|
-
return this._deltaManager.readonly;
|
|
374
|
-
}
|
|
375
|
-
/**
|
|
376
|
-
* Tells if user has no write permissions for file in storage
|
|
377
|
-
* It is undefined if we have not yet established websocket connection
|
|
378
|
-
* and do not know if user has write access to a file.
|
|
379
|
-
* @deprecated - use readOnlyInfo
|
|
380
|
-
*/
|
|
381
|
-
get readonlyPermissions() {
|
|
382
|
-
return this._deltaManager.readonlyPermissions;
|
|
383
|
-
}
|
|
384
362
|
get readOnlyInfo() {
|
|
385
363
|
return this._deltaManager.readOnlyInfo;
|
|
386
364
|
}
|
|
@@ -489,6 +467,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
489
467
|
try {
|
|
490
468
|
this._deltaManager.close(error);
|
|
491
469
|
(_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
|
|
470
|
+
this.connectionStateHandler.dispose();
|
|
492
471
|
(_b = this._context) === null || _b === void 0 ? void 0 : _b.dispose(error !== undefined ? new Error(error.message) : undefined);
|
|
493
472
|
common_utils_1.assert(this.connectionState === ConnectionState.Disconnected, 0x0cf /* "disconnect event was not raised!" */);
|
|
494
473
|
(_c = this._storageService) === null || _c === void 0 ? void 0 : _c.dispose();
|
|
@@ -538,89 +517,88 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
538
517
|
return JSON.stringify(combinedSummary);
|
|
539
518
|
}
|
|
540
519
|
async attach(request) {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
// If container is already attached or attach is in progress, throw an error.
|
|
545
|
-
common_utils_1.assert(this._attachState === container_definitions_1.AttachState.Detached && !this.attachStarted, 0x205 /* "attach() called more than once" */);
|
|
546
|
-
this.attachStarted = true;
|
|
547
|
-
// If attachment blobs were uploaded in detached state we will go through a different attach flow
|
|
548
|
-
const hasAttachmentBlobs = this.loader.services.detachedBlobStorage !== undefined
|
|
549
|
-
&& this.loader.services.detachedBlobStorage.size > 0;
|
|
550
|
-
try {
|
|
551
|
-
common_utils_1.assert(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
|
|
552
|
-
let summary;
|
|
553
|
-
if (!hasAttachmentBlobs) {
|
|
554
|
-
// Get the document state post attach - possibly can just call attach but we need to change the
|
|
555
|
-
// semantics around what the attach means as far as async code goes.
|
|
556
|
-
const appSummary = this.context.createSummary();
|
|
557
|
-
const protocolSummary = this.captureProtocolSummary();
|
|
558
|
-
summary = driver_utils_1.combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
559
|
-
// Set the state as attaching as we are starting the process of attaching container.
|
|
560
|
-
// This should be fired after taking the summary because it is the place where we are
|
|
561
|
-
// starting to attach the container to storage.
|
|
562
|
-
// Also, this should only be fired in detached container.
|
|
563
|
-
this._attachState = container_definitions_1.AttachState.Attaching;
|
|
564
|
-
this.context.notifyAttaching();
|
|
565
|
-
}
|
|
566
|
-
// Actually go and create the resolved document
|
|
567
|
-
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
568
|
-
driver_utils_1.ensureFluidResolvedUrl(createNewResolvedUrl);
|
|
569
|
-
if (this.service === undefined) {
|
|
570
|
-
this.service = await driver_utils_1.runWithRetry(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger), "containerAttach", this.logger, {});
|
|
520
|
+
await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.logger, { eventName: "Attach" }, async () => {
|
|
521
|
+
if (this._lifecycleState !== "loaded") {
|
|
522
|
+
throw new container_utils_1.UsageError(`containerNotValidForAttach [${this._lifecycleState}]`);
|
|
571
523
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
this.
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
524
|
+
// If container is already attached or attach is in progress, throw an error.
|
|
525
|
+
common_utils_1.assert(this._attachState === container_definitions_1.AttachState.Detached && !this.attachStarted, 0x205 /* "attach() called more than once" */);
|
|
526
|
+
this.attachStarted = true;
|
|
527
|
+
// If attachment blobs were uploaded in detached state we will go through a different attach flow
|
|
528
|
+
const hasAttachmentBlobs = this.loader.services.detachedBlobStorage !== undefined
|
|
529
|
+
&& this.loader.services.detachedBlobStorage.size > 0;
|
|
530
|
+
try {
|
|
531
|
+
common_utils_1.assert(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
|
|
532
|
+
let summary;
|
|
533
|
+
if (!hasAttachmentBlobs) {
|
|
534
|
+
// Get the document state post attach - possibly can just call attach but we need to change the
|
|
535
|
+
// semantics around what the attach means as far as async code goes.
|
|
536
|
+
const appSummary = this.context.createSummary();
|
|
537
|
+
const protocolSummary = this.captureProtocolSummary();
|
|
538
|
+
summary = driver_utils_1.combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
539
|
+
// Set the state as attaching as we are starting the process of attaching container.
|
|
540
|
+
// This should be fired after taking the summary because it is the place where we are
|
|
541
|
+
// starting to attach the container to storage.
|
|
542
|
+
// Also, this should only be fired in detached container.
|
|
543
|
+
this._attachState = container_definitions_1.AttachState.Attaching;
|
|
544
|
+
this.context.notifyAttaching();
|
|
545
|
+
}
|
|
546
|
+
// Actually go and create the resolved document
|
|
547
|
+
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
548
|
+
driver_utils_1.ensureFluidResolvedUrl(createNewResolvedUrl);
|
|
549
|
+
if (this.service === undefined) {
|
|
550
|
+
this.service = await driver_utils_1.runWithRetry(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger), "containerAttach", this.logger, {});
|
|
551
|
+
}
|
|
552
|
+
const resolvedUrl = this.service.resolvedUrl;
|
|
553
|
+
driver_utils_1.ensureFluidResolvedUrl(resolvedUrl);
|
|
554
|
+
this._resolvedUrl = resolvedUrl;
|
|
555
|
+
await this.connectStorageService();
|
|
556
|
+
if (hasAttachmentBlobs) {
|
|
557
|
+
// upload blobs to storage
|
|
558
|
+
common_utils_1.assert(!!this.loader.services.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
|
|
559
|
+
// build a table mapping IDs assigned locally to IDs assigned by storage and pass it to runtime to
|
|
560
|
+
// support blob handles that only know about the local IDs
|
|
561
|
+
const redirectTable = new Map();
|
|
562
|
+
// if new blobs are added while uploading, upload them too
|
|
563
|
+
while (redirectTable.size < this.loader.services.detachedBlobStorage.size) {
|
|
564
|
+
const newIds = this.loader.services.detachedBlobStorage.getBlobIds().filter((id) => !redirectTable.has(id));
|
|
565
|
+
for (const id of newIds) {
|
|
566
|
+
const blob = await this.loader.services.detachedBlobStorage.readBlob(id);
|
|
567
|
+
const response = await this.storageService.createBlob(blob);
|
|
568
|
+
redirectTable.set(id, response.id);
|
|
569
|
+
}
|
|
589
570
|
}
|
|
571
|
+
// take summary and upload
|
|
572
|
+
const appSummary = this.context.createSummary(redirectTable);
|
|
573
|
+
const protocolSummary = this.captureProtocolSummary();
|
|
574
|
+
summary = driver_utils_1.combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
575
|
+
this._attachState = container_definitions_1.AttachState.Attaching;
|
|
576
|
+
this.context.notifyAttaching();
|
|
577
|
+
await this.storageService.uploadSummaryWithContext(summary, {
|
|
578
|
+
referenceSequenceNumber: 0,
|
|
579
|
+
ackHandle: undefined,
|
|
580
|
+
proposalHandle: undefined,
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
this._attachState = container_definitions_1.AttachState.Attached;
|
|
584
|
+
this.emit("attached");
|
|
585
|
+
// Propagate current connection state through the system.
|
|
586
|
+
this.propagateConnectionState();
|
|
587
|
+
if (!this.closed) {
|
|
588
|
+
this.resumeInternal({ fetchOpsFromStorage: false, reason: "createDetached" });
|
|
590
589
|
}
|
|
591
|
-
// take summary and upload
|
|
592
|
-
const appSummary = this.context.createSummary(redirectTable);
|
|
593
|
-
const protocolSummary = this.captureProtocolSummary();
|
|
594
|
-
summary = driver_utils_1.combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
595
|
-
this._attachState = container_definitions_1.AttachState.Attaching;
|
|
596
|
-
this.context.notifyAttaching();
|
|
597
|
-
await this.storageService.uploadSummaryWithContext(summary, {
|
|
598
|
-
referenceSequenceNumber: 0,
|
|
599
|
-
ackHandle: undefined,
|
|
600
|
-
proposalHandle: undefined,
|
|
601
|
-
});
|
|
602
|
-
}
|
|
603
|
-
this._attachState = container_definitions_1.AttachState.Attached;
|
|
604
|
-
this.emit("attached");
|
|
605
|
-
// Propagate current connection state through the system.
|
|
606
|
-
this.propagateConnectionState();
|
|
607
|
-
if (!this.closed) {
|
|
608
|
-
this.resumeInternal({ fetchOpsFromStorage: false, reason: "createDetached" });
|
|
609
590
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
newError.addTelemetryProperties({ resolvedUrl: resolvedUrl.url });
|
|
591
|
+
catch (error) {
|
|
592
|
+
// add resolved URL on error object so that host has the ability to find this document and delete it
|
|
593
|
+
const newError = telemetry_utils_1.normalizeError(error);
|
|
594
|
+
const resolvedUrl = this.resolvedUrl;
|
|
595
|
+
if (driver_utils_1.isFluidResolvedUrl(resolvedUrl)) {
|
|
596
|
+
newError.addTelemetryProperties({ resolvedUrl: resolvedUrl.url });
|
|
597
|
+
}
|
|
598
|
+
this.close(newError);
|
|
599
|
+
throw newError;
|
|
620
600
|
}
|
|
621
|
-
|
|
622
|
-
throw newError;
|
|
623
|
-
}
|
|
601
|
+
}, { start: true, end: true, cancel: "generic" });
|
|
624
602
|
}
|
|
625
603
|
async request(path) {
|
|
626
604
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(this.logger, { eventName: "Request" }, async () => this.context.request(path), { end: true, cancel: "error" });
|
|
@@ -1114,6 +1092,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1114
1092
|
if (this.clientDetailsOverride !== undefined) {
|
|
1115
1093
|
merge_1.default(client.details, this.clientDetailsOverride);
|
|
1116
1094
|
}
|
|
1095
|
+
client.details.environment = [client.details.environment, ` loaderVersion:${packageVersion_1.pkgVersion}`].join(";");
|
|
1117
1096
|
return client;
|
|
1118
1097
|
}
|
|
1119
1098
|
/**
|
|
@@ -1123,14 +1102,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1123
1102
|
* If it's not true, runtime is not in position to send ops.
|
|
1124
1103
|
*/
|
|
1125
1104
|
activeConnection() {
|
|
1126
|
-
|
|
1105
|
+
return this.connectionState === ConnectionState.Connected &&
|
|
1127
1106
|
this._deltaManager.connectionMode === "write";
|
|
1128
|
-
// Check for presence of current client in quorum for "write" connections - inactive clients
|
|
1129
|
-
// would get leave op after some long timeout (5 min) and that should automatically transition
|
|
1130
|
-
// state to "read" mode.
|
|
1131
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1132
|
-
common_utils_1.assert(!active || this.getQuorum().getMember(this.clientId) !== undefined, 0x276 /* "active connection not present in quorum" */);
|
|
1133
|
-
return active;
|
|
1134
1107
|
}
|
|
1135
1108
|
createDeltaManager() {
|
|
1136
1109
|
const deltaManager = new deltaManager_1.DeltaManager(() => this.service, this.client, telemetry_utils_1.ChildLogger.create(this.subLogger, "DeltaManager"), this._canReconnect, () => this.activeConnection());
|