@fluid-experimental/attributor 2.0.0-internal.4.0.6 → 2.0.0-internal.4.1.0
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/CHANGELOG.md +5 -0
- package/README.md +38 -0
- package/dist/mixinAttributor.d.ts.map +1 -1
- package/dist/mixinAttributor.js +13 -1
- package/dist/mixinAttributor.js.map +1 -1
- package/lib/mixinAttributor.d.ts.map +1 -1
- package/lib/mixinAttributor.js +14 -2
- package/lib/mixinAttributor.js.map +1 -1
- package/package.json +22 -18
- package/src/mixinAttributor.ts +29 -7
package/CHANGELOG.md
ADDED
package/README.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
This package contains definitions and implementations for framework-provided attribution functionality.
|
|
4
4
|
|
|
5
|
+
<!-- AUTO-GENERATED-CONTENT:START (README_DEPENDENCY_GUIDELINES_SECTION:includeHeading=TRUE) -->
|
|
6
|
+
|
|
7
|
+
<!-- prettier-ignore-start -->
|
|
8
|
+
<!-- NOTE: This section is automatically generated using @fluid-tools/markdown-magic. Do not update these generated contents directly. -->
|
|
9
|
+
|
|
10
|
+
## Using Fluid Framework libraries
|
|
11
|
+
|
|
12
|
+
When taking a dependency on a Fluid Framework library, we recommend using a `^` (caret) version range, such as `^1.3.4`.
|
|
13
|
+
While Fluid Framework libraries may use different ranges with interdependencies between other Fluid Framework libraries,
|
|
14
|
+
library consumers should always prefer `^`.
|
|
15
|
+
|
|
16
|
+
Note that when depending on a library version of the form 2.0.0-internal.x.y.z, called the Fluid internal version
|
|
17
|
+
scheme, you must use a `>= <` dependency range. Standard `^` and `~` ranges will not work as expected. See the
|
|
18
|
+
[@fluid-tools/version-tools](https://github.com/microsoft/FluidFramework/blob/main/build-tools/packages/version-tools/README.md)
|
|
19
|
+
package for more information including tools to convert between version schemes.
|
|
20
|
+
|
|
21
|
+
<!-- prettier-ignore-end -->
|
|
22
|
+
|
|
23
|
+
<!-- AUTO-GENERATED-CONTENT:END -->
|
|
24
|
+
|
|
5
25
|
## Status
|
|
6
26
|
|
|
7
27
|
All attribution APIs (both in this package and elsewhere in `@fluidframework` packages) are marked as [alpha](https://api-extractor.com/pages/tsdoc/tag_alpha/) to enable fast iteration (as third-party use is not officially supported, breaking API changes can be made in minor versions).
|
|
@@ -104,3 +124,21 @@ The current design of the mixin's behavior is therefore motivated by the ability
|
|
|
104
124
|
The behavior of `"Fluid.Attribution.WriteOnNewFile"` supports the standard strategy of rolling out code that reads a new format and waiting for it to saturate before beginning to write that new format.
|
|
105
125
|
"reading the new format" corresponds to using a container runtime initialized with `mixinAttributor`, and "writing the new format" to enabling `"Fluid.Attribution.WriteOnNewFile"` in configuration.
|
|
106
126
|
During the "waiting to saturate" period, developers are free to experiment with turning the feature flag on locally and testing various compatability scenarios.
|
|
127
|
+
|
|
128
|
+
<!-- AUTO-GENERATED-CONTENT:START (README_TRADEMARK_SECTION:includeHeading=TRUE) -->
|
|
129
|
+
|
|
130
|
+
<!-- prettier-ignore-start -->
|
|
131
|
+
<!-- NOTE: This section is automatically generated using @fluid-tools/markdown-magic. Do not update these generated contents directly. -->
|
|
132
|
+
|
|
133
|
+
## Trademark
|
|
134
|
+
|
|
135
|
+
This project may contain Microsoft trademarks or logos for Microsoft projects, products, or services.
|
|
136
|
+
|
|
137
|
+
Use of these trademarks or logos must follow Microsoft's [Trademark & Brand
|
|
138
|
+
Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
|
|
139
|
+
|
|
140
|
+
Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
|
|
141
|
+
|
|
142
|
+
<!-- prettier-ignore-end -->
|
|
143
|
+
|
|
144
|
+
<!-- AUTO-GENERATED-CONTENT:END -->
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mixinAttributor.d.ts","sourceRoot":"","sources":["../src/mixinAttributor.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAErE,OAAO,EACN,eAAe,EACf,cAAc,EAId,MAAM,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"mixinAttributor.d.ts","sourceRoot":"","sources":["../src/mixinAttributor.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAErE,OAAO,EACN,eAAe,EACf,cAAc,EAId,MAAM,qCAAqC,CAAC;AAmB7C;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,sCAAsC,CAAC;AAEtE;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,yBAAgD,CAAC;AAExF;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACzC,QAAQ,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;CAChD;AAED;;;;;;GAMG;AACH,MAAM,WAAW,kBAAmB,SAAQ,yBAAyB;IACpE;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,cAAc,GAAG,eAAe,CAAC;IAE1C;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC;IAElC;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,kBAAkB,CAE5D;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,UAAU,uBAAuB,4BAiGrB,CAAC"}
|
package/dist/mixinAttributor.js
CHANGED
|
@@ -62,11 +62,23 @@ const mixinAttributor = (Base = container_runtime_1.ContainerRuntime) => class C
|
|
|
62
62
|
}
|
|
63
63
|
const runtime = (await Base.load(context, registryEntries, requestHandler, runtimeOptions, containerScope, existing, ctor));
|
|
64
64
|
runtime.runtimeAttributor = runtimeAttributor;
|
|
65
|
+
const logger = telemetry_utils_1.ChildLogger.create(runtime.logger, "Attributor");
|
|
65
66
|
// Note: this fetches attribution blobs relatively eagerly in the load flow; we may want to optimize
|
|
66
67
|
// this to avoid blocking on such information until application actually requests some op-based attribution
|
|
67
68
|
// info or we need to summarize. All that really needs to happen immediately is to start recording
|
|
68
69
|
// op seq# -> attributionInfo for new ops.
|
|
69
|
-
await
|
|
70
|
+
await telemetry_utils_1.PerformanceEvent.timedExecAsync(logger, {
|
|
71
|
+
eventName: "initialize",
|
|
72
|
+
}, async (event) => {
|
|
73
|
+
var _a;
|
|
74
|
+
void ((_a = runtime.runtimeAttributor) === null || _a === void 0 ? void 0 : _a.initialize(deltaManager, audience, baseSnapshot, async (id) => runtime.storage.readBlob(id), shouldTrackAttribution));
|
|
75
|
+
event.end({
|
|
76
|
+
attributionEnabledInConfig: shouldTrackAttribution,
|
|
77
|
+
attributionEnabledInDoc: runtime.runtimeAttributor
|
|
78
|
+
? runtime.runtimeAttributor.isEnabled
|
|
79
|
+
: false,
|
|
80
|
+
});
|
|
81
|
+
});
|
|
70
82
|
return runtime;
|
|
71
83
|
}
|
|
72
84
|
addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mixinAttributor.js","sourceRoot":"","sources":["../src/mixinAttributor.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 */\nimport {\n\tIDocumentMessage,\n\tISequencedDocumentMessage,\n\tISnapshotTree,\n} from \"@fluidframework/protocol-definitions\";\nimport { IAudience, IContainerContext, IDeltaManager } from \"@fluidframework/container-definitions\";\nimport { ContainerRuntime } from \"@fluidframework/container-runtime\";\nimport type { IContainerRuntimeOptions } from \"@fluidframework/container-runtime\";\nimport {\n\tAttributionInfo,\n\tAttributionKey,\n\tISummaryTreeWithStats,\n\tITelemetryContext,\n\tNamedFluidDataStoreRegistryEntries,\n} from \"@fluidframework/runtime-definitions\";\nimport { addSummarizeResultToSummary, SummaryTreeBuilder } from \"@fluidframework/runtime-utils\";\nimport { IContainerRuntime } from \"@fluidframework/container-runtime-definitions\";\nimport { IRequest, IResponse, FluidObject } from \"@fluidframework/core-interfaces\";\nimport { assert, bufferToString, unreachableCase } from \"@fluidframework/common-utils\";\nimport { UsageError } from \"@fluidframework/container-utils\";\nimport { loggerToMonitoringContext } from \"@fluidframework/telemetry-utils\";\nimport { Attributor, IAttributor, OpStreamAttributor } from \"./attributor\";\nimport { AttributorSerializer, chain, deltaEncoder, Encoder } from \"./encoders\";\nimport { makeLZ4Encoder } from \"./lz4Encoder\";\n\n// Summary tree keys\nconst attributorTreeName = \".attributor\";\nconst opBlobName = \"op\";\n\n/**\n * @alpha\n * Feature Gate Key -\n * Whether or not a container runtime instantiated using `mixinAttributor`'s load should generate an attributor on\n * new files. See package README for more notes on integration.\n */\nexport const enableOnNewFileKey = \"Fluid.Attribution.EnableOnNewFile\";\n\n/**\n * @alpha\n */\nexport const IRuntimeAttributor: keyof IProvideRuntimeAttributor = \"IRuntimeAttributor\";\n\n/**\n * @alpha\n */\nexport interface IProvideRuntimeAttributor {\n\treadonly IRuntimeAttributor: IRuntimeAttributor;\n}\n\n/**\n * Provides access to attribution information stored on the container runtime.\n *\n * Attributors are only populated after the container runtime they are injected into has initialized.\n * @sealed\n * @alpha\n */\nexport interface IRuntimeAttributor extends IProvideRuntimeAttributor {\n\t/**\n\t * @throws - If no AttributionInfo exists for this key.\n\t */\n\tget(key: AttributionKey): AttributionInfo;\n\n\t/**\n\t * @returns - Whether any AttributionInfo exists for the provided key.\n\t */\n\thas(key: AttributionKey): boolean;\n\n\t/**\n\t * @returns - Whether the runtime is currently tracking attribution information for the loaded container.\n\t * See {@link mixinAttributor} for more details on when this happens.\n\t */\n\treadonly isEnabled: boolean;\n}\n\n/**\n * @returns an IRuntimeAttributor for usage with `mixinAttributor`. The attributor will only be populated with data\n * once it's passed via scope to a container runtime load flow. See {@link mixinAttributor}.\n * @alpha\n */\nexport function createRuntimeAttributor(): IRuntimeAttributor {\n\treturn new RuntimeAttributor();\n}\n\n/**\n * Mixes in logic to load and store runtime-based attribution functionality.\n *\n * The `scope` passed to `load` should implement `IProvideRuntimeAttributor`.\n *\n * Existing documents without stored attributors will not start storing attribution information: if an\n * IRuntimeAttributor is passed via scope to load a document that never previously had attribution information,\n * that attributor's `has` method will always return `false`.\n * @param Base - base class, inherits from FluidAttributorRuntime\n * @alpha\n */\nexport const mixinAttributor = (Base: typeof ContainerRuntime = ContainerRuntime) =>\n\tclass ContainerRuntimeWithAttributor extends Base {\n\t\tpublic static async load(\n\t\t\tcontext: IContainerContext,\n\t\t\tregistryEntries: NamedFluidDataStoreRegistryEntries,\n\t\t\trequestHandler?:\n\t\t\t\t| ((request: IRequest, runtime: IContainerRuntime) => Promise<IResponse>)\n\t\t\t\t| undefined,\n\t\t\truntimeOptions: IContainerRuntimeOptions | undefined = {},\n\t\t\tcontainerScope: FluidObject | undefined = context.scope,\n\t\t\texisting?: boolean | undefined,\n\t\t\tctor: typeof ContainerRuntime = ContainerRuntimeWithAttributor as unknown as typeof ContainerRuntime,\n\t\t): Promise<ContainerRuntime> {\n\t\t\tconst runtimeAttributor = (\n\t\t\t\tcontainerScope as FluidObject<IProvideRuntimeAttributor> | undefined\n\t\t\t)?.IRuntimeAttributor;\n\t\t\tif (!runtimeAttributor) {\n\t\t\t\tthrow new UsageError(\n\t\t\t\t\t\"ContainerRuntimeWithAttributor must be passed a scope implementing IProvideRuntimeAttributor\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst pendingRuntimeState = context.pendingLocalState as {\n\t\t\t\tbaseSnapshot?: ISnapshotTree;\n\t\t\t};\n\t\t\tconst baseSnapshot: ISnapshotTree | undefined =\n\t\t\t\tpendingRuntimeState?.baseSnapshot ?? context.baseSnapshot;\n\n\t\t\tconst { audience, deltaManager } = context;\n\t\t\tassert(\n\t\t\t\taudience !== undefined,\n\t\t\t\t0x508 /* Audience must exist when instantiating attribution-providing runtime */,\n\t\t\t);\n\n\t\t\tconst mc = loggerToMonitoringContext(context.taggedLogger);\n\t\t\tconst shouldTrackAttribution = mc.config.getBoolean(enableOnNewFileKey) ?? false;\n\t\t\tif (shouldTrackAttribution) {\n\t\t\t\t(context.options.attribution ??= {}).track = true;\n\t\t\t}\n\n\t\t\tconst runtime = (await Base.load(\n\t\t\t\tcontext,\n\t\t\t\tregistryEntries,\n\t\t\t\trequestHandler,\n\t\t\t\truntimeOptions,\n\t\t\t\tcontainerScope,\n\t\t\t\texisting,\n\t\t\t\tctor,\n\t\t\t)) as ContainerRuntimeWithAttributor;\n\t\t\truntime.runtimeAttributor = runtimeAttributor as RuntimeAttributor;\n\n\t\t\t// Note: this fetches attribution blobs relatively eagerly in the load flow; we may want to optimize\n\t\t\t// this to avoid blocking on such information until application actually requests some op-based attribution\n\t\t\t// info or we need to summarize. All that really needs to happen immediately is to start recording\n\t\t\t// op seq# -> attributionInfo for new ops.\n\t\t\tawait runtime.runtimeAttributor.initialize(\n\t\t\t\tdeltaManager,\n\t\t\t\taudience,\n\t\t\t\tbaseSnapshot,\n\t\t\t\tasync (id) => runtime.storage.readBlob(id),\n\t\t\t\tshouldTrackAttribution,\n\t\t\t);\n\t\t\treturn runtime;\n\t\t}\n\n\t\tprivate runtimeAttributor: RuntimeAttributor | undefined;\n\n\t\tprotected addContainerStateToSummary(\n\t\t\tsummaryTree: ISummaryTreeWithStats,\n\t\t\tfullTree: boolean,\n\t\t\ttrackState: boolean,\n\t\t\ttelemetryContext?: ITelemetryContext,\n\t\t) {\n\t\t\tsuper.addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext);\n\t\t\tconst attributorSummary = this.runtimeAttributor?.summarize();\n\t\t\tif (attributorSummary) {\n\t\t\t\taddSummarizeResultToSummary(summaryTree, attributorTreeName, attributorSummary);\n\t\t\t}\n\t\t}\n\t} as unknown as typeof ContainerRuntime;\n\nclass RuntimeAttributor implements IRuntimeAttributor {\n\tpublic get IRuntimeAttributor(): IRuntimeAttributor {\n\t\treturn this;\n\t}\n\n\tpublic get(key: AttributionKey): AttributionInfo {\n\t\tassert(\n\t\t\tthis.opAttributor !== undefined,\n\t\t\t0x509 /* RuntimeAttributor must be initialized before getAttributionInfo can be called */,\n\t\t);\n\n\t\tif (key.type === \"detached\") {\n\t\t\tthrow new Error(\"Attribution of detached keys is not yet supported.\");\n\t\t}\n\n\t\tif (key.type === \"local\") {\n\t\t\t// Note: we can *almost* orchestrate this correctly with internal-only changes by looking up the current\n\t\t\t// client id in the audience. However, for read->write client transition, the container might have not yet\n\t\t\t// received a client id. This is left as a TODO as it might be more easily solved once the detached case\n\t\t\t// is settled (e.g. if it's reasonable for the host to know the current user information at container\n\t\t\t// creation time, we could just use that here as well).\n\t\t\tthrow new Error(\"Attribution of local keys is not yet supported.\");\n\t\t}\n\n\t\treturn this.opAttributor.getAttributionInfo(key.seq);\n\t}\n\n\tpublic has(key: AttributionKey): boolean {\n\t\tif (key.type === \"detached\") {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (key.type === \"local\") {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn this.opAttributor?.tryGetAttributionInfo(key.seq) !== undefined;\n\t}\n\n\tprivate encoder: Encoder<IAttributor, string> = {\n\t\tencode: unreachableCase,\n\t\tdecode: unreachableCase,\n\t};\n\n\tprivate opAttributor: IAttributor | undefined;\n\tpublic isEnabled = false;\n\n\tpublic async initialize(\n\t\tdeltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,\n\t\taudience: IAudience,\n\t\tbaseSnapshot: ISnapshotTree | undefined,\n\t\treadBlob: (id: string) => Promise<ArrayBufferLike>,\n\t\tshouldAddAttributorOnNewFile: boolean,\n\t): Promise<void> {\n\t\tconst attributorTree = baseSnapshot?.trees[attributorTreeName];\n\t\t// Existing documents that don't already have a snapshot containing runtime attribution info shouldn't\n\t\t// inject any for now--this causes some back-compat integration problems that aren't fully worked out.\n\t\tconst shouldExcludeAttributor =\n\t\t\t(baseSnapshot !== undefined && attributorTree === undefined) ||\n\t\t\t(baseSnapshot === undefined && !shouldAddAttributorOnNewFile);\n\t\tif (shouldExcludeAttributor) {\n\t\t\t// This gives a consistent error for calls to `get` on keys that don't exist.\n\t\t\tthis.opAttributor = new Attributor();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.isEnabled = true;\n\t\tthis.encoder = chain(\n\t\t\tnew AttributorSerializer(\n\t\t\t\t(entries) => new OpStreamAttributor(deltaManager, audience, entries),\n\t\t\t\tdeltaEncoder,\n\t\t\t),\n\t\t\tmakeLZ4Encoder(),\n\t\t);\n\n\t\tif (attributorTree !== undefined) {\n\t\t\tconst id = attributorTree.blobs[opBlobName];\n\t\t\tassert(\n\t\t\t\tid !== undefined,\n\t\t\t\t0x50a /* Attributor tree should have op attributor summary blob. */,\n\t\t\t);\n\t\t\tconst blobContents = await readBlob(id);\n\t\t\tconst attributorSnapshot = bufferToString(blobContents, \"utf8\");\n\t\t\tthis.opAttributor = this.encoder.decode(attributorSnapshot);\n\t\t} else {\n\t\t\tthis.opAttributor = new OpStreamAttributor(deltaManager, audience);\n\t\t}\n\t}\n\n\tpublic summarize(): ISummaryTreeWithStats | undefined {\n\t\tif (!this.isEnabled) {\n\t\t\t// Loaded existing document without attributor data: avoid injecting any data.\n\t\t\treturn undefined;\n\t\t}\n\n\t\tassert(\n\t\t\tthis.opAttributor !== undefined,\n\t\t\t0x50b /* RuntimeAttributor should be initialized before summarization */,\n\t\t);\n\t\tconst builder = new SummaryTreeBuilder();\n\t\tbuilder.addBlob(opBlobName, this.encoder.encode(this.opAttributor));\n\t\treturn builder.getSummaryTree();\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mixinAttributor.js","sourceRoot":"","sources":["../src/mixinAttributor.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 */\nimport {\n\tIDocumentMessage,\n\tISequencedDocumentMessage,\n\tISnapshotTree,\n} from \"@fluidframework/protocol-definitions\";\nimport { IAudience, IContainerContext, IDeltaManager } from \"@fluidframework/container-definitions\";\nimport { ContainerRuntime } from \"@fluidframework/container-runtime\";\nimport type { IContainerRuntimeOptions } from \"@fluidframework/container-runtime\";\nimport {\n\tAttributionInfo,\n\tAttributionKey,\n\tISummaryTreeWithStats,\n\tITelemetryContext,\n\tNamedFluidDataStoreRegistryEntries,\n} from \"@fluidframework/runtime-definitions\";\nimport { addSummarizeResultToSummary, SummaryTreeBuilder } from \"@fluidframework/runtime-utils\";\nimport { IContainerRuntime } from \"@fluidframework/container-runtime-definitions\";\nimport { IRequest, IResponse, FluidObject } from \"@fluidframework/core-interfaces\";\nimport { assert, bufferToString, unreachableCase } from \"@fluidframework/common-utils\";\nimport { UsageError } from \"@fluidframework/container-utils\";\nimport {\n\tChildLogger,\n\tPerformanceEvent,\n\tloggerToMonitoringContext,\n} from \"@fluidframework/telemetry-utils\";\nimport { Attributor, IAttributor, OpStreamAttributor } from \"./attributor\";\nimport { AttributorSerializer, chain, deltaEncoder, Encoder } from \"./encoders\";\nimport { makeLZ4Encoder } from \"./lz4Encoder\";\n\n// Summary tree keys\nconst attributorTreeName = \".attributor\";\nconst opBlobName = \"op\";\n\n/**\n * @alpha\n * Feature Gate Key -\n * Whether or not a container runtime instantiated using `mixinAttributor`'s load should generate an attributor on\n * new files. See package README for more notes on integration.\n */\nexport const enableOnNewFileKey = \"Fluid.Attribution.EnableOnNewFile\";\n\n/**\n * @alpha\n */\nexport const IRuntimeAttributor: keyof IProvideRuntimeAttributor = \"IRuntimeAttributor\";\n\n/**\n * @alpha\n */\nexport interface IProvideRuntimeAttributor {\n\treadonly IRuntimeAttributor: IRuntimeAttributor;\n}\n\n/**\n * Provides access to attribution information stored on the container runtime.\n *\n * Attributors are only populated after the container runtime they are injected into has initialized.\n * @sealed\n * @alpha\n */\nexport interface IRuntimeAttributor extends IProvideRuntimeAttributor {\n\t/**\n\t * @throws - If no AttributionInfo exists for this key.\n\t */\n\tget(key: AttributionKey): AttributionInfo;\n\n\t/**\n\t * @returns - Whether any AttributionInfo exists for the provided key.\n\t */\n\thas(key: AttributionKey): boolean;\n\n\t/**\n\t * @returns - Whether the runtime is currently tracking attribution information for the loaded container.\n\t * See {@link mixinAttributor} for more details on when this happens.\n\t */\n\treadonly isEnabled: boolean;\n}\n\n/**\n * @returns an IRuntimeAttributor for usage with `mixinAttributor`. The attributor will only be populated with data\n * once it's passed via scope to a container runtime load flow. See {@link mixinAttributor}.\n * @alpha\n */\nexport function createRuntimeAttributor(): IRuntimeAttributor {\n\treturn new RuntimeAttributor();\n}\n\n/**\n * Mixes in logic to load and store runtime-based attribution functionality.\n *\n * The `scope` passed to `load` should implement `IProvideRuntimeAttributor`.\n *\n * Existing documents without stored attributors will not start storing attribution information: if an\n * IRuntimeAttributor is passed via scope to load a document that never previously had attribution information,\n * that attributor's `has` method will always return `false`.\n * @param Base - base class, inherits from FluidAttributorRuntime\n * @alpha\n */\nexport const mixinAttributor = (Base: typeof ContainerRuntime = ContainerRuntime) =>\n\tclass ContainerRuntimeWithAttributor extends Base {\n\t\tpublic static async load(\n\t\t\tcontext: IContainerContext,\n\t\t\tregistryEntries: NamedFluidDataStoreRegistryEntries,\n\t\t\trequestHandler?:\n\t\t\t\t| ((request: IRequest, runtime: IContainerRuntime) => Promise<IResponse>)\n\t\t\t\t| undefined,\n\t\t\truntimeOptions: IContainerRuntimeOptions | undefined = {},\n\t\t\tcontainerScope: FluidObject | undefined = context.scope,\n\t\t\texisting?: boolean | undefined,\n\t\t\tctor: typeof ContainerRuntime = ContainerRuntimeWithAttributor as unknown as typeof ContainerRuntime,\n\t\t): Promise<ContainerRuntime> {\n\t\t\tconst runtimeAttributor = (\n\t\t\t\tcontainerScope as FluidObject<IProvideRuntimeAttributor> | undefined\n\t\t\t)?.IRuntimeAttributor;\n\t\t\tif (!runtimeAttributor) {\n\t\t\t\tthrow new UsageError(\n\t\t\t\t\t\"ContainerRuntimeWithAttributor must be passed a scope implementing IProvideRuntimeAttributor\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst pendingRuntimeState = context.pendingLocalState as {\n\t\t\t\tbaseSnapshot?: ISnapshotTree;\n\t\t\t};\n\t\t\tconst baseSnapshot: ISnapshotTree | undefined =\n\t\t\t\tpendingRuntimeState?.baseSnapshot ?? context.baseSnapshot;\n\n\t\t\tconst { audience, deltaManager } = context;\n\t\t\tassert(\n\t\t\t\taudience !== undefined,\n\t\t\t\t0x508 /* Audience must exist when instantiating attribution-providing runtime */,\n\t\t\t);\n\n\t\t\tconst mc = loggerToMonitoringContext(context.taggedLogger);\n\n\t\t\tconst shouldTrackAttribution = mc.config.getBoolean(enableOnNewFileKey) ?? false;\n\t\t\tif (shouldTrackAttribution) {\n\t\t\t\t(context.options.attribution ??= {}).track = true;\n\t\t\t}\n\n\t\t\tconst runtime = (await Base.load(\n\t\t\t\tcontext,\n\t\t\t\tregistryEntries,\n\t\t\t\trequestHandler,\n\t\t\t\truntimeOptions,\n\t\t\t\tcontainerScope,\n\t\t\t\texisting,\n\t\t\t\tctor,\n\t\t\t)) as ContainerRuntimeWithAttributor;\n\t\t\truntime.runtimeAttributor = runtimeAttributor as RuntimeAttributor;\n\n\t\t\tconst logger = ChildLogger.create(runtime.logger, \"Attributor\");\n\n\t\t\t// Note: this fetches attribution blobs relatively eagerly in the load flow; we may want to optimize\n\t\t\t// this to avoid blocking on such information until application actually requests some op-based attribution\n\t\t\t// info or we need to summarize. All that really needs to happen immediately is to start recording\n\t\t\t// op seq# -> attributionInfo for new ops.\n\t\t\tawait PerformanceEvent.timedExecAsync(\n\t\t\t\tlogger,\n\t\t\t\t{\n\t\t\t\t\teventName: \"initialize\",\n\t\t\t\t},\n\t\t\t\tasync (event) => {\n\t\t\t\t\tvoid runtime.runtimeAttributor?.initialize(\n\t\t\t\t\t\tdeltaManager,\n\t\t\t\t\t\taudience,\n\t\t\t\t\t\tbaseSnapshot,\n\t\t\t\t\t\tasync (id) => runtime.storage.readBlob(id),\n\t\t\t\t\t\tshouldTrackAttribution,\n\t\t\t\t\t);\n\t\t\t\t\tevent.end({\n\t\t\t\t\t\tattributionEnabledInConfig: shouldTrackAttribution,\n\t\t\t\t\t\tattributionEnabledInDoc: runtime.runtimeAttributor\n\t\t\t\t\t\t\t? runtime.runtimeAttributor.isEnabled\n\t\t\t\t\t\t\t: false,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\n\t\t\treturn runtime;\n\t\t}\n\n\t\tprivate runtimeAttributor: RuntimeAttributor | undefined;\n\n\t\tprotected addContainerStateToSummary(\n\t\t\tsummaryTree: ISummaryTreeWithStats,\n\t\t\tfullTree: boolean,\n\t\t\ttrackState: boolean,\n\t\t\ttelemetryContext?: ITelemetryContext,\n\t\t) {\n\t\t\tsuper.addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext);\n\t\t\tconst attributorSummary = this.runtimeAttributor?.summarize();\n\t\t\tif (attributorSummary) {\n\t\t\t\taddSummarizeResultToSummary(summaryTree, attributorTreeName, attributorSummary);\n\t\t\t}\n\t\t}\n\t} as unknown as typeof ContainerRuntime;\n\nclass RuntimeAttributor implements IRuntimeAttributor {\n\tpublic get IRuntimeAttributor(): IRuntimeAttributor {\n\t\treturn this;\n\t}\n\n\tpublic get(key: AttributionKey): AttributionInfo {\n\t\tassert(\n\t\t\tthis.opAttributor !== undefined,\n\t\t\t0x509 /* RuntimeAttributor must be initialized before getAttributionInfo can be called */,\n\t\t);\n\n\t\tif (key.type === \"detached\") {\n\t\t\tthrow new Error(\"Attribution of detached keys is not yet supported.\");\n\t\t}\n\n\t\tif (key.type === \"local\") {\n\t\t\t// Note: we can *almost* orchestrate this correctly with internal-only changes by looking up the current\n\t\t\t// client id in the audience. However, for read->write client transition, the container might have not yet\n\t\t\t// received a client id. This is left as a TODO as it might be more easily solved once the detached case\n\t\t\t// is settled (e.g. if it's reasonable for the host to know the current user information at container\n\t\t\t// creation time, we could just use that here as well).\n\t\t\tthrow new Error(\"Attribution of local keys is not yet supported.\");\n\t\t}\n\n\t\treturn this.opAttributor.getAttributionInfo(key.seq);\n\t}\n\n\tpublic has(key: AttributionKey): boolean {\n\t\tif (key.type === \"detached\") {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (key.type === \"local\") {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn this.opAttributor?.tryGetAttributionInfo(key.seq) !== undefined;\n\t}\n\n\tprivate encoder: Encoder<IAttributor, string> = {\n\t\tencode: unreachableCase,\n\t\tdecode: unreachableCase,\n\t};\n\n\tprivate opAttributor: IAttributor | undefined;\n\tpublic isEnabled = false;\n\n\tpublic async initialize(\n\t\tdeltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,\n\t\taudience: IAudience,\n\t\tbaseSnapshot: ISnapshotTree | undefined,\n\t\treadBlob: (id: string) => Promise<ArrayBufferLike>,\n\t\tshouldAddAttributorOnNewFile: boolean,\n\t): Promise<void> {\n\t\tconst attributorTree = baseSnapshot?.trees[attributorTreeName];\n\t\t// Existing documents that don't already have a snapshot containing runtime attribution info shouldn't\n\t\t// inject any for now--this causes some back-compat integration problems that aren't fully worked out.\n\t\tconst shouldExcludeAttributor =\n\t\t\t(baseSnapshot !== undefined && attributorTree === undefined) ||\n\t\t\t(baseSnapshot === undefined && !shouldAddAttributorOnNewFile);\n\t\tif (shouldExcludeAttributor) {\n\t\t\t// This gives a consistent error for calls to `get` on keys that don't exist.\n\t\t\tthis.opAttributor = new Attributor();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.isEnabled = true;\n\t\tthis.encoder = chain(\n\t\t\tnew AttributorSerializer(\n\t\t\t\t(entries) => new OpStreamAttributor(deltaManager, audience, entries),\n\t\t\t\tdeltaEncoder,\n\t\t\t),\n\t\t\tmakeLZ4Encoder(),\n\t\t);\n\n\t\tif (attributorTree !== undefined) {\n\t\t\tconst id = attributorTree.blobs[opBlobName];\n\t\t\tassert(\n\t\t\t\tid !== undefined,\n\t\t\t\t0x50a /* Attributor tree should have op attributor summary blob. */,\n\t\t\t);\n\t\t\tconst blobContents = await readBlob(id);\n\t\t\tconst attributorSnapshot = bufferToString(blobContents, \"utf8\");\n\t\t\tthis.opAttributor = this.encoder.decode(attributorSnapshot);\n\t\t} else {\n\t\t\tthis.opAttributor = new OpStreamAttributor(deltaManager, audience);\n\t\t}\n\t}\n\n\tpublic summarize(): ISummaryTreeWithStats | undefined {\n\t\tif (!this.isEnabled) {\n\t\t\t// Loaded existing document without attributor data: avoid injecting any data.\n\t\t\treturn undefined;\n\t\t}\n\n\t\tassert(\n\t\t\tthis.opAttributor !== undefined,\n\t\t\t0x50b /* RuntimeAttributor should be initialized before summarization */,\n\t\t);\n\t\tconst builder = new SummaryTreeBuilder();\n\t\tbuilder.addBlob(opBlobName, this.encoder.encode(this.opAttributor));\n\t\treturn builder.getSummaryTree();\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mixinAttributor.d.ts","sourceRoot":"","sources":["../src/mixinAttributor.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAErE,OAAO,EACN,eAAe,EACf,cAAc,EAId,MAAM,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"mixinAttributor.d.ts","sourceRoot":"","sources":["../src/mixinAttributor.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAErE,OAAO,EACN,eAAe,EACf,cAAc,EAId,MAAM,qCAAqC,CAAC;AAmB7C;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,sCAAsC,CAAC;AAEtE;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,yBAAgD,CAAC;AAExF;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACzC,QAAQ,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;CAChD;AAED;;;;;;GAMG;AACH,MAAM,WAAW,kBAAmB,SAAQ,yBAAyB;IACpE;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,cAAc,GAAG,eAAe,CAAC;IAE1C;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC;IAElC;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,kBAAkB,CAE5D;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,UAAU,uBAAuB,4BAiGrB,CAAC"}
|
package/lib/mixinAttributor.js
CHANGED
|
@@ -2,7 +2,7 @@ import { ContainerRuntime } from "@fluidframework/container-runtime";
|
|
|
2
2
|
import { addSummarizeResultToSummary, SummaryTreeBuilder } from "@fluidframework/runtime-utils";
|
|
3
3
|
import { assert, bufferToString, unreachableCase } from "@fluidframework/common-utils";
|
|
4
4
|
import { UsageError } from "@fluidframework/container-utils";
|
|
5
|
-
import { loggerToMonitoringContext } from "@fluidframework/telemetry-utils";
|
|
5
|
+
import { ChildLogger, PerformanceEvent, loggerToMonitoringContext, } from "@fluidframework/telemetry-utils";
|
|
6
6
|
import { Attributor, OpStreamAttributor } from "./attributor";
|
|
7
7
|
import { AttributorSerializer, chain, deltaEncoder } from "./encoders";
|
|
8
8
|
import { makeLZ4Encoder } from "./lz4Encoder";
|
|
@@ -58,11 +58,23 @@ export const mixinAttributor = (Base = ContainerRuntime) => class ContainerRunti
|
|
|
58
58
|
}
|
|
59
59
|
const runtime = (await Base.load(context, registryEntries, requestHandler, runtimeOptions, containerScope, existing, ctor));
|
|
60
60
|
runtime.runtimeAttributor = runtimeAttributor;
|
|
61
|
+
const logger = ChildLogger.create(runtime.logger, "Attributor");
|
|
61
62
|
// Note: this fetches attribution blobs relatively eagerly in the load flow; we may want to optimize
|
|
62
63
|
// this to avoid blocking on such information until application actually requests some op-based attribution
|
|
63
64
|
// info or we need to summarize. All that really needs to happen immediately is to start recording
|
|
64
65
|
// op seq# -> attributionInfo for new ops.
|
|
65
|
-
await
|
|
66
|
+
await PerformanceEvent.timedExecAsync(logger, {
|
|
67
|
+
eventName: "initialize",
|
|
68
|
+
}, async (event) => {
|
|
69
|
+
var _a;
|
|
70
|
+
void ((_a = runtime.runtimeAttributor) === null || _a === void 0 ? void 0 : _a.initialize(deltaManager, audience, baseSnapshot, async (id) => runtime.storage.readBlob(id), shouldTrackAttribution));
|
|
71
|
+
event.end({
|
|
72
|
+
attributionEnabledInConfig: shouldTrackAttribution,
|
|
73
|
+
attributionEnabledInDoc: runtime.runtimeAttributor
|
|
74
|
+
? runtime.runtimeAttributor.isEnabled
|
|
75
|
+
: false,
|
|
76
|
+
});
|
|
77
|
+
});
|
|
66
78
|
return runtime;
|
|
67
79
|
}
|
|
68
80
|
addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mixinAttributor.js","sourceRoot":"","sources":["../src/mixinAttributor.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 */\nimport {\n\tIDocumentMessage,\n\tISequencedDocumentMessage,\n\tISnapshotTree,\n} from \"@fluidframework/protocol-definitions\";\nimport { IAudience, IContainerContext, IDeltaManager } from \"@fluidframework/container-definitions\";\nimport { ContainerRuntime } from \"@fluidframework/container-runtime\";\nimport type { IContainerRuntimeOptions } from \"@fluidframework/container-runtime\";\nimport {\n\tAttributionInfo,\n\tAttributionKey,\n\tISummaryTreeWithStats,\n\tITelemetryContext,\n\tNamedFluidDataStoreRegistryEntries,\n} from \"@fluidframework/runtime-definitions\";\nimport { addSummarizeResultToSummary, SummaryTreeBuilder } from \"@fluidframework/runtime-utils\";\nimport { IContainerRuntime } from \"@fluidframework/container-runtime-definitions\";\nimport { IRequest, IResponse, FluidObject } from \"@fluidframework/core-interfaces\";\nimport { assert, bufferToString, unreachableCase } from \"@fluidframework/common-utils\";\nimport { UsageError } from \"@fluidframework/container-utils\";\nimport { loggerToMonitoringContext } from \"@fluidframework/telemetry-utils\";\nimport { Attributor, IAttributor, OpStreamAttributor } from \"./attributor\";\nimport { AttributorSerializer, chain, deltaEncoder, Encoder } from \"./encoders\";\nimport { makeLZ4Encoder } from \"./lz4Encoder\";\n\n// Summary tree keys\nconst attributorTreeName = \".attributor\";\nconst opBlobName = \"op\";\n\n/**\n * @alpha\n * Feature Gate Key -\n * Whether or not a container runtime instantiated using `mixinAttributor`'s load should generate an attributor on\n * new files. See package README for more notes on integration.\n */\nexport const enableOnNewFileKey = \"Fluid.Attribution.EnableOnNewFile\";\n\n/**\n * @alpha\n */\nexport const IRuntimeAttributor: keyof IProvideRuntimeAttributor = \"IRuntimeAttributor\";\n\n/**\n * @alpha\n */\nexport interface IProvideRuntimeAttributor {\n\treadonly IRuntimeAttributor: IRuntimeAttributor;\n}\n\n/**\n * Provides access to attribution information stored on the container runtime.\n *\n * Attributors are only populated after the container runtime they are injected into has initialized.\n * @sealed\n * @alpha\n */\nexport interface IRuntimeAttributor extends IProvideRuntimeAttributor {\n\t/**\n\t * @throws - If no AttributionInfo exists for this key.\n\t */\n\tget(key: AttributionKey): AttributionInfo;\n\n\t/**\n\t * @returns - Whether any AttributionInfo exists for the provided key.\n\t */\n\thas(key: AttributionKey): boolean;\n\n\t/**\n\t * @returns - Whether the runtime is currently tracking attribution information for the loaded container.\n\t * See {@link mixinAttributor} for more details on when this happens.\n\t */\n\treadonly isEnabled: boolean;\n}\n\n/**\n * @returns an IRuntimeAttributor for usage with `mixinAttributor`. The attributor will only be populated with data\n * once it's passed via scope to a container runtime load flow. See {@link mixinAttributor}.\n * @alpha\n */\nexport function createRuntimeAttributor(): IRuntimeAttributor {\n\treturn new RuntimeAttributor();\n}\n\n/**\n * Mixes in logic to load and store runtime-based attribution functionality.\n *\n * The `scope` passed to `load` should implement `IProvideRuntimeAttributor`.\n *\n * Existing documents without stored attributors will not start storing attribution information: if an\n * IRuntimeAttributor is passed via scope to load a document that never previously had attribution information,\n * that attributor's `has` method will always return `false`.\n * @param Base - base class, inherits from FluidAttributorRuntime\n * @alpha\n */\nexport const mixinAttributor = (Base: typeof ContainerRuntime = ContainerRuntime) =>\n\tclass ContainerRuntimeWithAttributor extends Base {\n\t\tpublic static async load(\n\t\t\tcontext: IContainerContext,\n\t\t\tregistryEntries: NamedFluidDataStoreRegistryEntries,\n\t\t\trequestHandler?:\n\t\t\t\t| ((request: IRequest, runtime: IContainerRuntime) => Promise<IResponse>)\n\t\t\t\t| undefined,\n\t\t\truntimeOptions: IContainerRuntimeOptions | undefined = {},\n\t\t\tcontainerScope: FluidObject | undefined = context.scope,\n\t\t\texisting?: boolean | undefined,\n\t\t\tctor: typeof ContainerRuntime = ContainerRuntimeWithAttributor as unknown as typeof ContainerRuntime,\n\t\t): Promise<ContainerRuntime> {\n\t\t\tconst runtimeAttributor = (\n\t\t\t\tcontainerScope as FluidObject<IProvideRuntimeAttributor> | undefined\n\t\t\t)?.IRuntimeAttributor;\n\t\t\tif (!runtimeAttributor) {\n\t\t\t\tthrow new UsageError(\n\t\t\t\t\t\"ContainerRuntimeWithAttributor must be passed a scope implementing IProvideRuntimeAttributor\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst pendingRuntimeState = context.pendingLocalState as {\n\t\t\t\tbaseSnapshot?: ISnapshotTree;\n\t\t\t};\n\t\t\tconst baseSnapshot: ISnapshotTree | undefined =\n\t\t\t\tpendingRuntimeState?.baseSnapshot ?? context.baseSnapshot;\n\n\t\t\tconst { audience, deltaManager } = context;\n\t\t\tassert(\n\t\t\t\taudience !== undefined,\n\t\t\t\t0x508 /* Audience must exist when instantiating attribution-providing runtime */,\n\t\t\t);\n\n\t\t\tconst mc = loggerToMonitoringContext(context.taggedLogger);\n\t\t\tconst shouldTrackAttribution = mc.config.getBoolean(enableOnNewFileKey) ?? false;\n\t\t\tif (shouldTrackAttribution) {\n\t\t\t\t(context.options.attribution ??= {}).track = true;\n\t\t\t}\n\n\t\t\tconst runtime = (await Base.load(\n\t\t\t\tcontext,\n\t\t\t\tregistryEntries,\n\t\t\t\trequestHandler,\n\t\t\t\truntimeOptions,\n\t\t\t\tcontainerScope,\n\t\t\t\texisting,\n\t\t\t\tctor,\n\t\t\t)) as ContainerRuntimeWithAttributor;\n\t\t\truntime.runtimeAttributor = runtimeAttributor as RuntimeAttributor;\n\n\t\t\t// Note: this fetches attribution blobs relatively eagerly in the load flow; we may want to optimize\n\t\t\t// this to avoid blocking on such information until application actually requests some op-based attribution\n\t\t\t// info or we need to summarize. All that really needs to happen immediately is to start recording\n\t\t\t// op seq# -> attributionInfo for new ops.\n\t\t\tawait runtime.runtimeAttributor.initialize(\n\t\t\t\tdeltaManager,\n\t\t\t\taudience,\n\t\t\t\tbaseSnapshot,\n\t\t\t\tasync (id) => runtime.storage.readBlob(id),\n\t\t\t\tshouldTrackAttribution,\n\t\t\t);\n\t\t\treturn runtime;\n\t\t}\n\n\t\tprivate runtimeAttributor: RuntimeAttributor | undefined;\n\n\t\tprotected addContainerStateToSummary(\n\t\t\tsummaryTree: ISummaryTreeWithStats,\n\t\t\tfullTree: boolean,\n\t\t\ttrackState: boolean,\n\t\t\ttelemetryContext?: ITelemetryContext,\n\t\t) {\n\t\t\tsuper.addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext);\n\t\t\tconst attributorSummary = this.runtimeAttributor?.summarize();\n\t\t\tif (attributorSummary) {\n\t\t\t\taddSummarizeResultToSummary(summaryTree, attributorTreeName, attributorSummary);\n\t\t\t}\n\t\t}\n\t} as unknown as typeof ContainerRuntime;\n\nclass RuntimeAttributor implements IRuntimeAttributor {\n\tpublic get IRuntimeAttributor(): IRuntimeAttributor {\n\t\treturn this;\n\t}\n\n\tpublic get(key: AttributionKey): AttributionInfo {\n\t\tassert(\n\t\t\tthis.opAttributor !== undefined,\n\t\t\t0x509 /* RuntimeAttributor must be initialized before getAttributionInfo can be called */,\n\t\t);\n\n\t\tif (key.type === \"detached\") {\n\t\t\tthrow new Error(\"Attribution of detached keys is not yet supported.\");\n\t\t}\n\n\t\tif (key.type === \"local\") {\n\t\t\t// Note: we can *almost* orchestrate this correctly with internal-only changes by looking up the current\n\t\t\t// client id in the audience. However, for read->write client transition, the container might have not yet\n\t\t\t// received a client id. This is left as a TODO as it might be more easily solved once the detached case\n\t\t\t// is settled (e.g. if it's reasonable for the host to know the current user information at container\n\t\t\t// creation time, we could just use that here as well).\n\t\t\tthrow new Error(\"Attribution of local keys is not yet supported.\");\n\t\t}\n\n\t\treturn this.opAttributor.getAttributionInfo(key.seq);\n\t}\n\n\tpublic has(key: AttributionKey): boolean {\n\t\tif (key.type === \"detached\") {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (key.type === \"local\") {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn this.opAttributor?.tryGetAttributionInfo(key.seq) !== undefined;\n\t}\n\n\tprivate encoder: Encoder<IAttributor, string> = {\n\t\tencode: unreachableCase,\n\t\tdecode: unreachableCase,\n\t};\n\n\tprivate opAttributor: IAttributor | undefined;\n\tpublic isEnabled = false;\n\n\tpublic async initialize(\n\t\tdeltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,\n\t\taudience: IAudience,\n\t\tbaseSnapshot: ISnapshotTree | undefined,\n\t\treadBlob: (id: string) => Promise<ArrayBufferLike>,\n\t\tshouldAddAttributorOnNewFile: boolean,\n\t): Promise<void> {\n\t\tconst attributorTree = baseSnapshot?.trees[attributorTreeName];\n\t\t// Existing documents that don't already have a snapshot containing runtime attribution info shouldn't\n\t\t// inject any for now--this causes some back-compat integration problems that aren't fully worked out.\n\t\tconst shouldExcludeAttributor =\n\t\t\t(baseSnapshot !== undefined && attributorTree === undefined) ||\n\t\t\t(baseSnapshot === undefined && !shouldAddAttributorOnNewFile);\n\t\tif (shouldExcludeAttributor) {\n\t\t\t// This gives a consistent error for calls to `get` on keys that don't exist.\n\t\t\tthis.opAttributor = new Attributor();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.isEnabled = true;\n\t\tthis.encoder = chain(\n\t\t\tnew AttributorSerializer(\n\t\t\t\t(entries) => new OpStreamAttributor(deltaManager, audience, entries),\n\t\t\t\tdeltaEncoder,\n\t\t\t),\n\t\t\tmakeLZ4Encoder(),\n\t\t);\n\n\t\tif (attributorTree !== undefined) {\n\t\t\tconst id = attributorTree.blobs[opBlobName];\n\t\t\tassert(\n\t\t\t\tid !== undefined,\n\t\t\t\t0x50a /* Attributor tree should have op attributor summary blob. */,\n\t\t\t);\n\t\t\tconst blobContents = await readBlob(id);\n\t\t\tconst attributorSnapshot = bufferToString(blobContents, \"utf8\");\n\t\t\tthis.opAttributor = this.encoder.decode(attributorSnapshot);\n\t\t} else {\n\t\t\tthis.opAttributor = new OpStreamAttributor(deltaManager, audience);\n\t\t}\n\t}\n\n\tpublic summarize(): ISummaryTreeWithStats | undefined {\n\t\tif (!this.isEnabled) {\n\t\t\t// Loaded existing document without attributor data: avoid injecting any data.\n\t\t\treturn undefined;\n\t\t}\n\n\t\tassert(\n\t\t\tthis.opAttributor !== undefined,\n\t\t\t0x50b /* RuntimeAttributor should be initialized before summarization */,\n\t\t);\n\t\tconst builder = new SummaryTreeBuilder();\n\t\tbuilder.addBlob(opBlobName, this.encoder.encode(this.opAttributor));\n\t\treturn builder.getSummaryTree();\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mixinAttributor.js","sourceRoot":"","sources":["../src/mixinAttributor.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 */\nimport {\n\tIDocumentMessage,\n\tISequencedDocumentMessage,\n\tISnapshotTree,\n} from \"@fluidframework/protocol-definitions\";\nimport { IAudience, IContainerContext, IDeltaManager } from \"@fluidframework/container-definitions\";\nimport { ContainerRuntime } from \"@fluidframework/container-runtime\";\nimport type { IContainerRuntimeOptions } from \"@fluidframework/container-runtime\";\nimport {\n\tAttributionInfo,\n\tAttributionKey,\n\tISummaryTreeWithStats,\n\tITelemetryContext,\n\tNamedFluidDataStoreRegistryEntries,\n} from \"@fluidframework/runtime-definitions\";\nimport { addSummarizeResultToSummary, SummaryTreeBuilder } from \"@fluidframework/runtime-utils\";\nimport { IContainerRuntime } from \"@fluidframework/container-runtime-definitions\";\nimport { IRequest, IResponse, FluidObject } from \"@fluidframework/core-interfaces\";\nimport { assert, bufferToString, unreachableCase } from \"@fluidframework/common-utils\";\nimport { UsageError } from \"@fluidframework/container-utils\";\nimport {\n\tChildLogger,\n\tPerformanceEvent,\n\tloggerToMonitoringContext,\n} from \"@fluidframework/telemetry-utils\";\nimport { Attributor, IAttributor, OpStreamAttributor } from \"./attributor\";\nimport { AttributorSerializer, chain, deltaEncoder, Encoder } from \"./encoders\";\nimport { makeLZ4Encoder } from \"./lz4Encoder\";\n\n// Summary tree keys\nconst attributorTreeName = \".attributor\";\nconst opBlobName = \"op\";\n\n/**\n * @alpha\n * Feature Gate Key -\n * Whether or not a container runtime instantiated using `mixinAttributor`'s load should generate an attributor on\n * new files. See package README for more notes on integration.\n */\nexport const enableOnNewFileKey = \"Fluid.Attribution.EnableOnNewFile\";\n\n/**\n * @alpha\n */\nexport const IRuntimeAttributor: keyof IProvideRuntimeAttributor = \"IRuntimeAttributor\";\n\n/**\n * @alpha\n */\nexport interface IProvideRuntimeAttributor {\n\treadonly IRuntimeAttributor: IRuntimeAttributor;\n}\n\n/**\n * Provides access to attribution information stored on the container runtime.\n *\n * Attributors are only populated after the container runtime they are injected into has initialized.\n * @sealed\n * @alpha\n */\nexport interface IRuntimeAttributor extends IProvideRuntimeAttributor {\n\t/**\n\t * @throws - If no AttributionInfo exists for this key.\n\t */\n\tget(key: AttributionKey): AttributionInfo;\n\n\t/**\n\t * @returns - Whether any AttributionInfo exists for the provided key.\n\t */\n\thas(key: AttributionKey): boolean;\n\n\t/**\n\t * @returns - Whether the runtime is currently tracking attribution information for the loaded container.\n\t * See {@link mixinAttributor} for more details on when this happens.\n\t */\n\treadonly isEnabled: boolean;\n}\n\n/**\n * @returns an IRuntimeAttributor for usage with `mixinAttributor`. The attributor will only be populated with data\n * once it's passed via scope to a container runtime load flow. See {@link mixinAttributor}.\n * @alpha\n */\nexport function createRuntimeAttributor(): IRuntimeAttributor {\n\treturn new RuntimeAttributor();\n}\n\n/**\n * Mixes in logic to load and store runtime-based attribution functionality.\n *\n * The `scope` passed to `load` should implement `IProvideRuntimeAttributor`.\n *\n * Existing documents without stored attributors will not start storing attribution information: if an\n * IRuntimeAttributor is passed via scope to load a document that never previously had attribution information,\n * that attributor's `has` method will always return `false`.\n * @param Base - base class, inherits from FluidAttributorRuntime\n * @alpha\n */\nexport const mixinAttributor = (Base: typeof ContainerRuntime = ContainerRuntime) =>\n\tclass ContainerRuntimeWithAttributor extends Base {\n\t\tpublic static async load(\n\t\t\tcontext: IContainerContext,\n\t\t\tregistryEntries: NamedFluidDataStoreRegistryEntries,\n\t\t\trequestHandler?:\n\t\t\t\t| ((request: IRequest, runtime: IContainerRuntime) => Promise<IResponse>)\n\t\t\t\t| undefined,\n\t\t\truntimeOptions: IContainerRuntimeOptions | undefined = {},\n\t\t\tcontainerScope: FluidObject | undefined = context.scope,\n\t\t\texisting?: boolean | undefined,\n\t\t\tctor: typeof ContainerRuntime = ContainerRuntimeWithAttributor as unknown as typeof ContainerRuntime,\n\t\t): Promise<ContainerRuntime> {\n\t\t\tconst runtimeAttributor = (\n\t\t\t\tcontainerScope as FluidObject<IProvideRuntimeAttributor> | undefined\n\t\t\t)?.IRuntimeAttributor;\n\t\t\tif (!runtimeAttributor) {\n\t\t\t\tthrow new UsageError(\n\t\t\t\t\t\"ContainerRuntimeWithAttributor must be passed a scope implementing IProvideRuntimeAttributor\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst pendingRuntimeState = context.pendingLocalState as {\n\t\t\t\tbaseSnapshot?: ISnapshotTree;\n\t\t\t};\n\t\t\tconst baseSnapshot: ISnapshotTree | undefined =\n\t\t\t\tpendingRuntimeState?.baseSnapshot ?? context.baseSnapshot;\n\n\t\t\tconst { audience, deltaManager } = context;\n\t\t\tassert(\n\t\t\t\taudience !== undefined,\n\t\t\t\t0x508 /* Audience must exist when instantiating attribution-providing runtime */,\n\t\t\t);\n\n\t\t\tconst mc = loggerToMonitoringContext(context.taggedLogger);\n\n\t\t\tconst shouldTrackAttribution = mc.config.getBoolean(enableOnNewFileKey) ?? false;\n\t\t\tif (shouldTrackAttribution) {\n\t\t\t\t(context.options.attribution ??= {}).track = true;\n\t\t\t}\n\n\t\t\tconst runtime = (await Base.load(\n\t\t\t\tcontext,\n\t\t\t\tregistryEntries,\n\t\t\t\trequestHandler,\n\t\t\t\truntimeOptions,\n\t\t\t\tcontainerScope,\n\t\t\t\texisting,\n\t\t\t\tctor,\n\t\t\t)) as ContainerRuntimeWithAttributor;\n\t\t\truntime.runtimeAttributor = runtimeAttributor as RuntimeAttributor;\n\n\t\t\tconst logger = ChildLogger.create(runtime.logger, \"Attributor\");\n\n\t\t\t// Note: this fetches attribution blobs relatively eagerly in the load flow; we may want to optimize\n\t\t\t// this to avoid blocking on such information until application actually requests some op-based attribution\n\t\t\t// info or we need to summarize. All that really needs to happen immediately is to start recording\n\t\t\t// op seq# -> attributionInfo for new ops.\n\t\t\tawait PerformanceEvent.timedExecAsync(\n\t\t\t\tlogger,\n\t\t\t\t{\n\t\t\t\t\teventName: \"initialize\",\n\t\t\t\t},\n\t\t\t\tasync (event) => {\n\t\t\t\t\tvoid runtime.runtimeAttributor?.initialize(\n\t\t\t\t\t\tdeltaManager,\n\t\t\t\t\t\taudience,\n\t\t\t\t\t\tbaseSnapshot,\n\t\t\t\t\t\tasync (id) => runtime.storage.readBlob(id),\n\t\t\t\t\t\tshouldTrackAttribution,\n\t\t\t\t\t);\n\t\t\t\t\tevent.end({\n\t\t\t\t\t\tattributionEnabledInConfig: shouldTrackAttribution,\n\t\t\t\t\t\tattributionEnabledInDoc: runtime.runtimeAttributor\n\t\t\t\t\t\t\t? runtime.runtimeAttributor.isEnabled\n\t\t\t\t\t\t\t: false,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\n\t\t\treturn runtime;\n\t\t}\n\n\t\tprivate runtimeAttributor: RuntimeAttributor | undefined;\n\n\t\tprotected addContainerStateToSummary(\n\t\t\tsummaryTree: ISummaryTreeWithStats,\n\t\t\tfullTree: boolean,\n\t\t\ttrackState: boolean,\n\t\t\ttelemetryContext?: ITelemetryContext,\n\t\t) {\n\t\t\tsuper.addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext);\n\t\t\tconst attributorSummary = this.runtimeAttributor?.summarize();\n\t\t\tif (attributorSummary) {\n\t\t\t\taddSummarizeResultToSummary(summaryTree, attributorTreeName, attributorSummary);\n\t\t\t}\n\t\t}\n\t} as unknown as typeof ContainerRuntime;\n\nclass RuntimeAttributor implements IRuntimeAttributor {\n\tpublic get IRuntimeAttributor(): IRuntimeAttributor {\n\t\treturn this;\n\t}\n\n\tpublic get(key: AttributionKey): AttributionInfo {\n\t\tassert(\n\t\t\tthis.opAttributor !== undefined,\n\t\t\t0x509 /* RuntimeAttributor must be initialized before getAttributionInfo can be called */,\n\t\t);\n\n\t\tif (key.type === \"detached\") {\n\t\t\tthrow new Error(\"Attribution of detached keys is not yet supported.\");\n\t\t}\n\n\t\tif (key.type === \"local\") {\n\t\t\t// Note: we can *almost* orchestrate this correctly with internal-only changes by looking up the current\n\t\t\t// client id in the audience. However, for read->write client transition, the container might have not yet\n\t\t\t// received a client id. This is left as a TODO as it might be more easily solved once the detached case\n\t\t\t// is settled (e.g. if it's reasonable for the host to know the current user information at container\n\t\t\t// creation time, we could just use that here as well).\n\t\t\tthrow new Error(\"Attribution of local keys is not yet supported.\");\n\t\t}\n\n\t\treturn this.opAttributor.getAttributionInfo(key.seq);\n\t}\n\n\tpublic has(key: AttributionKey): boolean {\n\t\tif (key.type === \"detached\") {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (key.type === \"local\") {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn this.opAttributor?.tryGetAttributionInfo(key.seq) !== undefined;\n\t}\n\n\tprivate encoder: Encoder<IAttributor, string> = {\n\t\tencode: unreachableCase,\n\t\tdecode: unreachableCase,\n\t};\n\n\tprivate opAttributor: IAttributor | undefined;\n\tpublic isEnabled = false;\n\n\tpublic async initialize(\n\t\tdeltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,\n\t\taudience: IAudience,\n\t\tbaseSnapshot: ISnapshotTree | undefined,\n\t\treadBlob: (id: string) => Promise<ArrayBufferLike>,\n\t\tshouldAddAttributorOnNewFile: boolean,\n\t): Promise<void> {\n\t\tconst attributorTree = baseSnapshot?.trees[attributorTreeName];\n\t\t// Existing documents that don't already have a snapshot containing runtime attribution info shouldn't\n\t\t// inject any for now--this causes some back-compat integration problems that aren't fully worked out.\n\t\tconst shouldExcludeAttributor =\n\t\t\t(baseSnapshot !== undefined && attributorTree === undefined) ||\n\t\t\t(baseSnapshot === undefined && !shouldAddAttributorOnNewFile);\n\t\tif (shouldExcludeAttributor) {\n\t\t\t// This gives a consistent error for calls to `get` on keys that don't exist.\n\t\t\tthis.opAttributor = new Attributor();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.isEnabled = true;\n\t\tthis.encoder = chain(\n\t\t\tnew AttributorSerializer(\n\t\t\t\t(entries) => new OpStreamAttributor(deltaManager, audience, entries),\n\t\t\t\tdeltaEncoder,\n\t\t\t),\n\t\t\tmakeLZ4Encoder(),\n\t\t);\n\n\t\tif (attributorTree !== undefined) {\n\t\t\tconst id = attributorTree.blobs[opBlobName];\n\t\t\tassert(\n\t\t\t\tid !== undefined,\n\t\t\t\t0x50a /* Attributor tree should have op attributor summary blob. */,\n\t\t\t);\n\t\t\tconst blobContents = await readBlob(id);\n\t\t\tconst attributorSnapshot = bufferToString(blobContents, \"utf8\");\n\t\t\tthis.opAttributor = this.encoder.decode(attributorSnapshot);\n\t\t} else {\n\t\t\tthis.opAttributor = new OpStreamAttributor(deltaManager, audience);\n\t\t}\n\t}\n\n\tpublic summarize(): ISummaryTreeWithStats | undefined {\n\t\tif (!this.isEnabled) {\n\t\t\t// Loaded existing document without attributor data: avoid injecting any data.\n\t\t\treturn undefined;\n\t\t}\n\n\t\tassert(\n\t\t\tthis.opAttributor !== undefined,\n\t\t\t0x50b /* RuntimeAttributor should be initialized before summarization */,\n\t\t);\n\t\tconst builder = new SummaryTreeBuilder();\n\t\tbuilder.addBlob(opBlobName, this.encoder.encode(this.opAttributor));\n\t\treturn builder.getSummaryTree();\n\t}\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluid-experimental/attributor",
|
|
3
|
-
"version": "2.0.0-internal.4.0
|
|
3
|
+
"version": "2.0.0-internal.4.1.0",
|
|
4
4
|
"description": "Operation attributor",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -36,29 +36,29 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@fluidframework/common-utils": "^1.1.1",
|
|
39
|
-
"@fluidframework/container-definitions": ">=2.0.0-internal.4.0
|
|
40
|
-
"@fluidframework/container-runtime": ">=2.0.0-internal.4.0
|
|
41
|
-
"@fluidframework/container-runtime-definitions": ">=2.0.0-internal.4.0
|
|
42
|
-
"@fluidframework/container-utils": ">=2.0.0-internal.4.0
|
|
43
|
-
"@fluidframework/core-interfaces": ">=2.0.0-internal.4.0
|
|
44
|
-
"@fluidframework/datastore-definitions": ">=2.0.0-internal.4.0
|
|
39
|
+
"@fluidframework/container-definitions": ">=2.0.0-internal.4.1.0 <2.0.0-internal.4.2.0",
|
|
40
|
+
"@fluidframework/container-runtime": ">=2.0.0-internal.4.1.0 <2.0.0-internal.4.2.0",
|
|
41
|
+
"@fluidframework/container-runtime-definitions": ">=2.0.0-internal.4.1.0 <2.0.0-internal.4.2.0",
|
|
42
|
+
"@fluidframework/container-utils": ">=2.0.0-internal.4.1.0 <2.0.0-internal.4.2.0",
|
|
43
|
+
"@fluidframework/core-interfaces": ">=2.0.0-internal.4.1.0 <2.0.0-internal.4.2.0",
|
|
44
|
+
"@fluidframework/datastore-definitions": ">=2.0.0-internal.4.1.0 <2.0.0-internal.4.2.0",
|
|
45
45
|
"@fluidframework/protocol-definitions": "^1.1.0",
|
|
46
|
-
"@fluidframework/runtime-definitions": ">=2.0.0-internal.4.0
|
|
47
|
-
"@fluidframework/runtime-utils": ">=2.0.0-internal.4.0
|
|
48
|
-
"@fluidframework/telemetry-utils": ">=2.0.0-internal.4.0
|
|
46
|
+
"@fluidframework/runtime-definitions": ">=2.0.0-internal.4.1.0 <2.0.0-internal.4.2.0",
|
|
47
|
+
"@fluidframework/runtime-utils": ">=2.0.0-internal.4.1.0 <2.0.0-internal.4.2.0",
|
|
48
|
+
"@fluidframework/telemetry-utils": ">=2.0.0-internal.4.1.0 <2.0.0-internal.4.2.0",
|
|
49
49
|
"lz4js": "^0.2.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@fluid-internal/stochastic-test-utils": ">=2.0.0-internal.4.0
|
|
53
|
-
"@fluid-tools/build-cli": "^0.
|
|
52
|
+
"@fluid-internal/stochastic-test-utils": ">=2.0.0-internal.4.1.0 <2.0.0-internal.4.2.0",
|
|
53
|
+
"@fluid-tools/build-cli": "^0.15.0",
|
|
54
54
|
"@fluidframework/build-common": "^1.1.0",
|
|
55
|
-
"@fluidframework/build-tools": "^0.
|
|
56
|
-
"@fluidframework/driver-definitions": ">=2.0.0-internal.4.0
|
|
55
|
+
"@fluidframework/build-tools": "^0.15.0",
|
|
56
|
+
"@fluidframework/driver-definitions": ">=2.0.0-internal.4.1.0 <2.0.0-internal.4.2.0",
|
|
57
57
|
"@fluidframework/eslint-config-fluid": "^2.0.0",
|
|
58
|
-
"@fluidframework/merge-tree": ">=2.0.0-internal.4.0
|
|
59
|
-
"@fluidframework/mocha-test-setup": ">=2.0.0-internal.4.0
|
|
60
|
-
"@fluidframework/sequence": ">=2.0.0-internal.4.0
|
|
61
|
-
"@fluidframework/test-runtime-utils": ">=2.0.0-internal.4.0
|
|
58
|
+
"@fluidframework/merge-tree": ">=2.0.0-internal.4.1.0 <2.0.0-internal.4.2.0",
|
|
59
|
+
"@fluidframework/mocha-test-setup": ">=2.0.0-internal.4.1.0 <2.0.0-internal.4.2.0",
|
|
60
|
+
"@fluidframework/sequence": ">=2.0.0-internal.4.1.0 <2.0.0-internal.4.2.0",
|
|
61
|
+
"@fluidframework/test-runtime-utils": ">=2.0.0-internal.4.1.0 <2.0.0-internal.4.2.0",
|
|
62
62
|
"@microsoft/api-extractor": "^7.34.4",
|
|
63
63
|
"@types/mocha": "^9.1.1",
|
|
64
64
|
"@types/node": "^14.18.38",
|
|
@@ -67,6 +67,9 @@
|
|
|
67
67
|
"cross-env": "^7.0.3",
|
|
68
68
|
"eslint": "~8.6.0",
|
|
69
69
|
"mocha": "^10.2.0",
|
|
70
|
+
"mocha-json-output-reporter": "^2.0.1",
|
|
71
|
+
"mocha-multi-reporters": "^1.5.1",
|
|
72
|
+
"moment": "^2.21.0",
|
|
70
73
|
"nyc": "^15.1.0",
|
|
71
74
|
"prettier": "~2.6.2",
|
|
72
75
|
"rimraf": "^4.4.0",
|
|
@@ -97,6 +100,7 @@
|
|
|
97
100
|
"test": "npm run test:mocha",
|
|
98
101
|
"test:coverage": "nyc npm test -- --reporter xunit --reporter-option output=nyc/junit-report.xml",
|
|
99
102
|
"test:mocha": "mocha --ignore 'dist/test/memory/**/*' --recursive 'dist/test/**/*.spec.js' -r node_modules/@fluidframework/mocha-test-setup --unhandled-rejections=strict",
|
|
103
|
+
"test:mocha:multireport": "cross-env FLUID_TEST_MULTIREPORT=1 npm run test:mocha",
|
|
100
104
|
"test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha",
|
|
101
105
|
"tsc": "tsc",
|
|
102
106
|
"typetests:gen": "fluid-type-test-generator",
|
package/src/mixinAttributor.ts
CHANGED
|
@@ -22,7 +22,11 @@ import { IContainerRuntime } from "@fluidframework/container-runtime-definitions
|
|
|
22
22
|
import { IRequest, IResponse, FluidObject } from "@fluidframework/core-interfaces";
|
|
23
23
|
import { assert, bufferToString, unreachableCase } from "@fluidframework/common-utils";
|
|
24
24
|
import { UsageError } from "@fluidframework/container-utils";
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
ChildLogger,
|
|
27
|
+
PerformanceEvent,
|
|
28
|
+
loggerToMonitoringContext,
|
|
29
|
+
} from "@fluidframework/telemetry-utils";
|
|
26
30
|
import { Attributor, IAttributor, OpStreamAttributor } from "./attributor";
|
|
27
31
|
import { AttributorSerializer, chain, deltaEncoder, Encoder } from "./encoders";
|
|
28
32
|
import { makeLZ4Encoder } from "./lz4Encoder";
|
|
@@ -131,6 +135,7 @@ export const mixinAttributor = (Base: typeof ContainerRuntime = ContainerRuntime
|
|
|
131
135
|
);
|
|
132
136
|
|
|
133
137
|
const mc = loggerToMonitoringContext(context.taggedLogger);
|
|
138
|
+
|
|
134
139
|
const shouldTrackAttribution = mc.config.getBoolean(enableOnNewFileKey) ?? false;
|
|
135
140
|
if (shouldTrackAttribution) {
|
|
136
141
|
(context.options.attribution ??= {}).track = true;
|
|
@@ -147,17 +152,34 @@ export const mixinAttributor = (Base: typeof ContainerRuntime = ContainerRuntime
|
|
|
147
152
|
)) as ContainerRuntimeWithAttributor;
|
|
148
153
|
runtime.runtimeAttributor = runtimeAttributor as RuntimeAttributor;
|
|
149
154
|
|
|
155
|
+
const logger = ChildLogger.create(runtime.logger, "Attributor");
|
|
156
|
+
|
|
150
157
|
// Note: this fetches attribution blobs relatively eagerly in the load flow; we may want to optimize
|
|
151
158
|
// this to avoid blocking on such information until application actually requests some op-based attribution
|
|
152
159
|
// info or we need to summarize. All that really needs to happen immediately is to start recording
|
|
153
160
|
// op seq# -> attributionInfo for new ops.
|
|
154
|
-
await
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
161
|
+
await PerformanceEvent.timedExecAsync(
|
|
162
|
+
logger,
|
|
163
|
+
{
|
|
164
|
+
eventName: "initialize",
|
|
165
|
+
},
|
|
166
|
+
async (event) => {
|
|
167
|
+
void runtime.runtimeAttributor?.initialize(
|
|
168
|
+
deltaManager,
|
|
169
|
+
audience,
|
|
170
|
+
baseSnapshot,
|
|
171
|
+
async (id) => runtime.storage.readBlob(id),
|
|
172
|
+
shouldTrackAttribution,
|
|
173
|
+
);
|
|
174
|
+
event.end({
|
|
175
|
+
attributionEnabledInConfig: shouldTrackAttribution,
|
|
176
|
+
attributionEnabledInDoc: runtime.runtimeAttributor
|
|
177
|
+
? runtime.runtimeAttributor.isEnabled
|
|
178
|
+
: false,
|
|
179
|
+
});
|
|
180
|
+
},
|
|
160
181
|
);
|
|
182
|
+
|
|
161
183
|
return runtime;
|
|
162
184
|
}
|
|
163
185
|
|