@fluidframework/container-loader 2.0.0-internal.4.0.6 → 2.0.0-internal.4.1.1

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 (92) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +38 -0
  3. package/dist/connectionManager.d.ts +2 -1
  4. package/dist/connectionManager.d.ts.map +1 -1
  5. package/dist/connectionManager.js +16 -6
  6. package/dist/connectionManager.js.map +1 -1
  7. package/dist/connectionStateHandler.d.ts.map +1 -1
  8. package/dist/connectionStateHandler.js +7 -0
  9. package/dist/connectionStateHandler.js.map +1 -1
  10. package/dist/container.d.ts +35 -2
  11. package/dist/container.d.ts.map +1 -1
  12. package/dist/container.js +88 -43
  13. package/dist/container.js.map +1 -1
  14. package/dist/containerContext.d.ts +3 -3
  15. package/dist/containerContext.d.ts.map +1 -1
  16. package/dist/containerContext.js +5 -4
  17. package/dist/containerContext.js.map +1 -1
  18. package/dist/containerStorageAdapter.d.ts +27 -2
  19. package/dist/containerStorageAdapter.d.ts.map +1 -1
  20. package/dist/containerStorageAdapter.js +64 -6
  21. package/dist/containerStorageAdapter.js.map +1 -1
  22. package/dist/deltaManager.d.ts +1 -3
  23. package/dist/deltaManager.d.ts.map +1 -1
  24. package/dist/deltaManager.js +3 -8
  25. package/dist/deltaManager.js.map +1 -1
  26. package/dist/index.d.ts +3 -2
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +2 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/loader.d.ts.map +1 -1
  31. package/dist/loader.js +4 -1
  32. package/dist/loader.js.map +1 -1
  33. package/dist/packageVersion.d.ts +1 -1
  34. package/dist/packageVersion.js +1 -1
  35. package/dist/packageVersion.js.map +1 -1
  36. package/dist/protocol.d.ts +1 -0
  37. package/dist/protocol.d.ts.map +1 -1
  38. package/dist/protocol.js +4 -2
  39. package/dist/protocol.js.map +1 -1
  40. package/dist/utils.d.ts.map +1 -1
  41. package/dist/utils.js.map +1 -1
  42. package/lib/connectionManager.d.ts +2 -1
  43. package/lib/connectionManager.d.ts.map +1 -1
  44. package/lib/connectionManager.js +16 -6
  45. package/lib/connectionManager.js.map +1 -1
  46. package/lib/connectionStateHandler.d.ts.map +1 -1
  47. package/lib/connectionStateHandler.js +7 -0
  48. package/lib/connectionStateHandler.js.map +1 -1
  49. package/lib/container.d.ts +35 -2
  50. package/lib/container.d.ts.map +1 -1
  51. package/lib/container.js +90 -45
  52. package/lib/container.js.map +1 -1
  53. package/lib/containerContext.d.ts +3 -3
  54. package/lib/containerContext.d.ts.map +1 -1
  55. package/lib/containerContext.js +5 -4
  56. package/lib/containerContext.js.map +1 -1
  57. package/lib/containerStorageAdapter.d.ts +27 -2
  58. package/lib/containerStorageAdapter.d.ts.map +1 -1
  59. package/lib/containerStorageAdapter.js +62 -6
  60. package/lib/containerStorageAdapter.js.map +1 -1
  61. package/lib/deltaManager.d.ts +1 -3
  62. package/lib/deltaManager.d.ts.map +1 -1
  63. package/lib/deltaManager.js +3 -8
  64. package/lib/deltaManager.js.map +1 -1
  65. package/lib/index.d.ts +3 -2
  66. package/lib/index.d.ts.map +1 -1
  67. package/lib/index.js +1 -1
  68. package/lib/index.js.map +1 -1
  69. package/lib/loader.d.ts.map +1 -1
  70. package/lib/loader.js +4 -1
  71. package/lib/loader.js.map +1 -1
  72. package/lib/packageVersion.d.ts +1 -1
  73. package/lib/packageVersion.js +1 -1
  74. package/lib/packageVersion.js.map +1 -1
  75. package/lib/protocol.d.ts +1 -0
  76. package/lib/protocol.d.ts.map +1 -1
  77. package/lib/protocol.js +3 -1
  78. package/lib/protocol.js.map +1 -1
  79. package/lib/utils.d.ts.map +1 -1
  80. package/lib/utils.js.map +1 -1
  81. package/package.json +17 -13
  82. package/src/connectionManager.ts +17 -7
  83. package/src/connectionStateHandler.ts +12 -0
  84. package/src/container.ts +138 -65
  85. package/src/containerContext.ts +6 -6
  86. package/src/containerStorageAdapter.ts +88 -5
  87. package/src/deltaManager.ts +3 -10
  88. package/src/index.ts +3 -1
  89. package/src/loader.ts +4 -1
  90. package/src/packageVersion.ts +1 -1
  91. package/src/protocol.ts +4 -1
  92. package/src/utils.ts +3 -1
@@ -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 {\n\tITelemetryLogger,\n\tITelemetryProperties,\n\tTelemetryEventCategory,\n} from \"@fluidframework/common-definitions\";\nimport { assert, Timer } from \"@fluidframework/common-utils\";\nimport { IConnectionDetailsInternal, IDeltaManager } from \"@fluidframework/container-definitions\";\nimport { ILocalSequencedClient } from \"@fluidframework/protocol-base\";\nimport { ISequencedClient, IClient } from \"@fluidframework/protocol-definitions\";\nimport { PerformanceEvent, loggerToMonitoringContext } from \"@fluidframework/telemetry-utils\";\nimport { ConnectionState } from \"./connectionState\";\nimport { CatchUpMonitor, ICatchUpMonitor } from \"./catchUpMonitor\";\nimport { IProtocolHandler } from \"./protocol\";\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.\nconst JoinOpTimeoutMs = 45000;\n\n// Timeout waiting for \"self\" join signal, before giving up\nconst JoinSignalTimeoutMs = 5000;\n\n/** Constructor parameter type for passing in dependencies needed by the ConnectionStateHandler */\nexport interface IConnectionStateHandlerInputs {\n\tlogger: ITelemetryLogger;\n\t/** Log to telemetry any change in state, included to Connecting */\n\tconnectionStateChanged: (\n\t\tvalue: ConnectionState,\n\t\toldState: ConnectionState,\n\t\treason?: string | undefined,\n\t) => void;\n\t/** Whether to expect the client to join in write mode on next connection */\n\tshouldClientJoinWrite: () => boolean;\n\t/** (Optional) How long should we wait on our previous client's Leave op before transitioning to Connected again */\n\tmaxClientLeaveWaitTime: number | undefined;\n\t/** Log an issue encountered while in the Connecting state. details will be logged as a JSON string */\n\tlogConnectionIssue: (\n\t\teventName: string,\n\t\tcategory: TelemetryEventCategory,\n\t\tdetails?: ITelemetryProperties,\n\t) => void;\n}\n\n/**\n * interface that connection state handler implements\n */\nexport interface IConnectionStateHandler {\n\treadonly connectionState: ConnectionState;\n\treadonly pendingClientId: string | undefined;\n\n\tcontainerSaved(): void;\n\tdispose(): void;\n\tinitProtocol(protocol: IProtocolHandler): void;\n\treceivedConnectEvent(details: IConnectionDetailsInternal): void;\n\treceivedDisconnectEvent(reason: string): void;\n}\n\nexport function createConnectionStateHandler(\n\tinputs: IConnectionStateHandlerInputs,\n\tdeltaManager: IDeltaManager<any, any>,\n\tclientId?: string,\n) {\n\tconst mc = loggerToMonitoringContext(inputs.logger);\n\treturn createConnectionStateHandlerCore(\n\t\tmc.config.getBoolean(\"Fluid.Container.CatchUpBeforeDeclaringConnected\") === true, // connectedRaisedWhenCaughtUp\n\t\tmc.config.getBoolean(\"Fluid.Container.EnableJoinSignalWait\") === true, // readClientsWaitForJoinSignal\n\t\tinputs,\n\t\tdeltaManager,\n\t\tclientId,\n\t);\n}\n\nexport function createConnectionStateHandlerCore(\n\tconnectedRaisedWhenCaughtUp: boolean,\n\treadClientsWaitForJoinSignal: boolean,\n\tinputs: IConnectionStateHandlerInputs,\n\tdeltaManager: IDeltaManager<any, any>,\n\tclientId?: string,\n) {\n\tif (!connectedRaisedWhenCaughtUp) {\n\t\treturn new ConnectionStateHandler(inputs, readClientsWaitForJoinSignal, clientId);\n\t}\n\treturn new ConnectionStateCatchup(\n\t\tinputs,\n\t\t(handler: IConnectionStateHandlerInputs) =>\n\t\t\tnew ConnectionStateHandler(handler, readClientsWaitForJoinSignal, clientId),\n\t\tdeltaManager,\n\t);\n}\n\n/**\n * Helper internal interface to abstract away Audience & Quorum\n */\ninterface IMembership {\n\ton(\n\t\teventName: \"addMember\" | \"removeMember\",\n\t\tlistener: (clientId: string, details: IClient | ISequencedClient) => void,\n\t);\n\tgetMember(clientId: string): undefined | unknown;\n}\n\n/**\n * Class that can be used as a base class for building IConnectionStateHandler adapters / pipeline.\n * It implements both ends of communication interfaces and passes data back and forward\n */\nclass ConnectionStateHandlerPassThrough\n\timplements IConnectionStateHandler, IConnectionStateHandlerInputs\n{\n\tprotected readonly pimpl: IConnectionStateHandler;\n\n\tconstructor(\n\t\tprotected readonly inputs: IConnectionStateHandlerInputs,\n\t\tpimplFactory: (handler: IConnectionStateHandlerInputs) => IConnectionStateHandler,\n\t) {\n\t\tthis.pimpl = pimplFactory(this);\n\t}\n\n\t/**\n\t * IConnectionStateHandler\n\t */\n\tpublic get connectionState() {\n\t\treturn this.pimpl.connectionState;\n\t}\n\tpublic get pendingClientId() {\n\t\treturn this.pimpl.pendingClientId;\n\t}\n\n\tpublic containerSaved() {\n\t\treturn this.pimpl.containerSaved();\n\t}\n\tpublic dispose() {\n\t\treturn this.pimpl.dispose();\n\t}\n\tpublic initProtocol(protocol: IProtocolHandler) {\n\t\treturn this.pimpl.initProtocol(protocol);\n\t}\n\tpublic receivedDisconnectEvent(reason: string) {\n\t\treturn this.pimpl.receivedDisconnectEvent(reason);\n\t}\n\n\tpublic receivedConnectEvent(details: IConnectionDetailsInternal) {\n\t\treturn this.pimpl.receivedConnectEvent(details);\n\t}\n\n\t/**\n\t * IConnectionStateHandlerInputs\n\t */\n\n\tpublic get logger() {\n\t\treturn this.inputs.logger;\n\t}\n\tpublic connectionStateChanged(\n\t\tvalue: ConnectionState,\n\t\toldState: ConnectionState,\n\t\treason?: string | undefined,\n\t) {\n\t\treturn this.inputs.connectionStateChanged(value, oldState, reason);\n\t}\n\tpublic shouldClientJoinWrite() {\n\t\treturn this.inputs.shouldClientJoinWrite();\n\t}\n\tpublic get maxClientLeaveWaitTime() {\n\t\treturn this.inputs.maxClientLeaveWaitTime;\n\t}\n\tpublic logConnectionIssue(\n\t\teventName: string,\n\t\tcategory: TelemetryEventCategory,\n\t\tdetails?: ITelemetryProperties,\n\t) {\n\t\treturn this.inputs.logConnectionIssue(eventName, category, details);\n\t}\n}\n\n/**\n * Implementation of IConnectionStateHandler pass-through adapter that waits for specific sequence number\n * before raising connected event\n */\nclass ConnectionStateCatchup extends ConnectionStateHandlerPassThrough {\n\tprivate catchUpMonitor: ICatchUpMonitor | undefined;\n\n\tconstructor(\n\t\tinputs: IConnectionStateHandlerInputs,\n\t\tpimplFactory: (handler: IConnectionStateHandlerInputs) => IConnectionStateHandler,\n\t\tprivate readonly deltaManager: IDeltaManager<any, any>,\n\t) {\n\t\tsuper(inputs, pimplFactory);\n\t\tthis._connectionState = this.pimpl.connectionState;\n\t}\n\n\tprivate _connectionState: ConnectionState;\n\tpublic get connectionState() {\n\t\treturn this._connectionState;\n\t}\n\n\tpublic connectionStateChanged(\n\t\tvalue: ConnectionState,\n\t\toldState: ConnectionState,\n\t\treason?: string | undefined,\n\t) {\n\t\tswitch (value) {\n\t\t\tcase ConnectionState.Connected:\n\t\t\t\tassert(\n\t\t\t\t\tthis._connectionState === ConnectionState.CatchingUp,\n\t\t\t\t\t0x3e1 /* connectivity transitions */,\n\t\t\t\t);\n\t\t\t\t// Create catch-up monitor here (not earlier), as we might get more exact info by now about how far\n\t\t\t\t// client is behind through join signal. This is only true if base layer uses signals (i.e. audience,\n\t\t\t\t// not quorum, including for \"rea\" connections) to make decisions about moving to \"connected\" state.\n\t\t\t\t// In addition to that, in its current form, doing this in ConnectionState.CatchingUp is dangerous as\n\t\t\t\t// we might get callback right away, and it will screw up state transition (as code outside of switch\n\t\t\t\t// statement will overwrite current state).\n\t\t\t\tassert(\n\t\t\t\t\tthis.catchUpMonitor === undefined,\n\t\t\t\t\t0x3eb /* catchUpMonitor should be gone */,\n\t\t\t\t);\n\t\t\t\tthis.catchUpMonitor = new CatchUpMonitor(\n\t\t\t\t\tthis.deltaManager,\n\t\t\t\t\tthis.transitionToConnectedState,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\tcase ConnectionState.Disconnected:\n\t\t\t\tthis.catchUpMonitor?.dispose();\n\t\t\t\tthis.catchUpMonitor = undefined;\n\t\t\t\tbreak;\n\t\t\tcase ConnectionState.CatchingUp:\n\t\t\t\tassert(\n\t\t\t\t\tthis._connectionState === ConnectionState.Disconnected,\n\t\t\t\t\t0x3e3 /* connectivity transitions */,\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t}\n\t\tthis._connectionState = value;\n\t\tthis.inputs.connectionStateChanged(value, oldState, reason);\n\t}\n\n\tprivate readonly transitionToConnectedState = () => {\n\t\t// Defensive measure, we should always be in Connecting state when this is called.\n\t\tconst state = this.pimpl.connectionState;\n\t\tassert(state === ConnectionState.Connected, 0x3e5 /* invariant broken */);\n\t\tassert(this._connectionState === ConnectionState.CatchingUp, 0x3e6 /* invariant broken */);\n\t\tthis._connectionState = ConnectionState.Connected;\n\t\tthis.inputs.connectionStateChanged(\n\t\t\tConnectionState.Connected,\n\t\t\tConnectionState.CatchingUp,\n\t\t\t\"caught up\",\n\t\t);\n\t};\n}\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 *\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 *\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 * 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 *\n * For (b) we log telemetry if it takes too long, but still only transition to Connected when the Join op/signal is\n * processed.\n *\n * For (c) this is optional behavior, controlled by the parameters of receivedConnectEvent\n */\nclass ConnectionStateHandler implements IConnectionStateHandler {\n\tprivate _connectionState = ConnectionState.Disconnected;\n\tprivate _pendingClientId: string | undefined;\n\tprivate readonly prevClientLeftTimer: Timer;\n\tprivate readonly joinOpTimer: Timer;\n\tprivate protocol?: IProtocolHandler;\n\tprivate connection?: IConnectionDetailsInternal;\n\tprivate _clientId?: string;\n\n\tprivate waitEvent: PerformanceEvent | undefined;\n\n\tpublic get connectionState(): ConnectionState {\n\t\treturn this._connectionState;\n\t}\n\n\tprivate get clientId(): string | undefined {\n\t\treturn this._clientId;\n\t}\n\n\tpublic get pendingClientId(): string | undefined {\n\t\treturn this._pendingClientId;\n\t}\n\n\tconstructor(\n\t\tprivate readonly handler: IConnectionStateHandlerInputs,\n\t\tprivate readonly readClientsWaitForJoinSignal: boolean,\n\t\tclientIdFromPausedSession?: string,\n\t) {\n\t\tthis._clientId = clientIdFromPausedSession;\n\t\tthis.prevClientLeftTimer = new Timer(\n\t\t\t// Default is 5 min for which we are going to wait for its own \"leave\" message. This is same as\n\t\t\t// the max time on server after which leave op is sent.\n\t\t\tthis.handler.maxClientLeaveWaitTime ?? 300000,\n\t\t\t() => {\n\t\t\t\tassert(\n\t\t\t\t\tthis.connectionState !== ConnectionState.Connected,\n\t\t\t\t\t0x2ac /* \"Connected when timeout waiting for leave from previous session fired!\" */,\n\t\t\t\t);\n\t\t\t\tthis.applyForConnectedState(\"timeout\");\n\t\t\t},\n\t\t);\n\n\t\tthis.joinOpTimer = new Timer(\n\t\t\t0, // default value is not used - startJoinOpTimer() explicitly provides timeout\n\t\t\t() => {\n\t\t\t\t// I've observed timer firing within couple ms from disconnect event, looks like\n\t\t\t\t// queued timer callback is not cancelled if timer is cancelled while callback sits in the queue.\n\t\t\t\tif (this.connectionState !== ConnectionState.CatchingUp) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst details = {\n\t\t\t\t\tprotocolInitialized: this.protocol !== undefined,\n\t\t\t\t\tpendingClientId: this.pendingClientId,\n\t\t\t\t\tclientJoined: this.hasMember(this.pendingClientId),\n\t\t\t\t\twaitingForLeaveOp: this.waitingForLeaveOp,\n\t\t\t\t};\n\t\t\t\tthis.handler.logConnectionIssue(\"NoJoinOp\", \"error\", details);\n\t\t\t},\n\t\t);\n\t}\n\n\tprivate startJoinOpTimer() {\n\t\tassert(!this.joinOpTimer.hasTimer, 0x234 /* \"has joinOpTimer\" */);\n\t\tassert(this.connection !== undefined, 0x4b3 /* have connection */);\n\t\tthis.joinOpTimer.start(\n\t\t\tthis.connection.mode === \"write\" ? JoinOpTimeoutMs : JoinSignalTimeoutMs,\n\t\t);\n\t}\n\n\tprivate stopJoinOpTimer() {\n\t\tassert(this.joinOpTimer.hasTimer, 0x235 /* \"no joinOpTimer\" */);\n\t\tthis.joinOpTimer.clear();\n\t}\n\n\tprivate get waitingForLeaveOp() {\n\t\treturn this.prevClientLeftTimer.hasTimer;\n\t}\n\n\tpublic dispose() {\n\t\tassert(!this.joinOpTimer.hasTimer, 0x2a5 /* \"join timer\" */);\n\t\tthis.prevClientLeftTimer.clear();\n\t}\n\n\tpublic containerSaved() {\n\t\t// If we were waiting for moving to Connected state, then only apply for state change. Since the container\n\t\t// is now saved and we don't have any ops to roundtrip, we can clear the timer and apply for connected state.\n\t\tif (this.waitingForLeaveOp) {\n\t\t\tthis.prevClientLeftTimer.clear();\n\t\t\tthis.applyForConnectedState(\"containerSaved\");\n\t\t}\n\t}\n\n\tprivate receivedAddMemberEvent(clientId: string) {\n\t\t// This is the only one that requires the pending client ID\n\t\tif (clientId === this.pendingClientId) {\n\t\t\tif (this.joinOpTimer.hasTimer) {\n\t\t\t\tthis.stopJoinOpTimer();\n\t\t\t} else if (this.shouldWaitForJoinSignal()) {\n\t\t\t\t// timer has already fired, meaning it took too long to get join op/signal.\n\t\t\t\t// Record how long it actually took to recover.\n\t\t\t\t// This is generic event, as it by itself is not an error.\n\t\t\t\t// We also have a case where NoJoinOp happens during container boot (we do not report it as error in such case),\n\t\t\t\t// if this log statement happens after boot - we do not want to consider it error case.\n\t\t\t\tthis.handler.logConnectionIssue(\"ReceivedJoinOp\", \"generic\");\n\t\t\t}\n\t\t\t// Start the event in case we are waiting for leave or timeout.\n\t\t\tif (this.waitingForLeaveOp) {\n\t\t\t\tthis.waitEvent = PerformanceEvent.start(this.handler.logger, {\n\t\t\t\t\teventName: \"WaitBeforeClientLeave\",\n\t\t\t\t\tdetails: JSON.stringify({\n\t\t\t\t\t\twaitOnClientId: this._clientId,\n\t\t\t\t\t\thadOutstandingOps: this.handler.shouldClientJoinWrite(),\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t}\n\t\t\tthis.applyForConnectedState(\"addMemberEvent\");\n\t\t}\n\t}\n\n\tprivate applyForConnectedState(\n\t\tsource: \"removeMemberEvent\" | \"addMemberEvent\" | \"timeout\" | \"containerSaved\",\n\t) {\n\t\tassert(\n\t\t\tthis.protocol !== undefined,\n\t\t\t0x236 /* \"In all cases it should be already installed\" */,\n\t\t);\n\n\t\tassert(\n\t\t\t!this.waitingForLeaveOp || this.hasMember(this.clientId),\n\t\t\t0x2e2 /* \"Must only wait for leave message when clientId in quorum\" */,\n\t\t);\n\n\t\t// Move to connected state only if we are in Connecting state, we have seen our join op\n\t\t// and there is no timer running which means we are not waiting for previous client to leave\n\t\t// or timeout has occurred while doing so.\n\t\tif (\n\t\t\tthis.pendingClientId !== this.clientId &&\n\t\t\tthis.hasMember(this.pendingClientId) &&\n\t\t\t!this.waitingForLeaveOp\n\t\t) {\n\t\t\tthis.waitEvent?.end({ source });\n\t\t\tthis.setConnectionState(ConnectionState.Connected);\n\t\t} else {\n\t\t\t// Adding this event temporarily so that we can get help debugging if something goes wrong.\n\t\t\t// We may not see any ops due to being disconnected all that time - that's not an error!\n\t\t\tconst error =\n\t\t\t\tsource === \"timeout\" && this.connectionState !== ConnectionState.Disconnected;\n\t\t\tthis.handler.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"connectedStateRejected\",\n\t\t\t\tcategory: error ? \"error\" : \"generic\",\n\t\t\t\tdetails: JSON.stringify({\n\t\t\t\t\tsource,\n\t\t\t\t\tpendingClientId: this.pendingClientId,\n\t\t\t\t\tclientId: this.clientId,\n\t\t\t\t\twaitingForLeaveOp: this.waitingForLeaveOp,\n\t\t\t\t\tclientJoined: this.hasMember(this.pendingClientId),\n\t\t\t\t}),\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate receivedRemoveMemberEvent(clientId: string) {\n\t\t// If the client which has left was us, then finish the timer.\n\t\tif (this.clientId === clientId) {\n\t\t\tthis.prevClientLeftTimer.clear();\n\t\t\tthis.applyForConnectedState(\"removeMemberEvent\");\n\t\t}\n\t}\n\n\tpublic receivedDisconnectEvent(reason: string) {\n\t\tthis.connection = undefined;\n\t\tthis.setConnectionState(ConnectionState.Disconnected, reason);\n\t}\n\n\tprivate shouldWaitForJoinSignal() {\n\t\tassert(\n\t\t\tthis.connection !== undefined,\n\t\t\t0x4b4 /* all callers call here with active connection */,\n\t\t);\n\t\treturn this.connection.mode === \"write\" || this.readClientsWaitForJoinSignal;\n\t}\n\n\t/**\n\t * The \"connect\" event indicates the connection to the Relay Service is live.\n\t * However, some additional conditions must be met before we can fully transition to\n\t * \"Connected\" state. This function handles that interim period, known as \"Connecting\" state.\n\t * @param details - Connection details returned from the Relay Service\n\t * @param deltaManager - DeltaManager to be used for delaying Connected transition until caught up.\n\t * If it's undefined, then don't delay and transition to Connected as soon as Leave/Join op are accounted for\n\t */\n\tpublic receivedConnectEvent(details: IConnectionDetailsInternal) {\n\t\tthis.connection = details;\n\n\t\tconst oldState = this._connectionState;\n\t\tthis._connectionState = ConnectionState.CatchingUp;\n\n\t\t// The following checks are wrong. They are only valid if user has write access to a file.\n\t\t// If user lost such access mid-session, user will not be able to get \"write\" connection.\n\t\t//\n\t\t// const writeConnection = details.mode === \"write\";\n\t\t// assert(!this.handler.shouldClientJoinWrite() || writeConnection,\n\t\t// 0x30a /* shouldClientJoinWrite should imply this is a writeConnection */);\n\t\t// assert(!this.waitingForLeaveOp || writeConnection,\n\t\t// 0x2a6 /* \"waitingForLeaveOp should imply writeConnection (we need to be ready to flush pending ops)\" */);\n\n\t\t// Stash the clientID to detect when transitioning from connecting (socket.io channel open) to connected\n\t\t// (have received the join message for the client ID)\n\t\t// This is especially important in the reconnect case. It's possible there could be outstanding\n\t\t// ops sent by this client, so we should keep the old client id until we see our own client's\n\t\t// join message. after we see the join message for our new connection with our new client id,\n\t\t// we know there can no longer be outstanding ops that we sent with the previous client id.\n\t\tthis._pendingClientId = details.clientId;\n\n\t\t// IMPORTANT: Report telemetry after we set _pendingClientId, but before transitioning to Connected state\n\t\tthis.handler.connectionStateChanged(ConnectionState.CatchingUp, oldState);\n\n\t\t// Check if we need to wait for join op/signal, and if we need to wait for leave op from previous connection.\n\t\t// Pending clientId could have joined already (i.e. join op/signal already processed):\n\t\t// We are fetching ops from storage in parallel to connecting to Relay Service,\n\t\t// and given async processes, it's possible that we have already processed our own join message before\n\t\t// connection was fully established.\n\t\tif (!this.hasMember(this._pendingClientId) && this.shouldWaitForJoinSignal()) {\n\t\t\t// We are waiting for our own join op / signal. When it is processed\n\t\t\t// we'll attempt to transition to Connected state via receivedAddMemberEvent() flow.\n\t\t\tthis.startJoinOpTimer();\n\t\t} else if (!this.waitingForLeaveOp) {\n\t\t\t// We're not waiting for Join or Leave op (if read-only connection those don't even apply),\n\t\t\t// go ahead and declare the state to be Connected!\n\t\t\t// If we are waiting for Leave op still, do nothing for now, we will transition to Connected later.\n\t\t\tthis.setConnectionState(ConnectionState.Connected);\n\t\t}\n\t\t// else - We are waiting for Leave op still, do nothing for now, we will transition to Connected later\n\t}\n\n\tprivate setConnectionState(value: ConnectionState.Disconnected, reason: string): void;\n\tprivate setConnectionState(value: ConnectionState.Connected): void;\n\tprivate setConnectionState(\n\t\tvalue: ConnectionState.Disconnected | ConnectionState.Connected,\n\t\treason?: string,\n\t): void {\n\t\tif (this.connectionState === value) {\n\t\t\t// Already in the desired state - exit early\n\t\t\tthis.handler.logger.sendErrorEvent({ eventName: \"setConnectionStateSame\", value });\n\t\t\treturn;\n\t\t}\n\n\t\tconst oldState = this._connectionState;\n\t\tthis._connectionState = value;\n\n\t\t// This is the only place in code that deals with quorum. The rest works with audience\n\t\t// The code below ensures that we do not send ops until we know that old \"write\" client's disconnect\n\t\t// produced (and sequenced) leave op\n\t\tlet client: ILocalSequencedClient | undefined;\n\t\tif (this._clientId !== undefined) {\n\t\t\tclient = this.protocol?.quorum?.getMember(this._clientId);\n\t\t}\n\t\tif (value === ConnectionState.Connected) {\n\t\t\tassert(\n\t\t\t\toldState === ConnectionState.CatchingUp,\n\t\t\t\t0x1d8 /* \"Should only transition from Connecting state\" */,\n\t\t\t);\n\t\t\t// Mark our old client should have left in the quorum if it's still there\n\t\t\tif (client !== undefined) {\n\t\t\t\tclient.shouldHaveLeft = true;\n\t\t\t}\n\t\t\tthis._clientId = this.pendingClientId;\n\t\t} else if (value === ConnectionState.Disconnected) {\n\t\t\t// Clear pending state immediately to prepare for reconnect\n\t\t\tthis._pendingClientId = undefined;\n\n\t\t\tif (this.joinOpTimer.hasTimer) {\n\t\t\t\tthis.stopJoinOpTimer();\n\t\t\t}\n\n\t\t\t// Only wait for \"leave\" message if the connected client exists in the quorum and had some non-acked ops\n\t\t\t// Also check if the timer is not already running as\n\t\t\t// we could receive \"Disconnected\" event multiple times without getting connected and in that case we\n\t\t\t// don't want to reset the timer as we still want to wait on original client which started this timer.\n\t\t\tif (\n\t\t\t\tclient !== undefined &&\n\t\t\t\tthis.handler.shouldClientJoinWrite() &&\n\t\t\t\t!this.waitingForLeaveOp // same as !this.prevClientLeftTimer.hasTimer\n\t\t\t) {\n\t\t\t\tthis.prevClientLeftTimer.restart();\n\t\t\t} else {\n\t\t\t\t// Adding this event temporarily so that we can get help debugging if something goes wrong.\n\t\t\t\tthis.handler.logger.sendTelemetryEvent({\n\t\t\t\t\teventName: \"noWaitOnDisconnected\",\n\t\t\t\t\tdetails: JSON.stringify({\n\t\t\t\t\t\tclientId: this._clientId,\n\t\t\t\t\t\tinQuorum: client !== undefined,\n\t\t\t\t\t\twaitingForLeaveOp: this.waitingForLeaveOp,\n\t\t\t\t\t\thadOutstandingOps: this.handler.shouldClientJoinWrite(),\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Report transition before we propagate event across layers\n\t\tthis.handler.connectionStateChanged(this._connectionState, oldState, reason);\n\t}\n\n\t// Helper method to switch between quorum and audience.\n\t// Old design was checking only quorum for \"write\" clients.\n\t// Latest change checks audience for all types of connections.\n\tprotected get membership(): IMembership | undefined {\n\t\t// We could always use audience here, and in practice it will probably be correct.\n\t\t// (including case when this.readClientsWaitForJoinSignal === false).\n\t\t// But only if it's superset of quorum, i.e. when filtered to \"write\" clients, they are always identical!\n\t\t// It's safer to assume that we have bugs and engaging kill-bit switch should bring us back to well-known\n\t\t// and tested state!\n\t\treturn this.readClientsWaitForJoinSignal ? this.protocol?.audience : this.protocol?.quorum;\n\t}\n\n\tpublic initProtocol(protocol: IProtocolHandler) {\n\t\tthis.protocol = protocol;\n\n\t\tthis.membership?.on(\"addMember\", (clientId, details) => {\n\t\t\tassert(\n\t\t\t\t(details as IClient).mode === \"read\" ||\n\t\t\t\t\tprotocol.quorum.getMember(clientId) !== undefined,\n\t\t\t\t0x4b5 /* Audience is subset of quorum */,\n\t\t\t);\n\t\t\tthis.receivedAddMemberEvent(clientId);\n\t\t});\n\n\t\tthis.membership?.on(\"removeMember\", (clientId) => {\n\t\t\tassert(\n\t\t\t\tprotocol.quorum.getMember(clientId) === undefined,\n\t\t\t\t0x4b6 /* Audience is subset of quorum */,\n\t\t\t);\n\t\t\tthis.receivedRemoveMemberEvent(clientId);\n\t\t});\n\n\t\t/* There is a tiny tiny race possible, where these events happen in this order:\n 1. A connection is established (no \"cached\" mode is used, so it happens in parallel / faster than other steps)\n 2. Some other client produces a summary\n 3. We get \"lucky\" and load from that summary as our initial snapshot\n 4. ConnectionStateHandler.initProtocol is called, \"self\" is already in the quorum.\n We could avoid this sequence (and delete test case for it) if we move connection lower in Container.load()\n */\n\t\tif (this.hasMember(this.pendingClientId)) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tthis.receivedAddMemberEvent(this.pendingClientId!);\n\t\t}\n\n\t\t// if we have a clientId from a previous container we need to wait for its leave message\n\t\tif (this.clientId !== undefined && this.hasMember(this.clientId)) {\n\t\t\tthis.prevClientLeftTimer.restart();\n\t\t}\n\t}\n\n\tprotected hasMember(clientId?: string) {\n\t\treturn this.membership?.getMember(clientId ?? \"\") !== undefined;\n\t}\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 {\n\tITelemetryLogger,\n\tITelemetryProperties,\n\tTelemetryEventCategory,\n} from \"@fluidframework/common-definitions\";\nimport { assert, Timer } from \"@fluidframework/common-utils\";\nimport { IConnectionDetailsInternal, IDeltaManager } from \"@fluidframework/container-definitions\";\nimport { ILocalSequencedClient } from \"@fluidframework/protocol-base\";\nimport { ISequencedClient, IClient } from \"@fluidframework/protocol-definitions\";\nimport { PerformanceEvent, loggerToMonitoringContext } from \"@fluidframework/telemetry-utils\";\nimport { ConnectionState } from \"./connectionState\";\nimport { CatchUpMonitor, ICatchUpMonitor } from \"./catchUpMonitor\";\nimport { IProtocolHandler } from \"./protocol\";\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.\nconst JoinOpTimeoutMs = 45000;\n\n// Timeout waiting for \"self\" join signal, before giving up\nconst JoinSignalTimeoutMs = 5000;\n\n/** Constructor parameter type for passing in dependencies needed by the ConnectionStateHandler */\nexport interface IConnectionStateHandlerInputs {\n\tlogger: ITelemetryLogger;\n\t/** Log to telemetry any change in state, included to Connecting */\n\tconnectionStateChanged: (\n\t\tvalue: ConnectionState,\n\t\toldState: ConnectionState,\n\t\treason?: string | undefined,\n\t) => void;\n\t/** Whether to expect the client to join in write mode on next connection */\n\tshouldClientJoinWrite: () => boolean;\n\t/** (Optional) How long should we wait on our previous client's Leave op before transitioning to Connected again */\n\tmaxClientLeaveWaitTime: number | undefined;\n\t/** Log an issue encountered while in the Connecting state. details will be logged as a JSON string */\n\tlogConnectionIssue: (\n\t\teventName: string,\n\t\tcategory: TelemetryEventCategory,\n\t\tdetails?: ITelemetryProperties,\n\t) => void;\n}\n\n/**\n * interface that connection state handler implements\n */\nexport interface IConnectionStateHandler {\n\treadonly connectionState: ConnectionState;\n\treadonly pendingClientId: string | undefined;\n\n\tcontainerSaved(): void;\n\tdispose(): void;\n\tinitProtocol(protocol: IProtocolHandler): void;\n\treceivedConnectEvent(details: IConnectionDetailsInternal): void;\n\treceivedDisconnectEvent(reason: string): void;\n}\n\nexport function createConnectionStateHandler(\n\tinputs: IConnectionStateHandlerInputs,\n\tdeltaManager: IDeltaManager<any, any>,\n\tclientId?: string,\n) {\n\tconst mc = loggerToMonitoringContext(inputs.logger);\n\treturn createConnectionStateHandlerCore(\n\t\tmc.config.getBoolean(\"Fluid.Container.CatchUpBeforeDeclaringConnected\") === true, // connectedRaisedWhenCaughtUp\n\t\tmc.config.getBoolean(\"Fluid.Container.EnableJoinSignalWait\") === true, // readClientsWaitForJoinSignal\n\t\tinputs,\n\t\tdeltaManager,\n\t\tclientId,\n\t);\n}\n\nexport function createConnectionStateHandlerCore(\n\tconnectedRaisedWhenCaughtUp: boolean,\n\treadClientsWaitForJoinSignal: boolean,\n\tinputs: IConnectionStateHandlerInputs,\n\tdeltaManager: IDeltaManager<any, any>,\n\tclientId?: string,\n) {\n\tif (!connectedRaisedWhenCaughtUp) {\n\t\treturn new ConnectionStateHandler(inputs, readClientsWaitForJoinSignal, clientId);\n\t}\n\treturn new ConnectionStateCatchup(\n\t\tinputs,\n\t\t(handler: IConnectionStateHandlerInputs) =>\n\t\t\tnew ConnectionStateHandler(handler, readClientsWaitForJoinSignal, clientId),\n\t\tdeltaManager,\n\t);\n}\n\n/**\n * Helper internal interface to abstract away Audience & Quorum\n */\ninterface IMembership {\n\ton(\n\t\teventName: \"addMember\" | \"removeMember\",\n\t\tlistener: (clientId: string, details: IClient | ISequencedClient) => void,\n\t);\n\tgetMember(clientId: string): undefined | unknown;\n}\n\n/**\n * Class that can be used as a base class for building IConnectionStateHandler adapters / pipeline.\n * It implements both ends of communication interfaces and passes data back and forward\n */\nclass ConnectionStateHandlerPassThrough\n\timplements IConnectionStateHandler, IConnectionStateHandlerInputs\n{\n\tprotected readonly pimpl: IConnectionStateHandler;\n\n\tconstructor(\n\t\tprotected readonly inputs: IConnectionStateHandlerInputs,\n\t\tpimplFactory: (handler: IConnectionStateHandlerInputs) => IConnectionStateHandler,\n\t) {\n\t\tthis.pimpl = pimplFactory(this);\n\t}\n\n\t/**\n\t * IConnectionStateHandler\n\t */\n\tpublic get connectionState() {\n\t\treturn this.pimpl.connectionState;\n\t}\n\tpublic get pendingClientId() {\n\t\treturn this.pimpl.pendingClientId;\n\t}\n\n\tpublic containerSaved() {\n\t\treturn this.pimpl.containerSaved();\n\t}\n\tpublic dispose() {\n\t\treturn this.pimpl.dispose();\n\t}\n\tpublic initProtocol(protocol: IProtocolHandler) {\n\t\treturn this.pimpl.initProtocol(protocol);\n\t}\n\tpublic receivedDisconnectEvent(reason: string) {\n\t\treturn this.pimpl.receivedDisconnectEvent(reason);\n\t}\n\n\tpublic receivedConnectEvent(details: IConnectionDetailsInternal) {\n\t\treturn this.pimpl.receivedConnectEvent(details);\n\t}\n\n\t/**\n\t * IConnectionStateHandlerInputs\n\t */\n\n\tpublic get logger() {\n\t\treturn this.inputs.logger;\n\t}\n\tpublic connectionStateChanged(\n\t\tvalue: ConnectionState,\n\t\toldState: ConnectionState,\n\t\treason?: string | undefined,\n\t) {\n\t\treturn this.inputs.connectionStateChanged(value, oldState, reason);\n\t}\n\tpublic shouldClientJoinWrite() {\n\t\treturn this.inputs.shouldClientJoinWrite();\n\t}\n\tpublic get maxClientLeaveWaitTime() {\n\t\treturn this.inputs.maxClientLeaveWaitTime;\n\t}\n\tpublic logConnectionIssue(\n\t\teventName: string,\n\t\tcategory: TelemetryEventCategory,\n\t\tdetails?: ITelemetryProperties,\n\t) {\n\t\treturn this.inputs.logConnectionIssue(eventName, category, details);\n\t}\n}\n\n/**\n * Implementation of IConnectionStateHandler pass-through adapter that waits for specific sequence number\n * before raising connected event\n */\nclass ConnectionStateCatchup extends ConnectionStateHandlerPassThrough {\n\tprivate catchUpMonitor: ICatchUpMonitor | undefined;\n\n\tconstructor(\n\t\tinputs: IConnectionStateHandlerInputs,\n\t\tpimplFactory: (handler: IConnectionStateHandlerInputs) => IConnectionStateHandler,\n\t\tprivate readonly deltaManager: IDeltaManager<any, any>,\n\t) {\n\t\tsuper(inputs, pimplFactory);\n\t\tthis._connectionState = this.pimpl.connectionState;\n\t}\n\n\tprivate _connectionState: ConnectionState;\n\tpublic get connectionState() {\n\t\treturn this._connectionState;\n\t}\n\n\tpublic connectionStateChanged(\n\t\tvalue: ConnectionState,\n\t\toldState: ConnectionState,\n\t\treason?: string | undefined,\n\t) {\n\t\tswitch (value) {\n\t\t\tcase ConnectionState.Connected:\n\t\t\t\tassert(\n\t\t\t\t\tthis._connectionState === ConnectionState.CatchingUp,\n\t\t\t\t\t0x3e1 /* connectivity transitions */,\n\t\t\t\t);\n\t\t\t\t// Create catch-up monitor here (not earlier), as we might get more exact info by now about how far\n\t\t\t\t// client is behind through join signal. This is only true if base layer uses signals (i.e. audience,\n\t\t\t\t// not quorum, including for \"rea\" connections) to make decisions about moving to \"connected\" state.\n\t\t\t\t// In addition to that, in its current form, doing this in ConnectionState.CatchingUp is dangerous as\n\t\t\t\t// we might get callback right away, and it will screw up state transition (as code outside of switch\n\t\t\t\t// statement will overwrite current state).\n\t\t\t\tassert(\n\t\t\t\t\tthis.catchUpMonitor === undefined,\n\t\t\t\t\t0x3eb /* catchUpMonitor should be gone */,\n\t\t\t\t);\n\t\t\t\tthis.catchUpMonitor = new CatchUpMonitor(\n\t\t\t\t\tthis.deltaManager,\n\t\t\t\t\tthis.transitionToConnectedState,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\tcase ConnectionState.Disconnected:\n\t\t\t\tthis.catchUpMonitor?.dispose();\n\t\t\t\tthis.catchUpMonitor = undefined;\n\t\t\t\tbreak;\n\t\t\tcase ConnectionState.CatchingUp:\n\t\t\t\tassert(\n\t\t\t\t\tthis._connectionState === ConnectionState.Disconnected,\n\t\t\t\t\t0x3e3 /* connectivity transitions */,\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t}\n\t\tthis._connectionState = value;\n\t\tthis.inputs.connectionStateChanged(value, oldState, reason);\n\t}\n\n\tprivate readonly transitionToConnectedState = () => {\n\t\t// Defensive measure, we should always be in Connecting state when this is called.\n\t\tconst state = this.pimpl.connectionState;\n\t\tassert(state === ConnectionState.Connected, 0x3e5 /* invariant broken */);\n\t\tassert(this._connectionState === ConnectionState.CatchingUp, 0x3e6 /* invariant broken */);\n\t\tthis._connectionState = ConnectionState.Connected;\n\t\tthis.inputs.connectionStateChanged(\n\t\t\tConnectionState.Connected,\n\t\t\tConnectionState.CatchingUp,\n\t\t\t\"caught up\",\n\t\t);\n\t};\n}\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 *\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 *\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 * 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 *\n * For (b) we log telemetry if it takes too long, but still only transition to Connected when the Join op/signal is\n * processed.\n *\n * For (c) this is optional behavior, controlled by the parameters of receivedConnectEvent\n */\nclass ConnectionStateHandler implements IConnectionStateHandler {\n\tprivate _connectionState = ConnectionState.Disconnected;\n\tprivate _pendingClientId: string | undefined;\n\tprivate readonly prevClientLeftTimer: Timer;\n\tprivate readonly joinOpTimer: Timer;\n\tprivate protocol?: IProtocolHandler;\n\tprivate connection?: IConnectionDetailsInternal;\n\tprivate _clientId?: string;\n\n\tprivate waitEvent: PerformanceEvent | undefined;\n\n\tpublic get connectionState(): ConnectionState {\n\t\treturn this._connectionState;\n\t}\n\n\tprivate get clientId(): string | undefined {\n\t\treturn this._clientId;\n\t}\n\n\tpublic get pendingClientId(): string | undefined {\n\t\treturn this._pendingClientId;\n\t}\n\n\tconstructor(\n\t\tprivate readonly handler: IConnectionStateHandlerInputs,\n\t\tprivate readonly readClientsWaitForJoinSignal: boolean,\n\t\tclientIdFromPausedSession?: string,\n\t) {\n\t\tthis._clientId = clientIdFromPausedSession;\n\t\tthis.prevClientLeftTimer = new Timer(\n\t\t\t// Default is 5 min for which we are going to wait for its own \"leave\" message. This is same as\n\t\t\t// the max time on server after which leave op is sent.\n\t\t\tthis.handler.maxClientLeaveWaitTime ?? 300000,\n\t\t\t() => {\n\t\t\t\tassert(\n\t\t\t\t\tthis.connectionState !== ConnectionState.Connected,\n\t\t\t\t\t0x2ac /* \"Connected when timeout waiting for leave from previous session fired!\" */,\n\t\t\t\t);\n\t\t\t\tthis.applyForConnectedState(\"timeout\");\n\t\t\t},\n\t\t);\n\n\t\tthis.joinOpTimer = new Timer(\n\t\t\t0, // default value is not used - startJoinOpTimer() explicitly provides timeout\n\t\t\t() => {\n\t\t\t\t// I've observed timer firing within couple ms from disconnect event, looks like\n\t\t\t\t// queued timer callback is not cancelled if timer is cancelled while callback sits in the queue.\n\t\t\t\tif (this.connectionState !== ConnectionState.CatchingUp) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst details = {\n\t\t\t\t\tprotocolInitialized: this.protocol !== undefined,\n\t\t\t\t\tpendingClientId: this.pendingClientId,\n\t\t\t\t\tclientJoined: this.hasMember(this.pendingClientId),\n\t\t\t\t\twaitingForLeaveOp: this.waitingForLeaveOp,\n\t\t\t\t};\n\t\t\t\tthis.handler.logConnectionIssue(\"NoJoinOp\", \"error\", details);\n\t\t\t},\n\t\t);\n\t}\n\n\tprivate startJoinOpTimer() {\n\t\tassert(!this.joinOpTimer.hasTimer, 0x234 /* \"has joinOpTimer\" */);\n\t\tassert(this.connection !== undefined, 0x4b3 /* have connection */);\n\t\tthis.joinOpTimer.start(\n\t\t\tthis.connection.mode === \"write\" ? JoinOpTimeoutMs : JoinSignalTimeoutMs,\n\t\t);\n\t}\n\n\tprivate stopJoinOpTimer() {\n\t\tassert(this.joinOpTimer.hasTimer, 0x235 /* \"no joinOpTimer\" */);\n\t\tthis.joinOpTimer.clear();\n\t}\n\n\tprivate get waitingForLeaveOp() {\n\t\treturn this.prevClientLeftTimer.hasTimer;\n\t}\n\n\tpublic dispose() {\n\t\tassert(!this.joinOpTimer.hasTimer, 0x2a5 /* \"join timer\" */);\n\t\tthis.prevClientLeftTimer.clear();\n\t}\n\n\tpublic containerSaved() {\n\t\t// If we were waiting for moving to Connected state, then only apply for state change. Since the container\n\t\t// is now saved and we don't have any ops to roundtrip, we can clear the timer and apply for connected state.\n\t\tif (this.waitingForLeaveOp) {\n\t\t\tthis.prevClientLeftTimer.clear();\n\t\t\tthis.applyForConnectedState(\"containerSaved\");\n\t\t}\n\t}\n\n\tprivate receivedAddMemberEvent(clientId: string) {\n\t\t// This is the only one that requires the pending client ID\n\t\tif (clientId === this.pendingClientId) {\n\t\t\tif (this.joinOpTimer.hasTimer) {\n\t\t\t\tthis.stopJoinOpTimer();\n\t\t\t} else if (this.shouldWaitForJoinSignal()) {\n\t\t\t\t// timer has already fired, meaning it took too long to get join op/signal.\n\t\t\t\t// Record how long it actually took to recover.\n\t\t\t\t// This is generic event, as it by itself is not an error.\n\t\t\t\t// We also have a case where NoJoinOp happens during container boot (we do not report it as error in such case),\n\t\t\t\t// if this log statement happens after boot - we do not want to consider it error case.\n\t\t\t\tthis.handler.logConnectionIssue(\"ReceivedJoinOp\", \"generic\");\n\t\t\t}\n\t\t\t// Start the event in case we are waiting for leave or timeout.\n\t\t\tif (this.waitingForLeaveOp) {\n\t\t\t\tthis.waitEvent = PerformanceEvent.start(this.handler.logger, {\n\t\t\t\t\teventName: \"WaitBeforeClientLeave\",\n\t\t\t\t\tdetails: JSON.stringify({\n\t\t\t\t\t\twaitOnClientId: this._clientId,\n\t\t\t\t\t\thadOutstandingOps: this.handler.shouldClientJoinWrite(),\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t}\n\t\t\tthis.applyForConnectedState(\"addMemberEvent\");\n\t\t} else if (clientId === this.clientId) {\n\t\t\t// If we see our clientId and it's not also our pending ID, it's our own join op\n\t\t\t// being replayed, so start the timer in case our previous client is still in quorum\n\t\t\tassert(\n\t\t\t\t!this.waitingForLeaveOp,\n\t\t\t\t0x5d2 /* Unexpected join op with current clientId while waiting */,\n\t\t\t);\n\t\t\tassert(\n\t\t\t\tthis.connectionState !== ConnectionState.Connected,\n\t\t\t\t0x5d3 /* Unexpected join op with current clientId while connected */,\n\t\t\t);\n\t\t\tthis.prevClientLeftTimer.restart();\n\t\t}\n\t}\n\n\tprivate applyForConnectedState(\n\t\tsource: \"removeMemberEvent\" | \"addMemberEvent\" | \"timeout\" | \"containerSaved\",\n\t) {\n\t\tassert(\n\t\t\tthis.protocol !== undefined,\n\t\t\t0x236 /* \"In all cases it should be already installed\" */,\n\t\t);\n\n\t\tassert(\n\t\t\t!this.waitingForLeaveOp || this.hasMember(this.clientId),\n\t\t\t0x2e2 /* \"Must only wait for leave message when clientId in quorum\" */,\n\t\t);\n\n\t\t// Move to connected state only if we are in Connecting state, we have seen our join op\n\t\t// and there is no timer running which means we are not waiting for previous client to leave\n\t\t// or timeout has occurred while doing so.\n\t\tif (\n\t\t\tthis.pendingClientId !== this.clientId &&\n\t\t\tthis.hasMember(this.pendingClientId) &&\n\t\t\t!this.waitingForLeaveOp\n\t\t) {\n\t\t\tthis.waitEvent?.end({ source });\n\t\t\tthis.setConnectionState(ConnectionState.Connected);\n\t\t} else {\n\t\t\t// Adding this event temporarily so that we can get help debugging if something goes wrong.\n\t\t\t// We may not see any ops due to being disconnected all that time - that's not an error!\n\t\t\tconst error =\n\t\t\t\tsource === \"timeout\" && this.connectionState !== ConnectionState.Disconnected;\n\t\t\tthis.handler.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"connectedStateRejected\",\n\t\t\t\tcategory: error ? \"error\" : \"generic\",\n\t\t\t\tdetails: JSON.stringify({\n\t\t\t\t\tsource,\n\t\t\t\t\tpendingClientId: this.pendingClientId,\n\t\t\t\t\tclientId: this.clientId,\n\t\t\t\t\twaitingForLeaveOp: this.waitingForLeaveOp,\n\t\t\t\t\tclientJoined: this.hasMember(this.pendingClientId),\n\t\t\t\t}),\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate receivedRemoveMemberEvent(clientId: string) {\n\t\t// If the client which has left was us, then finish the timer.\n\t\tif (this.clientId === clientId) {\n\t\t\tthis.prevClientLeftTimer.clear();\n\t\t\tthis.applyForConnectedState(\"removeMemberEvent\");\n\t\t}\n\t}\n\n\tpublic receivedDisconnectEvent(reason: string) {\n\t\tthis.connection = undefined;\n\t\tthis.setConnectionState(ConnectionState.Disconnected, reason);\n\t}\n\n\tprivate shouldWaitForJoinSignal() {\n\t\tassert(\n\t\t\tthis.connection !== undefined,\n\t\t\t0x4b4 /* all callers call here with active connection */,\n\t\t);\n\t\treturn this.connection.mode === \"write\" || this.readClientsWaitForJoinSignal;\n\t}\n\n\t/**\n\t * The \"connect\" event indicates the connection to the Relay Service is live.\n\t * However, some additional conditions must be met before we can fully transition to\n\t * \"Connected\" state. This function handles that interim period, known as \"Connecting\" state.\n\t * @param details - Connection details returned from the Relay Service\n\t * @param deltaManager - DeltaManager to be used for delaying Connected transition until caught up.\n\t * If it's undefined, then don't delay and transition to Connected as soon as Leave/Join op are accounted for\n\t */\n\tpublic receivedConnectEvent(details: IConnectionDetailsInternal) {\n\t\tthis.connection = details;\n\n\t\tconst oldState = this._connectionState;\n\t\tthis._connectionState = ConnectionState.CatchingUp;\n\n\t\t// The following checks are wrong. They are only valid if user has write access to a file.\n\t\t// If user lost such access mid-session, user will not be able to get \"write\" connection.\n\t\t//\n\t\t// const writeConnection = details.mode === \"write\";\n\t\t// assert(!this.handler.shouldClientJoinWrite() || writeConnection,\n\t\t// 0x30a /* shouldClientJoinWrite should imply this is a writeConnection */);\n\t\t// assert(!this.waitingForLeaveOp || writeConnection,\n\t\t// 0x2a6 /* \"waitingForLeaveOp should imply writeConnection (we need to be ready to flush pending ops)\" */);\n\n\t\t// Stash the clientID to detect when transitioning from connecting (socket.io channel open) to connected\n\t\t// (have received the join message for the client ID)\n\t\t// This is especially important in the reconnect case. It's possible there could be outstanding\n\t\t// ops sent by this client, so we should keep the old client id until we see our own client's\n\t\t// join message. after we see the join message for our new connection with our new client id,\n\t\t// we know there can no longer be outstanding ops that we sent with the previous client id.\n\t\tthis._pendingClientId = details.clientId;\n\n\t\t// IMPORTANT: Report telemetry after we set _pendingClientId, but before transitioning to Connected state\n\t\tthis.handler.connectionStateChanged(ConnectionState.CatchingUp, oldState);\n\n\t\t// Check if we need to wait for join op/signal, and if we need to wait for leave op from previous connection.\n\t\t// Pending clientId could have joined already (i.e. join op/signal already processed):\n\t\t// We are fetching ops from storage in parallel to connecting to Relay Service,\n\t\t// and given async processes, it's possible that we have already processed our own join message before\n\t\t// connection was fully established.\n\t\tif (!this.hasMember(this._pendingClientId) && this.shouldWaitForJoinSignal()) {\n\t\t\t// We are waiting for our own join op / signal. When it is processed\n\t\t\t// we'll attempt to transition to Connected state via receivedAddMemberEvent() flow.\n\t\t\tthis.startJoinOpTimer();\n\t\t} else if (!this.waitingForLeaveOp) {\n\t\t\t// We're not waiting for Join or Leave op (if read-only connection those don't even apply),\n\t\t\t// go ahead and declare the state to be Connected!\n\t\t\t// If we are waiting for Leave op still, do nothing for now, we will transition to Connected later.\n\t\t\tthis.setConnectionState(ConnectionState.Connected);\n\t\t}\n\t\t// else - We are waiting for Leave op still, do nothing for now, we will transition to Connected later\n\t}\n\n\tprivate setConnectionState(value: ConnectionState.Disconnected, reason: string): void;\n\tprivate setConnectionState(value: ConnectionState.Connected): void;\n\tprivate setConnectionState(\n\t\tvalue: ConnectionState.Disconnected | ConnectionState.Connected,\n\t\treason?: string,\n\t): void {\n\t\tif (this.connectionState === value) {\n\t\t\t// Already in the desired state - exit early\n\t\t\tthis.handler.logger.sendErrorEvent({ eventName: \"setConnectionStateSame\", value });\n\t\t\treturn;\n\t\t}\n\n\t\tconst oldState = this._connectionState;\n\t\tthis._connectionState = value;\n\n\t\t// This is the only place in code that deals with quorum. The rest works with audience\n\t\t// The code below ensures that we do not send ops until we know that old \"write\" client's disconnect\n\t\t// produced (and sequenced) leave op\n\t\tlet client: ILocalSequencedClient | undefined;\n\t\tif (this._clientId !== undefined) {\n\t\t\tclient = this.protocol?.quorum?.getMember(this._clientId);\n\t\t}\n\t\tif (value === ConnectionState.Connected) {\n\t\t\tassert(\n\t\t\t\toldState === ConnectionState.CatchingUp,\n\t\t\t\t0x1d8 /* \"Should only transition from Connecting state\" */,\n\t\t\t);\n\t\t\t// Mark our old client should have left in the quorum if it's still there\n\t\t\tif (client !== undefined) {\n\t\t\t\tclient.shouldHaveLeft = true;\n\t\t\t}\n\t\t\tthis._clientId = this.pendingClientId;\n\t\t} else if (value === ConnectionState.Disconnected) {\n\t\t\t// Clear pending state immediately to prepare for reconnect\n\t\t\tthis._pendingClientId = undefined;\n\n\t\t\tif (this.joinOpTimer.hasTimer) {\n\t\t\t\tthis.stopJoinOpTimer();\n\t\t\t}\n\n\t\t\t// Only wait for \"leave\" message if the connected client exists in the quorum and had some non-acked ops\n\t\t\t// Also check if the timer is not already running as\n\t\t\t// we could receive \"Disconnected\" event multiple times without getting connected and in that case we\n\t\t\t// don't want to reset the timer as we still want to wait on original client which started this timer.\n\t\t\tif (\n\t\t\t\tclient !== undefined &&\n\t\t\t\tthis.handler.shouldClientJoinWrite() &&\n\t\t\t\t!this.waitingForLeaveOp // same as !this.prevClientLeftTimer.hasTimer\n\t\t\t) {\n\t\t\t\tthis.prevClientLeftTimer.restart();\n\t\t\t} else {\n\t\t\t\t// Adding this event temporarily so that we can get help debugging if something goes wrong.\n\t\t\t\tthis.handler.logger.sendTelemetryEvent({\n\t\t\t\t\teventName: \"noWaitOnDisconnected\",\n\t\t\t\t\tdetails: JSON.stringify({\n\t\t\t\t\t\tclientId: this._clientId,\n\t\t\t\t\t\tinQuorum: client !== undefined,\n\t\t\t\t\t\twaitingForLeaveOp: this.waitingForLeaveOp,\n\t\t\t\t\t\thadOutstandingOps: this.handler.shouldClientJoinWrite(),\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Report transition before we propagate event across layers\n\t\tthis.handler.connectionStateChanged(this._connectionState, oldState, reason);\n\t}\n\n\t// Helper method to switch between quorum and audience.\n\t// Old design was checking only quorum for \"write\" clients.\n\t// Latest change checks audience for all types of connections.\n\tprotected get membership(): IMembership | undefined {\n\t\t// We could always use audience here, and in practice it will probably be correct.\n\t\t// (including case when this.readClientsWaitForJoinSignal === false).\n\t\t// But only if it's superset of quorum, i.e. when filtered to \"write\" clients, they are always identical!\n\t\t// It's safer to assume that we have bugs and engaging kill-bit switch should bring us back to well-known\n\t\t// and tested state!\n\t\treturn this.readClientsWaitForJoinSignal ? this.protocol?.audience : this.protocol?.quorum;\n\t}\n\n\tpublic initProtocol(protocol: IProtocolHandler) {\n\t\tthis.protocol = protocol;\n\n\t\tthis.membership?.on(\"addMember\", (clientId, details) => {\n\t\t\tassert(\n\t\t\t\t(details as IClient).mode === \"read\" ||\n\t\t\t\t\tprotocol.quorum.getMember(clientId) !== undefined,\n\t\t\t\t0x4b5 /* Audience is subset of quorum */,\n\t\t\t);\n\t\t\tthis.receivedAddMemberEvent(clientId);\n\t\t});\n\n\t\tthis.membership?.on(\"removeMember\", (clientId) => {\n\t\t\tassert(\n\t\t\t\tprotocol.quorum.getMember(clientId) === undefined,\n\t\t\t\t0x4b6 /* Audience is subset of quorum */,\n\t\t\t);\n\t\t\tthis.receivedRemoveMemberEvent(clientId);\n\t\t});\n\n\t\t/* There is a tiny tiny race possible, where these events happen in this order:\n 1. A connection is established (no \"cached\" mode is used, so it happens in parallel / faster than other steps)\n 2. Some other client produces a summary\n 3. We get \"lucky\" and load from that summary as our initial snapshot\n 4. ConnectionStateHandler.initProtocol is called, \"self\" is already in the quorum.\n We could avoid this sequence (and delete test case for it) if we move connection lower in Container.load()\n */\n\t\tif (this.hasMember(this.pendingClientId)) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tthis.receivedAddMemberEvent(this.pendingClientId!);\n\t\t}\n\n\t\t// if we have a clientId from a previous container we need to wait for its leave message\n\t\tif (this.clientId !== undefined && this.hasMember(this.clientId)) {\n\t\t\tthis.prevClientLeftTimer.restart();\n\t\t}\n\t}\n\n\tprotected hasMember(clientId?: string) {\n\t\treturn this.membership?.getMember(clientId ?? \"\") !== undefined;\n\t}\n}\n"]}
@@ -6,11 +6,16 @@ import { ITelemetryLogger, ITelemetryProperties } from "@fluidframework/common-d
6
6
  import { IRequest, IResponse, IFluidRouter, FluidObject } from "@fluidframework/core-interfaces";
7
7
  import { IAudience, IContainer, IContainerEvents, IDeltaManager, ICriticalContainerError, AttachState, ReadOnlyInfo, IContainerLoadMode, IFluidCodeDetails } from "@fluidframework/container-definitions";
8
8
  import { IDocumentStorageService, IFluidResolvedUrl, IResolvedUrl } from "@fluidframework/driver-definitions";
9
- import { IClientConfiguration, IClientDetails, IDocumentMessage, IProtocolState, IQuorumClients, ISequencedDocumentMessage, IVersion } from "@fluidframework/protocol-definitions";
9
+ import { IClientConfiguration, IClientDetails, IDocumentMessage, IQuorumClients, ISequencedDocumentMessage, ISnapshotTree, IVersion } from "@fluidframework/protocol-definitions";
10
10
  import { EventEmitterWithErrorHandling, TelemetryLogger } from "@fluidframework/telemetry-utils";
11
11
  import { ILoaderOptions, Loader } from "./loader";
12
+ import { ISerializableBlobContents } from "./containerStorageAdapter";
12
13
  import { ConnectionState } from "./connectionState";
13
14
  import { ProtocolHandlerBuilder } from "./protocol";
15
+ /**
16
+ * @deprecated this is an internal interface and will not longer be exported in future versions
17
+ * @internal
18
+ */
14
19
  export interface IContainerLoadOptions {
15
20
  /**
16
21
  * Disables the Container from reconnecting if false, allows reconnect otherwise.
@@ -30,6 +35,10 @@ export interface IContainerLoadOptions {
30
35
  */
31
36
  loadMode?: IContainerLoadMode;
32
37
  }
38
+ /**
39
+ * @deprecated this is an internal interface and will not longer be exported in future versions
40
+ * @internal
41
+ */
33
42
  export interface IContainerConfig {
34
43
  resolvedUrl?: IFluidResolvedUrl;
35
44
  canReconnect?: boolean;
@@ -69,11 +78,27 @@ export declare function ReportIfTooLong(logger: ITelemetryLogger, eventName: str
69
78
  /**
70
79
  * State saved by a container at close time, to be used to load a new instance
71
80
  * of the container to the same state
81
+ * @deprecated this is an internal interface and will not longer be exported in future versions
82
+ * @internal
72
83
  */
73
84
  export interface IPendingContainerState {
74
85
  pendingRuntimeState: unknown;
86
+ /**
87
+ * Snapshot from which container initially loaded.
88
+ */
89
+ baseSnapshot: ISnapshotTree;
90
+ /**
91
+ * Serializable blobs from the base snapshot. Used to load offline since
92
+ * storage is not available.
93
+ */
94
+ snapshotBlobs: ISerializableBlobContents;
95
+ /**
96
+ * All ops since base snapshot sequence number up to the latest op
97
+ * seen when the container was closed. Used to apply stashed (saved pending)
98
+ * ops at the same sequence number at which they were made.
99
+ */
100
+ savedOps: ISequencedDocumentMessage[];
75
101
  url: string;
76
- protocol: IProtocolState;
77
102
  term: number;
78
103
  clientId?: string;
79
104
  }
@@ -86,6 +111,7 @@ export declare class Container extends EventEmitterWithErrorHandling<IContainerE
86
111
  static version: string;
87
112
  /**
88
113
  * Load an existing container.
114
+ * @internal
89
115
  */
90
116
  static load(loader: Loader, loadOptions: IContainerLoadOptions, pendingLocalState?: IPendingContainerState, protocolHandlerBuilder?: ProtocolHandlerBuilder): Promise<Container>;
91
117
  /**
@@ -137,6 +163,9 @@ export declare class Container extends EventEmitterWithErrorHandling<IContainerE
137
163
  private _resolvedUrl;
138
164
  private attachStarted;
139
165
  private _dirtyContainer;
166
+ private readonly savedOps;
167
+ private baseSnapshot?;
168
+ private baseSnapshotBlobs?;
140
169
  private lastVisible;
141
170
  private readonly visibilityEventHandler;
142
171
  private readonly connectionStateHandler;
@@ -172,6 +201,7 @@ export declare class Container extends EventEmitterWithErrorHandling<IContainerE
172
201
  */
173
202
  get scopes(): string[] | undefined;
174
203
  get clientDetails(): IClientDetails;
204
+ private get offlineLoadEnabled();
175
205
  /**
176
206
  * Get the code details that are currently specified for the container.
177
207
  * @returns The current code details if any are specified, undefined if none are specified.
@@ -202,6 +232,9 @@ export declare class Container extends EventEmitterWithErrorHandling<IContainerE
202
232
  * {@inheritDoc @fluidframework/container-definitions#IContainer.entryPoint}
203
233
  */
204
234
  getEntryPoint?(): Promise<FluidObject | undefined>;
235
+ /**
236
+ * @internal
237
+ */
205
238
  constructor(loader: Loader, config: IContainerConfig, protocolHandlerBuilder?: ProtocolHandlerBuilder | undefined);
206
239
  /**
207
240
  * Retrieves the quorum associated with the document
@@ -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":"AAAA;;;GAGG;AAQH,OAAO,EACN,gBAAgB,EAChB,oBAAoB,EAEpB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AACjG,OAAO,EACN,SAAS,EAET,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,uBAAuB,EAEvB,WAAW,EAEX,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EAGjB,MAAM,uCAAuC,CAAC;AAE/C,OAAO,EAEN,uBAAuB,EACvB,iBAAiB,EACjB,YAAY,EACZ,MAAM,oCAAoC,CAAC;AAY5C,OAAO,EAEN,oBAAoB,EACpB,cAAc,EAGd,gBAAgB,EAChB,cAAc,EAGd,yBAAyB,EAGzB,aAAa,EAGb,QAAQ,EAGR,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAEN,6BAA6B,EAG7B,eAAe,EAMf,MAAM,iCAAiC,CAAC;AAMzC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAkB,MAAM,UAAU,CAAC;AAElE,OAAO,EAIN,yBAAyB,EACzB,MAAM,2BAA2B,CAAC;AAUnC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAIN,sBAAsB,EACtB,MAAM,YAAY,CAAC;AAOpB;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACrC;;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;CAC9B;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAChC,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;OAEG;IACH,qBAAqB,CAAC,EAAE,cAAc,CAAC;IACvC;;OAEG;IACH,wBAAwB,CAAC,EAAE,sBAAsB,CAAC;CAClD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,UAAU,oBA0EjE;AAMD;;;;;GAKG;AACH,wBAAsB,eAAe,CACpC,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,iBAO3C;AAED;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACtC,mBAAmB,EAAE,OAAO,CAAC;IAC7B;;OAEG;IACH,YAAY,EAAE,aAAa,CAAC;IAC5B;;;OAGG;IACH,aAAa,EAAE,yBAAyB,CAAC;IACzC;;;;OAIG;IACH,QAAQ,EAAE,yBAAyB,EAAE,CAAC;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAID;;GAEG;AACH,qBAAa,SACZ,SAAQ,6BAA6B,CAAC,gBAAgB,CACtD,YAAW,UAAU;IA2XpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAEvB,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAC;IA3XzC,OAAc,OAAO,SAAY;IAEjC;;;OAGG;WACiB,IAAI,CACvB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,qBAAqB,EAClC,iBAAiB,CAAC,EAAE,sBAAsB,EAC1C,sBAAsB,CAAC,EAAE,sBAAsB,GAC7C,OAAO,CAAC,SAAS,CAAC;IA0DrB;;OAEG;WACiB,cAAc,CACjC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,iBAAiB,EAC9B,sBAAsB,CAAC,EAAE,sBAAsB,GAC7C,OAAO,CAAC,SAAS,CAAC;IAcrB;;;OAGG;WACiB,6BAA6B,CAChD,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,sBAAsB,CAAC,EAAE,sBAAsB,GAC7C,OAAO,CAAC,SAAS,CAAC;IAed,SAAS,EAAE,eAAe,CAAC;IAIlC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiB;IAE/C,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAoB;IAEvC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,eAAe,CAMG;IAE1B,OAAO,CAAC,SAAS;IAUjB,IAAW,MAAM,IAAI,OAAO,CAO3B;IAED,OAAO,CAAC,YAAY,CAAwB;IAE5C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA0B;IACzD,IAAW,OAAO,IAAI,uBAAuB,CAE5C;IAED,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAA6B;IACnE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkC;IAChE,OAAO,CAAC,OAAO,CAA+B;IAE9C,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,KAAK,OAAO,GAKlB;IACD,OAAO,CAAC,gBAAgB,CAA+B;IACvD,OAAO,KAAK,eAAe,GAK1B;IAED,gHAAgH;IAChH,OAAO,CAAC,0BAA0B,CAAQ;IAC1C,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAgB;IAC1D,OAAO,CAAC,8BAA8B,CAAa;IACnD,OAAO,CAAC,kBAAkB,CAAuB;IACjD,OAAO,CAAC,YAAY,CAAgC;IACpD,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmC;IAC5D,OAAO,CAAC,YAAY,CAAC,CAAgB;IACrC,OAAO,CAAC,iBAAiB,CAAC,CAA4B;IAEtD,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAA2B;IAClE,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAA0B;IAEjE,OAAO,CAAC,oBAAoB,CAAqB;IAEjD,OAAO,CAAC,mBAAmB,CAAkC;IAE7D,OAAO,KAAK,cAAc,GAEzB;IAED,IAAW,YAAY,IAAI,YAAY,CAEtC;IAED,IAAW,WAAW,IAAI,YAAY,GAAG,SAAS,CAEjD;IAED,IAAW,iBAAiB,IAAI,QAAQ,GAAG,SAAS,CAEnD;IAED,IAAW,YAAY,IAAI,YAAY,CAEtC;IAED,IAAW,WAAW,IAAI,WAAW,CAEpC;IAED;;OAEG;IACI,aAAa,CAAC,QAAQ,EAAE,OAAO;IAItC,IAAW,YAAY,IAAI,aAAa,CAAC,yBAAyB,EAAE,gBAAgB,CAAC,CAEpF;IAED,IAAW,eAAe,IAAI,eAAe,CAE5C;IAED,IAAW,SAAS,IAAI,OAAO,CAE9B;IAED;;;OAGG;IACH,IAAW,oBAAoB,IAAI,oBAAoB,GAAG,SAAS,CAElE;IAED,OAAO,CAAC,SAAS,CAAqB;IAEtC;;;OAGG;IACH,IAAW,QAAQ,IAAI,MAAM,GAAG,SAAS,CAExC;IAED;;;OAGG;IACH,IAAW,MAAM,IAAI,MAAM,EAAE,GAAG,SAAS,CAExC;IAED,IAAW,aAAa,IAAI,cAAc,CAEzC;IAED,OAAO,KAAK,kBAAkB,GAM7B;IAED;;;OAGG;IACI,uBAAuB,IAAI,iBAAiB,GAAG,SAAS;IAI/D;;;;OAIG;IACI,oBAAoB,IAAI,iBAAiB,GAAG,SAAS;IAI5D;;OAEG;IACH,IAAW,QAAQ,IAAI,SAAS,CAE/B;IAED;;;;OAIG;IACH,IAAW,OAAO,YAEjB;IAED,OAAO,KAAK,cAAc,GAEzB;IACD,OAAO,KAAK,WAAW,GAEtB;IACD,SAAgB,OAAO,EAAE,cAAc,CAAC;IACxC,OAAO,KAAK,KAAK,GAEhB;IACD,OAAO,KAAK,UAAU,GAErB;IAED;;OAEG;IACU,aAAa,CAAC,IAAI,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAgC/D;;OAEG;gBAEe,MAAM,EAAE,MAAM,EAC/B,MAAM,EAAE,gBAAgB,EACP,sBAAsB,CAAC,oCAAwB;IA0KjE;;OAEG;IACI,SAAS,IAAI,cAAc;IAI3B,OAAO,CAAC,CAAC,KAAK,CAAC,EAAE,uBAAuB;IAKxC,KAAK,CAAC,KAAK,CAAC,EAAE,uBAAuB;IAS5C,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,SAAS;IAiDjB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW;IAoDZ,4BAA4B,IAAI,MAAM;IAoC7C,IAAW,WAAW,IAAI,WAAW,CAEpC;IAEM,SAAS,IAAI,MAAM;IAsBb,MAAM,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAqJxC,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IASxD,OAAO,CAAC,wBAAwB;IAsBzB,OAAO;IAad,OAAO,CAAC,eAAe;IAehB,UAAU;IAQjB,OAAO,CAAC,kBAAkB;IAQ1B,OAAO,CAAC,cAAc;IAcT,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAYhE,kBAAkB,CAAC,WAAW,EAAE,iBAAiB;YAqBhD,mBAAmB;YAoBnB,UAAU;IAKxB,OAAO,CAAC,sBAAsB;IAM9B,OAAO,CAAC,oBAAoB;IAW5B;;;;OAIG;YACW,IAAI;YAiMJ,cAAc;YA4Bd,6BAA6B;YAyC7B,qBAAqB;YAuBrB,mCAAmC;IAiCjD,OAAO,CAAC,uBAAuB;IAgD/B,OAAO,CAAC,sBAAsB;IA2B9B,OAAO,CAAC,wBAAwB;IAQhC,OAAO,KAAK,MAAM,GAsBjB;IAED;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,kBAAkB;YAgEZ,2BAA2B;IAiBzC,OAAO,CAAC,iCAAiC;IAwDzC,OAAO,CAAC,wBAAwB;IAoChC,OAAO,CAAC,sBAAsB;IAwB9B,+DAA+D;IAC/D,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,oBAAoB;IAmB5B,OAAO,CAAC,aAAa;IAyBrB,OAAO,CAAC,oBAAoB;IA4C5B,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,aAAa;IAUrB;;;;OAIG;YACW,iBAAiB;YAqBjB,0BAA0B;YAS1B,kBAAkB;IAsChC,OAAO,CAAC,yBAAyB;IAQjC;;;;;OAKG;IACH,OAAO,CAAC,wBAAwB;CAWhC"}
package/dist/container.js CHANGED
@@ -10,6 +10,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.Container = exports.ReportIfTooLong = exports.waitContainerToCatchUp = void 0;
11
11
  // eslint-disable-next-line import/no-internal-modules
12
12
  const merge_1 = __importDefault(require("lodash/merge"));
13
+ // eslint-disable-next-line import/no-internal-modules
14
+ const cloneDeep_1 = __importDefault(require("lodash/cloneDeep"));
13
15
  const uuid_1 = require("uuid");
14
16
  const common_utils_1 = require("@fluidframework/common-utils");
15
17
  const container_definitions_1 = require("@fluidframework/container-definitions");
@@ -131,8 +133,11 @@ const summarizerClientType = "summarizer";
131
133
  * @deprecated - In the next release Container will no longer be exported, IContainer should be used in its place.
132
134
  */
133
135
  class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
136
+ /**
137
+ * @internal
138
+ */
134
139
  constructor(loader, config, protocolHandlerBuilder) {
135
- var _a, _b;
140
+ var _a, _b, _c;
136
141
  super((name, error) => {
137
142
  this.mc.logger.sendErrorEvent({
138
143
  eventName: "ContainerEventHandlerException",
@@ -168,6 +173,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
168
173
  this.messageCountAfterDisconnection = 0;
169
174
  this.attachStarted = false;
170
175
  this._dirtyContainer = false;
176
+ this.savedOps = [];
171
177
  this.setAutoReconnectTime = common_utils_1.performance.now();
172
178
  this._disposed = false;
173
179
  this.clientDetailsOverride = config.clientDetailsOverride;
@@ -212,9 +218,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
212
218
  });
213
219
  // Prefix all events in this file with container-loader
214
220
  this.mc = (0, telemetry_utils_1.loggerToMonitoringContext)(telemetry_utils_1.ChildLogger.create(this.subLogger, "Container"));
215
- this.options = Object.assign({}, this.loader.services.options);
221
+ this.options = (0, cloneDeep_1.default)(this.loader.services.options);
216
222
  this._deltaManager = this.createDeltaManager();
217
- this._clientId = (_a = config.serializedContainerState) === null || _a === void 0 ? void 0 : _a.clientId;
218
223
  this.connectionStateHandler = (0, connectionStateHandler_1.createConnectionStateHandler)({
219
224
  logger: this.mc.logger,
220
225
  connectionStateChanged: (value, oldState, reason) => {
@@ -253,7 +258,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
253
258
  this.connect();
254
259
  }
255
260
  },
256
- }, this.deltaManager, this._clientId);
261
+ }, this.deltaManager, (_a = config.serializedContainerState) === null || _a === void 0 ? void 0 : _a.clientId);
257
262
  this.on(savedContainerEvent, () => {
258
263
  this.connectionStateHandler.containerSaved();
259
264
  });
@@ -266,7 +271,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
266
271
  // Whether the combined summary tree has been forced on by either the loader option or the monitoring context.
267
272
  // Even if not forced on via this flag, combined summaries may still be enabled by service policy.
268
273
  const forceEnableSummarizeProtocolTree = (_b = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2")) !== null && _b !== void 0 ? _b : this.loader.services.options.summarizeProtocolTree;
269
- this.storageAdapter = new containerStorageAdapter_1.ContainerStorageAdapter(this.loader.services.detachedBlobStorage, this.mc.logger, addProtocolSummaryIfMissing, forceEnableSummarizeProtocolTree);
274
+ this.storageAdapter = new containerStorageAdapter_1.ContainerStorageAdapter(this.loader.services.detachedBlobStorage, this.mc.logger, (_c = config.serializedContainerState) === null || _c === void 0 ? void 0 : _c.snapshotBlobs, addProtocolSummaryIfMissing, forceEnableSummarizeProtocolTree);
270
275
  const isDomAvailable = typeof document === "object" &&
271
276
  document !== null &&
272
277
  typeof document.addEventListener === "function" &&
@@ -290,6 +295,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
290
295
  }
291
296
  /**
292
297
  * Load an existing container.
298
+ * @internal
293
299
  */
294
300
  static async load(loader, loadOptions, pendingLocalState, protocolHandlerBuilder) {
295
301
  const container = new Container(loader, {
@@ -438,6 +444,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
438
444
  get clientDetails() {
439
445
  return this._deltaManager.clientDetails;
440
446
  }
447
+ get offlineLoadEnabled() {
448
+ var _a;
449
+ // summarizer will not have any pending state we want to save
450
+ return (((_a = this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad")) !== null && _a !== void 0 ? _a : false) &&
451
+ this.clientDetails.capabilities.interactive);
452
+ }
441
453
  /**
442
454
  * Get the code details that are currently specified for the container.
443
455
  * @returns The current code details if any are specified, undefined if none are specified.
@@ -615,15 +627,21 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
615
627
  // runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
616
628
  // container at the same time we get pending state, otherwise this container could reconnect and resubmit with
617
629
  // a new clientId and a future container using stale pending state without the new clientId would resubmit them
630
+ if (!this.offlineLoadEnabled) {
631
+ throw new container_utils_1.UsageError("Can't get pending local state unless offline load is enabled");
632
+ }
618
633
  (0, common_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Attached, 0x0d1 /* "Container should be attached before close" */);
619
634
  (0, common_utils_1.assert)(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
620
635
  (0, common_utils_1.assert)(!!this._protocolHandler, 0x2e3 /* "Must have a valid protocol handler instance" */);
621
- (0, common_utils_1.assert)(this._protocolHandler.attributes.term !== undefined, 0x37e /* Must have a valid protocol handler instance */);
636
+ (0, common_utils_1.assert)(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
637
+ (0, common_utils_1.assert)(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
622
638
  const pendingState = {
623
639
  pendingRuntimeState: this.context.getPendingLocalState(),
640
+ baseSnapshot: this.baseSnapshot,
641
+ snapshotBlobs: this.baseSnapshotBlobs,
642
+ savedOps: this.savedOps,
624
643
  url: this.resolvedUrl.url,
625
- protocol: this.protocolHandler.getProtocolState(),
626
- term: this._protocolHandler.attributes.term,
644
+ term: protocol_1.OnlyValidTermValue,
627
645
  clientId: this.clientId,
628
646
  };
629
647
  this.mc.logger.sendTelemetryEvent({ eventName: "CloseAndGetPendingLocalState" });
@@ -675,7 +693,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
675
693
  // starting to attach the container to storage.
676
694
  // Also, this should only be fired in detached container.
677
695
  this._attachState = container_definitions_1.AttachState.Attaching;
678
- this.context.notifyAttaching((0, utils_1.getSnapshotTreeFromSerializedContainer)(summary));
696
+ this.emit("attaching");
697
+ if (this.offlineLoadEnabled) {
698
+ const snapshot = (0, utils_1.getSnapshotTreeFromSerializedContainer)(summary);
699
+ this.baseSnapshot = snapshot;
700
+ this.baseSnapshotBlobs =
701
+ (0, containerStorageAdapter_1.getBlobContentsFromTreeWithBlobContents)(snapshot);
702
+ }
679
703
  }
680
704
  // Actually go and create the resolved document
681
705
  const createNewResolvedUrl = await this.urlResolver.resolve(request);
@@ -712,7 +736,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
712
736
  const protocolSummary = this.captureProtocolSummary();
713
737
  summary = (0, driver_utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
714
738
  this._attachState = container_definitions_1.AttachState.Attaching;
715
- this.context.notifyAttaching((0, utils_1.getSnapshotTreeFromSerializedContainer)(summary));
739
+ this.emit("attaching");
740
+ if (this.offlineLoadEnabled) {
741
+ const snapshot = (0, utils_1.getSnapshotTreeFromSerializedContainer)(summary);
742
+ this.baseSnapshot = snapshot;
743
+ this.baseSnapshotBlobs =
744
+ (0, containerStorageAdapter_1.getBlobContentsFromTreeWithBlobContents)(snapshot);
745
+ }
716
746
  await this.storageAdapter.uploadSummaryWithContext(summary, {
717
747
  referenceSequenceNumber: 0,
718
748
  ackHandle: undefined,
@@ -870,6 +900,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
870
900
  * @param specifiedVersion - Version SHA to load snapshot. If not specified, will fetch the latest snapshot.
871
901
  */
872
902
  async load(specifiedVersion, loadMode, pendingLocalState) {
903
+ var _a;
873
904
  if (this._resolvedUrl === undefined) {
874
905
  throw new Error("Attempting to load without a resolved url");
875
906
  }
@@ -890,7 +921,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
890
921
  };
891
922
  // Start websocket connection as soon as possible. Note that there is no op handler attached yet, but the
892
923
  // DeltaManager is resilient to this and will wait to start processing ops until after it is attached.
893
- if (loadMode.deltaConnection === undefined) {
924
+ if (loadMode.deltaConnection === undefined && !pendingLocalState) {
894
925
  this.connectToDeltaStream(connectionArgs);
895
926
  }
896
927
  if (!pendingLocalState) {
@@ -908,15 +939,23 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
908
939
  // Fetch specified snapshot.
909
940
  const { snapshot, versionId } = pendingLocalState === undefined
910
941
  ? await this.fetchSnapshotTree(specifiedVersion)
911
- : { snapshot: undefined, versionId: undefined };
912
- (0, common_utils_1.assert)(snapshot !== undefined || pendingLocalState !== undefined, 0x237 /* "Snapshot should exist" */);
913
- const attributes = pendingLocalState === undefined
914
- ? await this.getDocumentAttributes(this.storageAdapter, snapshot)
915
- : {
916
- sequenceNumber: pendingLocalState.protocol.sequenceNumber,
917
- minimumSequenceNumber: pendingLocalState.protocol.minimumSequenceNumber,
918
- term: pendingLocalState.term,
919
- };
942
+ : { snapshot: pendingLocalState.baseSnapshot, versionId: undefined };
943
+ if (pendingLocalState) {
944
+ this.baseSnapshot = pendingLocalState.baseSnapshot;
945
+ this.baseSnapshotBlobs = pendingLocalState.snapshotBlobs;
946
+ }
947
+ else {
948
+ (0, common_utils_1.assert)(snapshot !== undefined, 0x237 /* "Snapshot should exist" */);
949
+ if (this.offlineLoadEnabled) {
950
+ this.baseSnapshot = snapshot;
951
+ // Save contents of snapshot now, otherwise closeAndGetPendingLocalState() must be async
952
+ this.baseSnapshotBlobs = await (0, containerStorageAdapter_1.getBlobContentsFromTree)(snapshot, this.storage);
953
+ }
954
+ }
955
+ const attributes = await this.getDocumentAttributes(this.storageAdapter, snapshot);
956
+ // If we saved ops, we will replay them and don't need DeltaManager to fetch them
957
+ const sequenceNumber = (_a = pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.savedOps[pendingLocalState.savedOps.length - 1]) === null || _a === void 0 ? void 0 : _a.sequenceNumber;
958
+ const dmAttributes = sequenceNumber !== undefined ? Object.assign(Object.assign({}, attributes), { sequenceNumber }) : attributes;
920
959
  let opsBeforeReturnP;
921
960
  // Attach op handlers to finish initialization and be able to start processing ops
922
961
  // Kick off any ops fetching if required.
@@ -924,32 +963,35 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
924
963
  case undefined:
925
964
  // Start prefetch, but not set opsBeforeReturnP - boot is not blocked by it!
926
965
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
927
- this.attachDeltaManagerOpHandler(attributes, loadMode.deltaConnection !== "none" ? "all" : "none");
966
+ this.attachDeltaManagerOpHandler(dmAttributes, loadMode.deltaConnection !== "none" ? "all" : "none");
928
967
  break;
929
968
  case "cached":
930
- opsBeforeReturnP = this.attachDeltaManagerOpHandler(attributes, "cached");
969
+ opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "cached");
931
970
  break;
932
971
  case "all":
933
- opsBeforeReturnP = this.attachDeltaManagerOpHandler(attributes, "all");
972
+ opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "all");
934
973
  break;
935
974
  default:
936
975
  (0, common_utils_1.unreachableCase)(loadMode.opsBeforeReturn);
937
976
  }
938
977
  // ...load in the existing quorum
939
978
  // Initialize the protocol handler
940
- if (pendingLocalState === undefined) {
941
- await this.initializeProtocolStateFromSnapshot(attributes, this.storageAdapter, snapshot);
942
- }
943
- else {
944
- this.initializeProtocolState(attributes, {
945
- members: pendingLocalState.protocol.members,
946
- proposals: pendingLocalState.protocol.proposals,
947
- values: pendingLocalState.protocol.values,
948
- });
949
- }
979
+ await this.initializeProtocolStateFromSnapshot(attributes, this.storageAdapter, snapshot);
950
980
  const codeDetails = this.getCodeDetailsFromQuorum();
951
981
  await this.instantiateContext(true, // existing
952
982
  codeDetails, snapshot, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.pendingRuntimeState);
983
+ // replay saved ops
984
+ if (pendingLocalState) {
985
+ for (const message of pendingLocalState.savedOps) {
986
+ this.processRemoteMessage(message);
987
+ // allow runtime to apply stashed ops at this op's sequence number
988
+ await this.context.notifyOpReplay(message);
989
+ }
990
+ pendingLocalState.savedOps = [];
991
+ // now set clientId to stashed clientId so live ops are correctly processed as local
992
+ (0, common_utils_1.assert)(this.clientId === undefined, 0x5d6 /* Unexpected clientId when setting stashed clientId */);
993
+ this._clientId = pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.clientId;
994
+ }
953
995
  // We might have hit some failure that did not manifest itself in exception in this flow,
954
996
  // do not start op processing in such case - static version of Container.load() will handle it correctly.
955
997
  if (!this.closed) {
@@ -962,6 +1004,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
962
1004
  }
963
1005
  switch (loadMode.deltaConnection) {
964
1006
  case undefined:
1007
+ if (pendingLocalState) {
1008
+ // connect to delta stream now since we did not before
1009
+ this.connectToDeltaStream(connectionArgs);
1010
+ }
1011
+ // intentional fallthrough
965
1012
  case "delayed":
966
1013
  (0, common_utils_1.assert)(this.inboundQueuePausedFromInit, 0x346 /* inboundQueuePausedFromInit should be true */);
967
1014
  this.inboundQueuePausedFromInit = false;
@@ -994,7 +1041,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
994
1041
  async createDetached(source) {
995
1042
  const attributes = {
996
1043
  sequenceNumber: detachedContainerRefSeqNumber,
997
- term: 1,
1044
+ term: protocol_1.OnlyValidTermValue,
998
1045
  minimumSequenceNumber: 0,
999
1046
  };
1000
1047
  await this.attachDeltaManagerOpHandler(attributes);
@@ -1037,7 +1084,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1037
1084
  return {
1038
1085
  minimumSequenceNumber: 0,
1039
1086
  sequenceNumber: 0,
1040
- term: 1,
1087
+ term: protocol_1.OnlyValidTermValue,
1041
1088
  };
1042
1089
  }
1043
1090
  // Backward compatibility: old docs would have ".attributes" instead of "attributes"
@@ -1045,10 +1092,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1045
1092
  ? tree.trees[".protocol"].blobs.attributes
1046
1093
  : tree.blobs[".attributes"];
1047
1094
  const attributes = await (0, driver_utils_1.readAndParse)(storage, attributesHash);
1048
- // Backward compatibility for older summaries with no term
1049
- if (attributes.term === undefined) {
1050
- attributes.term = 1;
1051
- }
1052
1095
  return attributes;
1053
1096
  }
1054
1097
  async initializeProtocolStateFromSnapshot(attributes, storage, snapshot) {
@@ -1168,7 +1211,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1168
1211
  }
1169
1212
  createDeltaManager() {
1170
1213
  const serviceProvider = () => this.service;
1171
- const deltaManager = new deltaManager_1.DeltaManager(serviceProvider, telemetry_utils_1.ChildLogger.create(this.subLogger, "DeltaManager"), () => this.activeConnection(), (props) => new connectionManager_1.ConnectionManager(serviceProvider, this.client, this._canReconnect, telemetry_utils_1.ChildLogger.create(this.subLogger, "ConnectionManager"), props));
1214
+ const deltaManager = new deltaManager_1.DeltaManager(serviceProvider, telemetry_utils_1.ChildLogger.create(this.subLogger, "DeltaManager"), () => this.activeConnection(), (props) => new connectionManager_1.ConnectionManager(serviceProvider, () => this.isDirty, this.client, this._canReconnect, telemetry_utils_1.ChildLogger.create(this.subLogger, "ConnectionManager"), props));
1172
1215
  // Disable inbound queues as Container is not ready to accept any ops until we are fully loaded!
1173
1216
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
1174
1217
  deltaManager.inbound.pause();
@@ -1207,8 +1250,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1207
1250
  return deltaManager;
1208
1251
  }
1209
1252
  async attachDeltaManagerOpHandler(attributes, prefetchType) {
1210
- var _a;
1211
- return this._deltaManager.attachOpHandler(attributes.minimumSequenceNumber, attributes.sequenceNumber, (_a = attributes.term) !== null && _a !== void 0 ? _a : 1, {
1253
+ return this._deltaManager.attachOpHandler(attributes.minimumSequenceNumber, attributes.sequenceNumber, {
1212
1254
  process: (message) => this.processRemoteMessage(message),
1213
1255
  processSignal: (message) => {
1214
1256
  this.processSignal(message);
@@ -1271,7 +1313,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1271
1313
  this.messageCountAfterDisconnection = 0;
1272
1314
  }
1273
1315
  // Both protocol and context should not be undefined if we got so far.
1274
- this.setContextConnectedState(state, (_a = this._deltaManager.connectionManager.readOnlyInfo.readonly) !== null && _a !== void 0 ? _a : false);
1316
+ this.setContextConnectedState(state, (_a = this.readOnlyInfo.readonly) !== null && _a !== void 0 ? _a : false);
1275
1317
  this.protocolHandler.setConnectionState(state, this.clientId);
1276
1318
  (0, telemetry_utils_1.raiseConnectedEvent)(this.mc.logger, this, state, this.clientId, disconnectedReason);
1277
1319
  if (logOpsOnReconnect) {
@@ -1329,6 +1371,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
1329
1371
  return this._deltaManager.submit(type, contents, batch, metadata, compression, referenceSequenceNumber);
1330
1372
  }
1331
1373
  processRemoteMessage(message) {
1374
+ if (this.offlineLoadEnabled) {
1375
+ this.savedOps.push(message);
1376
+ }
1332
1377
  const local = this.clientId === message.clientId;
1333
1378
  // Allow the protocol handler to process the message
1334
1379
  const result = this.protocolHandler.processMessage(message, local);