@fluidframework/datastore 2.0.0-internal.4.2.0 → 2.0.0-internal.4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/channelContext.d.ts +12 -5
- package/dist/channelContext.d.ts.map +1 -1
- package/dist/channelContext.js +74 -5
- package/dist/channelContext.js.map +1 -1
- package/dist/channelDeltaConnection.d.ts +4 -5
- package/dist/channelDeltaConnection.d.ts.map +1 -1
- package/dist/channelDeltaConnection.js +3 -4
- package/dist/channelDeltaConnection.js.map +1 -1
- package/dist/dataStoreRuntime.d.ts.map +1 -1
- package/dist/dataStoreRuntime.js +1 -2
- package/dist/dataStoreRuntime.js.map +1 -1
- package/dist/localChannelContext.d.ts +7 -16
- package/dist/localChannelContext.d.ts.map +1 -1
- package/dist/localChannelContext.js +52 -65
- package/dist/localChannelContext.js.map +1 -1
- package/dist/remoteChannelContext.d.ts +3 -8
- package/dist/remoteChannelContext.d.ts.map +1 -1
- package/dist/remoteChannelContext.js +22 -89
- package/dist/remoteChannelContext.js.map +1 -1
- package/lib/channelContext.d.ts +12 -5
- package/lib/channelContext.d.ts.map +1 -1
- package/lib/channelContext.js +70 -3
- package/lib/channelContext.js.map +1 -1
- package/lib/channelDeltaConnection.d.ts +4 -5
- package/lib/channelDeltaConnection.d.ts.map +1 -1
- package/lib/channelDeltaConnection.js +3 -4
- package/lib/channelDeltaConnection.js.map +1 -1
- package/lib/dataStoreRuntime.d.ts.map +1 -1
- package/lib/dataStoreRuntime.js +1 -2
- package/lib/dataStoreRuntime.js.map +1 -1
- package/lib/localChannelContext.d.ts +7 -16
- package/lib/localChannelContext.d.ts.map +1 -1
- package/lib/localChannelContext.js +54 -67
- package/lib/localChannelContext.js.map +1 -1
- package/lib/remoteChannelContext.d.ts +3 -8
- package/lib/remoteChannelContext.d.ts.map +1 -1
- package/lib/remoteChannelContext.js +25 -92
- package/lib/remoteChannelContext.js.map +1 -1
- package/package.json +14 -15
- package/src/channelContext.ts +105 -6
- package/src/channelDeltaConnection.ts +4 -5
- package/src/dataStoreRuntime.ts +4 -8
- package/src/localChannelContext.ts +105 -140
- package/src/remoteChannelContext.ts +58 -125
- 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/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/src/packageVersion.ts +0 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/datastore",
|
|
3
|
-
"version": "2.0.0-internal.4.
|
|
3
|
+
"version": "2.0.0-internal.4.3.0",
|
|
4
4
|
"description": "Fluid data store implementation",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -37,18 +37,18 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
39
39
|
"@fluidframework/common-utils": "^1.1.1",
|
|
40
|
-
"@fluidframework/container-definitions": ">=2.0.0-internal.4.
|
|
41
|
-
"@fluidframework/container-utils": ">=2.0.0-internal.4.
|
|
42
|
-
"@fluidframework/core-interfaces": ">=2.0.0-internal.4.
|
|
43
|
-
"@fluidframework/datastore-definitions": ">=2.0.0-internal.4.
|
|
44
|
-
"@fluidframework/driver-definitions": ">=2.0.0-internal.4.
|
|
45
|
-
"@fluidframework/driver-utils": ">=2.0.0-internal.4.
|
|
46
|
-
"@fluidframework/garbage-collector": ">=2.0.0-internal.4.
|
|
40
|
+
"@fluidframework/container-definitions": ">=2.0.0-internal.4.3.0 <2.0.0-internal.4.4.0",
|
|
41
|
+
"@fluidframework/container-utils": ">=2.0.0-internal.4.3.0 <2.0.0-internal.4.4.0",
|
|
42
|
+
"@fluidframework/core-interfaces": ">=2.0.0-internal.4.3.0 <2.0.0-internal.4.4.0",
|
|
43
|
+
"@fluidframework/datastore-definitions": ">=2.0.0-internal.4.3.0 <2.0.0-internal.4.4.0",
|
|
44
|
+
"@fluidframework/driver-definitions": ">=2.0.0-internal.4.3.0 <2.0.0-internal.4.4.0",
|
|
45
|
+
"@fluidframework/driver-utils": ">=2.0.0-internal.4.3.0 <2.0.0-internal.4.4.0",
|
|
46
|
+
"@fluidframework/garbage-collector": ">=2.0.0-internal.4.3.0 <2.0.0-internal.4.4.0",
|
|
47
47
|
"@fluidframework/protocol-base": "^0.1039.1000",
|
|
48
48
|
"@fluidframework/protocol-definitions": "^1.1.0",
|
|
49
|
-
"@fluidframework/runtime-definitions": ">=2.0.0-internal.4.
|
|
50
|
-
"@fluidframework/runtime-utils": ">=2.0.0-internal.4.
|
|
51
|
-
"@fluidframework/telemetry-utils": ">=2.0.0-internal.4.
|
|
49
|
+
"@fluidframework/runtime-definitions": ">=2.0.0-internal.4.3.0 <2.0.0-internal.4.4.0",
|
|
50
|
+
"@fluidframework/runtime-utils": ">=2.0.0-internal.4.3.0 <2.0.0-internal.4.4.0",
|
|
51
|
+
"@fluidframework/telemetry-utils": ">=2.0.0-internal.4.3.0 <2.0.0-internal.4.4.0",
|
|
52
52
|
"lodash": "^4.17.21",
|
|
53
53
|
"uuid": "^8.3.1"
|
|
54
54
|
},
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"@fluidframework/build-tools": "^0.17.0",
|
|
59
59
|
"@fluidframework/datastore-previous": "npm:@fluidframework/datastore@2.0.0-internal.4.1.0",
|
|
60
60
|
"@fluidframework/eslint-config-fluid": "^2.0.0",
|
|
61
|
-
"@fluidframework/mocha-test-setup": ">=2.0.0-internal.4.
|
|
62
|
-
"@fluidframework/test-runtime-utils": ">=2.0.0-internal.4.
|
|
61
|
+
"@fluidframework/mocha-test-setup": ">=2.0.0-internal.4.3.0 <2.0.0-internal.4.4.0",
|
|
62
|
+
"@fluidframework/test-runtime-utils": ">=2.0.0-internal.4.3.0 <2.0.0-internal.4.4.0",
|
|
63
63
|
"@microsoft/api-extractor": "^7.34.4",
|
|
64
64
|
"@types/mocha": "^9.1.1",
|
|
65
65
|
"@types/node": "^14.18.38",
|
|
@@ -85,14 +85,13 @@
|
|
|
85
85
|
}
|
|
86
86
|
},
|
|
87
87
|
"scripts": {
|
|
88
|
-
"build": "
|
|
88
|
+
"build": "concurrently npm:build:compile npm:lint && npm run build:docs",
|
|
89
89
|
"build:commonjs": "npm run tsc && npm run typetests:gen && npm run build:test",
|
|
90
90
|
"build:compile": "concurrently npm:build:commonjs npm:build:esnext",
|
|
91
91
|
"build:docs": "api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/",
|
|
92
92
|
"build:esnext": "tsc --project ./tsconfig.esnext.json",
|
|
93
93
|
"build:full": "npm run build",
|
|
94
94
|
"build:full:compile": "npm run build:compile",
|
|
95
|
-
"build:genver": "gen-version",
|
|
96
95
|
"build:test": "tsc --project ./src/test/tsconfig.json",
|
|
97
96
|
"ci:build:docs": "api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/",
|
|
98
97
|
"clean": "rimraf dist lib *.tsbuildinfo *.build.log",
|
package/src/channelContext.ts
CHANGED
|
@@ -5,7 +5,12 @@
|
|
|
5
5
|
|
|
6
6
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
7
7
|
import { IFluidHandle } from "@fluidframework/core-interfaces";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
IChannel,
|
|
10
|
+
IChannelAttributes,
|
|
11
|
+
IChannelFactory,
|
|
12
|
+
IFluidDataStoreRuntime,
|
|
13
|
+
} from "@fluidframework/datastore-definitions";
|
|
9
14
|
import { IDocumentStorageService } from "@fluidframework/driver-definitions";
|
|
10
15
|
import { ISequencedDocumentMessage, ISnapshotTree } from "@fluidframework/protocol-definitions";
|
|
11
16
|
import {
|
|
@@ -14,10 +19,15 @@ import {
|
|
|
14
19
|
ISummarizeResult,
|
|
15
20
|
ISummaryTreeWithStats,
|
|
16
21
|
ITelemetryContext,
|
|
22
|
+
IFluidDataStoreContext,
|
|
17
23
|
} from "@fluidframework/runtime-definitions";
|
|
18
24
|
import { addBlobToSummary } from "@fluidframework/runtime-utils";
|
|
19
|
-
import {
|
|
25
|
+
import { DataCorruptionError } from "@fluidframework/container-utils";
|
|
26
|
+
import { readAndParse } from "@fluidframework/driver-utils";
|
|
27
|
+
import { TelemetryDataTag } from "@fluidframework/telemetry-utils";
|
|
20
28
|
import { ChannelStorageService } from "./channelStorageService";
|
|
29
|
+
import { ChannelDeltaConnection } from "./channelDeltaConnection";
|
|
30
|
+
import { ISharedObjectRegistry } from "./dataStoreRuntime";
|
|
21
31
|
|
|
22
32
|
export const attributesBlobKey = ".attributes";
|
|
23
33
|
|
|
@@ -56,8 +66,12 @@ export interface IChannelContext {
|
|
|
56
66
|
updateUsedRoutes(usedRoutes: string[]): void;
|
|
57
67
|
}
|
|
58
68
|
|
|
59
|
-
export
|
|
60
|
-
|
|
69
|
+
export interface ChannelServiceEndpoints {
|
|
70
|
+
deltaConnection: ChannelDeltaConnection;
|
|
71
|
+
objectStorage: ChannelStorageService;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function createChannelServiceEndpoints(
|
|
61
75
|
connected: boolean,
|
|
62
76
|
submitFn: (content: any, localOpMetadata: unknown) => void,
|
|
63
77
|
dirtyFn: () => void,
|
|
@@ -66,9 +80,8 @@ export function createServiceEndpoints(
|
|
|
66
80
|
logger: ITelemetryLogger,
|
|
67
81
|
tree?: ISnapshotTree,
|
|
68
82
|
extraBlobs?: Map<string, ArrayBufferLike>,
|
|
69
|
-
) {
|
|
83
|
+
): ChannelServiceEndpoints {
|
|
70
84
|
const deltaConnection = new ChannelDeltaConnection(
|
|
71
|
-
id,
|
|
72
85
|
connected,
|
|
73
86
|
(message, localOpMetadata) => submitFn(message, localOpMetadata),
|
|
74
87
|
dirtyFn,
|
|
@@ -113,3 +126,89 @@ export async function summarizeChannelAsync(
|
|
|
113
126
|
addBlobToSummary(summarizeResult, attributesBlobKey, JSON.stringify(channel.attributes));
|
|
114
127
|
return summarizeResult;
|
|
115
128
|
}
|
|
129
|
+
|
|
130
|
+
export async function loadChannelFactoryAndAttributes(
|
|
131
|
+
dataStoreContext: IFluidDataStoreContext,
|
|
132
|
+
services: ChannelServiceEndpoints,
|
|
133
|
+
channelId: string,
|
|
134
|
+
registry: ISharedObjectRegistry,
|
|
135
|
+
attachMessageType?: string,
|
|
136
|
+
): Promise<{ factory: IChannelFactory; attributes: IChannelAttributes }> {
|
|
137
|
+
let attributes: IChannelAttributes | undefined;
|
|
138
|
+
if (await services.objectStorage.contains(attributesBlobKey)) {
|
|
139
|
+
attributes = await readAndParse<IChannelAttributes | undefined>(
|
|
140
|
+
services.objectStorage,
|
|
141
|
+
attributesBlobKey,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// This is a backward compatibility case where the attach message doesn't include attributes. They must
|
|
146
|
+
// include attach message type.
|
|
147
|
+
// Since old attach messages will not have attributes, we need to keep this as long as we support old attach
|
|
148
|
+
// messages.
|
|
149
|
+
const channelFactoryType = attributes ? attributes.type : attachMessageType;
|
|
150
|
+
if (channelFactoryType === undefined) {
|
|
151
|
+
throw new DataCorruptionError("channelTypeNotAvailable", {
|
|
152
|
+
channelId: {
|
|
153
|
+
value: channelId,
|
|
154
|
+
tag: TelemetryDataTag.CodeArtifact,
|
|
155
|
+
},
|
|
156
|
+
dataStoreId: {
|
|
157
|
+
value: dataStoreContext.id,
|
|
158
|
+
tag: TelemetryDataTag.CodeArtifact,
|
|
159
|
+
},
|
|
160
|
+
dataStorePackagePath: dataStoreContext.packagePath.join("/"),
|
|
161
|
+
channelFactoryType: attachMessageType,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
const factory = registry.get(channelFactoryType);
|
|
165
|
+
if (factory === undefined) {
|
|
166
|
+
// TODO: dataStoreId may require a different tag from PackageData #7488
|
|
167
|
+
throw new DataCorruptionError("channelFactoryNotRegisteredForGivenType", {
|
|
168
|
+
channelId: {
|
|
169
|
+
value: channelId,
|
|
170
|
+
tag: TelemetryDataTag.CodeArtifact,
|
|
171
|
+
},
|
|
172
|
+
dataStoreId: {
|
|
173
|
+
value: dataStoreContext.id,
|
|
174
|
+
tag: TelemetryDataTag.CodeArtifact,
|
|
175
|
+
},
|
|
176
|
+
dataStorePackagePath: dataStoreContext.packagePath.join("/"),
|
|
177
|
+
channelFactoryType,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
// This is a backward compatibility case where the attach message doesn't include attributes. Get the attributes
|
|
181
|
+
// from the factory.
|
|
182
|
+
attributes = attributes ?? factory.attributes;
|
|
183
|
+
return { factory, attributes };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export async function loadChannel(
|
|
187
|
+
dataStoreRuntime: IFluidDataStoreRuntime,
|
|
188
|
+
attributes: IChannelAttributes,
|
|
189
|
+
factory: IChannelFactory,
|
|
190
|
+
services: ChannelServiceEndpoints,
|
|
191
|
+
logger: ITelemetryLogger,
|
|
192
|
+
channelId: string,
|
|
193
|
+
): Promise<IChannel> {
|
|
194
|
+
// Compare snapshot version to collaborative object version
|
|
195
|
+
if (
|
|
196
|
+
attributes.snapshotFormatVersion !== undefined &&
|
|
197
|
+
attributes.snapshotFormatVersion !== factory.attributes.snapshotFormatVersion
|
|
198
|
+
) {
|
|
199
|
+
logger.sendTelemetryEvent({
|
|
200
|
+
eventName: "ChannelAttributesVersionMismatch",
|
|
201
|
+
channelType: { value: attributes.type, tag: TelemetryDataTag.CodeArtifact },
|
|
202
|
+
channelSnapshotVersion: {
|
|
203
|
+
value: `${attributes.snapshotFormatVersion}@${attributes.packageVersion}`,
|
|
204
|
+
tag: TelemetryDataTag.CodeArtifact,
|
|
205
|
+
},
|
|
206
|
+
channelCodeVersion: {
|
|
207
|
+
value: `${factory.attributes.snapshotFormatVersion}@${factory.attributes.packageVersion}`,
|
|
208
|
+
tag: TelemetryDataTag.CodeArtifact,
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return factory.load(dataStoreRuntime, channelId, services, attributes);
|
|
214
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { assert } from "@fluidframework/common-utils";
|
|
7
|
-
import {
|
|
7
|
+
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
|
|
8
8
|
import { IDeltaConnection, IDeltaHandler } from "@fluidframework/datastore-definitions";
|
|
9
9
|
import { DataProcessingError } from "@fluidframework/container-utils";
|
|
10
10
|
import { IFluidHandle } from "@fluidframework/core-interfaces";
|
|
@@ -21,9 +21,8 @@ export class ChannelDeltaConnection implements IDeltaConnection {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
constructor(
|
|
24
|
-
public objectId: string,
|
|
25
24
|
private _connected: boolean,
|
|
26
|
-
public readonly submit: (
|
|
25
|
+
public readonly submit: (content: any, localOpMetadata: unknown) => void,
|
|
27
26
|
public readonly dirty: () => void,
|
|
28
27
|
public readonly addedGCOutboundReference: (
|
|
29
28
|
srcHandle: IFluidHandle,
|
|
@@ -65,7 +64,7 @@ export class ChannelDeltaConnection implements IDeltaConnection {
|
|
|
65
64
|
this.handler.rollback(content, localOpMetadata);
|
|
66
65
|
}
|
|
67
66
|
|
|
68
|
-
public applyStashedOp(
|
|
69
|
-
return this.handler.applyStashedOp(
|
|
67
|
+
public applyStashedOp(content: any): unknown {
|
|
68
|
+
return this.handler.applyStashedOp(content);
|
|
70
69
|
}
|
|
71
70
|
}
|
package/src/dataStoreRuntime.ts
CHANGED
|
@@ -278,7 +278,7 @@ export class FluidDataStoreRuntime
|
|
|
278
278
|
return;
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
-
let channelContext:
|
|
281
|
+
let channelContext: RemoteChannelContext | RehydratedLocalChannelContext;
|
|
282
282
|
// If already exists on storage, then create a remote channel. However, if it is case of rehydrating a
|
|
283
283
|
// container from snapshot where we load detached container from a snapshot, isLocalDataStore would be
|
|
284
284
|
// true. In this case create a RehydratedLocalChannelContext.
|
|
@@ -302,12 +302,9 @@ export class FluidDataStoreRuntime
|
|
|
302
302
|
// the channel visible. So do it now. Otherwise, add it to local channel context queue, so
|
|
303
303
|
// that it can be make it visible later with the data store.
|
|
304
304
|
if (dataStoreContext.attachState !== AttachState.Detached) {
|
|
305
|
-
|
|
305
|
+
channelContext.makeVisible();
|
|
306
306
|
} else {
|
|
307
|
-
this.localChannelContextQueue.set(
|
|
308
|
-
path,
|
|
309
|
-
channelContext as LocalChannelContextBase,
|
|
310
|
-
);
|
|
307
|
+
this.localChannelContextQueue.set(path, channelContext);
|
|
311
308
|
}
|
|
312
309
|
} else {
|
|
313
310
|
channelContext = new RemoteChannelContext(
|
|
@@ -478,7 +475,6 @@ export class FluidDataStoreRuntime
|
|
|
478
475
|
// Channels (DDS) should not be created in summarizer client.
|
|
479
476
|
this.identifyLocalChangeInSummarizer("DDSCreatedInSummarizer", id, type);
|
|
480
477
|
|
|
481
|
-
assert(!!context.channel, 0x17a /* "Channel should be loaded when created!!" */);
|
|
482
478
|
return context.channel;
|
|
483
479
|
}
|
|
484
480
|
|
|
@@ -490,7 +486,7 @@ export class FluidDataStoreRuntime
|
|
|
490
486
|
public bindChannel(channel: IChannel): void {
|
|
491
487
|
assert(
|
|
492
488
|
this.notBoundedChannelContextSet.has(channel.id),
|
|
493
|
-
0x17b /* "Channel to be
|
|
489
|
+
0x17b /* "Channel to be bound should be in not bounded set" */,
|
|
494
490
|
);
|
|
495
491
|
this.notBoundedChannelContextSet.delete(channel.id);
|
|
496
492
|
// If our data store is attached, then attach the channel.
|
|
@@ -8,65 +8,58 @@ import cloneDeep from "lodash/cloneDeep";
|
|
|
8
8
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
9
9
|
import { IDocumentStorageService } from "@fluidframework/driver-definitions";
|
|
10
10
|
import { ISequencedDocumentMessage, ISnapshotTree } from "@fluidframework/protocol-definitions";
|
|
11
|
-
import {
|
|
12
|
-
IChannel,
|
|
13
|
-
IFluidDataStoreRuntime,
|
|
14
|
-
IChannelFactory,
|
|
15
|
-
IChannelAttributes,
|
|
16
|
-
} from "@fluidframework/datastore-definitions";
|
|
11
|
+
import { IChannel, IFluidDataStoreRuntime } from "@fluidframework/datastore-definitions";
|
|
17
12
|
import {
|
|
18
13
|
IFluidDataStoreContext,
|
|
19
14
|
IGarbageCollectionData,
|
|
20
15
|
ISummarizeResult,
|
|
21
16
|
ITelemetryContext,
|
|
22
17
|
} from "@fluidframework/runtime-definitions";
|
|
23
|
-
import { readAndParse } from "@fluidframework/driver-utils";
|
|
24
18
|
import { DataProcessingError } from "@fluidframework/container-utils";
|
|
25
|
-
import { assert, Lazy } from "@fluidframework/common-utils";
|
|
19
|
+
import { assert, Lazy, LazyPromise } from "@fluidframework/common-utils";
|
|
26
20
|
import { IFluidHandle } from "@fluidframework/core-interfaces";
|
|
27
21
|
import {
|
|
28
|
-
|
|
22
|
+
ChannelServiceEndpoints,
|
|
23
|
+
createChannelServiceEndpoints,
|
|
29
24
|
IChannelContext,
|
|
25
|
+
loadChannel,
|
|
26
|
+
loadChannelFactoryAndAttributes,
|
|
30
27
|
summarizeChannel,
|
|
31
28
|
summarizeChannelAsync,
|
|
32
29
|
} from "./channelContext";
|
|
33
|
-
import { ChannelDeltaConnection } from "./channelDeltaConnection";
|
|
34
30
|
import { ISharedObjectRegistry } from "./dataStoreRuntime";
|
|
35
|
-
import { ChannelStorageService } from "./channelStorageService";
|
|
36
31
|
|
|
37
32
|
/**
|
|
38
33
|
* Channel context for a locally created channel
|
|
39
34
|
*/
|
|
40
35
|
export abstract class LocalChannelContextBase implements IChannelContext {
|
|
41
|
-
public channel: IChannel | undefined;
|
|
42
36
|
private globallyVisible = false;
|
|
43
37
|
protected readonly pending: ISequencedDocumentMessage[] = [];
|
|
44
|
-
protected factory: IChannelFactory | undefined;
|
|
45
38
|
constructor(
|
|
46
39
|
protected readonly id: string,
|
|
47
|
-
protected readonly registry: ISharedObjectRegistry,
|
|
48
40
|
protected readonly runtime: IFluidDataStoreRuntime,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}>,
|
|
41
|
+
protected readonly services: Lazy<ChannelServiceEndpoints>,
|
|
42
|
+
private readonly channelP: Promise<IChannel>,
|
|
43
|
+
private _channel?: IChannel,
|
|
53
44
|
) {
|
|
54
45
|
assert(!this.id.includes("/"), 0x30f /* Channel context ID cannot contain slashes */);
|
|
55
46
|
}
|
|
56
47
|
|
|
57
48
|
public async getChannel(): Promise<IChannel> {
|
|
58
|
-
|
|
59
|
-
|
|
49
|
+
if (this._channel === undefined) {
|
|
50
|
+
return this.channelP.then((c) => (this._channel = c));
|
|
51
|
+
}
|
|
52
|
+
return this.channelP;
|
|
60
53
|
}
|
|
61
54
|
|
|
62
55
|
public get isLoaded(): boolean {
|
|
63
|
-
return this.
|
|
56
|
+
return this._channel !== undefined;
|
|
64
57
|
}
|
|
65
58
|
|
|
66
59
|
public setConnectionState(connected: boolean, clientId?: string) {
|
|
67
60
|
// Connection events are ignored if the data store is not yet globallyVisible or loaded
|
|
68
61
|
if (this.globallyVisible && this.isLoaded) {
|
|
69
|
-
this.
|
|
62
|
+
this.services.value.deltaConnection.setConnectionState(connected);
|
|
70
63
|
}
|
|
71
64
|
}
|
|
72
65
|
|
|
@@ -84,7 +77,7 @@ export abstract class LocalChannelContextBase implements IChannelContext {
|
|
|
84
77
|
// delay loading. So after the container is attached and some other client joins which start generating
|
|
85
78
|
// ops for this channel. So not loaded local channel can still receive ops and we store them to process later.
|
|
86
79
|
if (this.isLoaded) {
|
|
87
|
-
this.
|
|
80
|
+
this.services.value.deltaConnection.process(message, local, localOpMetadata);
|
|
88
81
|
} else {
|
|
89
82
|
assert(
|
|
90
83
|
local === false,
|
|
@@ -100,7 +93,7 @@ export abstract class LocalChannelContextBase implements IChannelContext {
|
|
|
100
93
|
this.globallyVisible,
|
|
101
94
|
0x2d4 /* "Local channel must be globally visible when resubmitting op" */,
|
|
102
95
|
);
|
|
103
|
-
this.
|
|
96
|
+
this.services.value.deltaConnection.reSubmit(content, localOpMetadata);
|
|
104
97
|
}
|
|
105
98
|
public rollback(content: any, localOpMetadata: unknown) {
|
|
106
99
|
assert(this.isLoaded, 0x2ee /* "Channel should be loaded to rollback ops" */);
|
|
@@ -108,7 +101,7 @@ export abstract class LocalChannelContextBase implements IChannelContext {
|
|
|
108
101
|
this.globallyVisible,
|
|
109
102
|
0x2ef /* "Local channel must be globally visible when rolling back op" */,
|
|
110
103
|
);
|
|
111
|
-
this.
|
|
104
|
+
this.services.value.deltaConnection.rollback(content, localOpMetadata);
|
|
112
105
|
}
|
|
113
106
|
|
|
114
107
|
public applyStashedOp() {
|
|
@@ -126,20 +119,17 @@ export abstract class LocalChannelContextBase implements IChannelContext {
|
|
|
126
119
|
trackState: boolean = false,
|
|
127
120
|
telemetryContext?: ITelemetryContext,
|
|
128
121
|
): Promise<ISummarizeResult> {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
0x18c /* "Channel should be loaded to summarize" */,
|
|
132
|
-
);
|
|
133
|
-
return summarizeChannelAsync(this.channel, fullTree, trackState, telemetryContext);
|
|
122
|
+
const channel = await this.getChannel();
|
|
123
|
+
return summarizeChannelAsync(channel, fullTree, trackState, telemetryContext);
|
|
134
124
|
}
|
|
135
125
|
|
|
136
126
|
public getAttachSummary(telemetryContext?: ITelemetryContext): ISummarizeResult {
|
|
137
127
|
assert(
|
|
138
|
-
this.
|
|
128
|
+
this._channel !== undefined,
|
|
139
129
|
0x18d /* "Channel should be loaded to take snapshot" */,
|
|
140
130
|
);
|
|
141
131
|
return summarizeChannel(
|
|
142
|
-
this.
|
|
132
|
+
this._channel,
|
|
143
133
|
true /* fullTree */,
|
|
144
134
|
false /* trackState */,
|
|
145
135
|
telemetryContext,
|
|
@@ -152,8 +142,8 @@ export abstract class LocalChannelContextBase implements IChannelContext {
|
|
|
152
142
|
}
|
|
153
143
|
|
|
154
144
|
if (this.isLoaded) {
|
|
155
|
-
assert(!!this.
|
|
156
|
-
this.
|
|
145
|
+
assert(!!this._channel, 0x192 /* "Channel should be there if loaded!!" */);
|
|
146
|
+
this._channel.connect(this.services.value);
|
|
157
147
|
}
|
|
158
148
|
this.globallyVisible = true;
|
|
159
149
|
}
|
|
@@ -165,11 +155,8 @@ export abstract class LocalChannelContextBase implements IChannelContext {
|
|
|
165
155
|
* @param fullGC - true to bypass optimizations and force full generation of GC data.
|
|
166
156
|
*/
|
|
167
157
|
public async getGCData(fullGC: boolean = false): Promise<IGarbageCollectionData> {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
0x193 /* "Channel should be loaded to run GC" */,
|
|
171
|
-
);
|
|
172
|
-
return this.channel.getGCData(fullGC);
|
|
158
|
+
const channel = await this.getChannel();
|
|
159
|
+
return channel.getGCData(fullGC);
|
|
173
160
|
}
|
|
174
161
|
|
|
175
162
|
public updateUsedRoutes(usedRoutes: string[]) {
|
|
@@ -182,13 +169,7 @@ export abstract class LocalChannelContextBase implements IChannelContext {
|
|
|
182
169
|
}
|
|
183
170
|
|
|
184
171
|
export class RehydratedLocalChannelContext extends LocalChannelContextBase {
|
|
185
|
-
private readonly services: Lazy<{
|
|
186
|
-
readonly deltaConnection: ChannelDeltaConnection;
|
|
187
|
-
readonly objectStorage: ChannelStorageService;
|
|
188
|
-
}>;
|
|
189
|
-
|
|
190
172
|
private readonly dirtyFn: () => void;
|
|
191
|
-
|
|
192
173
|
constructor(
|
|
193
174
|
id: string,
|
|
194
175
|
registry: ISharedObjectRegistry,
|
|
@@ -201,88 +182,69 @@ export class RehydratedLocalChannelContext extends LocalChannelContextBase {
|
|
|
201
182
|
addedGCOutboundReferenceFn: (srcHandle: IFluidHandle, outboundHandle: IFluidHandle) => void,
|
|
202
183
|
private readonly snapshotTree: ISnapshotTree,
|
|
203
184
|
) {
|
|
204
|
-
super(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
185
|
+
super(
|
|
186
|
+
id,
|
|
187
|
+
runtime,
|
|
188
|
+
new Lazy(() => {
|
|
189
|
+
const blobMap: Map<string, ArrayBufferLike> = new Map<string, ArrayBufferLike>();
|
|
190
|
+
const clonedSnapshotTree = cloneDeep(this.snapshotTree);
|
|
191
|
+
// 0.47 back-compat Need to sanitize if snapshotTree.blobs still contains blob contents too.
|
|
192
|
+
// This is for older snapshot which is generated by loader <=0.47 version which still contains
|
|
193
|
+
// the contents within blobs. After a couple of revisions we can remove it.
|
|
194
|
+
if (this.isSnapshotInOldFormatAndCollectBlobs(clonedSnapshotTree, blobMap)) {
|
|
195
|
+
this.sanitizeSnapshot(clonedSnapshotTree);
|
|
196
|
+
}
|
|
197
|
+
return createChannelServiceEndpoints(
|
|
198
|
+
dataStoreContext.connected,
|
|
199
|
+
submitFn,
|
|
200
|
+
this.dirtyFn,
|
|
201
|
+
addedGCOutboundReferenceFn,
|
|
202
|
+
storageService,
|
|
203
|
+
logger,
|
|
204
|
+
clonedSnapshotTree,
|
|
205
|
+
blobMap,
|
|
206
|
+
);
|
|
207
|
+
}),
|
|
208
|
+
new LazyPromise<IChannel>(async () => {
|
|
209
|
+
try {
|
|
210
|
+
const { attributes, factory } = await loadChannelFactoryAndAttributes(
|
|
211
|
+
dataStoreContext,
|
|
212
|
+
this.services.value,
|
|
213
|
+
this.id,
|
|
214
|
+
registry,
|
|
215
|
+
);
|
|
216
|
+
const channel = await loadChannel(
|
|
217
|
+
runtime,
|
|
218
|
+
attributes,
|
|
219
|
+
factory,
|
|
220
|
+
this.services.value,
|
|
221
|
+
logger,
|
|
222
|
+
this.id,
|
|
223
|
+
);
|
|
224
|
+
// Send all pending messages to the channel
|
|
225
|
+
for (const message of this.pending) {
|
|
226
|
+
this.services.value.deltaConnection.process(
|
|
227
|
+
message,
|
|
228
|
+
false,
|
|
229
|
+
undefined /* localOpMetadata */,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
return channel;
|
|
233
|
+
} catch (err) {
|
|
234
|
+
throw DataProcessingError.wrapIfUnrecognized(
|
|
235
|
+
err,
|
|
236
|
+
"rehydratedLocalChannelContextFailedToLoadChannel",
|
|
237
|
+
undefined,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
}),
|
|
241
|
+
);
|
|
213
242
|
|
|
214
|
-
this.services = new Lazy(() => {
|
|
215
|
-
return createServiceEndpoints(
|
|
216
|
-
this.id,
|
|
217
|
-
dataStoreContext.connected,
|
|
218
|
-
submitFn,
|
|
219
|
-
this.dirtyFn,
|
|
220
|
-
addedGCOutboundReferenceFn,
|
|
221
|
-
storageService,
|
|
222
|
-
logger,
|
|
223
|
-
clonedSnapshotTree,
|
|
224
|
-
blobMap,
|
|
225
|
-
);
|
|
226
|
-
});
|
|
227
243
|
this.dirtyFn = () => {
|
|
228
244
|
dirtyFn(id);
|
|
229
245
|
};
|
|
230
246
|
}
|
|
231
247
|
|
|
232
|
-
public async getChannel(): Promise<IChannel> {
|
|
233
|
-
if (this.channel === undefined) {
|
|
234
|
-
this.channel = await this.loadChannel().catch((err) => {
|
|
235
|
-
throw DataProcessingError.wrapIfUnrecognized(
|
|
236
|
-
err,
|
|
237
|
-
"rehydratedLocalChannelContextFailedToLoadChannel",
|
|
238
|
-
undefined,
|
|
239
|
-
);
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
return this.channel;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
private async loadChannel(): Promise<IChannel> {
|
|
246
|
-
assert(!this.isLoaded, 0x18e /* "Channel must not already be loaded when loading" */);
|
|
247
|
-
assert(
|
|
248
|
-
await this.services.value.objectStorage.contains(".attributes"),
|
|
249
|
-
0x190 /* ".attributes blob should be present" */,
|
|
250
|
-
);
|
|
251
|
-
const attributes = await readAndParse<IChannelAttributes>(
|
|
252
|
-
this.services.value.objectStorage,
|
|
253
|
-
".attributes",
|
|
254
|
-
);
|
|
255
|
-
|
|
256
|
-
assert(
|
|
257
|
-
this.factory === undefined,
|
|
258
|
-
0x208 /* "Factory should be undefined before loading" */,
|
|
259
|
-
);
|
|
260
|
-
this.factory = this.registry.get(attributes.type);
|
|
261
|
-
if (this.factory === undefined) {
|
|
262
|
-
throw new Error(`Channel Factory ${attributes.type} not registered`);
|
|
263
|
-
}
|
|
264
|
-
// Services will be assigned during this load.
|
|
265
|
-
const channel = await this.factory.load(
|
|
266
|
-
this.runtime,
|
|
267
|
-
this.id,
|
|
268
|
-
this.services.value,
|
|
269
|
-
attributes,
|
|
270
|
-
);
|
|
271
|
-
|
|
272
|
-
// Commit changes.
|
|
273
|
-
this.channel = channel;
|
|
274
|
-
|
|
275
|
-
// Send all pending messages to the channel
|
|
276
|
-
for (const message of this.pending) {
|
|
277
|
-
this.services.value.deltaConnection.process(
|
|
278
|
-
message,
|
|
279
|
-
false,
|
|
280
|
-
undefined /* localOpMetadata */,
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
|
-
return this.channel;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
248
|
private isSnapshotInOldFormatAndCollectBlobs(
|
|
287
249
|
snapshotTree: ISnapshotTree,
|
|
288
250
|
blobMap: Map<string, ArrayBufferLike>,
|
|
@@ -318,11 +280,8 @@ export class RehydratedLocalChannelContext extends LocalChannelContextBase {
|
|
|
318
280
|
}
|
|
319
281
|
|
|
320
282
|
export class LocalChannelContext extends LocalChannelContextBase {
|
|
321
|
-
private readonly services: Lazy<{
|
|
322
|
-
readonly deltaConnection: ChannelDeltaConnection;
|
|
323
|
-
readonly objectStorage: ChannelStorageService;
|
|
324
|
-
}>;
|
|
325
283
|
private readonly dirtyFn: () => void;
|
|
284
|
+
public readonly channel: IChannel;
|
|
326
285
|
constructor(
|
|
327
286
|
id: string,
|
|
328
287
|
registry: ISharedObjectRegistry,
|
|
@@ -335,24 +294,30 @@ export class LocalChannelContext extends LocalChannelContextBase {
|
|
|
335
294
|
dirtyFn: (address: string) => void,
|
|
336
295
|
addedGCOutboundReferenceFn: (srcHandle: IFluidHandle, outboundHandle: IFluidHandle) => void,
|
|
337
296
|
) {
|
|
338
|
-
super(id, registry, runtime, () => this.services);
|
|
339
297
|
assert(type !== undefined, 0x209 /* "Factory Type should be defined" */);
|
|
340
|
-
|
|
341
|
-
if (
|
|
298
|
+
const factory = registry.get(type);
|
|
299
|
+
if (factory === undefined) {
|
|
342
300
|
throw new Error(`Channel Factory ${type} not registered`);
|
|
343
301
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
302
|
+
const channel = factory.create(runtime, id);
|
|
303
|
+
super(
|
|
304
|
+
id,
|
|
305
|
+
runtime,
|
|
306
|
+
new Lazy(() => {
|
|
307
|
+
return createChannelServiceEndpoints(
|
|
308
|
+
dataStoreContext.connected,
|
|
309
|
+
submitFn,
|
|
310
|
+
this.dirtyFn,
|
|
311
|
+
addedGCOutboundReferenceFn,
|
|
312
|
+
storageService,
|
|
313
|
+
logger,
|
|
314
|
+
);
|
|
315
|
+
}),
|
|
316
|
+
Promise.resolve(channel),
|
|
317
|
+
channel,
|
|
318
|
+
);
|
|
319
|
+
this.channel = channel;
|
|
320
|
+
|
|
356
321
|
this.dirtyFn = () => {
|
|
357
322
|
dirtyFn(id);
|
|
358
323
|
};
|