@fluidframework/container-runtime 0.58.3000-61081 → 0.59.1001-62246
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blobManager.d.ts +13 -1
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +52 -0
- package/dist/blobManager.js.map +1 -1
- package/dist/connectionTelemetry.js +7 -7
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +27 -3
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +100 -14
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.js +8 -1
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +9 -3
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +22 -6
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +13 -5
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +39 -18
- package/dist/dataStores.js.map +1 -1
- package/dist/deltaScheduler.d.ts +4 -5
- package/dist/deltaScheduler.d.ts.map +1 -1
- package/dist/deltaScheduler.js +54 -35
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/garbageCollection.d.ts +31 -27
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +76 -75
- package/dist/garbageCollection.js.map +1 -1
- package/dist/orderedClientElection.d.ts +6 -57
- package/dist/orderedClientElection.d.ts.map +1 -1
- package/dist/orderedClientElection.js +25 -140
- package/dist/orderedClientElection.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/summarizerClientElection.d.ts +0 -2
- package/dist/summarizerClientElection.d.ts.map +1 -1
- package/dist/summarizerClientElection.js +2 -7
- package/dist/summarizerClientElection.js.map +1 -1
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +3 -14
- package/dist/summaryManager.js.map +1 -1
- package/lib/blobManager.d.ts +13 -1
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +52 -0
- package/lib/blobManager.js.map +1 -1
- package/lib/connectionTelemetry.js +7 -7
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +27 -3
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +101 -15
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.js +8 -1
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +9 -3
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +22 -6
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +13 -5
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +39 -18
- package/lib/dataStores.js.map +1 -1
- package/lib/deltaScheduler.d.ts +4 -5
- package/lib/deltaScheduler.d.ts.map +1 -1
- package/lib/deltaScheduler.js +54 -35
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/garbageCollection.d.ts +31 -27
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +75 -74
- package/lib/garbageCollection.js.map +1 -1
- package/lib/orderedClientElection.d.ts +6 -57
- package/lib/orderedClientElection.d.ts.map +1 -1
- package/lib/orderedClientElection.js +25 -140
- package/lib/orderedClientElection.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/summarizerClientElection.d.ts +0 -2
- package/lib/summarizerClientElection.d.ts.map +1 -1
- package/lib/summarizerClientElection.js +2 -7
- package/lib/summarizerClientElection.js.map +1 -1
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +3 -14
- package/lib/summaryManager.js.map +1 -1
- package/package.json +33 -21
- package/src/blobManager.ts +60 -1
- package/src/connectionTelemetry.ts +7 -7
- package/src/containerRuntime.ts +106 -17
- package/src/dataStore.ts +7 -1
- package/src/dataStoreContext.ts +22 -7
- package/src/dataStores.ts +40 -19
- package/src/deltaScheduler.ts +65 -39
- package/src/garbageCollection.ts +92 -78
- package/src/orderedClientElection.ts +25 -154
- package/src/packageVersion.ts +1 -1
- package/src/summarizerClientElection.ts +2 -7
- package/src/summaryManager.ts +4 -15
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"summaryManager.js","sourceRoot":"","sources":["../src/summaryManager.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 { IDisposable, IEvent, IEventProvider, ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { assert } from \"@fluidframework/common-utils\";\nimport { ChildLogger, PerformanceEvent } from \"@fluidframework/telemetry-utils\";\nimport { DriverErrorType } from \"@fluidframework/driver-definitions\";\nimport { ISummarizerClientElection } from \"./summarizerClientElection\";\nimport { IThrottler } from \"./throttler\";\nimport {\n ISummarizer,\n ISummarizerOptions,\n SummarizerStopReason,\n} from \"./summarizerTypes\";\nimport { SummaryCollection } from \"./summaryCollection\";\n\nconst defaultInitialDelayMs = 5000;\nconst defaultOpsToBypassInitialDelay = 4000;\n\nexport enum SummaryManagerState {\n Off = 0,\n Starting = 1,\n Running = 2,\n Stopping = 3,\n}\n\n// Please note that all reasons in this list are not errors,\n// and thus they are not raised today to parent container as error.\n// If this needs to be changed in future, we should re-evaluate what and how we raise to summarizer\ntype StopReason = Extract<SummarizerStopReason, \"parentNotConnected\" | \"parentShouldNotSummarize\">;\ntype ShouldSummarizeState =\n | { shouldSummarize: true; }\n | { shouldSummarize: false; stopReason: StopReason; };\n\nexport interface IConnectedEvents extends IEvent {\n (event: \"connected\", listener: (clientId: string) => void);\n (event: \"disconnected\", listener: () => void);\n}\n\n/**\n * IConnectedState describes an object that SummaryManager can watch to observe connection/disconnection.\n *\n * Under current implementation, its role will be fulfilled by the ContainerRuntime, but this could be replaced\n * with anything else that fulfills the contract if we want to shift the layer that the SummaryManager lives at.\n */\nexport interface IConnectedState extends IEventProvider<IConnectedEvents> {\n readonly connected: boolean;\n\n /**\n * Under current implementation this is undefined if we've never connected, otherwise it's the clientId from our\n * latest connection (even if we've since disconnected!). Although this happens to be the behavior we want in\n * SummaryManager, I suspect that globally we may eventually want to modify this behavior (e.g. make clientId\n * undefined while disconnected). To protect against this, let's assume this field can't be trusted while\n * disconnected and instead separately track \"latest clientId\" in SummaryManager.\n */\n readonly clientId: string | undefined;\n}\n\nexport interface ISummaryManagerConfig {\n initialDelayMs: number;\n opsToBypassInitialDelay: number;\n}\n\n/**\n * SummaryManager is created by parent container (i.e. interactive container with clientType !== \"summarizer\") only.\n * It observes changes in calculated summarizer and reacts to changes by either creating summarizer client or\n * stopping existing summarizer client.\n */\nexport class SummaryManager implements IDisposable {\n private readonly logger: ITelemetryLogger;\n private readonly opsToBypassInitialDelay: number;\n private readonly initialDelayMs: number;\n private latestClientId: string | undefined;\n private state = SummaryManagerState.Off;\n private summarizer?: ISummarizer;\n private _disposed = false;\n\n public get disposed() {\n return this._disposed;\n }\n\n public get currentState() { return this.state; }\n\n constructor(\n private readonly clientElection: ISummarizerClientElection,\n private readonly connectedState: IConnectedState,\n private readonly summaryCollection:\n Pick<SummaryCollection, \"opsSinceLastAck\" | \"addOpListener\" | \"removeOpListener\">,\n parentLogger: ITelemetryLogger,\n /** Creates summarizer by asking interactive container to spawn summarizing container and\n * get back its Summarizer instance. */\n private readonly requestSummarizerFn: () => Promise<ISummarizer>,\n private readonly startThrottler: IThrottler,\n {\n initialDelayMs = defaultInitialDelayMs,\n opsToBypassInitialDelay = defaultOpsToBypassInitialDelay,\n }: Readonly<Partial<ISummaryManagerConfig>> = {},\n private readonly summarizerOptions?: Readonly<Partial<ISummarizerOptions>>,\n ) {\n this.logger = ChildLogger.create(\n parentLogger,\n \"SummaryManager\",\n {all:{ clientId: () => this.latestClientId }});\n\n this.connectedState.on(\"connected\", this.handleConnected);\n this.connectedState.on(\"disconnected\", this.handleDisconnected);\n this.latestClientId = this.connectedState.clientId;\n\n this.opsToBypassInitialDelay = opsToBypassInitialDelay;\n this.initialDelayMs = initialDelayMs;\n }\n\n /**\n * Until start is called, the SummaryManager won't begin attempting to start summarization. This ensures there's\n * a window between construction and starting where the caller can attach listeners.\n */\n public start(): void {\n this.clientElection.on(\"electedSummarizerChanged\", this.refreshSummarizer);\n this.refreshSummarizer();\n }\n\n private readonly handleConnected = (clientId: string) => {\n this.latestClientId = clientId;\n // If we have a summarizer, it should have been either cancelled on disconnected by now.\n // But because of lastSummary process, it can still hang around, so there is not much we can\n // check or assert.\n this.refreshSummarizer();\n };\n\n private readonly handleDisconnected = () => {\n this.refreshSummarizer();\n };\n\n private static readonly isStartingOrRunning = (state: SummaryManagerState) =>\n state === SummaryManagerState.Starting || state === SummaryManagerState.Running;\n\n private getShouldSummarizeState(): ShouldSummarizeState {\n // Note that if we're in the Running state, the electedClient may be a summarizer client, so we can't\n // enforce connectedState.clientId === clientElection.electedClientId. But once we're Running, we should\n // only transition to Stopping when the electedParentId changes. Stopping the summarizer without\n // changing the electedParent will just cause us to transition to Starting again.\n if (!this.connectedState.connected) {\n return { shouldSummarize: false, stopReason: \"parentNotConnected\" };\n } else if (this.connectedState.clientId !== this.clientElection.electedParentId ||\n (this.state !== SummaryManagerState.Running &&\n this.connectedState.clientId !== this.clientElection.electedClientId)) {\n return { shouldSummarize: false, stopReason: \"parentShouldNotSummarize\" };\n } else if (this.disposed) {\n assert(false, 0x260 /* \"Disposed should mean disconnected!\" */);\n } else {\n return { shouldSummarize: true };\n }\n }\n\n private readonly refreshSummarizer = () => {\n // Transition states depending on shouldSummarize, which is a calculated property\n // that is only true if this client is connected and is the elected summarizer.\n const shouldSummarizeState = this.getShouldSummarizeState();\n switch (this.state) {\n case SummaryManagerState.Off: {\n if (shouldSummarizeState.shouldSummarize) {\n this.startSummarization();\n }\n return;\n }\n case SummaryManagerState.Starting: {\n // Cannot take any action until summarizer is created\n // state transition will occur after creation\n return;\n }\n case SummaryManagerState.Running: {\n if (shouldSummarizeState.shouldSummarize === false) {\n this.stop(shouldSummarizeState.stopReason);\n }\n return;\n }\n case SummaryManagerState.Stopping: {\n // Cannot take any action until running summarizer finishes\n // state transition will occur after it stops\n return;\n }\n default: {\n return;\n }\n }\n };\n\n private startSummarization() {\n assert(this.state === SummaryManagerState.Off, 0x261 /* \"Expected: off\" */);\n this.state = SummaryManagerState.Starting;\n\n assert(this.summarizer === undefined, 0x262 /* \"Old summarizer is still working!\" */);\n\n let reason = \"unknown\";\n\n this.delayBeforeCreatingSummarizer().then(async (startWithInitialDelay: boolean) => {\n // Re-validate that it need to be running. Due to asynchrony, it may be not the case anymore\n // but only if creation was delayed. If it was not, then we want to ensure we always create\n // a summarizer to kick off lastSummary. Without that, we would not be able to summarize and get\n // document out of broken state if it has too many ops and ordering service keeps nacking main\n // container (and thus it goes into cycle of reconnects)\n if (startWithInitialDelay && this.getShouldSummarizeState().shouldSummarize === false) {\n return;\n }\n\n // We transition to Running before requesting the summarizer, because after requesting we can't predict\n // when the electedClient will be replaced with the new summarizer client.\n // The alternative would be to let connectedState.clientId !== clientElection.electedClientId when\n // state === Starting || state === Running.\n assert(this.state === SummaryManagerState.Starting, 0x263 /* \"Expected: starting\" */);\n this.state = SummaryManagerState.Running;\n\n const summarizer = await this.requestSummarizerFn();\n\n // Re-validate that it need to be running. Due to asynchrony, it may be not the case anymore\n const shouldSummarizeState = this.getShouldSummarizeState();\n if (shouldSummarizeState.shouldSummarize === false) {\n this.state = SummaryManagerState.Starting;\n summarizer.stop(shouldSummarizeState.stopReason);\n return;\n }\n\n this.summarizer = summarizer;\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const clientId = this.latestClientId!;\n\n reason = await PerformanceEvent.timedExecAsync(\n this.logger,\n { eventName: \"RunningSummarizer\", attempt: this.startThrottler.numAttempts },\n async () => summarizer.run(clientId, this.summarizerOptions),\n );\n }).catch((error) => {\n // Most of exceptions happen due to container being closed while loading it, due to\n // summarizer container loosing connection while load.\n // Not worth reporting such errors as errors. That said, we might miss some real errors if\n // we ignore blindly, so try to narrow signature we are looking for - skip logging\n // error only if this client should no longer be a summarizer (which in practice\n // means it also lost connection), and error happened on load (we do not have summarizer).\n // We could annotate the error raised in Container.load where the container closed during load with no error\n // and check for that case here, but that does not seem to be necessary.\n if (this.getShouldSummarizeState().shouldSummarize || this.summarizer !== undefined) {\n // Report any failure as an error unless it was due to cancellation (like \"disconnected\" error)\n // If failure happened on container load, we may not yet realized that socket disconnected, so check\n // offlineError.\n const category = error?.errorType === DriverErrorType.offlineError ? \"generic\" : \"error\";\n this.logger.sendTelemetryEvent(\n {\n eventName: \"SummarizerException\",\n category,\n },\n error);\n\n // Note that summarizer may keep going (like doing last summary).\n // Ideally we await stopping process, but this code path is due to a bug\n // that needs to be fixed either way.\n if (SummaryManager.isStartingOrRunning(this.state)) {\n this.stop(\"summarizerException\");\n }\n }\n }).finally(() => {\n assert(this.state !== SummaryManagerState.Off, 0x264 /* \"Expected: Not Off\" */);\n this.state = SummaryManagerState.Off;\n\n this.summarizer = undefined;\n\n this.logger.sendTelemetryEvent({\n eventName: \"EndingSummarizer\",\n reason,\n });\n\n if (this.getShouldSummarizeState().shouldSummarize) {\n this.startSummarization();\n }\n });\n }\n\n private stop(reason: SummarizerStopReason) {\n if (!SummaryManager.isStartingOrRunning(this.state)) {\n return;\n }\n this.state = SummaryManagerState.Stopping;\n\n // Stopping the running summarizer client should trigger a change\n // in states when the running summarizer closes\n this.summarizer?.stop(reason);\n }\n\n /**\n * Implements initial delay before creating summarizer\n * @returns true, if creation is delayed due to heuristics (not many ops to summarize).\n * False if summarizer should start immediately due to too many unsummarized ops.\n */\n private async delayBeforeCreatingSummarizer(): Promise<boolean> {\n // throttle creation of new summarizer containers to prevent spamming the server with websocket connections\n let delayMs = this.startThrottler.getDelay();\n\n // We have been elected the summarizer. Some day we may be able to summarize with a live document but for\n // now we play it safe and launch a second copy.\n this.logger.sendTelemetryEvent({\n eventName: \"CreatingSummarizer\",\n throttlerDelay: delayMs,\n initialDelay: this.initialDelayMs,\n startThrottlerMaxDelayMs: this.startThrottler.maxDelayMs,\n opsSinceLastAck: this.summaryCollection.opsSinceLastAck,\n opsToBypassInitialDelay: this.opsToBypassInitialDelay,\n });\n\n // This delay helps ensure that last summarizer that might be left from previous client\n // has enough time to complete its last summary and thus new summarizer not conflict with previous one.\n // If, however, there are too many unsummarized ops, try to resolve it as quickly as possible, with\n // understanding that we may see nacks because of such quick action.\n // A better design would be for summarizer election logic to always select current summarizer as\n // summarizing client (i.e. clientType === \"summarizer\" can be elected) to ensure that nobody else can\n // summarizer while it finishes its work and moves to exit.\n // It also helps with pure boot scenario (single client) to offset expensive work a bit out from\n // critical boot sequence.\n let startWithInitialDelay = false;\n if (this.summaryCollection.opsSinceLastAck < this.opsToBypassInitialDelay) {\n startWithInitialDelay = true;\n delayMs = Math.max(delayMs, this.initialDelayMs);\n }\n\n if (delayMs > 0) {\n let timer;\n let resolveOpPromiseFn;\n // Create a listener that will break the delay if we've exceeded the initial delay ops count.\n const opsListenerFn = () => {\n if (this.summaryCollection.opsSinceLastAck >= this.opsToBypassInitialDelay) {\n clearTimeout(timer);\n resolveOpPromiseFn();\n }\n };\n // Create a Promise that will resolve when the delay expires.\n const delayPromise = new Promise<void>((resolve) => {\n timer = setTimeout(() => resolve(), delayMs);\n });\n // Create a Promise that will resolve if the ops count passes the threshold.\n const opPromise = new Promise<void>((resolve) => { resolveOpPromiseFn = resolve; });\n this.summaryCollection.addOpListener(opsListenerFn);\n await Promise.race([ delayPromise, opPromise ]);\n this.summaryCollection.removeOpListener(opsListenerFn);\n }\n return startWithInitialDelay;\n }\n\n public readonly summarizeOnDemand: ISummarizer[\"summarizeOnDemand\"] = (...args) => {\n if (this.summarizer === undefined) {\n throw Error(\"No running summarizer client\");\n // TODO: could spawn a summarizer client temporarily.\n }\n return this.summarizer.summarizeOnDemand(...args);\n };\n\n public readonly enqueueSummarize: ISummarizer[\"enqueueSummarize\"] = (...args) => {\n if (this.summarizer === undefined) {\n throw Error(\"No running summarizer client\");\n // TODO: could spawn a summarizer client temporarily.\n }\n return this.summarizer.enqueueSummarize(...args);\n };\n\n public dispose() {\n this.clientElection.off(\"electedSummarizerChanged\", this.refreshSummarizer);\n this.connectedState.off(\"connected\", this.handleConnected);\n this.connectedState.off(\"disconnected\", this.handleDisconnected);\n this._disposed = true;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"summaryManager.js","sourceRoot":"","sources":["../src/summaryManager.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 { IDisposable, IEvent, IEventProvider, ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { assert } from \"@fluidframework/common-utils\";\nimport { ChildLogger, PerformanceEvent } from \"@fluidframework/telemetry-utils\";\nimport { DriverErrorType } from \"@fluidframework/driver-definitions\";\nimport { ISummarizerClientElection } from \"./summarizerClientElection\";\nimport { IThrottler } from \"./throttler\";\nimport {\n ISummarizer,\n ISummarizerOptions,\n SummarizerStopReason,\n} from \"./summarizerTypes\";\nimport { SummaryCollection } from \"./summaryCollection\";\n\nconst defaultInitialDelayMs = 5000;\nconst defaultOpsToBypassInitialDelay = 4000;\n\nexport enum SummaryManagerState {\n Off = 0,\n Starting = 1,\n Running = 2,\n Stopping = 3,\n}\n\n// Please note that all reasons in this list are not errors,\n// and thus they are not raised today to parent container as error.\n// If this needs to be changed in future, we should re-evaluate what and how we raise to summarizer\ntype StopReason = Extract<SummarizerStopReason, \"parentNotConnected\" | \"parentShouldNotSummarize\">;\ntype ShouldSummarizeState =\n | { shouldSummarize: true; }\n | { shouldSummarize: false; stopReason: StopReason; };\n\nexport interface IConnectedEvents extends IEvent {\n (event: \"connected\", listener: (clientId: string) => void);\n (event: \"disconnected\", listener: () => void);\n}\n\n/**\n * IConnectedState describes an object that SummaryManager can watch to observe connection/disconnection.\n *\n * Under current implementation, its role will be fulfilled by the ContainerRuntime, but this could be replaced\n * with anything else that fulfills the contract if we want to shift the layer that the SummaryManager lives at.\n */\nexport interface IConnectedState extends IEventProvider<IConnectedEvents> {\n readonly connected: boolean;\n\n /**\n * Under current implementation this is undefined if we've never connected, otherwise it's the clientId from our\n * latest connection (even if we've since disconnected!). Although this happens to be the behavior we want in\n * SummaryManager, I suspect that globally we may eventually want to modify this behavior (e.g. make clientId\n * undefined while disconnected). To protect against this, let's assume this field can't be trusted while\n * disconnected and instead separately track \"latest clientId\" in SummaryManager.\n */\n readonly clientId: string | undefined;\n}\n\nexport interface ISummaryManagerConfig {\n initialDelayMs: number;\n opsToBypassInitialDelay: number;\n}\n\n/**\n * SummaryManager is created by parent container (i.e. interactive container with clientType !== \"summarizer\") only.\n * It observes changes in calculated summarizer and reacts to changes by either creating summarizer client or\n * stopping existing summarizer client.\n */\nexport class SummaryManager implements IDisposable {\n private readonly logger: ITelemetryLogger;\n private readonly opsToBypassInitialDelay: number;\n private readonly initialDelayMs: number;\n private latestClientId: string | undefined;\n private state = SummaryManagerState.Off;\n private summarizer?: ISummarizer;\n private _disposed = false;\n\n public get disposed() {\n return this._disposed;\n }\n\n public get currentState() { return this.state; }\n\n constructor(\n private readonly clientElection: ISummarizerClientElection,\n private readonly connectedState: IConnectedState,\n private readonly summaryCollection:\n Pick<SummaryCollection, \"opsSinceLastAck\" | \"addOpListener\" | \"removeOpListener\">,\n parentLogger: ITelemetryLogger,\n /** Creates summarizer by asking interactive container to spawn summarizing container and\n * get back its Summarizer instance. */\n private readonly requestSummarizerFn: () => Promise<ISummarizer>,\n private readonly startThrottler: IThrottler,\n {\n initialDelayMs = defaultInitialDelayMs,\n opsToBypassInitialDelay = defaultOpsToBypassInitialDelay,\n }: Readonly<Partial<ISummaryManagerConfig>> = {},\n private readonly summarizerOptions?: Readonly<Partial<ISummarizerOptions>>,\n ) {\n this.logger = ChildLogger.create(\n parentLogger,\n \"SummaryManager\",\n {all:{ clientId: () => this.latestClientId }});\n\n this.connectedState.on(\"connected\", this.handleConnected);\n this.connectedState.on(\"disconnected\", this.handleDisconnected);\n this.latestClientId = this.connectedState.clientId;\n\n this.opsToBypassInitialDelay = opsToBypassInitialDelay;\n this.initialDelayMs = initialDelayMs;\n }\n\n /**\n * Until start is called, the SummaryManager won't begin attempting to start summarization. This ensures there's\n * a window between construction and starting where the caller can attach listeners.\n */\n public start(): void {\n this.clientElection.on(\"electedSummarizerChanged\", this.refreshSummarizer);\n this.refreshSummarizer();\n }\n\n private readonly handleConnected = (clientId: string) => {\n this.latestClientId = clientId;\n // If we have a summarizer, it should have been either cancelled on disconnected by now.\n // But because of lastSummary process, it can still hang around, so there is not much we can\n // check or assert.\n this.refreshSummarizer();\n };\n\n private readonly handleDisconnected = () => {\n this.refreshSummarizer();\n };\n\n private static readonly isStartingOrRunning = (state: SummaryManagerState) =>\n state === SummaryManagerState.Starting || state === SummaryManagerState.Running;\n\n private getShouldSummarizeState(): ShouldSummarizeState {\n if (!this.connectedState.connected) {\n return { shouldSummarize: false, stopReason: \"parentNotConnected\" };\n } else if (this.connectedState.clientId !== this.clientElection.electedClientId) {\n return { shouldSummarize: false, stopReason: \"parentShouldNotSummarize\" };\n } else if (this.disposed) {\n assert(false, 0x260 /* \"Disposed should mean disconnected!\" */);\n } else {\n return { shouldSummarize: true };\n }\n }\n\n private readonly refreshSummarizer = () => {\n // Transition states depending on shouldSummarize, which is a calculated property\n // that is only true if this client is connected and is the elected summarizer.\n const shouldSummarizeState = this.getShouldSummarizeState();\n switch (this.state) {\n case SummaryManagerState.Off: {\n if (shouldSummarizeState.shouldSummarize) {\n this.startSummarization();\n }\n return;\n }\n case SummaryManagerState.Starting: {\n // Cannot take any action until summarizer is created\n // state transition will occur after creation\n return;\n }\n case SummaryManagerState.Running: {\n if (shouldSummarizeState.shouldSummarize === false) {\n this.stop(shouldSummarizeState.stopReason);\n }\n return;\n }\n case SummaryManagerState.Stopping: {\n // Cannot take any action until running summarizer finishes\n // state transition will occur after it stops\n return;\n }\n default: {\n return;\n }\n }\n };\n\n private startSummarization() {\n assert(this.state === SummaryManagerState.Off, 0x261 /* \"Expected: off\" */);\n this.state = SummaryManagerState.Starting;\n\n assert(this.summarizer === undefined, 0x262 /* \"Old summarizer is still working!\" */);\n\n let reason = \"unknown\";\n\n this.delayBeforeCreatingSummarizer().then(async (startWithInitialDelay: boolean) => {\n // Re-validate that it need to be running. Due to asynchrony, it may be not the case anymore\n // but only if creation was delayed. If it was not, then we want to ensure we always create\n // a summarizer to kick off lastSummary. Without that, we would not be able to summarize and get\n // document out of broken state if it has too many ops and ordering service keeps nacking main\n // container (and thus it goes into cycle of reconnects)\n if (startWithInitialDelay && this.getShouldSummarizeState().shouldSummarize === false) {\n return;\n }\n\n const summarizer = await this.requestSummarizerFn();\n\n // Re-validate that it need to be running. Due to asynchrony, it may be not the case anymore\n const shouldSummarizeState = this.getShouldSummarizeState();\n if (shouldSummarizeState.shouldSummarize === false) {\n summarizer.stop(shouldSummarizeState.stopReason);\n return;\n }\n\n assert(this.state === SummaryManagerState.Starting, 0x263 /* \"Expected: starting\" */);\n this.state = SummaryManagerState.Running;\n\n this.summarizer = summarizer;\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const clientId = this.latestClientId!;\n\n reason = await PerformanceEvent.timedExecAsync(\n this.logger,\n { eventName: \"RunningSummarizer\", attempt: this.startThrottler.numAttempts },\n async () => summarizer.run(clientId, this.summarizerOptions),\n );\n }).catch((error) => {\n // Most of exceptions happen due to container being closed while loading it, due to\n // summarizer container loosing connection while load.\n // Not worth reporting such errors as errors. That said, we might miss some real errors if\n // we ignore blindly, so try to narrow signature we are looking for - skip logging\n // error only if this client should no longer be a summarizer (which in practice\n // means it also lost connection), and error happened on load (we do not have summarizer).\n // We could annotate the error raised in Container.load where the container closed during load with no error\n // and check for that case here, but that does not seem to be necessary.\n if (this.getShouldSummarizeState().shouldSummarize || this.summarizer !== undefined) {\n // Report any failure as an error unless it was due to cancellation (like \"disconnected\" error)\n // If failure happened on container load, we may not yet realized that socket disconnected, so check\n // offlineError.\n const category = error?.errorType === DriverErrorType.offlineError ? \"generic\" : \"error\";\n this.logger.sendTelemetryEvent(\n {\n eventName: \"SummarizerException\",\n category,\n },\n error);\n\n // Note that summarizer may keep going (like doing last summary).\n // Ideally we await stopping process, but this code path is due to a bug\n // that needs to be fixed either way.\n if (SummaryManager.isStartingOrRunning(this.state)) {\n this.stop(\"summarizerException\");\n }\n }\n }).finally(() => {\n assert(this.state !== SummaryManagerState.Off, 0x264 /* \"Expected: Not Off\" */);\n this.state = SummaryManagerState.Off;\n\n this.summarizer = undefined;\n\n this.logger.sendTelemetryEvent({\n eventName: \"EndingSummarizer\",\n reason,\n });\n\n if (this.getShouldSummarizeState().shouldSummarize) {\n this.startSummarization();\n }\n });\n }\n\n private stop(reason: SummarizerStopReason) {\n if (!SummaryManager.isStartingOrRunning(this.state)) {\n return;\n }\n this.state = SummaryManagerState.Stopping;\n\n // Stopping the running summarizer client should trigger a change\n // in states when the running summarizer closes\n this.summarizer?.stop(reason);\n }\n\n /**\n * Implements initial delay before creating summarizer\n * @returns true, if creation is delayed due to heuristics (not many ops to summarize).\n * False if summarizer should start immediately due to too many unsummarized ops.\n */\n private async delayBeforeCreatingSummarizer(): Promise<boolean> {\n // throttle creation of new summarizer containers to prevent spamming the server with websocket connections\n let delayMs = this.startThrottler.getDelay();\n\n // We have been elected the summarizer. Some day we may be able to summarize with a live document but for\n // now we play it safe and launch a second copy.\n this.logger.sendTelemetryEvent({\n eventName: \"CreatingSummarizer\",\n throttlerDelay: delayMs,\n initialDelay: this.initialDelayMs,\n startThrottlerMaxDelayMs: this.startThrottler.maxDelayMs,\n opsSinceLastAck: this.summaryCollection.opsSinceLastAck,\n opsToBypassInitialDelay: this.opsToBypassInitialDelay,\n });\n\n // This delay helps ensure that last summarizer that might be left from previous client\n // has enough time to complete its last summary and thus new summarizer not conflict with previous one.\n // If, however, there are too many unsummarized ops, try to resolve it as quickly as possible, with\n // understanding that we may see nacks because of such quick action.\n // A better design would be for summarizer election logic to always select current summarizer as\n // summarizing client (i.e. clientType === \"summarizer\" can be elected) to ensure that nobody else can\n // summarizer while it finishes its work and moves to exit.\n // It also helps with pure boot scenario (single client) to offset expensive work a bit out from\n // critical boot sequence.\n let startWithInitialDelay = false;\n if (this.summaryCollection.opsSinceLastAck < this.opsToBypassInitialDelay) {\n startWithInitialDelay = true;\n delayMs = Math.max(delayMs, this.initialDelayMs);\n }\n\n if (delayMs > 0) {\n let timer;\n let resolveOpPromiseFn;\n // Create a listener that will break the delay if we've exceeded the initial delay ops count.\n const opsListenerFn = () => {\n if (this.summaryCollection.opsSinceLastAck >= this.opsToBypassInitialDelay) {\n clearTimeout(timer);\n resolveOpPromiseFn();\n }\n };\n // Create a Promise that will resolve when the delay expires.\n const delayPromise = new Promise<void>((resolve) => {\n timer = setTimeout(() => resolve(), delayMs);\n });\n // Create a Promise that will resolve if the ops count passes the threshold.\n const opPromise = new Promise<void>((resolve) => { resolveOpPromiseFn = resolve; });\n this.summaryCollection.addOpListener(opsListenerFn);\n await Promise.race([ delayPromise, opPromise ]);\n this.summaryCollection.removeOpListener(opsListenerFn);\n }\n return startWithInitialDelay;\n }\n\n public readonly summarizeOnDemand: ISummarizer[\"summarizeOnDemand\"] = (...args) => {\n if (this.summarizer === undefined) {\n throw Error(\"No running summarizer client\");\n // TODO: could spawn a summarizer client temporarily.\n }\n return this.summarizer.summarizeOnDemand(...args);\n };\n\n public readonly enqueueSummarize: ISummarizer[\"enqueueSummarize\"] = (...args) => {\n if (this.summarizer === undefined) {\n throw Error(\"No running summarizer client\");\n // TODO: could spawn a summarizer client temporarily.\n }\n return this.summarizer.enqueueSummarize(...args);\n };\n\n public dispose() {\n this.clientElection.off(\"electedSummarizerChanged\", this.refreshSummarizer);\n this.connectedState.off(\"connected\", this.handleConnected);\n this.connectedState.off(\"disconnected\", this.handleDisconnected);\n this._disposed = true;\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/container-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.59.1001-62246",
|
|
4
4
|
"description": "Fluid container runtime",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -64,29 +64,29 @@
|
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
66
66
|
"@fluidframework/common-utils": "^0.32.1",
|
|
67
|
-
"@fluidframework/container-definitions": "^0.
|
|
68
|
-
"@fluidframework/container-runtime-definitions": "0.
|
|
69
|
-
"@fluidframework/container-utils": "0.
|
|
70
|
-
"@fluidframework/core-interfaces": "^0.
|
|
71
|
-
"@fluidframework/datastore": "0.
|
|
72
|
-
"@fluidframework/driver-definitions": "^0.
|
|
73
|
-
"@fluidframework/driver-utils": "0.
|
|
74
|
-
"@fluidframework/garbage-collector": "0.
|
|
75
|
-
"@fluidframework/protocol-base": "^0.
|
|
76
|
-
"@fluidframework/protocol-definitions": "^0.
|
|
77
|
-
"@fluidframework/runtime-definitions": "0.
|
|
78
|
-
"@fluidframework/runtime-utils": "0.
|
|
79
|
-
"@fluidframework/telemetry-utils": "0.
|
|
67
|
+
"@fluidframework/container-definitions": "^0.48.1000",
|
|
68
|
+
"@fluidframework/container-runtime-definitions": "0.59.1001-62246",
|
|
69
|
+
"@fluidframework/container-utils": "0.59.1001-62246",
|
|
70
|
+
"@fluidframework/core-interfaces": "^0.43.1000",
|
|
71
|
+
"@fluidframework/datastore": "0.59.1001-62246",
|
|
72
|
+
"@fluidframework/driver-definitions": "^0.46.1000",
|
|
73
|
+
"@fluidframework/driver-utils": "0.59.1001-62246",
|
|
74
|
+
"@fluidframework/garbage-collector": "0.59.1001-62246",
|
|
75
|
+
"@fluidframework/protocol-base": "^0.1036.1000",
|
|
76
|
+
"@fluidframework/protocol-definitions": "^0.1028.1000",
|
|
77
|
+
"@fluidframework/runtime-definitions": "0.59.1001-62246",
|
|
78
|
+
"@fluidframework/runtime-utils": "0.59.1001-62246",
|
|
79
|
+
"@fluidframework/telemetry-utils": "0.59.1001-62246",
|
|
80
80
|
"double-ended-queue": "^2.1.0-0",
|
|
81
81
|
"uuid": "^8.3.1"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
84
|
"@fluidframework/build-common": "^0.23.0",
|
|
85
|
-
"@fluidframework/build-tools": "^0.2.
|
|
86
|
-
"@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime
|
|
87
|
-
"@fluidframework/eslint-config-fluid": "^0.
|
|
88
|
-
"@fluidframework/mocha-test-setup": "0.
|
|
89
|
-
"@fluidframework/test-runtime-utils": "0.
|
|
85
|
+
"@fluidframework/build-tools": "^0.2.61288",
|
|
86
|
+
"@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@^0.58.0",
|
|
87
|
+
"@fluidframework/eslint-config-fluid": "^0.28.1000",
|
|
88
|
+
"@fluidframework/mocha-test-setup": "0.59.1001-62246",
|
|
89
|
+
"@fluidframework/test-runtime-utils": "0.59.1001-62246",
|
|
90
90
|
"@microsoft/api-extractor": "^7.16.1",
|
|
91
91
|
"@rushstack/eslint-config": "^2.5.1",
|
|
92
92
|
"@types/double-ended-queue": "^2.1.0",
|
|
@@ -114,9 +114,9 @@
|
|
|
114
114
|
"typescript-formatter": "7.1.0"
|
|
115
115
|
},
|
|
116
116
|
"typeValidation": {
|
|
117
|
-
"version": "0.
|
|
117
|
+
"version": "0.59.1000",
|
|
118
118
|
"broken": {
|
|
119
|
-
"0.58.
|
|
119
|
+
"0.58.2002": {
|
|
120
120
|
"InterfaceDeclaration_IBaseSummarizeResult": {
|
|
121
121
|
"forwardCompat": false
|
|
122
122
|
},
|
|
@@ -137,6 +137,18 @@
|
|
|
137
137
|
},
|
|
138
138
|
"InterfaceDeclaration_IGeneratedSummaryStats": {
|
|
139
139
|
"forwardCompat": false
|
|
140
|
+
},
|
|
141
|
+
"ClassDeclaration_ContainerRuntime": {
|
|
142
|
+
"forwardCompat": false
|
|
143
|
+
},
|
|
144
|
+
"InterfaceDeclaration_IGarbageCollectionRuntime": {
|
|
145
|
+
"forwardCompat": false
|
|
146
|
+
},
|
|
147
|
+
"InterfaceDeclaration_IGCStats": {
|
|
148
|
+
"forwardCompat": false
|
|
149
|
+
},
|
|
150
|
+
"InterfaceDeclaration_IRootSummaryTreeWithStats": {
|
|
151
|
+
"forwardCompat": false
|
|
140
152
|
}
|
|
141
153
|
}
|
|
142
154
|
}
|
package/src/blobManager.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
|
11
11
|
import { assert, Deferred } from "@fluidframework/common-utils";
|
|
12
12
|
import { IContainerRuntime } from "@fluidframework/container-runtime-definitions";
|
|
13
13
|
import { AttachState } from "@fluidframework/container-definitions";
|
|
14
|
-
import { ISummaryTreeWithStats } from "@fluidframework/runtime-definitions";
|
|
14
|
+
import { IGarbageCollectionData, ISummaryTreeWithStats } from "@fluidframework/runtime-definitions";
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* This class represents blob (long string)
|
|
@@ -205,6 +205,65 @@ export class BlobManager {
|
|
|
205
205
|
});
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Generates data used for garbage collection. Each blob uploaded represents a node in the GC graph as it can be
|
|
210
|
+
* individually referenced by storing its handle in a referenced DDS. Returns the list of blob ids as GC nodes.
|
|
211
|
+
* @param fullGC - true to bypass optimizations and force full generation of GC data. BlobManager doesn't care
|
|
212
|
+
* about this for now because the data is a simple list of blob ids.
|
|
213
|
+
*/
|
|
214
|
+
public getGCData(fullGC: boolean = false): IGarbageCollectionData {
|
|
215
|
+
const getGCNodePath = (blobId: string) => { return `/${BlobManager.basePath}/${blobId}`; };
|
|
216
|
+
const gcData: IGarbageCollectionData = { gcNodes: {} };
|
|
217
|
+
/**
|
|
218
|
+
* The node path is of the format `/_blobs/blobId`. This path must match the path of the blob handle returned
|
|
219
|
+
* by the createBlob API because blobs are marked referenced by storing these handles in a referenced DDS.
|
|
220
|
+
*/
|
|
221
|
+
this.blobIds.forEach((blobId: string) => {
|
|
222
|
+
gcData.gcNodes[getGCNodePath(blobId)] = [];
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* For all blobs in the redirect table, the handle returned on creation is based off of the localId. So, these
|
|
227
|
+
* nodes can be referenced by storing the localId handle. When that happens, the corresponding storageId node
|
|
228
|
+
* must also be marked referenced. So, we add a route from the localId node to the storageId node.
|
|
229
|
+
* Note that because of de-duping, there can be multiple localIds that all redirect to the same storageId or
|
|
230
|
+
* a blob may be referenced via its storageId handle.
|
|
231
|
+
*/
|
|
232
|
+
if (this.redirectTable !== undefined) {
|
|
233
|
+
for (const [localId, storageId] of this.redirectTable) {
|
|
234
|
+
// Add node for the localId and add a route to the storageId node. The storageId node will have been
|
|
235
|
+
// added above when adding nodes for this.blobIds.
|
|
236
|
+
gcData.gcNodes[getGCNodePath(localId)] = [getGCNodePath(storageId)];
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return gcData;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* When running GC in test mode, this is called to delete blobs that are unused.
|
|
245
|
+
* @param unusedRoutes - These are the blob node ids that are unused and should be deleted.
|
|
246
|
+
*/
|
|
247
|
+
public deleteUnusedRoutes(unusedRoutes: string[]): void {
|
|
248
|
+
// The routes or blob node paths are in the same format as returned in getGCData - `/_blobs/blobId`.
|
|
249
|
+
for (const route of unusedRoutes) {
|
|
250
|
+
const pathParts = route.split("/");
|
|
251
|
+
assert(
|
|
252
|
+
pathParts.length === 3 && pathParts[1] === BlobManager.basePath,
|
|
253
|
+
0x2d5 /* "Invalid blob node id in unused routes." */,
|
|
254
|
+
);
|
|
255
|
+
const blobId = pathParts[2];
|
|
256
|
+
|
|
257
|
+
// The unused blobId could be a localId. If so, remove it from the redirect table and continue. The
|
|
258
|
+
// corresponding storageId may still be used either directly or via other localIds.
|
|
259
|
+
if (this.redirectTable?.has(blobId)) {
|
|
260
|
+
this.redirectTable.delete(blobId);
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
this.blobIds.delete(blobId);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
208
267
|
public summarize(): ISummaryTreeWithStats {
|
|
209
268
|
// If we have a redirect table it means the container is about to transition to "Attaching" state, so we need
|
|
210
269
|
// to return an actual snapshot containing all the real storage IDs we know about.
|
|
@@ -109,16 +109,16 @@ class OpPerfTelemetry {
|
|
|
109
109
|
if (msg.type === MessageType.Operation &&
|
|
110
110
|
this.clientSequenceNumberForLatencyStatistics === msg.clientSequenceNumber) {
|
|
111
111
|
assert(this.opProcessingTimes.opStartTimeSittingInboundQueue === undefined,
|
|
112
|
-
"opStartTimeSittingInboundQueue should be undefined");
|
|
112
|
+
0x2c8 /* "opStartTimeSittingInboundQueue should be undefined" */);
|
|
113
113
|
assert(this.opPerfData.durationInboundQueue === undefined,
|
|
114
|
-
"durationInboundQueue should be undefined");
|
|
114
|
+
0x2c9 /* "durationInboundQueue should be undefined" */);
|
|
115
115
|
this.opProcessingTimes.opStartTimeSittingInboundQueue = Date.now();
|
|
116
116
|
|
|
117
117
|
assert(this.opPerfData.durationOutboundQueue === undefined,
|
|
118
|
-
"durationOutboundQueue should be undefined");
|
|
118
|
+
0x2ca /* "durationOutboundQueue should be undefined" */);
|
|
119
119
|
|
|
120
120
|
assert(this.opProcessingTimes.opStartTimeForLatencyStatistics !== undefined,
|
|
121
|
-
"opStartTimeForLatencyStatistics should be undefined");
|
|
121
|
+
0x2cb /* "opStartTimeForLatencyStatistics should be undefined" */);
|
|
122
122
|
|
|
123
123
|
this.opPerfData.durationOutboundQueue = this.opProcessingTimes.opStartTimeSittingInboundQueue
|
|
124
124
|
- this.opProcessingTimes.opStartTimeForLatencyStatistics;
|
|
@@ -188,9 +188,9 @@ class OpPerfTelemetry {
|
|
|
188
188
|
if (this.clientSequenceNumberForLatencyStatistics === undefined &&
|
|
189
189
|
message.clientSequenceNumber % 500 === 1) {
|
|
190
190
|
assert(this.opProcessingTimes.opStartTimeSittingInboundQueue === undefined,
|
|
191
|
-
"OpTimeSittingInboundQueue should be undefined");
|
|
191
|
+
0x2cc /* "OpTimeSittingInboundQueue should be undefined" */);
|
|
192
192
|
assert(this.opPerfData.durationInboundQueue === undefined,
|
|
193
|
-
"durationInboundQueue should be undefined");
|
|
193
|
+
0x2cd /* "durationInboundQueue should be undefined" */);
|
|
194
194
|
this.opProcessingTimes.opStartTimeForLatencyStatistics = Date.now();
|
|
195
195
|
this.clientSequenceNumberForLatencyStatistics = message.clientSequenceNumber;
|
|
196
196
|
}
|
|
@@ -211,7 +211,7 @@ class OpPerfTelemetry {
|
|
|
211
211
|
if (this.sequenceNumberForMsnTracking !== undefined &&
|
|
212
212
|
message.minimumSequenceNumber >= this.sequenceNumberForMsnTracking) {
|
|
213
213
|
assert(this.msnTrackingTimestamp !== undefined,
|
|
214
|
-
"msnTrackingTimestamp should not be undefined");
|
|
214
|
+
0x2ce /* "msnTrackingTimestamp should not be undefined" */);
|
|
215
215
|
this.logger.sendPerformanceEvent({
|
|
216
216
|
eventName: "MsnStatistics",
|
|
217
217
|
sequenceNumber,
|
package/src/containerRuntime.ts
CHANGED
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
FluidObject,
|
|
11
11
|
IFluidHandle,
|
|
12
12
|
IFluidHandleContext,
|
|
13
|
-
IFluidObject,
|
|
14
13
|
IFluidRouter,
|
|
15
14
|
IRequest,
|
|
16
15
|
IResponse,
|
|
@@ -101,6 +100,7 @@ import {
|
|
|
101
100
|
seqFromTree,
|
|
102
101
|
calculateStats,
|
|
103
102
|
} from "@fluidframework/runtime-utils";
|
|
103
|
+
import { GCDataBuilder } from "@fluidframework/garbage-collector";
|
|
104
104
|
import { v4 as uuid } from "uuid";
|
|
105
105
|
import { ContainerFluidHandleContext } from "./containerHandleContext";
|
|
106
106
|
import { FluidDataStoreRegistry } from "./dataStoreRegistry";
|
|
@@ -141,6 +141,7 @@ import { formExponentialFn, Throttler } from "./throttler";
|
|
|
141
141
|
import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
|
|
142
142
|
import {
|
|
143
143
|
GarbageCollector,
|
|
144
|
+
GCNodeType,
|
|
144
145
|
gcTreeKey,
|
|
145
146
|
IGarbageCollectionRuntime,
|
|
146
147
|
IGarbageCollector,
|
|
@@ -637,7 +638,7 @@ export class ScheduleManager {
|
|
|
637
638
|
|
|
638
639
|
// This could be the beginning of a new batch or an individual message.
|
|
639
640
|
this.emitter.emit("batchBegin", message);
|
|
640
|
-
this.deltaScheduler.batchBegin();
|
|
641
|
+
this.deltaScheduler.batchBegin(message);
|
|
641
642
|
|
|
642
643
|
const batch = (message?.metadata as IRuntimeMessageMetadata)?.batch;
|
|
643
644
|
if (batch) {
|
|
@@ -658,7 +659,7 @@ export class ScheduleManager {
|
|
|
658
659
|
this.hitError = true;
|
|
659
660
|
this.batchClientId = undefined;
|
|
660
661
|
this.emitter.emit("batchEnd", error, message);
|
|
661
|
-
this.deltaScheduler.batchEnd();
|
|
662
|
+
this.deltaScheduler.batchEnd(message);
|
|
662
663
|
return;
|
|
663
664
|
}
|
|
664
665
|
|
|
@@ -668,7 +669,7 @@ export class ScheduleManager {
|
|
|
668
669
|
if (this.batchClientId === undefined || batch === false) {
|
|
669
670
|
this.batchClientId = undefined;
|
|
670
671
|
this.emitter.emit("batchEnd", undefined, message);
|
|
671
|
-
this.deltaScheduler.batchEnd();
|
|
672
|
+
this.deltaScheduler.batchEnd(message);
|
|
672
673
|
return;
|
|
673
674
|
}
|
|
674
675
|
}
|
|
@@ -907,7 +908,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
907
908
|
return this._flushMode;
|
|
908
909
|
}
|
|
909
910
|
|
|
910
|
-
public get scope():
|
|
911
|
+
public get scope(): FluidObject {
|
|
911
912
|
return this.containerScope;
|
|
912
913
|
}
|
|
913
914
|
|
|
@@ -1076,13 +1077,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1076
1077
|
this.garbageCollector = GarbageCollector.create(
|
|
1077
1078
|
this,
|
|
1078
1079
|
this.runtimeOptions.gcOptions,
|
|
1079
|
-
(
|
|
1080
|
-
(nodePath: string) => this.dataStores.getNodePackagePath(nodePath),
|
|
1081
|
-
/**
|
|
1082
|
-
* Returns the timestamp of the last message seen by this client. This is used by garbage collector as
|
|
1083
|
-
* the current reference timestamp for tracking unreferenced objects.
|
|
1084
|
-
*/
|
|
1085
|
-
() => this.deltaManager.lastMessage?.timestamp ?? this.messageAtLastSummary?.timestamp,
|
|
1080
|
+
(nodePath: string) => this.getGCNodePackagePath(nodePath),
|
|
1086
1081
|
() => this.messageAtLastSummary?.timestamp,
|
|
1087
1082
|
context.baseSnapshot,
|
|
1088
1083
|
async <T>(id: string) => readAndParse<T>(this.storage, id),
|
|
@@ -1134,7 +1129,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1134
1129
|
),
|
|
1135
1130
|
(id: string) => this.summarizerNode.deleteChild(id),
|
|
1136
1131
|
this.mc.logger,
|
|
1137
|
-
async () => this.garbageCollector.
|
|
1132
|
+
async () => this.garbageCollector.getBaseGCDetails(),
|
|
1138
1133
|
(path: string, timestampMs: number, packagePath?: readonly string[]) => this.garbageCollector.nodeUpdated(
|
|
1139
1134
|
path,
|
|
1140
1135
|
"Changed",
|
|
@@ -1833,7 +1828,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1833
1828
|
*/
|
|
1834
1829
|
private async createRootDataStoreLegacy(pkg: string | string[], rootDataStoreId: string): Promise<IFluidRouter> {
|
|
1835
1830
|
const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
|
|
1836
|
-
|
|
1831
|
+
// back-compat 0.59.1000 - makeVisibleAndAttachGraph was added in this version to IFluidDataStoreChannel. For
|
|
1832
|
+
// older versions, we still have to call bindToContext.
|
|
1833
|
+
if (fluidDataStore.makeVisibleAndAttachGraph !== undefined) {
|
|
1834
|
+
fluidDataStore.makeVisibleAndAttachGraph();
|
|
1835
|
+
} else {
|
|
1836
|
+
fluidDataStore.bindToContext();
|
|
1837
|
+
}
|
|
1837
1838
|
return fluidDataStore;
|
|
1838
1839
|
}
|
|
1839
1840
|
|
|
@@ -1905,7 +1906,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1905
1906
|
const fluidDataStore = await this.dataStores._createFluidDataStoreContext(
|
|
1906
1907
|
Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props).realize();
|
|
1907
1908
|
if (isRoot) {
|
|
1908
|
-
|
|
1909
|
+
// back-compat 0.59.1000 - makeVisibleAndAttachGraph was added in this version to IFluidDataStoreChannel.
|
|
1910
|
+
// For older versions, we still have to call bindToContext.
|
|
1911
|
+
if (fluidDataStore.makeVisibleAndAttachGraph !== undefined) {
|
|
1912
|
+
fluidDataStore.makeVisibleAndAttachGraph();
|
|
1913
|
+
} else {
|
|
1914
|
+
fluidDataStore.bindToContext();
|
|
1915
|
+
}
|
|
1909
1916
|
this.logger.sendTelemetryEvent({
|
|
1910
1917
|
eventName: "Root datastore with props",
|
|
1911
1918
|
hasProps: props !== undefined,
|
|
@@ -2111,7 +2118,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2111
2118
|
* @param fullGC - true to bypass optimizations and force full generation of GC data.
|
|
2112
2119
|
*/
|
|
2113
2120
|
public async getGCData(fullGC?: boolean): Promise<IGarbageCollectionData> {
|
|
2114
|
-
|
|
2121
|
+
const builder = new GCDataBuilder();
|
|
2122
|
+
const dsGCData = await this.dataStores.getGCData(fullGC);
|
|
2123
|
+
builder.addNodes(dsGCData.gcNodes);
|
|
2124
|
+
|
|
2125
|
+
const blobsGCData = this.blobManager.getGCData(fullGC);
|
|
2126
|
+
builder.addNodes(blobsGCData.gcNodes);
|
|
2127
|
+
return builder.getGCData();
|
|
2115
2128
|
}
|
|
2116
2129
|
|
|
2117
2130
|
/**
|
|
@@ -2127,7 +2140,83 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2127
2140
|
// always referenced, so the used routes is only self-route (empty string).
|
|
2128
2141
|
this.summarizerNode.updateUsedRoutes([""]);
|
|
2129
2142
|
|
|
2130
|
-
|
|
2143
|
+
const dataStoreUsedRoutes: string[] = [];
|
|
2144
|
+
for (const route of usedRoutes) {
|
|
2145
|
+
if (route.split("/")[1] !== BlobManager.basePath) {
|
|
2146
|
+
dataStoreUsedRoutes.push(route);
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes, gcTimestamp);
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
/**
|
|
2154
|
+
* When running GC in test mode, this is called to delete objects whose routes are unused. This enables testing
|
|
2155
|
+
* scenarios with accessing deleted content.
|
|
2156
|
+
* @param unusedRoutes - The routes that are unused in all data stores in this Container.
|
|
2157
|
+
*/
|
|
2158
|
+
public deleteUnusedRoutes(unusedRoutes: string[]) {
|
|
2159
|
+
const blobManagerUnusedRoutes: string[] = [];
|
|
2160
|
+
const dataStoreUnusedRoutes: string[] = [];
|
|
2161
|
+
for (const route of unusedRoutes) {
|
|
2162
|
+
if (this.isBlobPath(route)) {
|
|
2163
|
+
blobManagerUnusedRoutes.push(route);
|
|
2164
|
+
} else {
|
|
2165
|
+
dataStoreUnusedRoutes.push(route);
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
this.blobManager.deleteUnusedRoutes(blobManagerUnusedRoutes);
|
|
2170
|
+
this.dataStores.deleteUnusedRoutes(dataStoreUnusedRoutes);
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
/**
|
|
2174
|
+
* Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
|
|
2175
|
+
*/
|
|
2176
|
+
public getCurrentReferenceTimestampMs(): number | undefined {
|
|
2177
|
+
// Use the timestamp of the last message seen by this client as that is server generated. If no messages have
|
|
2178
|
+
// been processed, use the timestamp of the message from the last summary.
|
|
2179
|
+
return this.deltaManager.lastMessage?.timestamp ?? this.messageAtLastSummary?.timestamp;
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
/**
|
|
2183
|
+
* Returns the type of the GC node. Currently, there are nodes that belong to data store and nodes that belong
|
|
2184
|
+
* to the blob manager.
|
|
2185
|
+
*/
|
|
2186
|
+
public getNodeType(nodePath: string): GCNodeType {
|
|
2187
|
+
if (this.isBlobPath(nodePath)) {
|
|
2188
|
+
return GCNodeType.Blob;
|
|
2189
|
+
}
|
|
2190
|
+
if (this.dataStores.isDataStoreNode(nodePath)) {
|
|
2191
|
+
return GCNodeType.DataStore;
|
|
2192
|
+
}
|
|
2193
|
+
// Root node ("/") and DDS nodes belong to "Other" node types.
|
|
2194
|
+
return GCNodeType.Other;
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
/**
|
|
2198
|
+
* Called by GC to retrieve the package path of the node with the given path. The node should belong to a
|
|
2199
|
+
* data store or an attachment blob.
|
|
2200
|
+
*/
|
|
2201
|
+
public getGCNodePackagePath(nodePath: string): readonly string[] | undefined {
|
|
2202
|
+
// If the node is a blob, return "_blobs" as the package path.
|
|
2203
|
+
if (this.isBlobPath(nodePath)) {
|
|
2204
|
+
return ["_blobs"];
|
|
2205
|
+
}
|
|
2206
|
+
const dataStorePkgPath = this.dataStores.getDataStorePackagePath(nodePath);
|
|
2207
|
+
assert(dataStorePkgPath !== undefined, 0x2d6 /* "Package path requested for unknown node type." */);
|
|
2208
|
+
return dataStorePkgPath;
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
/**
|
|
2212
|
+
* Returns whether a given path is for attachment blobs that are in the format - "/BlobManager.basePath/...".
|
|
2213
|
+
*/
|
|
2214
|
+
private isBlobPath(path: string): boolean {
|
|
2215
|
+
const pathParts = path.split("/");
|
|
2216
|
+
if (pathParts.length < 2 || pathParts[1] !== BlobManager.basePath) {
|
|
2217
|
+
return false;
|
|
2218
|
+
}
|
|
2219
|
+
return true;
|
|
2131
2220
|
}
|
|
2132
2221
|
|
|
2133
2222
|
/**
|
package/src/dataStore.ts
CHANGED
|
@@ -75,7 +75,13 @@ class DataStore implements IDataStore {
|
|
|
75
75
|
alias,
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
-
this.
|
|
78
|
+
// back-compat 0.58.2000 - makeVisibleAndAttachGraph was added in this version to IFluidDataStoreChannel. For
|
|
79
|
+
// older versions, we still have to call bindToContext.
|
|
80
|
+
if (this.fluidDataStoreChannel.makeVisibleAndAttachGraph !== undefined) {
|
|
81
|
+
this.fluidDataStoreChannel.makeVisibleAndAttachGraph();
|
|
82
|
+
} else {
|
|
83
|
+
this.fluidDataStoreChannel.bindToContext();
|
|
84
|
+
}
|
|
79
85
|
|
|
80
86
|
if (this.runtime.attachState === AttachState.Detached) {
|
|
81
87
|
const localResult = this.datastores.processAliasMessageCore(message);
|
package/src/dataStoreContext.ts
CHANGED
|
@@ -132,7 +132,7 @@ export interface ILocalFluidDataStoreContextProps extends IFluidDataStoreContext
|
|
|
132
132
|
readonly pkg: Readonly<string[]> | undefined;
|
|
133
133
|
readonly snapshotTree: ISnapshotTree | undefined;
|
|
134
134
|
readonly isRootDataStore: boolean | undefined;
|
|
135
|
-
readonly
|
|
135
|
+
readonly makeLocallyVisibleFn: () => void;
|
|
136
136
|
/**
|
|
137
137
|
* @deprecated 0.16 Issue #1635, #3631
|
|
138
138
|
*/
|
|
@@ -261,7 +261,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
261
261
|
private readonly existing: boolean,
|
|
262
262
|
private bindState: BindState,
|
|
263
263
|
public readonly isLocalDataStore: boolean,
|
|
264
|
-
|
|
264
|
+
private readonly makeLocallyVisibleFn: () => void,
|
|
265
265
|
) {
|
|
266
266
|
super();
|
|
267
267
|
|
|
@@ -284,7 +284,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
284
284
|
assert(this.bindState === BindState.NotBound, 0x13b /* "datastore context is already in bound state" */);
|
|
285
285
|
this.bindState = BindState.Binding;
|
|
286
286
|
assert(this.channel !== undefined, 0x13c /* "undefined channel on datastore context" */);
|
|
287
|
-
|
|
287
|
+
this.makeLocallyVisible();
|
|
288
288
|
this.bindState = BindState.Bound;
|
|
289
289
|
};
|
|
290
290
|
|
|
@@ -627,6 +627,15 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
627
627
|
return this._containerRuntime.submitDataStoreSignal(this.id, type, content);
|
|
628
628
|
}
|
|
629
629
|
|
|
630
|
+
/**
|
|
631
|
+
* This is called by the data store channel when it becomes locally visible indicating that it is ready to become
|
|
632
|
+
* globally visible now.
|
|
633
|
+
*/
|
|
634
|
+
public makeLocallyVisible() {
|
|
635
|
+
assert(this.channel !== undefined, 0x2cf /* "undefined channel on datastore context" */);
|
|
636
|
+
this.makeLocallyVisibleFn();
|
|
637
|
+
}
|
|
638
|
+
|
|
630
639
|
protected bindRuntime(channel: IFluidDataStoreChannel) {
|
|
631
640
|
if (this.channel) {
|
|
632
641
|
throw new Error("Runtime already bound");
|
|
@@ -864,7 +873,7 @@ export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
|
|
|
864
873
|
props.snapshotTree !== undefined ? true : false /* existing */,
|
|
865
874
|
props.snapshotTree ? BindState.Bound : BindState.NotBound,
|
|
866
875
|
true /* isLocalDataStore */,
|
|
867
|
-
props.
|
|
876
|
+
props.makeLocallyVisibleFn,
|
|
868
877
|
);
|
|
869
878
|
|
|
870
879
|
this.snapshotTree = props.snapshotTree;
|
|
@@ -993,7 +1002,7 @@ export class LocalDetachedFluidDataStoreContext
|
|
|
993
1002
|
|
|
994
1003
|
public async attachRuntime(
|
|
995
1004
|
registry: IProvideFluidDataStoreFactory,
|
|
996
|
-
|
|
1005
|
+
dataStoreChannel: IFluidDataStoreChannel)
|
|
997
1006
|
{
|
|
998
1007
|
assert(this.detachedRuntimeCreation, 0x154 /* "runtime creation is already attached" */);
|
|
999
1008
|
assert(this.channelDeferred === undefined, 0x155 /* "channel deferral is already set" */);
|
|
@@ -1009,10 +1018,16 @@ export class LocalDetachedFluidDataStoreContext
|
|
|
1009
1018
|
this.detachedRuntimeCreation = false;
|
|
1010
1019
|
this.channelDeferred = new Deferred<IFluidDataStoreChannel>();
|
|
1011
1020
|
|
|
1012
|
-
super.bindRuntime(
|
|
1021
|
+
super.bindRuntime(dataStoreChannel);
|
|
1013
1022
|
|
|
1014
1023
|
if (await this.isRoot()) {
|
|
1015
|
-
|
|
1024
|
+
// back-compat 0.59.1000 - makeVisibleAndAttachGraph was added in this version to IFluidDataStoreChannel.
|
|
1025
|
+
// For older versions, we still have to call bindToContext.
|
|
1026
|
+
if (dataStoreChannel.makeVisibleAndAttachGraph !== undefined) {
|
|
1027
|
+
dataStoreChannel.makeVisibleAndAttachGraph();
|
|
1028
|
+
} else {
|
|
1029
|
+
dataStoreChannel.bindToContext();
|
|
1030
|
+
}
|
|
1016
1031
|
}
|
|
1017
1032
|
}
|
|
1018
1033
|
|