@fluidframework/container-loader 1.2.2 → 2.0.0-internal.1.0.0.81589

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 (105) hide show
  1. package/dist/audience.d.ts +2 -2
  2. package/dist/audience.d.ts.map +1 -1
  3. package/dist/audience.js.map +1 -1
  4. package/dist/catchUpMonitor.d.ts +40 -0
  5. package/dist/catchUpMonitor.d.ts.map +1 -0
  6. package/dist/catchUpMonitor.js +74 -0
  7. package/dist/catchUpMonitor.js.map +1 -0
  8. package/dist/connectionManager.d.ts.map +1 -1
  9. package/dist/connectionManager.js +0 -1
  10. package/dist/connectionManager.js.map +1 -1
  11. package/dist/connectionState.d.ts +0 -5
  12. package/dist/connectionState.d.ts.map +1 -1
  13. package/dist/connectionState.js +0 -5
  14. package/dist/connectionState.js.map +1 -1
  15. package/dist/connectionStateHandler.d.ts +12 -4
  16. package/dist/connectionStateHandler.d.ts.map +1 -1
  17. package/dist/connectionStateHandler.js +47 -15
  18. package/dist/connectionStateHandler.js.map +1 -1
  19. package/dist/container.d.ts +8 -6
  20. package/dist/container.d.ts.map +1 -1
  21. package/dist/container.js +82 -46
  22. package/dist/container.js.map +1 -1
  23. package/dist/deltaManager.d.ts.map +1 -1
  24. package/dist/deltaManager.js +6 -6
  25. package/dist/deltaManager.js.map +1 -1
  26. package/dist/deltaManagerProxy.d.ts +4 -1
  27. package/dist/deltaManagerProxy.d.ts.map +1 -1
  28. package/dist/deltaQueue.d.ts +9 -2
  29. package/dist/deltaQueue.d.ts.map +1 -1
  30. package/dist/deltaQueue.js +31 -26
  31. package/dist/deltaQueue.js.map +1 -1
  32. package/dist/index.d.ts +1 -0
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/loader.d.ts +7 -0
  36. package/dist/loader.d.ts.map +1 -1
  37. package/dist/loader.js +4 -3
  38. package/dist/loader.js.map +1 -1
  39. package/dist/packageVersion.d.ts +1 -1
  40. package/dist/packageVersion.d.ts.map +1 -1
  41. package/dist/packageVersion.js +1 -1
  42. package/dist/packageVersion.js.map +1 -1
  43. package/dist/protocol.d.ts +22 -0
  44. package/dist/protocol.d.ts.map +1 -0
  45. package/dist/protocol.js +52 -0
  46. package/dist/protocol.js.map +1 -0
  47. package/lib/audience.d.ts +2 -2
  48. package/lib/audience.d.ts.map +1 -1
  49. package/lib/audience.js.map +1 -1
  50. package/lib/catchUpMonitor.d.ts +40 -0
  51. package/lib/catchUpMonitor.d.ts.map +1 -0
  52. package/lib/catchUpMonitor.js +69 -0
  53. package/lib/catchUpMonitor.js.map +1 -0
  54. package/lib/connectionManager.d.ts.map +1 -1
  55. package/lib/connectionManager.js +0 -1
  56. package/lib/connectionManager.js.map +1 -1
  57. package/lib/connectionState.d.ts +0 -5
  58. package/lib/connectionState.d.ts.map +1 -1
  59. package/lib/connectionState.js +0 -5
  60. package/lib/connectionState.js.map +1 -1
  61. package/lib/connectionStateHandler.d.ts +12 -4
  62. package/lib/connectionStateHandler.d.ts.map +1 -1
  63. package/lib/connectionStateHandler.js +47 -15
  64. package/lib/connectionStateHandler.js.map +1 -1
  65. package/lib/container.d.ts +8 -6
  66. package/lib/container.d.ts.map +1 -1
  67. package/lib/container.js +82 -46
  68. package/lib/container.js.map +1 -1
  69. package/lib/deltaManager.d.ts.map +1 -1
  70. package/lib/deltaManager.js +6 -6
  71. package/lib/deltaManager.js.map +1 -1
  72. package/lib/deltaManagerProxy.d.ts +4 -1
  73. package/lib/deltaManagerProxy.d.ts.map +1 -1
  74. package/lib/deltaQueue.d.ts +9 -2
  75. package/lib/deltaQueue.d.ts.map +1 -1
  76. package/lib/deltaQueue.js +32 -27
  77. package/lib/deltaQueue.js.map +1 -1
  78. package/lib/index.d.ts +1 -0
  79. package/lib/index.d.ts.map +1 -1
  80. package/lib/index.js.map +1 -1
  81. package/lib/loader.d.ts +7 -0
  82. package/lib/loader.d.ts.map +1 -1
  83. package/lib/loader.js +4 -3
  84. package/lib/loader.js.map +1 -1
  85. package/lib/packageVersion.d.ts +1 -1
  86. package/lib/packageVersion.d.ts.map +1 -1
  87. package/lib/packageVersion.js +1 -1
  88. package/lib/packageVersion.js.map +1 -1
  89. package/lib/protocol.d.ts +22 -0
  90. package/lib/protocol.d.ts.map +1 -0
  91. package/lib/protocol.js +48 -0
  92. package/lib/protocol.js.map +1 -0
  93. package/package.json +23 -15
  94. package/src/audience.ts +2 -2
  95. package/src/catchUpMonitor.ts +99 -0
  96. package/src/connectionManager.ts +0 -1
  97. package/src/connectionState.ts +0 -6
  98. package/src/connectionStateHandler.ts +55 -15
  99. package/src/container.ts +115 -63
  100. package/src/deltaManager.ts +6 -4
  101. package/src/deltaQueue.ts +34 -28
  102. package/src/index.ts +4 -0
  103. package/src/loader.ts +13 -2
  104. package/src/packageVersion.ts +1 -1
  105. package/src/protocol.ts +96 -0
@@ -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, ITelemetryProperties } from \"@fluidframework/common-definitions\";\nimport { assert, Timer } from \"@fluidframework/common-utils\";\nimport { IConnectionDetails } from \"@fluidframework/container-definitions\";\nimport { ILocalSequencedClient, IProtocolHandler } from \"@fluidframework/protocol-base\";\nimport { ConnectionMode, IQuorumClients } from \"@fluidframework/protocol-definitions\";\nimport { PerformanceEvent } from \"@fluidframework/telemetry-utils\";\nimport { ConnectionState } from \"./connectionState\";\n\n/** Constructor parameter type for passing in dependencies needed by the ConnectionStateHandler */\nexport interface IConnectionStateHandlerInputs {\n /** Provides access to the clients currently in the quorum */\n quorumClients: () => IQuorumClients | undefined;\n /** Log to telemetry any change in state, included to Connecting */\n logConnectionStateChangeTelemetry:\n (value: ConnectionState, oldState: ConnectionState, reason?: string | undefined) => void;\n /** Whether to expect the client to join in write mode on next connection */\n shouldClientJoinWrite: () => boolean;\n /** (Optional) How long should we wait on our previous client's Leave op before transitioning to Connected again */\n maxClientLeaveWaitTime: number | undefined;\n /** Log an issue encountered while in the Connecting state. details will be logged as a JSON string */\n logConnectionIssue: (eventName: string, details?: ITelemetryProperties) => void;\n /** Callback whenever the ConnectionState changes between Disconnected and Connected */\n connectionStateChanged: () => void;\n}\n\nconst JoinOpTimeoutMs = 45000;\n\n/**\n * In the lifetime of a container, the connection will likely disconnect and reconnect periodically.\n * This class ensures that any ops sent by this container instance on previous connection are either\n * sequenced or blocked by the server before emitting the new \"connected\" event and allowing runtime to resubmit ops.\n *\n * Each connection is assigned a clientId by the service, and the connection is book-ended by a Join and a Leave op\n * generated by the service. Due to the distributed nature of the ordering service, in the case of reconnect we cannot\n * make any assumptions about ordering of operations between the old and new connections - i.e. new Join op could\n * be sequenced before old Leave op (and some acks from pending ops that were in flight when we disconnected).\n *\n * The job of this class is to encapsulate the transition period during reconnect, which is identified by\n * ConnectionState.CatchingUp. Specifically, before moving to Connected state with the new clientId, it ensures that:\n * (A) We process the Leave op for the previous clientId. This allows us to properly handle any acks from in-flight ops\n * that got sequenced with the old clientId (we'll recognize them as local ops). After the Leave op, any other\n * pending ops can safely be submitted with the new clientId without fear of duplication in the sequenced op stream.\n * (B) We process the Join op for the new clientId (identified when the underlying connection was first established),\n * indicating the service is ready to sequence ops sent with the new clientId.\n *\n * For (A) we give up waiting after some time (same timeout as server uses), and go ahead and transition to Connected.\n * For (B) we log telemetry if it takes too long, but still only transition to Connected when the Join op is processed\n * and we are added to the Quorum.\n */\nexport class ConnectionStateHandler {\n private _connectionState = ConnectionState.Disconnected;\n private _pendingClientId: 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: IConnectionStateHandlerInputs,\n private readonly logger: ITelemetryLogger,\n private _clientId?: string,\n ) {\n this.prevClientLeftTimer = new Timer(\n // Default is 5 min for which we are going to wait for its own \"leave\" message. This is same as\n // the max time on server after which leave op is sent.\n this.handler.maxClientLeaveWaitTime ?? 300000,\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 JoinOpTimeoutMs,\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.CatchingUp) {\n return;\n }\n const quorumClients = this.handler.quorumClients();\n const details = {\n quorumInitialized: quorumClients !== undefined,\n hasPendingClientId: this.pendingClientId !== undefined,\n inQuorum: quorumClients?.getMember(this.pendingClientId ?? \"\") !== undefined,\n waitingForLeaveOp: this.waitingForLeaveOp,\n };\n this.handler.logConnectionIssue(\"NoJoinOp\", details);\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 private get waitingForLeaveOp() {\n return this.prevClientLeftTimer.hasTimer;\n }\n\n public dispose() {\n assert(!this.joinOpTimer.hasTimer, 0x2a5 /* \"join timer\" */);\n this.prevClientLeftTimer.clear();\n }\n\n public containerSaved() {\n // If we were waiting for moving to Connected state, then only apply for state change. Since the container\n // is now saved and we don't have any ops to roundtrip, we can clear the timer and apply for connected state.\n if (this.waitingForLeaveOp) {\n this.prevClientLeftTimer.clear();\n this.applyForConnectedState(\"containerSaved\");\n }\n }\n\n private 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.waitingForLeaveOp) {\n this.waitEvent = PerformanceEvent.start(this.logger, {\n eventName: \"WaitBeforeClientLeave\",\n details: JSON.stringify({\n waitOnClientId: this._clientId,\n hadOutstandingOps: this.handler.shouldClientJoinWrite(),\n }),\n });\n }\n this.applyForConnectedState(\"addMemberEvent\");\n }\n }\n\n private applyForConnectedState(source: \"removeMemberEvent\" | \"addMemberEvent\" | \"timeout\" | \"containerSaved\") {\n const quorumClients = this.handler.quorumClients();\n assert(quorumClients !== undefined, 0x236 /* \"In all cases it should be already installed\" */);\n\n assert(this.waitingForLeaveOp === false ||\n (this.clientId !== undefined && quorumClients.getMember(this.clientId) !== undefined),\n 0x2e2 /* \"Must only wait for leave message when clientId in quorum\" */);\n\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 occurred while doing so.\n if (this.pendingClientId !== this.clientId\n && this.pendingClientId !== undefined\n && quorumClients.getMember(this.pendingClientId) !== undefined\n && !this.waitingForLeaveOp\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 details: JSON.stringify({\n source,\n pendingClientId: this.pendingClientId,\n clientId: this.clientId,\n waitingForLeaveOp: this.waitingForLeaveOp,\n inQuorum: quorumClients?.getMember(this.pendingClientId ?? \"\") !== undefined,\n }),\n });\n }\n }\n\n private 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 /**\n * The \"connect\" event indicates the connection to the Relay Service is live.\n * However, some additional conditions must be met before we can fully transition to\n * \"Connected\" state. This function handles that interim period, known as \"Connecting\" state.\n * @param connectionMode - Read or Write connection\n * @param details - Connection details returned from the ordering service\n */\n public receivedConnectEvent(\n connectionMode: ConnectionMode,\n details: IConnectionDetails,\n ) {\n const oldState = this._connectionState;\n this._connectionState = ConnectionState.CatchingUp;\n\n const writeConnection = connectionMode === \"write\";\n assert(writeConnection || !this.handler.shouldClientJoinWrite(),\n 0x30a /* shouldClientJoinWrite should imply this is a writeConnection */);\n assert(writeConnection || !this.waitingForLeaveOp,\n 0x2a6 /* \"waitingForLeaveOp should imply writeConnection (we need to be ready to flush pending ops)\" */);\n\n // Note that this may be undefined since the connection is established proactively on load\n // and the quorum may still be under initialization.\n const quorumClients: IQuorumClients | undefined = this.handler.quorumClients();\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 our 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 // IMPORTANT: Report telemetry after we set _pendingClientId, but before transitioning to Connected state\n this.handler.logConnectionStateChangeTelemetry(ConnectionState.CatchingUp, oldState);\n\n // For write connections, this pending clientId could be in the quorum already (i.e. join op already processed).\n // We are fetching ops from storage in parallel to connecting to Relay Service,\n // and given async processes, it's possible that we have already processed our own join message before\n // connection was fully established.\n // If quorumClients itself is undefined, we expect it will process the join op after it's initialized.\n const waitingForJoinOp = writeConnection && quorumClients?.getMember(this._pendingClientId) === undefined;\n\n if (waitingForJoinOp) {\n // Previous client left, and we are waiting for our own join op. When it is processed we'll join the quorum\n // and attempt to transition to Connected state via receivedAddMemberEvent.\n this.startJoinOpTimer();\n } else if (!this.waitingForLeaveOp) {\n // We're not waiting for Join or Leave op (if read-only connection those don't even apply),\n // go ahead and declare the state to be Connected!\n // If we are waiting for Leave op still, do nothing for now, we will transition to Connected later.\n this.setConnectionState(ConnectionState.Connected);\n }\n }\n\n private setConnectionState(value: ConnectionState.Disconnected, reason: string): void;\n private setConnectionState(value: ConnectionState.Connected): void;\n private setConnectionState(value: ConnectionState, reason?: string): void {\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 quorumClients = this.handler.quorumClients();\n let client: ILocalSequencedClient | undefined;\n if (this._clientId !== undefined) {\n client = quorumClients?.getMember(this._clientId);\n }\n if (value === ConnectionState.Connected) {\n assert(oldState === ConnectionState.CatchingUp,\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 details: JSON.stringify({\n inQuorum: client !== undefined,\n waitingForLeaveOp: this.waitingForLeaveOp,\n hadOutstandingOps: this.handler.shouldClientJoinWrite(),\n }),\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 public initProtocol(protocol: IProtocolHandler) {\n protocol.quorum.on(\"addMember\", (clientId, _details) => {\n this.receivedAddMemberEvent(clientId);\n });\n\n protocol.quorum.on(\"removeMember\", (clientId) => {\n this.receivedRemoveMemberEvent(clientId);\n });\n\n // if we have a clientId from a previous container we need to wait for its leave message\n if (this.clientId !== undefined && protocol.quorum.getMember(this.clientId) !== undefined) {\n this.prevClientLeftTimer.restart();\n }\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, ITelemetryProperties } from \"@fluidframework/common-definitions\";\nimport { assert, Timer } from \"@fluidframework/common-utils\";\nimport { IConnectionDetails, IDeltaManager } from \"@fluidframework/container-definitions\";\nimport { ILocalSequencedClient, IProtocolHandler } from \"@fluidframework/protocol-base\";\nimport { ConnectionMode, IQuorumClients } from \"@fluidframework/protocol-definitions\";\nimport { PerformanceEvent } from \"@fluidframework/telemetry-utils\";\nimport { ConnectionState } from \"./connectionState\";\nimport { CatchUpMonitor, ICatchUpMonitor, ImmediateCatchUpMonitor } from \"./catchUpMonitor\";\n\n/** Constructor parameter type for passing in dependencies needed by the ConnectionStateHandler */\nexport interface IConnectionStateHandlerInputs {\n /** Provides access to the clients currently in the quorum */\n quorumClients: () => IQuorumClients | undefined;\n /** Log to telemetry any change in state, included to Connecting */\n logConnectionStateChangeTelemetry:\n (value: ConnectionState, oldState: ConnectionState, reason?: string | undefined) => void;\n /** Whether to expect the client to join in write mode on next connection */\n shouldClientJoinWrite: () => boolean;\n /** (Optional) How long should we wait on our previous client's Leave op before transitioning to Connected again */\n maxClientLeaveWaitTime: number | undefined;\n /** Log an issue encountered while in the Connecting state. details will be logged as a JSON string */\n logConnectionIssue: (eventName: string, details?: ITelemetryProperties) => void;\n /** Callback whenever the ConnectionState changes between Disconnected and Connected */\n connectionStateChanged: () => void;\n}\n\nconst JoinOpTimeoutMs = 45000;\n\n/**\n * In the lifetime of a container, the connection will likely disconnect and reconnect periodically.\n * This class ensures that any ops sent by this container instance on previous connection are either\n * sequenced or blocked by the server before emitting the new \"connected\" event and allowing runtime to resubmit ops.\n *\n * Each connection is assigned a clientId by the service, and the connection is book-ended by a Join and a Leave op\n * generated by the service. Due to the distributed nature of the Relay Service, in the case of reconnect we cannot\n * make any assumptions about ordering of operations between the old and new connections - i.e. new Join op could\n * be sequenced before old Leave op (and some acks from pending ops that were in flight when we disconnected).\n *\n * The job of this class is to encapsulate the transition period during reconnect, which is identified by\n * ConnectionState.CatchingUp. Specifically, before moving to Connected state with the new clientId, it ensures that:\n * (A) We process the Leave op for the previous clientId. This allows us to properly handle any acks from in-flight ops\n * that got sequenced with the old clientId (we'll recognize them as local ops). After the Leave op, any other\n * pending ops can safely be submitted with the new clientId without fear of duplication in the sequenced op stream.\n * (B) We process the Join op for the new clientId (identified when the underlying connection was first established),\n * indicating the service is ready to sequence ops sent with the new clientId.\n * (C) We process all ops known at the time the underlying connection was established (so we are \"caught up\")\n *\n * For (A) we give up waiting after some time (same timeout as server uses), and go ahead and transition to Connected.\n * For (B) we log telemetry if it takes too long, but still only transition to Connected when the Join op is processed\n * and we are added to the Quorum.\n * For (C) this is optional behavior, controlled by the parameters of receivedConnectEvent\n */\nexport class ConnectionStateHandler {\n private _connectionState = ConnectionState.Disconnected;\n private _pendingClientId: string | undefined;\n private catchUpMonitor: ICatchUpMonitor | 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: IConnectionStateHandlerInputs,\n private readonly logger: ITelemetryLogger,\n private _clientId?: string,\n ) {\n this.prevClientLeftTimer = new Timer(\n // Default is 5 min for which we are going to wait for its own \"leave\" message. This is same as\n // the max time on server after which leave op is sent.\n this.handler.maxClientLeaveWaitTime ?? 300000,\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 JoinOpTimeoutMs,\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.CatchingUp) {\n return;\n }\n const quorumClients = this.handler.quorumClients();\n const details = {\n quorumInitialized: quorumClients !== undefined,\n pendingClientId: this.pendingClientId,\n inQuorum: quorumClients?.getMember(this.pendingClientId ?? \"\") !== undefined,\n waitingForLeaveOp: this.waitingForLeaveOp,\n };\n this.handler.logConnectionIssue(\"NoJoinOp\", details);\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 private get waitingForLeaveOp() {\n return this.prevClientLeftTimer.hasTimer;\n }\n\n public dispose() {\n assert(!this.joinOpTimer.hasTimer, 0x2a5 /* \"join timer\" */);\n this.prevClientLeftTimer.clear();\n }\n\n public containerSaved() {\n // If we were waiting for moving to Connected state, then only apply for state change. Since the container\n // is now saved and we don't have any ops to roundtrip, we can clear the timer and apply for connected state.\n if (this.waitingForLeaveOp) {\n this.prevClientLeftTimer.clear();\n this.applyForConnectedState(\"containerSaved\");\n }\n }\n\n private 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.waitingForLeaveOp) {\n this.waitEvent = PerformanceEvent.start(this.logger, {\n eventName: \"WaitBeforeClientLeave\",\n details: JSON.stringify({\n waitOnClientId: this._clientId,\n hadOutstandingOps: this.handler.shouldClientJoinWrite(),\n }),\n });\n }\n this.applyForConnectedState(\"addMemberEvent\");\n }\n }\n\n private applyForConnectedState(source: \"removeMemberEvent\" | \"addMemberEvent\" | \"timeout\" | \"containerSaved\") {\n const quorumClients = this.handler.quorumClients();\n assert(quorumClients !== undefined, 0x236 /* \"In all cases it should be already installed\" */);\n\n assert(this.waitingForLeaveOp === false ||\n (this.clientId !== undefined && quorumClients.getMember(this.clientId) !== undefined),\n 0x2e2 /* \"Must only wait for leave message when clientId in quorum\" */);\n\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 occurred while doing so.\n if (this.pendingClientId !== this.clientId\n && this.pendingClientId !== undefined\n && quorumClients.getMember(this.pendingClientId) !== undefined\n && !this.waitingForLeaveOp\n ) {\n this.waitEvent?.end({ source });\n\n assert(this.catchUpMonitor !== undefined,\n \"catchUpMonitor should always be set if pendingClientId is set\");\n this.catchUpMonitor.on(\"caughtUp\", this.transitionToConnectedState);\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 details: JSON.stringify({\n source,\n pendingClientId: this.pendingClientId,\n clientId: this.clientId,\n waitingForLeaveOp: this.waitingForLeaveOp,\n inQuorum: quorumClients?.getMember(this.pendingClientId ?? \"\") !== undefined,\n }),\n });\n }\n }\n\n private 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 this.setConnectionState(ConnectionState.Disconnected, reason);\n }\n\n private readonly transitionToConnectedState = () => {\n // Defensive measure, we should always be in CatchingUp state when this is called.\n if (this._connectionState === ConnectionState.CatchingUp) {\n this.setConnectionState(ConnectionState.Connected);\n } else {\n this.logger.sendTelemetryEvent({\n eventName: \"cannotTransitionToConnectedState\",\n connectionState: ConnectionState[this._connectionState],\n });\n }\n };\n\n /**\n * The \"connect\" event indicates the connection to the Relay Service is live.\n * However, some additional conditions must be met before we can fully transition to\n * \"Connected\" state. This function handles that interim period, known as \"Connecting\" state.\n * @param connectionMode - Read or Write connection\n * @param details - Connection details returned from the Relay Service\n * @param deltaManager - DeltaManager to be used for delaying Connected transition until caught up.\n * If it's undefined, then don't delay and transition to Connected as soon as Leave/Join op are accounted for\n */\n public receivedConnectEvent(\n connectionMode: ConnectionMode,\n details: IConnectionDetails,\n deltaManager?: IDeltaManager<any, any>,\n ) {\n const oldState = this._connectionState;\n this._connectionState = ConnectionState.CatchingUp;\n\n const writeConnection = connectionMode === \"write\";\n assert(!this.handler.shouldClientJoinWrite() || writeConnection,\n 0x30a /* shouldClientJoinWrite should imply this is a writeConnection */);\n assert(!this.waitingForLeaveOp || writeConnection,\n 0x2a6 /* \"waitingForLeaveOp should imply writeConnection (we need to be ready to flush pending ops)\" */);\n\n // Defensive measure in case catchUpMonitor from previous connection attempt wasn't already cleared\n this.catchUpMonitor?.dispose();\n\n // Note that this may be undefined since the connection is established proactively on load\n // and the quorum may still be under initialization.\n const quorumClients: IQuorumClients | undefined = this.handler.quorumClients();\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 our 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 // We may want to catch up to known ops as of now before transitioning to Connected state\n this.catchUpMonitor = deltaManager !== undefined\n ? new CatchUpMonitor(deltaManager)\n : new ImmediateCatchUpMonitor();\n\n // IMPORTANT: Report telemetry after we set _pendingClientId, but before transitioning to Connected state\n this.handler.logConnectionStateChangeTelemetry(ConnectionState.CatchingUp, oldState);\n\n // For write connections, this pending clientId could be in the quorum already (i.e. join op already processed).\n // We are fetching ops from storage in parallel to connecting to Relay Service,\n // and given async processes, it's possible that we have already processed our own join message before\n // connection was fully established.\n // If quorumClients itself is undefined, we expect it will process the join op after it's initialized.\n const waitingForJoinOp = writeConnection && quorumClients?.getMember(this._pendingClientId) === undefined;\n\n if (waitingForJoinOp) {\n // Previous client left, and we are waiting for our own join op. When it is processed we'll join the quorum\n // and attempt to transition to Connected state via receivedAddMemberEvent.\n this.startJoinOpTimer();\n } else if (!this.waitingForLeaveOp) {\n // We're not waiting for Join or Leave op (if read-only connection those don't even apply),\n // but we do need to wait until we are caught up (to now-known ops) before transitioning to Connected state.\n this.catchUpMonitor.on(\"caughtUp\", this.transitionToConnectedState);\n }\n // else - We are waiting for Leave op still, do nothing for now, we will transition to Connected later\n }\n\n /** Clear all the state used during the Connecting phase (set in receivedConnectEvent) */\n private clearPendingConnectionState() {\n this._pendingClientId = undefined;\n\n this.catchUpMonitor?.dispose();\n this.catchUpMonitor = undefined;\n\n if (this.joinOpTimer.hasTimer) {\n this.stopJoinOpTimer();\n }\n }\n\n private setConnectionState(value: ConnectionState.Disconnected, reason: string): void;\n private setConnectionState(value: ConnectionState.Connected): void;\n private setConnectionState(value: ConnectionState, reason?: string): void {\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 quorumClients = this.handler.quorumClients();\n let client: ILocalSequencedClient | undefined;\n if (this._clientId !== undefined) {\n client = quorumClients?.getMember(this._clientId);\n }\n if (value === ConnectionState.Connected) {\n assert(oldState === ConnectionState.CatchingUp,\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 // Clear pending state immediately to prepare for reconnect\n this.clearPendingConnectionState();\n\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 details: JSON.stringify({\n inQuorum: client !== undefined,\n waitingForLeaveOp: this.waitingForLeaveOp,\n hadOutstandingOps: this.handler.shouldClientJoinWrite(),\n }),\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 public initProtocol(protocol: IProtocolHandler) {\n protocol.quorum.on(\"addMember\", (clientId, _details) => {\n this.receivedAddMemberEvent(clientId);\n });\n\n protocol.quorum.on(\"removeMember\", (clientId) => {\n this.receivedRemoveMemberEvent(clientId);\n });\n\n // if we have a clientId from a previous container we need to wait for its leave message\n if (this.clientId !== undefined && protocol.quorum.getMember(this.clientId) !== undefined) {\n this.prevClientLeftTimer.restart();\n }\n }\n}\n"]}
@@ -9,6 +9,7 @@ import { IClientConfiguration, IClientDetails, IDocumentMessage, IProtocolState,
9
9
  import { EventEmitterWithErrorHandling, TelemetryLogger } from "@fluidframework/telemetry-utils";
10
10
  import { ILoaderOptions, Loader } from "./loader";
11
11
  import { ConnectionState } from "./connectionState";
12
+ import { ProtocolHandlerBuilder } from "./protocol";
12
13
  export interface IContainerLoadOptions {
13
14
  /**
14
15
  * Disables the Container from reconnecting if false, allows reconnect otherwise.
@@ -65,20 +66,21 @@ export interface IPendingContainerState {
65
66
  }
66
67
  export declare class Container extends EventEmitterWithErrorHandling<IContainerEvents> implements IContainer {
67
68
  private readonly loader;
69
+ private readonly protocolHandlerBuilder?;
68
70
  static version: string;
69
71
  /**
70
72
  * Load an existing container.
71
73
  */
72
- static load(loader: Loader, loadOptions: IContainerLoadOptions, pendingLocalState?: IPendingContainerState): Promise<Container>;
74
+ static load(loader: Loader, loadOptions: IContainerLoadOptions, pendingLocalState?: IPendingContainerState, protocolHandlerBuilder?: ProtocolHandlerBuilder): Promise<Container>;
73
75
  /**
74
76
  * Create a new container in a detached state.
75
77
  */
76
- static createDetached(loader: Loader, codeDetails: IFluidCodeDetails): Promise<Container>;
78
+ static createDetached(loader: Loader, codeDetails: IFluidCodeDetails, protocolHandlerBuilder?: ProtocolHandlerBuilder): Promise<Container>;
77
79
  /**
78
80
  * Create a new container in a detached state that is initialized with a
79
81
  * snapshot from a previous detached container.
80
82
  */
81
- static rehydrateDetachedFromSnapshot(loader: Loader, snapshot: string): Promise<Container>;
83
+ static rehydrateDetachedFromSnapshot(loader: Loader, snapshot: string, protocolHandlerBuilder?: ProtocolHandlerBuilder): Promise<Container>;
82
84
  subLogger: TelemetryLogger;
83
85
  private readonly _canReconnect;
84
86
  private readonly mc;
@@ -93,7 +95,7 @@ export declare class Container extends EventEmitterWithErrorHandling<IContainerE
93
95
  private readonly clientDetailsOverride;
94
96
  private readonly _deltaManager;
95
97
  private service;
96
- private readonly _audience;
98
+ private _initialClients;
97
99
  private _context;
98
100
  private get context();
99
101
  private _protocolHandler;
@@ -159,7 +161,7 @@ export declare class Container extends EventEmitterWithErrorHandling<IContainerE
159
161
  /**
160
162
  * Returns true if container is dirty.
161
163
  * Which means data loss if container is closed at that same moment
162
- * Most likely that happens when there is no network connection to ordering service
164
+ * Most likely that happens when there is no network connection to Relay Service
163
165
  */
164
166
  get isDirty(): boolean;
165
167
  private get serviceFactory();
@@ -167,7 +169,7 @@ export declare class Container extends EventEmitterWithErrorHandling<IContainerE
167
169
  readonly options: ILoaderOptions;
168
170
  private get scope();
169
171
  private get codeLoader();
170
- constructor(loader: Loader, config: IContainerConfig);
172
+ constructor(loader: Loader, config: IContainerConfig, protocolHandlerBuilder?: ProtocolHandlerBuilder | undefined);
171
173
  /**
172
174
  * Retrieves the quorum associated with the document
173
175
  */
@@ -1 +1 @@
1
- {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"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"}
package/lib/container.js CHANGED
@@ -9,7 +9,6 @@ import { assert, performance, unreachableCase } from "@fluidframework/common-uti
9
9
  import { AttachState, isFluidCodeDetails, } from "@fluidframework/container-definitions";
10
10
  import { DataCorruptionError, extractSafePropertiesFromMessage, GenericError, UsageError, } from "@fluidframework/container-utils";
11
11
  import { readAndParse, OnlineStatus, isOnline, ensureFluidResolvedUrl, combineAppAndProtocolSummary, runWithRetry, isFluidResolvedUrl, isRuntimeMessage, isUnpackedRuntimeMessage, } from "@fluidframework/driver-utils";
12
- import { ProtocolOpHandlerWithClientValidation, } from "@fluidframework/protocol-base";
13
12
  import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
14
13
  import { ChildLogger, EventEmitterWithErrorHandling, PerformanceEvent, raiseConnectedEvent, TelemetryLogger, connectedEventName, disconnectedEventName, normalizeError, loggerToMonitoringContext, wrapError, } from "@fluidframework/telemetry-utils";
15
14
  import { Audience } from "./audience";
@@ -28,6 +27,7 @@ import { initQuorumValuesFromCodeDetails, getCodeDetailsFromQuorumValues, Quorum
28
27
  import { CollabWindowTracker } from "./collabWindowTracker";
29
28
  import { ConnectionManager } from "./connectionManager";
30
29
  import { ConnectionState } from "./connectionState";
30
+ import { ProtocolHandler, } from "./protocol";
31
31
  const detachedContainerRefSeqNumber = 0;
32
32
  const dirtyContainerEvent = "dirty";
33
33
  const savedContainerEvent = "saved";
@@ -57,6 +57,10 @@ export async function waitContainerToCatchUp(container) {
57
57
  : new GenericError(baseMessage));
58
58
  };
59
59
  container.on("closed", closedCallback);
60
+ // Depending on config, transition to "connected" state may include the guarantee
61
+ // that all known ops have been processed. If so, we may introduce additional wait here.
62
+ // Waiting for "connected" state in either case gets us at least to our own Join op
63
+ // which is a reasonable approximation of "caught up"
60
64
  const waitForOps = () => {
61
65
  assert(container.connectionState === ConnectionState.CatchingUp
62
66
  || container.connectionState === ConnectionState.Connected, 0x0cd /* "Container disconnected while waiting for ops!" */);
@@ -98,9 +102,22 @@ export async function waitContainerToCatchUp(container) {
98
102
  const getCodeProposal =
99
103
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
100
104
  (quorum) => { var _a; return (_a = quorum.get("code")) !== null && _a !== void 0 ? _a : quorum.get("code2"); };
105
+ /**
106
+ * Helper function to report to telemetry cases where operation takes longer than expected (1s)
107
+ * @param logger - logger to use
108
+ * @param eventName - event name
109
+ * @param action - functor to call and measure
110
+ */
111
+ async function ReportIfTooLong(logger, eventName, action) {
112
+ const event = PerformanceEvent.start(logger, { eventName });
113
+ const props = await action();
114
+ if (event.duration > 1000) {
115
+ event.end(props);
116
+ }
117
+ }
101
118
  const summarizerClientType = "summarizer";
102
119
  export class Container extends EventEmitterWithErrorHandling {
103
- constructor(loader, config) {
120
+ constructor(loader, config, protocolHandlerBuilder) {
104
121
  var _a, _b;
105
122
  super((name, error) => {
106
123
  this.mc.logger.sendErrorEvent({
@@ -109,6 +126,7 @@ export class Container extends EventEmitterWithErrorHandling {
109
126
  }, error);
110
127
  });
111
128
  this.loader = loader;
129
+ this.protocolHandlerBuilder = protocolHandlerBuilder;
112
130
  // Tells if container can reconnect on losing fist connection
113
131
  // If false, container gets closed on loss of connection.
114
132
  this._canReconnect = true;
@@ -122,7 +140,6 @@ export class Container extends EventEmitterWithErrorHandling {
122
140
  this.attachStarted = false;
123
141
  this._dirtyContainer = false;
124
142
  this.setAutoReconnectTime = performance.now();
125
- this._audience = new Audience();
126
143
  this.clientDetailsOverride = config.clientDetailsOverride;
127
144
  this._resolvedUrl = config.resolvedUrl;
128
145
  if (config.canReconnect !== undefined) {
@@ -142,6 +159,7 @@ export class Container extends EventEmitterWithErrorHandling {
142
159
  containerAttachState: () => this._attachState,
143
160
  containerLifecycleState: () => this._lifecycleState,
144
161
  containerConnectionState: () => ConnectionState[this.connectionState],
162
+ serializedContainer: config.serializedContainerState !== undefined,
145
163
  },
146
164
  // we need to be judicious with our logging here to avoid generating too much data
147
165
  // all data logged here should be broadly applicable, and not specific to a
@@ -154,6 +172,7 @@ export class Container extends EventEmitterWithErrorHandling {
154
172
  containerLoadedFromVersionId: () => { var _a; return (_a = this.loadedFromVersion) === null || _a === void 0 ? void 0 : _a.id; },
155
173
  containerLoadedFromVersionDate: () => { var _a; return (_a = this.loadedFromVersion) === null || _a === void 0 ? void 0 : _a.date; },
156
174
  // message information to associate errors with the specific execution state
175
+ // dmLastMsqSeqNumber: if present, same as dmLastProcessedSeqNumber
157
176
  dmLastMsqSeqNumber: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.sequenceNumber; },
158
177
  dmLastMsqSeqTimestamp: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.timestamp; },
159
178
  dmLastMsqSeqClientId: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.clientId; },
@@ -253,13 +272,13 @@ export class Container extends EventEmitterWithErrorHandling {
253
272
  /**
254
273
  * Load an existing container.
255
274
  */
256
- static async load(loader, loadOptions, pendingLocalState) {
275
+ static async load(loader, loadOptions, pendingLocalState, protocolHandlerBuilder) {
257
276
  const container = new Container(loader, {
258
277
  clientDetailsOverride: loadOptions.clientDetailsOverride,
259
278
  resolvedUrl: loadOptions.resolvedUrl,
260
279
  canReconnect: loadOptions.canReconnect,
261
280
  serializedContainerState: pendingLocalState,
262
- });
281
+ }, protocolHandlerBuilder);
263
282
  return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load" }, async (event) => new Promise((resolve, reject) => {
264
283
  var _a, _b;
265
284
  const version = loadOptions.version;
@@ -293,8 +312,8 @@ export class Container extends EventEmitterWithErrorHandling {
293
312
  /**
294
313
  * Create a new container in a detached state.
295
314
  */
296
- static async createDetached(loader, codeDetails) {
297
- const container = new Container(loader, {});
315
+ static async createDetached(loader, codeDetails, protocolHandlerBuilder) {
316
+ const container = new Container(loader, {}, protocolHandlerBuilder);
298
317
  return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "CreateDetached" }, async (_event) => {
299
318
  await container.createDetached(codeDetails);
300
319
  return container;
@@ -304,8 +323,8 @@ export class Container extends EventEmitterWithErrorHandling {
304
323
  * Create a new container in a detached state that is initialized with a
305
324
  * snapshot from a previous detached container.
306
325
  */
307
- static async rehydrateDetachedFromSnapshot(loader, snapshot) {
308
- const container = new Container(loader, {});
326
+ static async rehydrateDetachedFromSnapshot(loader, snapshot, protocolHandlerBuilder) {
327
+ const container = new Container(loader, {}, protocolHandlerBuilder);
309
328
  return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "RehydrateDetachedFromSnapshot" }, async (_event) => {
310
329
  const deserializedSummary = JSON.parse(snapshot);
311
330
  await container.rehydrateDetachedFromSnapshot(deserializedSummary);
@@ -418,12 +437,12 @@ export class Container extends EventEmitterWithErrorHandling {
418
437
  * Retrieves the audience associated with the document
419
438
  */
420
439
  get audience() {
421
- return this._audience;
440
+ return this.protocolHandler.audience;
422
441
  }
423
442
  /**
424
443
  * Returns true if container is dirty.
425
444
  * Which means data loss if container is closed at that same moment
426
- * Most likely that happens when there is no network connection to ordering service
445
+ * Most likely that happens when there is no network connection to Relay Service
427
446
  */
428
447
  get isDirty() {
429
448
  return this._dirtyContainer;
@@ -489,7 +508,7 @@ export class Container extends EventEmitterWithErrorHandling {
489
508
  assert(this.attachState === AttachState.Attached, 0x0d1 /* "Container should be attached before close" */);
490
509
  assert(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
491
510
  assert(!!this._protocolHandler, 0x2e3 /* "Must have a valid protocol handler instance" */);
492
- assert(this._protocolHandler.attributes.term !== undefined, 0x30b /* Must have a valid protocol handler instance */);
511
+ assert(this._protocolHandler.attributes.term !== undefined, "Must have a valid protocol handler instance");
493
512
  const pendingState = {
494
513
  pendingRuntimeState: this.context.getPendingLocalState(),
495
514
  url: this.resolvedUrl.url,
@@ -788,20 +807,21 @@ export class Container extends EventEmitterWithErrorHandling {
788
807
  // ...load in the existing quorum
789
808
  // Initialize the protocol handler
790
809
  this._protocolHandler = pendingLocalState === undefined
791
- ? await this.initializeProtocolStateFromSnapshot(attributes, this.storageService, snapshot)
792
- : await this.initializeProtocolState(attributes, pendingLocalState.protocol.members, pendingLocalState.protocol.proposals, pendingLocalState.protocol.values);
810
+ ? await this.initializeProtocolStateFromSnapshot(attributes, this.storageService, snapshot) : await this.initializeProtocolState(attributes, {
811
+ members: pendingLocalState.protocol.members,
812
+ proposals: pendingLocalState.protocol.proposals,
813
+ values: pendingLocalState.protocol.values,
814
+ });
793
815
  const codeDetails = this.getCodeDetailsFromQuorum();
794
816
  await this.instantiateContext(true, // existing
795
817
  codeDetails, snapshot, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.pendingRuntimeState);
796
- // Internal context is fully loaded at this point
797
- this.setLoaded();
798
818
  // We might have hit some failure that did not manifest itself in exception in this flow,
799
819
  // do not start op processing in such case - static version of Container.load() will handle it correctly.
800
820
  if (!this.closed) {
801
821
  if (opsBeforeReturnP !== undefined) {
802
822
  this._deltaManager.inbound.resume();
803
- await opsBeforeReturnP;
804
- await this._deltaManager.inbound.waitTillProcessingDone();
823
+ await ReportIfTooLong(this.mc.logger, "WaitOps", async () => { await opsBeforeReturnP; return {}; });
824
+ await ReportIfTooLong(this.mc.logger, "WaitOpProcessing", async () => this._deltaManager.inbound.waitTillProcessingDone());
805
825
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
806
826
  this._deltaManager.inbound.pause();
807
827
  }
@@ -827,9 +847,13 @@ export class Container extends EventEmitterWithErrorHandling {
827
847
  if (this.closed) {
828
848
  throw new Error("Container was closed while load()");
829
849
  }
850
+ // Internal context is fully loaded at this point
851
+ this.setLoaded();
830
852
  return {
831
853
  sequenceNumber: attributes.sequenceNumber,
832
854
  version: versionId,
855
+ dmLastProcessedSeqNumber: this._deltaManager.lastSequenceNumber,
856
+ dmLastKnownSeqNumber: this._deltaManager.lastKnownSeqNumber,
833
857
  };
834
858
  }
835
859
  async createDetached(source) {
@@ -841,9 +865,11 @@ export class Container extends EventEmitterWithErrorHandling {
841
865
  await this.attachDeltaManagerOpHandler(attributes);
842
866
  // Need to just seed the source data in the code quorum. Quorum itself is empty
843
867
  const qValues = initQuorumValuesFromCodeDetails(source);
844
- this._protocolHandler = await this.initializeProtocolState(attributes, [], // members
845
- [], // proposals
846
- qValues);
868
+ this._protocolHandler = await this.initializeProtocolState(attributes, {
869
+ members: [],
870
+ proposals: [],
871
+ values: qValues,
872
+ });
847
873
  // The load context - given we seeded the quorum - will be great
848
874
  await this.instantiateContextDetached(false);
849
875
  this.setLoaded();
@@ -862,9 +888,11 @@ export class Container extends EventEmitterWithErrorHandling {
862
888
  const qValues = await readAndParse(this._storage, baseTree.blobs.quorumValues);
863
889
  const codeDetails = getCodeDetailsFromQuorumValues(qValues);
864
890
  this._protocolHandler =
865
- await this.initializeProtocolState(attributes, [], // members
866
- [], // proposals
867
- codeDetails !== undefined ? initQuorumValuesFromCodeDetails(codeDetails) : []);
891
+ await this.initializeProtocolState(attributes, {
892
+ members: [],
893
+ proposals: [],
894
+ values: codeDetails !== undefined ? initQuorumValuesFromCodeDetails(codeDetails) : [],
895
+ });
868
896
  await this.instantiateContextDetached(true, // existing
869
897
  snapshotTree);
870
898
  this.setLoaded();
@@ -906,22 +934,27 @@ export class Container extends EventEmitterWithErrorHandling {
906
934
  return attributes;
907
935
  }
908
936
  async initializeProtocolStateFromSnapshot(attributes, storage, snapshot) {
909
- let members = [];
910
- let proposals = [];
911
- let values = [];
937
+ const quorumSnapshot = {
938
+ members: [],
939
+ proposals: [],
940
+ values: [],
941
+ };
912
942
  if (snapshot !== undefined) {
913
943
  const baseTree = getProtocolSnapshotTree(snapshot);
914
- [members, proposals, values] = await Promise.all([
944
+ [quorumSnapshot.members, quorumSnapshot.proposals, quorumSnapshot.values] = await Promise.all([
915
945
  readAndParse(storage, baseTree.blobs.quorumMembers),
916
946
  readAndParse(storage, baseTree.blobs.quorumProposals),
917
947
  readAndParse(storage, baseTree.blobs.quorumValues),
918
948
  ]);
919
949
  }
920
- const protocolHandler = await this.initializeProtocolState(attributes, members, proposals, values);
950
+ const protocolHandler = await this.initializeProtocolState(attributes, quorumSnapshot);
921
951
  return protocolHandler;
922
952
  }
923
- async initializeProtocolState(attributes, members, proposals, values) {
924
- const protocol = new ProtocolOpHandlerWithClientValidation(attributes.minimumSequenceNumber, attributes.sequenceNumber, attributes.term, members, proposals, values, (key, value) => this.submitMessage(MessageType.Propose, { key, value }));
953
+ async initializeProtocolState(attributes, quorumSnapshot) {
954
+ var _a, _b;
955
+ const protocolHandlerBuilder = (_a = this.protocolHandlerBuilder) !== null && _a !== void 0 ? _a : ((...args) => new ProtocolHandler(...args, new Audience()));
956
+ const protocol = protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) => this.submitMessage(MessageType.Propose, { key, value }), (_b = this._initialClients) !== null && _b !== void 0 ? _b : []);
957
+ this._initialClients = undefined;
925
958
  const protocolLogger = ChildLogger.create(this.subLogger, "ProtocolHandler");
926
959
  protocol.quorum.on("error", (error) => {
927
960
  protocolLogger.sendErrorEvent(error);
@@ -1015,14 +1048,25 @@ export class Container extends EventEmitterWithErrorHandling {
1015
1048
  deltaManager.inbound.pause();
1016
1049
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
1017
1050
  deltaManager.inboundSignal.pause();
1018
- deltaManager.on("connect", (details, opsBehind) => {
1051
+ deltaManager.on("connect", (details, _opsBehind) => {
1019
1052
  var _a;
1020
- // Back-compat for new client and old server.
1021
- this._audience.clear();
1022
- for (const priorClient of (_a = details.initialClients) !== null && _a !== void 0 ? _a : []) {
1023
- this._audience.addMember(priorClient.clientId, priorClient.client);
1053
+ if (this._protocolHandler === undefined) {
1054
+ // Store the initial clients so that they can be submitted to the
1055
+ // protocol handler when it is created.
1056
+ this._initialClients = details.initialClients;
1024
1057
  }
1025
- this.connectionStateHandler.receivedConnectEvent(this.connectionMode, details);
1058
+ else {
1059
+ // When reconnecting, the protocol handler is already created,
1060
+ // so we can update the audience right now.
1061
+ this._protocolHandler.audience.clear();
1062
+ for (const priorClient of (_a = details.initialClients) !== null && _a !== void 0 ? _a : []) {
1063
+ this._protocolHandler.audience.addMember(priorClient.clientId, priorClient.client);
1064
+ }
1065
+ }
1066
+ const deltaManagerForCatchingUp = this.mc.config.getBoolean("Fluid.Container.CatchUpBeforeDeclaringConnected") === true ?
1067
+ this.deltaManager
1068
+ : undefined;
1069
+ this.connectionStateHandler.receivedConnectEvent(this.connectionMode, details, deltaManagerForCatchingUp);
1026
1070
  });
1027
1071
  deltaManager.on("disconnect", (reason) => {
1028
1072
  var _a;
@@ -1194,15 +1238,7 @@ export class Container extends EventEmitterWithErrorHandling {
1194
1238
  processSignal(message) {
1195
1239
  // No clientId indicates a system signal message.
1196
1240
  if (message.clientId === null) {
1197
- const innerContent = message.content;
1198
- if (innerContent.type === MessageType.ClientJoin) {
1199
- const newClient = innerContent.content;
1200
- this._audience.addMember(newClient.clientId, newClient.client);
1201
- }
1202
- else if (innerContent.type === MessageType.ClientLeave) {
1203
- const leftClientId = innerContent.content;
1204
- this._audience.removeMember(leftClientId);
1205
- }
1241
+ this.protocolHandler.processSignal(message);
1206
1242
  }
1207
1243
  else {
1208
1244
  const local = this.clientId === message.clientId;