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