@fluidframework/datastore 1.4.0-121020 → 2.0.0-dev-rc.1.0.0.225277
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/.eslintrc.js +5 -7
- package/.mocharc.js +12 -0
- package/CHANGELOG.md +273 -0
- package/README.md +41 -0
- package/api-extractor-esm.json +4 -0
- package/api-extractor-lint.json +4 -0
- package/api-extractor.json +2 -2
- package/api-report/datastore.api.md +168 -0
- package/dist/channelContext.cjs +86 -0
- package/dist/channelContext.cjs.map +1 -0
- package/dist/channelContext.d.ts +15 -9
- package/dist/channelContext.d.ts.map +1 -1
- package/dist/{channelDeltaConnection.js → channelDeltaConnection.cjs} +14 -15
- package/dist/channelDeltaConnection.cjs.map +1 -0
- package/dist/channelDeltaConnection.d.ts +4 -5
- package/dist/channelDeltaConnection.d.ts.map +1 -1
- package/dist/{channelStorageService.js → channelStorageService.cjs} +13 -16
- package/dist/channelStorageService.cjs.map +1 -0
- package/dist/channelStorageService.d.ts +2 -2
- package/dist/channelStorageService.d.ts.map +1 -1
- package/dist/{dataStoreRuntime.js → dataStoreRuntime.cjs} +302 -225
- package/dist/dataStoreRuntime.cjs.map +1 -0
- package/dist/dataStoreRuntime.d.ts +81 -37
- package/dist/dataStoreRuntime.d.ts.map +1 -1
- package/dist/datastore-alpha.d.ts +317 -0
- package/dist/datastore-beta.d.ts +47 -0
- package/dist/datastore-public.d.ts +47 -0
- package/dist/datastore-untrimmed.d.ts +324 -0
- package/dist/{fluidHandle.js → fluidHandle.cjs} +44 -16
- package/dist/fluidHandle.cjs.map +1 -0
- package/dist/fluidHandle.d.ts +33 -6
- package/dist/fluidHandle.d.ts.map +1 -1
- package/dist/index.cjs +15 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/localChannelContext.cjs +190 -0
- package/dist/localChannelContext.cjs.map +1 -0
- package/dist/localChannelContext.d.ts +12 -21
- package/dist/localChannelContext.d.ts.map +1 -1
- package/dist/{localChannelStorageService.js → localChannelStorageService.cjs} +3 -3
- package/dist/localChannelStorageService.cjs.map +1 -0
- package/dist/localChannelStorageService.d.ts.map +1 -1
- package/dist/remoteChannelContext.cjs +124 -0
- package/dist/remoteChannelContext.cjs.map +1 -0
- package/dist/remoteChannelContext.d.ts +5 -10
- package/dist/remoteChannelContext.d.ts.map +1 -1
- package/dist/tsdoc-metadata.json +11 -0
- package/lib/{channelContext.d.ts → channelContext.d.mts} +17 -11
- package/lib/channelContext.d.mts.map +1 -0
- package/lib/channelContext.mjs +78 -0
- package/lib/channelContext.mjs.map +1 -0
- package/lib/{channelDeltaConnection.d.ts → channelDeltaConnection.d.mts} +5 -6
- package/lib/channelDeltaConnection.d.mts.map +1 -0
- package/lib/{channelDeltaConnection.js → channelDeltaConnection.mjs} +11 -12
- package/lib/channelDeltaConnection.mjs.map +1 -0
- package/lib/{channelStorageService.d.ts → channelStorageService.d.mts} +3 -3
- package/lib/channelStorageService.d.mts.map +1 -0
- package/lib/{channelStorageService.js → channelStorageService.mjs} +13 -16
- package/lib/channelStorageService.mjs.map +1 -0
- package/lib/{dataStoreRuntime.d.ts → dataStoreRuntime.d.mts} +82 -38
- package/lib/dataStoreRuntime.d.mts.map +1 -0
- package/lib/{dataStoreRuntime.js → dataStoreRuntime.mjs} +286 -209
- package/lib/dataStoreRuntime.mjs.map +1 -0
- package/lib/datastore-alpha.d.mts +317 -0
- package/lib/datastore-beta.d.mts +47 -0
- package/lib/datastore-public.d.mts +47 -0
- package/lib/datastore-untrimmed.d.mts +324 -0
- package/lib/fluidHandle.d.mts +57 -0
- package/lib/fluidHandle.d.mts.map +1 -0
- package/lib/{fluidHandle.js → fluidHandle.mjs} +44 -16
- package/lib/fluidHandle.mjs.map +1 -0
- package/lib/index.d.mts +7 -0
- package/lib/index.d.mts.map +1 -0
- package/lib/index.mjs +7 -0
- package/lib/index.mjs.map +1 -0
- package/lib/{localChannelContext.d.ts → localChannelContext.d.mts} +14 -23
- package/lib/localChannelContext.d.mts.map +1 -0
- package/lib/{localChannelContext.js → localChannelContext.mjs} +73 -85
- package/lib/localChannelContext.mjs.map +1 -0
- package/lib/{localChannelStorageService.d.ts → localChannelStorageService.d.mts} +1 -1
- package/lib/localChannelStorageService.d.mts.map +1 -0
- package/lib/{localChannelStorageService.js → localChannelStorageService.mjs} +2 -2
- package/lib/localChannelStorageService.mjs.map +1 -0
- package/lib/{remoteChannelContext.d.ts → remoteChannelContext.d.mts} +8 -13
- package/lib/remoteChannelContext.d.mts.map +1 -0
- package/lib/remoteChannelContext.mjs +120 -0
- package/lib/remoteChannelContext.mjs.map +1 -0
- package/package.json +139 -71
- package/{lib/index.js → prettier.config.cjs} +4 -3
- package/src/channelContext.ts +168 -71
- package/src/channelDeltaConnection.ts +52 -47
- package/src/channelStorageService.ts +59 -55
- package/src/dataStoreRuntime.ts +1158 -983
- package/src/fluidHandle.ts +92 -64
- package/src/index.ts +8 -2
- package/src/localChannelContext.ts +278 -272
- package/src/localChannelStorageService.ts +48 -46
- package/src/remoteChannelContext.ts +237 -300
- package/tsc-multi.test.json +4 -0
- package/tsconfig.json +11 -13
- package/dist/channelContext.js +0 -35
- package/dist/channelContext.js.map +0 -1
- package/dist/channelDeltaConnection.js.map +0 -1
- package/dist/channelStorageService.js.map +0 -1
- package/dist/dataStoreRuntime.js.map +0 -1
- package/dist/fluidHandle.js.map +0 -1
- package/dist/index.js +0 -19
- package/dist/index.js.map +0 -1
- package/dist/localChannelContext.js +0 -202
- package/dist/localChannelContext.js.map +0 -1
- package/dist/localChannelStorageService.js.map +0 -1
- package/dist/packageVersion.d.ts +0 -9
- package/dist/packageVersion.d.ts.map +0 -1
- package/dist/packageVersion.js +0 -12
- package/dist/packageVersion.js.map +0 -1
- package/dist/remoteChannelContext.js +0 -207
- package/dist/remoteChannelContext.js.map +0 -1
- package/lib/channelContext.d.ts.map +0 -1
- package/lib/channelContext.js +0 -29
- package/lib/channelContext.js.map +0 -1
- package/lib/channelDeltaConnection.d.ts.map +0 -1
- package/lib/channelDeltaConnection.js.map +0 -1
- package/lib/channelStorageService.d.ts.map +0 -1
- package/lib/channelStorageService.js.map +0 -1
- package/lib/dataStoreRuntime.d.ts.map +0 -1
- package/lib/dataStoreRuntime.js.map +0 -1
- package/lib/fluidHandle.d.ts +0 -30
- package/lib/fluidHandle.d.ts.map +0 -1
- package/lib/fluidHandle.js.map +0 -1
- package/lib/index.d.ts +0 -7
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/localChannelContext.d.ts.map +0 -1
- package/lib/localChannelContext.js.map +0 -1
- package/lib/localChannelStorageService.d.ts.map +0 -1
- package/lib/localChannelStorageService.js.map +0 -1
- package/lib/packageVersion.d.ts +0 -9
- package/lib/packageVersion.d.ts.map +0 -1
- package/lib/packageVersion.js +0 -9
- package/lib/packageVersion.js.map +0 -1
- package/lib/remoteChannelContext.d.ts.map +0 -1
- package/lib/remoteChannelContext.js +0 -203
- package/lib/remoteChannelContext.js.map +0 -1
- package/src/packageVersion.ts +0 -9
- package/tsconfig.esnext.json +0 -7
|
@@ -2,19 +2,22 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import { DataProcessingError, UsageError } from "@fluidframework/
|
|
7
|
-
import { assert, Deferred, LazyPromise,
|
|
8
|
-
import {
|
|
5
|
+
import { TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
6
|
+
import { DataProcessingError, generateStack, LoggingError, raiseConnectedEvent, createChildMonitoringContext, tagCodeArtifacts, UsageError, } from "@fluidframework/telemetry-utils";
|
|
7
|
+
import { assert, Deferred, LazyPromise, unreachableCase } from "@fluidframework/core-utils";
|
|
8
|
+
import { AttachState, } from "@fluidframework/container-definitions";
|
|
9
9
|
import { buildSnapshotTree } from "@fluidframework/driver-utils";
|
|
10
10
|
import { SummaryType, } from "@fluidframework/protocol-definitions";
|
|
11
11
|
import { CreateSummarizerNodeSource, VisibilityState, } from "@fluidframework/runtime-definitions";
|
|
12
|
-
import { convertSnapshotTreeToSummaryTree, convertSummaryTreeToITree, generateHandleContextPath, RequestParser, SummaryTreeBuilder, create404Response, createResponseError, exceptionToResponse, } from "@fluidframework/runtime-utils";
|
|
13
|
-
import { GCDataBuilder, removeRouteFromAllNodes, unpackChildNodesGCDetails, unpackChildNodesUsedRoutes, } from "@fluidframework/garbage-collector";
|
|
12
|
+
import { convertSnapshotTreeToSummaryTree, convertSummaryTreeToITree, generateHandleContextPath, RequestParser, SummaryTreeBuilder, create404Response, createResponseError, exceptionToResponse, GCDataBuilder, unpackChildNodesUsedRoutes, } from "@fluidframework/runtime-utils";
|
|
14
13
|
import { v4 as uuid } from "uuid";
|
|
15
|
-
import { summarizeChannel } from "./channelContext";
|
|
16
|
-
import { LocalChannelContext, LocalChannelContextBase, RehydratedLocalChannelContext } from "./localChannelContext";
|
|
17
|
-
import { RemoteChannelContext } from "./remoteChannelContext";
|
|
14
|
+
import { summarizeChannel } from "./channelContext.mjs";
|
|
15
|
+
import { LocalChannelContext, LocalChannelContextBase, RehydratedLocalChannelContext, } from "./localChannelContext.mjs";
|
|
16
|
+
import { RemoteChannelContext } from "./remoteChannelContext.mjs";
|
|
17
|
+
import { FluidObjectHandle } from "./fluidHandle.mjs";
|
|
18
|
+
/**
|
|
19
|
+
* @alpha
|
|
20
|
+
*/
|
|
18
21
|
export var DataStoreMessageType;
|
|
19
22
|
(function (DataStoreMessageType) {
|
|
20
23
|
// Creates a new channel
|
|
@@ -23,16 +26,83 @@ export var DataStoreMessageType;
|
|
|
23
26
|
})(DataStoreMessageType || (DataStoreMessageType = {}));
|
|
24
27
|
/**
|
|
25
28
|
* Base data store class
|
|
29
|
+
* @alpha
|
|
26
30
|
*/
|
|
27
31
|
export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
28
|
-
|
|
32
|
+
get connected() {
|
|
33
|
+
return this.dataStoreContext.connected;
|
|
34
|
+
}
|
|
35
|
+
get clientId() {
|
|
36
|
+
return this.dataStoreContext.clientId;
|
|
37
|
+
}
|
|
38
|
+
get clientDetails() {
|
|
39
|
+
return this.dataStoreContext.clientDetails;
|
|
40
|
+
}
|
|
41
|
+
get isAttached() {
|
|
42
|
+
return this.attachState !== AttachState.Detached;
|
|
43
|
+
}
|
|
44
|
+
get attachState() {
|
|
45
|
+
return this._attachState;
|
|
46
|
+
}
|
|
47
|
+
get absolutePath() {
|
|
48
|
+
return generateHandleContextPath(this.id, this.routeContext);
|
|
49
|
+
}
|
|
50
|
+
get routeContext() {
|
|
51
|
+
return this.dataStoreContext.IFluidHandleContext;
|
|
52
|
+
}
|
|
53
|
+
get idCompressor() {
|
|
54
|
+
return this.dataStoreContext.idCompressor;
|
|
55
|
+
}
|
|
56
|
+
get IFluidHandleContext() {
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
get rootRoutingContext() {
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
get channelsRoutingContext() {
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
get objectsRoutingContext() {
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
get disposed() {
|
|
69
|
+
return this._disposed;
|
|
70
|
+
}
|
|
71
|
+
get logger() {
|
|
72
|
+
return this.mc.logger;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Invokes the given callback and expects that no ops are submitted
|
|
76
|
+
* until execution finishes. If an op is submitted, an error will be raised.
|
|
77
|
+
*
|
|
78
|
+
* Can be disabled by feature gate `Fluid.ContainerRuntime.DisableOpReentryCheck`
|
|
79
|
+
*
|
|
80
|
+
* @param callback - the callback to be invoked
|
|
81
|
+
*/
|
|
82
|
+
ensureNoDataModelChanges(callback) {
|
|
83
|
+
// back-compat ADO:2309
|
|
84
|
+
return this.dataStoreContext.ensureNoDataModelChanges === undefined
|
|
85
|
+
? callback()
|
|
86
|
+
: this.dataStoreContext.ensureNoDataModelChanges(callback);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Create an instance of a DataStore runtime.
|
|
90
|
+
*
|
|
91
|
+
* @param dataStoreContext - Context object for the runtime.
|
|
92
|
+
* @param sharedObjectRegistry - The registry of shared objects that this data store will be able to instantiate.
|
|
93
|
+
* @param existing - Pass 'true' if loading this datastore from an existing file; pass 'false' otherwise.
|
|
94
|
+
* @param provideEntryPoint - Function to initialize the entryPoint object for the data store runtime. The
|
|
95
|
+
* handle to this data store runtime will point to the object returned by this function. If this function is not
|
|
96
|
+
* provided, the handle will be left undefined. This is here so we can start making handles a first-class citizen
|
|
97
|
+
* and the primary way of interacting with some Fluid objects, and should be used if possible.
|
|
98
|
+
*/
|
|
99
|
+
constructor(dataStoreContext, sharedObjectRegistry, existing, provideEntryPoint) {
|
|
29
100
|
super();
|
|
30
101
|
this.dataStoreContext = dataStoreContext;
|
|
31
102
|
this.sharedObjectRegistry = sharedObjectRegistry;
|
|
32
103
|
this._disposed = false;
|
|
33
104
|
this.contexts = new Map();
|
|
34
|
-
this.
|
|
35
|
-
this.pendingAttach = new Map();
|
|
105
|
+
this.pendingAttach = new Set();
|
|
36
106
|
this.deferredAttached = new Deferred();
|
|
37
107
|
this.localChannelContextQueue = new Map();
|
|
38
108
|
this.notBoundedChannelContextSet = new Set();
|
|
@@ -40,20 +110,21 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
40
110
|
// store becomes visible.
|
|
41
111
|
this.pendingHandlesToMakeVisible = new Set();
|
|
42
112
|
assert(!dataStoreContext.id.includes("/"), 0x30e /* Id cannot contain slashes. DataStoreContext should have validated this. */);
|
|
43
|
-
this.
|
|
113
|
+
this.mc = createChildMonitoringContext({
|
|
114
|
+
logger: dataStoreContext.logger,
|
|
115
|
+
namespace: "FluidDataStoreRuntime",
|
|
116
|
+
properties: {
|
|
117
|
+
all: { dataStoreId: uuid() },
|
|
118
|
+
},
|
|
119
|
+
});
|
|
44
120
|
this.id = dataStoreContext.id;
|
|
45
121
|
this.options = dataStoreContext.options;
|
|
46
122
|
this.deltaManager = dataStoreContext.deltaManager;
|
|
47
123
|
this.quorum = dataStoreContext.getQuorum();
|
|
48
124
|
this.audience = dataStoreContext.getAudience();
|
|
49
125
|
const tree = dataStoreContext.baseSnapshot;
|
|
50
|
-
this.channelsBaseGCDetails = new LazyPromise(async () => {
|
|
51
|
-
var _a, _b, _c;
|
|
52
|
-
const baseGCDetails = await ((_c = (_b = (_a = this.dataStoreContext).getBaseGCDetails) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : this.dataStoreContext.getInitialGCSummaryDetails());
|
|
53
|
-
return unpackChildNodesGCDetails(baseGCDetails);
|
|
54
|
-
});
|
|
55
126
|
// Must always receive the data store type inside of the attributes
|
|
56
|
-
if (
|
|
127
|
+
if (tree?.trees !== undefined) {
|
|
57
128
|
Object.keys(tree.trees).forEach((path) => {
|
|
58
129
|
// Issue #4414
|
|
59
130
|
if (path === "_search") {
|
|
@@ -77,29 +148,31 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
77
148
|
}
|
|
78
149
|
}
|
|
79
150
|
else {
|
|
80
|
-
channelContext = new RemoteChannelContext(this, dataStoreContext, dataStoreContext.storage, (content, localOpMetadata) => this.submitChannelOp(path, content, localOpMetadata), (address) => this.setChannelDirty(address), (srcHandle, outboundHandle) => this.addedGCOutboundReference(srcHandle, outboundHandle), path, tree.trees[path], this.sharedObjectRegistry, undefined /* extraBlobs */, this.dataStoreContext.getCreateChildSummarizerNodeFn(path, {
|
|
151
|
+
channelContext = new RemoteChannelContext(this, dataStoreContext, dataStoreContext.storage, (content, localOpMetadata) => this.submitChannelOp(path, content, localOpMetadata), (address) => this.setChannelDirty(address), (srcHandle, outboundHandle) => this.addedGCOutboundReference(srcHandle, outboundHandle), path, tree.trees[path], this.sharedObjectRegistry, undefined /* extraBlobs */, this.dataStoreContext.getCreateChildSummarizerNodeFn(path, {
|
|
152
|
+
type: CreateSummarizerNodeSource.FromSummary,
|
|
153
|
+
}));
|
|
81
154
|
}
|
|
82
|
-
const deferred = new Deferred();
|
|
83
|
-
deferred.resolve(channelContext);
|
|
84
155
|
this.contexts.set(path, channelContext);
|
|
85
|
-
this.contextsDeferred.set(path, deferred);
|
|
86
156
|
});
|
|
87
157
|
}
|
|
158
|
+
this.entryPoint = new FluidObjectHandle(new LazyPromise(async () => provideEntryPoint(this)), "", this.objectsRoutingContext);
|
|
88
159
|
this.attachListener();
|
|
89
|
-
// If exists on storage or loaded from a snapshot, it should already be bound.
|
|
90
|
-
this.bindState = existing ? BindState.Bound : BindState.NotBound;
|
|
91
160
|
this._attachState = dataStoreContext.attachState;
|
|
92
161
|
/**
|
|
93
162
|
* If existing flag is false, this is a new data store and is not visible. The existing flag can be true in two
|
|
94
163
|
* conditions:
|
|
164
|
+
*
|
|
95
165
|
* 1. It's a local data store that is created when a detached container is rehydrated. In this case, the data
|
|
96
|
-
*
|
|
166
|
+
* store is locally visible because the snapshot it is loaded from contains locally visible data stores only.
|
|
167
|
+
*
|
|
97
168
|
* 2. It's a remote data store that is created when an attached container is loaded is loaded from snapshot or
|
|
98
|
-
*
|
|
169
|
+
* when an attach op comes in. In both these cases, the data store is already globally visible.
|
|
99
170
|
*/
|
|
100
171
|
if (existing) {
|
|
101
|
-
this.visibilityState =
|
|
102
|
-
|
|
172
|
+
this.visibilityState =
|
|
173
|
+
dataStoreContext.attachState === AttachState.Detached
|
|
174
|
+
? VisibilityState.LocallyVisible
|
|
175
|
+
: VisibilityState.GloballyVisible;
|
|
103
176
|
}
|
|
104
177
|
else {
|
|
105
178
|
this.visibilityState = VisibilityState.NotVisible;
|
|
@@ -108,43 +181,10 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
108
181
|
if (existing) {
|
|
109
182
|
this.deferredAttached.resolve();
|
|
110
183
|
}
|
|
184
|
+
// By default, a data store can log maximum 10 local changes telemetry in summarizer.
|
|
185
|
+
this.localChangesTelemetryCount =
|
|
186
|
+
this.mc.config.getNumber("Fluid.Telemetry.LocalChangesTelemetryCount") ?? 10;
|
|
111
187
|
}
|
|
112
|
-
/**
|
|
113
|
-
* Loads the data store runtime
|
|
114
|
-
* @param context - The data store context
|
|
115
|
-
* @param sharedObjectRegistry - The registry of shared objects used by this data store
|
|
116
|
-
* @param existing - If loading from an existing file.
|
|
117
|
-
*/
|
|
118
|
-
static load(context, sharedObjectRegistry, existing) {
|
|
119
|
-
return new FluidDataStoreRuntime(context, sharedObjectRegistry, existing);
|
|
120
|
-
}
|
|
121
|
-
get IFluidRouter() { return this; }
|
|
122
|
-
get connected() {
|
|
123
|
-
return this.dataStoreContext.connected;
|
|
124
|
-
}
|
|
125
|
-
get clientId() {
|
|
126
|
-
return this.dataStoreContext.clientId;
|
|
127
|
-
}
|
|
128
|
-
get clientDetails() {
|
|
129
|
-
return this.dataStoreContext.clientDetails;
|
|
130
|
-
}
|
|
131
|
-
get isAttached() {
|
|
132
|
-
return this.attachState !== AttachState.Detached;
|
|
133
|
-
}
|
|
134
|
-
get attachState() {
|
|
135
|
-
return this._attachState;
|
|
136
|
-
}
|
|
137
|
-
get absolutePath() {
|
|
138
|
-
return generateHandleContextPath(this.id, this.routeContext);
|
|
139
|
-
}
|
|
140
|
-
get routeContext() {
|
|
141
|
-
return this.dataStoreContext.IFluidHandleContext;
|
|
142
|
-
}
|
|
143
|
-
get IFluidHandleContext() { return this; }
|
|
144
|
-
get rootRoutingContext() { return this; }
|
|
145
|
-
get channelsRoutingContext() { return this; }
|
|
146
|
-
get objectsRoutingContext() { return this; }
|
|
147
|
-
get disposed() { return this._disposed; }
|
|
148
188
|
dispose() {
|
|
149
189
|
if (this._disposed) {
|
|
150
190
|
return;
|
|
@@ -161,18 +201,17 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
161
201
|
const parser = RequestParser.create(request);
|
|
162
202
|
const id = parser.pathParts[0];
|
|
163
203
|
if (id === "_channels" || id === "_custom") {
|
|
164
|
-
return this.request(parser.createSubRequest(1));
|
|
204
|
+
return await this.request(parser.createSubRequest(1));
|
|
165
205
|
}
|
|
166
206
|
// Check for a data type reference first
|
|
167
|
-
|
|
207
|
+
const context = this.contexts.get(id);
|
|
208
|
+
if (context !== undefined && parser.isLeaf(1)) {
|
|
168
209
|
try {
|
|
169
|
-
|
|
170
|
-
const value = await this.contextsDeferred.get(id).promise;
|
|
171
|
-
const channel = await value.getChannel();
|
|
210
|
+
const channel = await context.getChannel();
|
|
172
211
|
return { mimeType: "fluid/object", status: 200, value: channel };
|
|
173
212
|
}
|
|
174
213
|
catch (error) {
|
|
175
|
-
this.logger.sendErrorEvent({ eventName: "GetChannelFailedInRequest" }, error);
|
|
214
|
+
this.mc.logger.sendErrorEvent({ eventName: "GetChannelFailedInRequest" }, error);
|
|
176
215
|
return createResponseError(500, `Failed to get Channel: ${error}`, request);
|
|
177
216
|
}
|
|
178
217
|
}
|
|
@@ -185,37 +224,56 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
185
224
|
}
|
|
186
225
|
async getChannel(id) {
|
|
187
226
|
this.verifyNotClosed();
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
195
|
-
const context = await this.contextsDeferred.get(id).promise;
|
|
196
|
-
const channel = await context.getChannel();
|
|
197
|
-
return channel;
|
|
227
|
+
const context = this.contexts.get(id);
|
|
228
|
+
if (context === undefined) {
|
|
229
|
+
throw new LoggingError("Channel does not exist");
|
|
230
|
+
}
|
|
231
|
+
return context.getChannel();
|
|
198
232
|
}
|
|
199
|
-
|
|
233
|
+
/**
|
|
234
|
+
* Api which allows caller to create the channel first and then add it to the runtime.
|
|
235
|
+
* The channel type should be present in the registry, otherwise the runtime would reject
|
|
236
|
+
* the channel. Also the runtime used to create the channel object should be same to which
|
|
237
|
+
* it is added.
|
|
238
|
+
* @param channel - channel which needs to be added to the runtime.
|
|
239
|
+
*/
|
|
240
|
+
addChannel(channel) {
|
|
241
|
+
const id = channel.id;
|
|
200
242
|
if (id.includes("/")) {
|
|
201
243
|
throw new UsageError(`Id cannot contain slashes: ${id}`);
|
|
202
244
|
}
|
|
203
245
|
this.verifyNotClosed();
|
|
204
|
-
assert(!this.contexts.has(id),
|
|
205
|
-
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
210
|
-
this.contextsDeferred.get(id).resolve(context);
|
|
246
|
+
assert(!this.contexts.has(id), 0x865 /* addChannel() with existing ID */);
|
|
247
|
+
const type = channel.attributes.type;
|
|
248
|
+
const factory = this.sharedObjectRegistry.get(channel.attributes.type);
|
|
249
|
+
if (factory === undefined) {
|
|
250
|
+
throw new Error(`Channel Factory ${type} not registered`);
|
|
211
251
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
252
|
+
this.createChannelContext(channel);
|
|
253
|
+
// Channels (DDS) should not be created in summarizer client.
|
|
254
|
+
this.identifyLocalChangeInSummarizer("DDSCreatedInSummarizer", id, type);
|
|
255
|
+
}
|
|
256
|
+
createChannel(id = uuid(), type) {
|
|
257
|
+
if (id.includes("/")) {
|
|
258
|
+
throw new UsageError(`Id cannot contain slashes: ${id}`);
|
|
216
259
|
}
|
|
217
|
-
|
|
218
|
-
|
|
260
|
+
this.verifyNotClosed();
|
|
261
|
+
assert(!this.contexts.has(id), 0x179 /* "createChannel() with existing ID" */);
|
|
262
|
+
assert(type !== undefined, 0x209 /* "Factory Type should be defined" */);
|
|
263
|
+
const factory = this.sharedObjectRegistry.get(type);
|
|
264
|
+
if (factory === undefined) {
|
|
265
|
+
throw new Error(`Channel Factory ${type} not registered`);
|
|
266
|
+
}
|
|
267
|
+
const channel = factory.create(this, id);
|
|
268
|
+
this.createChannelContext(channel);
|
|
269
|
+
// Channels (DDS) should not be created in summarizer client.
|
|
270
|
+
this.identifyLocalChangeInSummarizer("DDSCreatedInSummarizer", id, type);
|
|
271
|
+
return channel;
|
|
272
|
+
}
|
|
273
|
+
createChannelContext(channel) {
|
|
274
|
+
this.notBoundedChannelContextSet.add(channel.id);
|
|
275
|
+
const context = new LocalChannelContext(channel, this, this.dataStoreContext, this.dataStoreContext.storage, this.logger, (content, localOpMetadata) => this.submitChannelOp(channel.id, content, localOpMetadata), (address) => this.setChannelDirty(address), (srcHandle, outboundHandle) => this.addedGCOutboundReference(srcHandle, outboundHandle));
|
|
276
|
+
this.contexts.set(channel.id, context);
|
|
219
277
|
}
|
|
220
278
|
/**
|
|
221
279
|
* Binds a channel with the runtime. If the runtime is attached we will attach the channel right away.
|
|
@@ -223,7 +281,7 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
223
281
|
* @param channel - channel to be registered.
|
|
224
282
|
*/
|
|
225
283
|
bindChannel(channel) {
|
|
226
|
-
assert(this.notBoundedChannelContextSet.has(channel.id), 0x17b /* "Channel to be
|
|
284
|
+
assert(this.notBoundedChannelContextSet.has(channel.id), 0x17b /* "Channel to be bound should be in not bounded set" */);
|
|
227
285
|
this.notBoundedChannelContextSet.delete(channel.id);
|
|
228
286
|
// If our data store is attached, then attach the channel.
|
|
229
287
|
if (this.isAttached) {
|
|
@@ -245,11 +303,14 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
245
303
|
}
|
|
246
304
|
/**
|
|
247
305
|
* This function is called when a data store becomes root. It does the following:
|
|
306
|
+
*
|
|
248
307
|
* 1. Marks the data store locally visible in the container.
|
|
308
|
+
*
|
|
249
309
|
* 2. Attaches the graph of all the handles bound to it.
|
|
310
|
+
*
|
|
250
311
|
* 3. Calls into the data store context to mark it visible in the container too. If the container is globally
|
|
251
|
-
*
|
|
252
|
-
*
|
|
312
|
+
* visible, it will mark us globally visible. Otherwise, it will mark us globally visible when it becomes
|
|
313
|
+
* globally visible.
|
|
253
314
|
*/
|
|
254
315
|
makeVisibleAndAttachGraph() {
|
|
255
316
|
if (this.visibilityState !== VisibilityState.NotVisible) {
|
|
@@ -260,7 +321,7 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
260
321
|
handle.attachGraph();
|
|
261
322
|
});
|
|
262
323
|
this.pendingHandlesToMakeVisible.clear();
|
|
263
|
-
this.
|
|
324
|
+
this.dataStoreContext.makeLocallyVisible();
|
|
264
325
|
}
|
|
265
326
|
/**
|
|
266
327
|
* This function is called when a handle to this data store is added to a visible DDS.
|
|
@@ -268,20 +329,6 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
268
329
|
attachGraph() {
|
|
269
330
|
this.makeVisibleAndAttachGraph();
|
|
270
331
|
}
|
|
271
|
-
/**
|
|
272
|
-
* Binds this runtime to the container
|
|
273
|
-
* This includes the following:
|
|
274
|
-
* 1. Sending an Attach op that includes all existing state
|
|
275
|
-
* 2. Attaching the graph if the data store becomes attached.
|
|
276
|
-
*/
|
|
277
|
-
bindToContext() {
|
|
278
|
-
if (this.bindState !== BindState.NotBound) {
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
this.bindState = BindState.Binding;
|
|
282
|
-
this.dataStoreContext.bindToContext();
|
|
283
|
-
this.bindState = BindState.Bound;
|
|
284
|
-
}
|
|
285
332
|
bind(handle) {
|
|
286
333
|
// If visible, attach the incoming handle's graph. Else, this will be done when we become visible.
|
|
287
334
|
if (this.visibilityState !== VisibilityState.NotVisible) {
|
|
@@ -303,9 +350,14 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
303
350
|
getAudience() {
|
|
304
351
|
return this.audience;
|
|
305
352
|
}
|
|
306
|
-
async uploadBlob(blob) {
|
|
353
|
+
async uploadBlob(blob, signal) {
|
|
307
354
|
this.verifyNotClosed();
|
|
308
|
-
return this.dataStoreContext.uploadBlob(blob);
|
|
355
|
+
return this.dataStoreContext.uploadBlob(blob, signal);
|
|
356
|
+
}
|
|
357
|
+
createRemoteChannelContext(attachMessage, summarizerNodeParams) {
|
|
358
|
+
const flatBlobs = new Map();
|
|
359
|
+
const snapshotTree = buildSnapshotTree(attachMessage.snapshot.entries, flatBlobs);
|
|
360
|
+
return new RemoteChannelContext(this, this.dataStoreContext, this.dataStoreContext.storage, (content, localContentMetadata) => this.submitChannelOp(attachMessage.id, content, localContentMetadata), (address) => this.setChannelDirty(address), (srcHandle, outboundHandle) => this.addedGCOutboundReference(srcHandle, outboundHandle), attachMessage.id, snapshotTree, this.sharedObjectRegistry, flatBlobs, this.dataStoreContext.getCreateChildSummarizerNodeFn(attachMessage.id, summarizerNodeParams), attachMessage.type);
|
|
309
361
|
}
|
|
310
362
|
process(message, local, localOpMetadata) {
|
|
311
363
|
this.verifyNotClosed();
|
|
@@ -318,28 +370,17 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
318
370
|
// If a non-local operation then go and create the object
|
|
319
371
|
// Otherwise mark it as officially attached.
|
|
320
372
|
if (local) {
|
|
321
|
-
assert(this.pendingAttach.
|
|
322
|
-
this.pendingAttach.delete(id);
|
|
373
|
+
assert(this.pendingAttach.delete(id), 0x17c /* "Unexpected attach (local) channel OP" */);
|
|
323
374
|
}
|
|
324
375
|
else {
|
|
325
|
-
assert(!this.contexts.has(id), 0x17d);
|
|
326
|
-
const
|
|
327
|
-
const snapshotTree = buildSnapshotTree(attachMessage.snapshot.entries, flatBlobs);
|
|
328
|
-
const remoteChannelContext = new RemoteChannelContext(this, this.dataStoreContext, this.dataStoreContext.storage, (content, localContentMetadata) => this.submitChannelOp(id, content, localContentMetadata), (address) => this.setChannelDirty(address), (srcHandle, outboundHandle) => this.addedGCOutboundReference(srcHandle, outboundHandle), id, snapshotTree, this.sharedObjectRegistry, flatBlobs, this.dataStoreContext.getCreateChildSummarizerNodeFn(id, {
|
|
376
|
+
assert(!this.contexts.has(id), 0x17d /* "Unexpected attach channel OP" */);
|
|
377
|
+
const summarizerNodeParams = {
|
|
329
378
|
type: CreateSummarizerNodeSource.FromAttach,
|
|
330
379
|
sequenceNumber: message.sequenceNumber,
|
|
331
380
|
snapshot: attachMessage.snapshot,
|
|
332
|
-
}
|
|
381
|
+
};
|
|
382
|
+
const remoteChannelContext = this.createRemoteChannelContext(attachMessage, summarizerNodeParams);
|
|
333
383
|
this.contexts.set(id, remoteChannelContext);
|
|
334
|
-
if (this.contextsDeferred.has(id)) {
|
|
335
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
336
|
-
this.contextsDeferred.get(id).resolve(remoteChannelContext);
|
|
337
|
-
}
|
|
338
|
-
else {
|
|
339
|
-
const deferred = new Deferred();
|
|
340
|
-
deferred.resolve(remoteChannelContext);
|
|
341
|
-
this.contextsDeferred.set(id, deferred);
|
|
342
|
-
}
|
|
343
384
|
}
|
|
344
385
|
break;
|
|
345
386
|
}
|
|
@@ -361,13 +402,13 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
361
402
|
return (
|
|
362
403
|
// Added in createChannel
|
|
363
404
|
// Removed when bindChannel is called
|
|
364
|
-
!this.notBoundedChannelContextSet.has(id)
|
|
405
|
+
!this.notBoundedChannelContextSet.has(id) &&
|
|
365
406
|
// Added in bindChannel only if this is not attached yet
|
|
366
407
|
// Removed when this is attached by calling attachGraph
|
|
367
|
-
|
|
408
|
+
!this.localChannelContextQueue.has(id) &&
|
|
368
409
|
// Added in attachChannel called by bindChannel
|
|
369
410
|
// Removed when attach op is broadcast
|
|
370
|
-
|
|
411
|
+
!this.pendingAttach.has(id));
|
|
371
412
|
}
|
|
372
413
|
/**
|
|
373
414
|
* Returns the outbound routes of this channel. Currently, all contexts in this channel are considered
|
|
@@ -398,11 +439,15 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
398
439
|
* Generates data used for garbage collection. This includes a list of GC nodes that represent this channel
|
|
399
440
|
* including any of its child channel contexts. Each node has a set of outbound routes to other GC nodes in the
|
|
400
441
|
* document. It does the following:
|
|
442
|
+
*
|
|
401
443
|
* 1. Calls into each child context to get its GC data.
|
|
444
|
+
*
|
|
402
445
|
* 2. Prefixes the child context's id to the GC nodes in the child's GC data. This makes sure that the node can be
|
|
403
|
-
*
|
|
446
|
+
* identified as belonging to the child.
|
|
447
|
+
*
|
|
404
448
|
* 3. Adds a GC node for this channel to the nodes received from the children. All these nodes together represent
|
|
405
|
-
*
|
|
449
|
+
* the GC data of this channel.
|
|
450
|
+
*
|
|
406
451
|
* @param fullGC - true to bypass optimizations and force full generation of GC data.
|
|
407
452
|
*/
|
|
408
453
|
async getGCData(fullGC = false) {
|
|
@@ -413,7 +458,8 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
413
458
|
// Get GC data only for attached contexts. Detached contexts are not connected in the GC reference
|
|
414
459
|
// graph so any references they might have won't be connected as well.
|
|
415
460
|
return this.isChannelAttached(contextId);
|
|
416
|
-
})
|
|
461
|
+
})
|
|
462
|
+
.map(async ([contextId, context]) => {
|
|
417
463
|
const contextGCData = await context.getGCData(fullGC);
|
|
418
464
|
// Prefix the child's id to the ids of its GC nodes so they can be identified as belonging to the child.
|
|
419
465
|
// This also gradually builds the id of each node to be a path from the root.
|
|
@@ -426,11 +472,8 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
426
472
|
* After GC has run, called to notify this channel of routes that are used in it. It calls the child contexts to
|
|
427
473
|
* update their used routes.
|
|
428
474
|
* @param usedRoutes - The routes that are used in all contexts in this channel.
|
|
429
|
-
* @param gcTimestamp - The time when GC was run that generated these used routes. If any node becomes unreferenced
|
|
430
|
-
* as part of this GC run, this should be used to update the time when it happens.
|
|
431
475
|
*/
|
|
432
|
-
updateUsedRoutes(usedRoutes
|
|
433
|
-
var _a;
|
|
476
|
+
updateUsedRoutes(usedRoutes) {
|
|
434
477
|
// Get a map of channel ids to routes used in it.
|
|
435
478
|
const usedContextRoutes = unpackChildNodesUsedRoutes(usedRoutes);
|
|
436
479
|
// Verify that the used routes are correct.
|
|
@@ -439,7 +482,7 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
439
482
|
}
|
|
440
483
|
// Update the used routes in each context. Used routes is empty for unused context.
|
|
441
484
|
for (const [contextId, context] of this.contexts) {
|
|
442
|
-
context.updateUsedRoutes(
|
|
485
|
+
context.updateUsedRoutes(usedContextRoutes.get(contextId) ?? []);
|
|
443
486
|
}
|
|
444
487
|
}
|
|
445
488
|
/**
|
|
@@ -449,31 +492,7 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
449
492
|
* @param outboundHandle - The handle of the outbound node that is referenced.
|
|
450
493
|
*/
|
|
451
494
|
addedGCOutboundReference(srcHandle, outboundHandle) {
|
|
452
|
-
|
|
453
|
-
(_b = (_a = this.dataStoreContext).addedGCOutboundReference) === null || _b === void 0 ? void 0 : _b.call(_a, srcHandle, outboundHandle);
|
|
454
|
-
}
|
|
455
|
-
/**
|
|
456
|
-
* Returns the base GC details for the channel with the given id. This is used to initialize its GC state.
|
|
457
|
-
* @param channelId - The id of the channel context that is asked for the initial GC details.
|
|
458
|
-
* @returns the requested channel's base GC details.
|
|
459
|
-
*/
|
|
460
|
-
async getChannelBaseGCDetails(channelId) {
|
|
461
|
-
var _a;
|
|
462
|
-
let channelBaseGCDetails = (await this.channelsBaseGCDetails).get(channelId);
|
|
463
|
-
if (channelBaseGCDetails === undefined) {
|
|
464
|
-
channelBaseGCDetails = {};
|
|
465
|
-
}
|
|
466
|
-
else if (((_a = channelBaseGCDetails.gcData) === null || _a === void 0 ? void 0 : _a.gcNodes) !== undefined) {
|
|
467
|
-
// Note: if the child channel has an explicit handle route to its parent, it will be removed here and
|
|
468
|
-
// expected to be added back by the parent when getGCData is called.
|
|
469
|
-
removeRouteFromAllNodes(channelBaseGCDetails.gcData.gcNodes, this.absolutePath);
|
|
470
|
-
}
|
|
471
|
-
// Currently, channel context's are always considered used. So, it there are no used routes for it, we still
|
|
472
|
-
// need to mark it as used. Add self-route (empty string) to the channel context's used routes.
|
|
473
|
-
if (channelBaseGCDetails.usedRoutes === undefined || channelBaseGCDetails.usedRoutes.length === 0) {
|
|
474
|
-
channelBaseGCDetails.usedRoutes = [""];
|
|
475
|
-
}
|
|
476
|
-
return channelBaseGCDetails;
|
|
495
|
+
this.dataStoreContext.addedGCOutboundReference?.(srcHandle, outboundHandle);
|
|
477
496
|
}
|
|
478
497
|
/**
|
|
479
498
|
* Returns a summary at the current sequence number.
|
|
@@ -492,7 +511,8 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
492
511
|
// If the object is registered - and we have received the sequenced op creating the object
|
|
493
512
|
// (i.e. it has a base mapping) - then we go ahead and summarize
|
|
494
513
|
return isAttached;
|
|
495
|
-
})
|
|
514
|
+
})
|
|
515
|
+
.map(async ([contextId, context]) => {
|
|
496
516
|
const contextSummary = await context.summarize(fullTree, trackState, telemetryContext);
|
|
497
517
|
summaryBuilder.addWithStats(contextId, contextSummary);
|
|
498
518
|
}));
|
|
@@ -503,20 +523,20 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
503
523
|
* back-compat 0.59.1000 - getAttachSummary() is called when making a data store globally visible (previously
|
|
504
524
|
* attaching state). Ideally, attachGraph() should have already be called making it locally visible. However,
|
|
505
525
|
* before visibility state was added, this may not have been the case and getAttachSummary() could be called:
|
|
506
|
-
*
|
|
507
|
-
*
|
|
526
|
+
*
|
|
527
|
+
* 1. Before attaching the data store - When a detached container is attached.
|
|
528
|
+
*
|
|
529
|
+
* 2. After attaching the data store - When a data store is created and bound in an attached container.
|
|
508
530
|
*
|
|
509
531
|
* The basic idea is that all local object should become locally visible before they are globally visible.
|
|
510
532
|
*/
|
|
511
533
|
this.attachGraph();
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
* );
|
|
519
|
-
*/
|
|
534
|
+
// This assert cannot be added now due to back-compat. To be uncommented when the following issue is fixed -
|
|
535
|
+
// https://github.com/microsoft/FluidFramework/issues/9688.
|
|
536
|
+
//
|
|
537
|
+
// assert(this.visibilityState === VisibilityState.LocallyVisible,
|
|
538
|
+
// "The data store should be locally visible when generating attach summary",
|
|
539
|
+
// );
|
|
520
540
|
const summaryBuilder = new SummaryTreeBuilder();
|
|
521
541
|
// Craft the .attributes file for each shared object
|
|
522
542
|
for (const [contextId, context] of this.contexts) {
|
|
@@ -544,9 +564,15 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
544
564
|
submitMessage(type, content, localOpMetadata) {
|
|
545
565
|
this.submit(type, content, localOpMetadata);
|
|
546
566
|
}
|
|
547
|
-
|
|
567
|
+
/**
|
|
568
|
+
* Submits the signal to be sent to other clients.
|
|
569
|
+
* @param type - Type of the signal.
|
|
570
|
+
* @param content - Content of the signal.
|
|
571
|
+
* @param targetClientId - When specified, the signal is only sent to the provided client id.
|
|
572
|
+
*/
|
|
573
|
+
submitSignal(type, content, targetClientId) {
|
|
548
574
|
this.verifyNotClosed();
|
|
549
|
-
return this.dataStoreContext.submitSignal(type, content);
|
|
575
|
+
return this.dataStoreContext.submitSignal(type, content, targetClientId);
|
|
550
576
|
}
|
|
551
577
|
/**
|
|
552
578
|
* Will return when the data store is attached.
|
|
@@ -574,7 +600,7 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
574
600
|
snapshot,
|
|
575
601
|
type: channel.attributes.type,
|
|
576
602
|
};
|
|
577
|
-
this.pendingAttach.
|
|
603
|
+
this.pendingAttach.add(channel.id);
|
|
578
604
|
this.submit(DataStoreMessageType.Attach, message);
|
|
579
605
|
const context = this.contexts.get(channel.id);
|
|
580
606
|
context.makeVisible();
|
|
@@ -597,15 +623,14 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
597
623
|
reSubmit(type, content, localOpMetadata) {
|
|
598
624
|
this.verifyNotClosed();
|
|
599
625
|
switch (type) {
|
|
600
|
-
case DataStoreMessageType.ChannelOp:
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
}
|
|
626
|
+
case DataStoreMessageType.ChannelOp: {
|
|
627
|
+
// For Operations, find the right channel and trigger resubmission on it.
|
|
628
|
+
const envelope = content;
|
|
629
|
+
const channelContext = this.contexts.get(envelope.address);
|
|
630
|
+
assert(!!channelContext, 0x183 /* "There should be a channel context for the op" */);
|
|
631
|
+
channelContext.reSubmit(envelope.contents, localOpMetadata);
|
|
632
|
+
break;
|
|
633
|
+
}
|
|
609
634
|
case DataStoreMessageType.Attach:
|
|
610
635
|
// For Attach messages, just submit them again.
|
|
611
636
|
this.submit(type, content, localOpMetadata);
|
|
@@ -622,25 +647,42 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
622
647
|
rollback(type, content, localOpMetadata) {
|
|
623
648
|
this.verifyNotClosed();
|
|
624
649
|
switch (type) {
|
|
625
|
-
case DataStoreMessageType.ChannelOp:
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
}
|
|
650
|
+
case DataStoreMessageType.ChannelOp: {
|
|
651
|
+
// For Operations, find the right channel and trigger resubmission on it.
|
|
652
|
+
const envelope = content;
|
|
653
|
+
const channelContext = this.contexts.get(envelope.address);
|
|
654
|
+
assert(!!channelContext, 0x2ed /* "There should be a channel context for the op" */);
|
|
655
|
+
channelContext.rollback(envelope.contents, localOpMetadata);
|
|
656
|
+
break;
|
|
657
|
+
}
|
|
634
658
|
default:
|
|
635
659
|
throw new LoggingError(`Can't rollback ${type} message`);
|
|
636
660
|
}
|
|
637
661
|
}
|
|
638
662
|
async applyStashedOp(content) {
|
|
639
|
-
const
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
663
|
+
const type = content?.type;
|
|
664
|
+
switch (type) {
|
|
665
|
+
case DataStoreMessageType.Attach: {
|
|
666
|
+
const attachMessage = content.content;
|
|
667
|
+
// local means this node will throw if summarized; this is fine because only interactive clients will have stashed ops
|
|
668
|
+
const summarizerNodeParams = {
|
|
669
|
+
type: CreateSummarizerNodeSource.Local,
|
|
670
|
+
};
|
|
671
|
+
const context = this.createRemoteChannelContext(attachMessage, summarizerNodeParams);
|
|
672
|
+
this.pendingAttach.add(attachMessage.id);
|
|
673
|
+
this.contexts.set(attachMessage.id, context);
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
case DataStoreMessageType.ChannelOp: {
|
|
677
|
+
const envelope = content.content;
|
|
678
|
+
const channelContext = this.contexts.get(envelope.address);
|
|
679
|
+
assert(!!channelContext, 0x184 /* "There should be a channel context for the op" */);
|
|
680
|
+
await channelContext.getChannel();
|
|
681
|
+
return channelContext.applyStashedOp(envelope.contents);
|
|
682
|
+
}
|
|
683
|
+
default:
|
|
684
|
+
unreachableCase(type);
|
|
685
|
+
}
|
|
644
686
|
}
|
|
645
687
|
setChannelDirty(address) {
|
|
646
688
|
this.verifyNotClosed();
|
|
@@ -649,7 +691,10 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
649
691
|
processChannelOp(message, local, localOpMetadata) {
|
|
650
692
|
this.verifyNotClosed();
|
|
651
693
|
const envelope = message.contents;
|
|
652
|
-
const transformed =
|
|
694
|
+
const transformed = {
|
|
695
|
+
...message,
|
|
696
|
+
contents: envelope.contents,
|
|
697
|
+
};
|
|
653
698
|
const channelContext = this.contexts.get(envelope.address);
|
|
654
699
|
assert(!!channelContext, 0x185 /* "Channel not found" */);
|
|
655
700
|
channelContext.processOp(transformed, local, localOpMetadata);
|
|
@@ -691,12 +736,37 @@ export class FluidDataStoreRuntime extends TypedEventEmitter {
|
|
|
691
736
|
throw new LoggingError("Runtime is closed");
|
|
692
737
|
}
|
|
693
738
|
}
|
|
739
|
+
/**
|
|
740
|
+
* Summarizer client should not have local changes. These changes can become part of the summary and can break
|
|
741
|
+
* eventual consistency. For example, the next summary (say at ref seq# 100) may contain these changes whereas
|
|
742
|
+
* other clients that are up-to-date till seq# 100 may not have them yet.
|
|
743
|
+
*/
|
|
744
|
+
identifyLocalChangeInSummarizer(eventName, channelId, channelType) {
|
|
745
|
+
if (this.clientDetails.type !== "summarizer" || this.localChangesTelemetryCount <= 0) {
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
// Log a telemetry if there are local changes in the summarizer. This will give us data on how often
|
|
749
|
+
// this is happening and which data stores do this. The eventual goal is to disallow local changes
|
|
750
|
+
// in the summarizer and the data will help us plan this.
|
|
751
|
+
this.mc.logger.sendTelemetryEvent({
|
|
752
|
+
eventName,
|
|
753
|
+
...tagCodeArtifacts({
|
|
754
|
+
channelType,
|
|
755
|
+
channelId,
|
|
756
|
+
fluidDataStoreId: this.id,
|
|
757
|
+
fluidDataStorePackagePath: this.dataStoreContext.packagePath.join("/"),
|
|
758
|
+
}),
|
|
759
|
+
stack: generateStack(),
|
|
760
|
+
});
|
|
761
|
+
this.localChangesTelemetryCount--;
|
|
762
|
+
}
|
|
694
763
|
}
|
|
695
764
|
/**
|
|
696
765
|
* Mixin class that adds request handler to FluidDataStoreRuntime
|
|
697
766
|
* Request handler is only called when data store can't resolve request, i.e. for custom requests.
|
|
698
767
|
* @param Base - base class, inherits from FluidDataStoreRuntime
|
|
699
768
|
* @param requestHandler - request handler to mix in
|
|
769
|
+
* @internal
|
|
700
770
|
*/
|
|
701
771
|
export const mixinRequestHandler = (requestHandler, Base = FluidDataStoreRuntime) => class RuntimeWithRequestHandler extends Base {
|
|
702
772
|
async request(request) {
|
|
@@ -712,6 +782,7 @@ export const mixinRequestHandler = (requestHandler, Base = FluidDataStoreRuntime
|
|
|
712
782
|
* @param handler - handler that returns info about blob to be added to summary.
|
|
713
783
|
* Or undefined not to add anything to summary.
|
|
714
784
|
* @param Base - base class, inherits from FluidDataStoreRuntime
|
|
785
|
+
* @alpha
|
|
715
786
|
*/
|
|
716
787
|
export const mixinSummaryHandler = (handler, Base = FluidDataStoreRuntime) => class RuntimeWithSummarizerHandler extends Base {
|
|
717
788
|
addBlob(summary, path, content) {
|
|
@@ -736,11 +807,17 @@ export const mixinSummaryHandler = (handler, Base = FluidDataStoreRuntime) => cl
|
|
|
736
807
|
}
|
|
737
808
|
async summarize(...args) {
|
|
738
809
|
const summary = await super.summarize(...args);
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
810
|
+
try {
|
|
811
|
+
const content = await handler(this);
|
|
812
|
+
if (content !== undefined) {
|
|
813
|
+
this.addBlob(summary, content.path, content.content);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
catch (e) {
|
|
817
|
+
// Any error coming from app-provided handler should be marked as DataProcessingError
|
|
818
|
+
throw DataProcessingError.wrapIfUnrecognized(e, "mixinSummaryHandler");
|
|
742
819
|
}
|
|
743
820
|
return summary;
|
|
744
821
|
}
|
|
745
822
|
};
|
|
746
|
-
//# sourceMappingURL=dataStoreRuntime.
|
|
823
|
+
//# sourceMappingURL=dataStoreRuntime.mjs.map
|