@fluidframework/container-loader 2.0.0-internal.5.1.0 → 2.0.0-internal.5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/catchUpMonitor.d.ts +1 -1
- package/dist/catchUpMonitor.d.ts.map +1 -1
- package/dist/catchUpMonitor.js.map +1 -1
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +3 -0
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +10 -8
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +20 -27
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +164 -104
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +22 -58
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +27 -200
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +1 -1
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/noopHeuristic.d.ts +23 -0
- package/dist/noopHeuristic.d.ts.map +1 -0
- package/dist/{collabWindowTracker.js → noopHeuristic.js} +30 -42
- package/dist/noopHeuristic.js.map +1 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/protocol.d.ts +1 -12
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +0 -18
- package/dist/protocol.js.map +1 -1
- package/dist/protocolTreeDocumentStorageService.d.ts +1 -1
- package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/dist/protocolTreeDocumentStorageService.js.map +1 -1
- package/dist/retriableDocumentStorageService.d.ts +1 -1
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/lib/catchUpMonitor.d.ts +1 -1
- package/lib/catchUpMonitor.d.ts.map +1 -1
- package/lib/catchUpMonitor.js.map +1 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +3 -0
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +10 -8
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +20 -27
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +166 -106
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +22 -58
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +27 -200
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +1 -1
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/noopHeuristic.d.ts +23 -0
- package/lib/noopHeuristic.d.ts.map +1 -0
- package/lib/{collabWindowTracker.js → noopHeuristic.js} +30 -42
- package/lib/noopHeuristic.js.map +1 -0
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/protocol.d.ts +1 -12
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +0 -18
- package/lib/protocol.js.map +1 -1
- package/lib/protocolTreeDocumentStorageService.d.ts +1 -1
- package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/lib/protocolTreeDocumentStorageService.js.map +1 -1
- package/lib/retriableDocumentStorageService.d.ts +1 -1
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/package.json +11 -11
- package/src/catchUpMonitor.ts +1 -1
- package/src/connectionManager.ts +2 -1
- package/src/connectionStateHandler.ts +14 -9
- package/src/container.ts +247 -126
- package/src/containerContext.ts +32 -318
- package/src/containerStorageAdapter.ts +1 -1
- package/src/{collabWindowTracker.ts → noopHeuristic.ts} +37 -47
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +0 -39
- package/src/protocolTreeDocumentStorageService.ts +1 -1
- package/src/retriableDocumentStorageService.ts +2 -1
- package/dist/collabWindowTracker.d.ts +0 -19
- package/dist/collabWindowTracker.d.ts.map +0 -1
- package/dist/collabWindowTracker.js.map +0 -1
- package/lib/collabWindowTracker.d.ts +0 -19
- package/lib/collabWindowTracker.d.ts.map +0 -1
- package/lib/collabWindowTracker.js.map +0 -1
package/src/containerContext.ts
CHANGED
|
@@ -3,27 +3,19 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { ITelemetryLoggerExt
|
|
7
|
-
import { assert, LazyPromise, TypedEventEmitter } from "@fluidframework/common-utils";
|
|
6
|
+
import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
|
|
8
7
|
import {
|
|
9
8
|
IAudience,
|
|
10
9
|
IContainerContext,
|
|
11
10
|
IDeltaManager,
|
|
12
11
|
ILoader,
|
|
13
|
-
IRuntime,
|
|
14
12
|
ICriticalContainerError,
|
|
15
13
|
AttachState,
|
|
16
14
|
ILoaderOptions,
|
|
17
|
-
IRuntimeFactory,
|
|
18
|
-
IProvideRuntimeFactory,
|
|
19
15
|
IFluidCodeDetails,
|
|
20
|
-
IFluidCodeDetailsComparer,
|
|
21
|
-
IProvideFluidCodeDetailsComparer,
|
|
22
|
-
ICodeDetailsLoader,
|
|
23
|
-
IFluidModuleWithDetails,
|
|
24
16
|
IBatchMessage,
|
|
25
17
|
} from "@fluidframework/container-definitions";
|
|
26
|
-
import {
|
|
18
|
+
import { FluidObject } from "@fluidframework/core-interfaces";
|
|
27
19
|
import { IDocumentStorageService } from "@fluidframework/driver-definitions";
|
|
28
20
|
import {
|
|
29
21
|
IClientConfiguration,
|
|
@@ -32,130 +24,46 @@ import {
|
|
|
32
24
|
IQuorum,
|
|
33
25
|
IQuorumClients,
|
|
34
26
|
ISequencedDocumentMessage,
|
|
35
|
-
ISignalMessage,
|
|
36
27
|
ISnapshotTree,
|
|
37
|
-
ISummaryTree,
|
|
38
28
|
IVersion,
|
|
39
29
|
MessageType,
|
|
40
30
|
ISummaryContent,
|
|
41
31
|
} from "@fluidframework/protocol-definitions";
|
|
42
|
-
import { UsageError } from "@fluidframework/container-utils";
|
|
43
|
-
import { Container } from "./container";
|
|
44
|
-
|
|
45
|
-
const PackageNotFactoryError = "Code package does not implement IRuntimeFactory";
|
|
46
32
|
|
|
47
33
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* "runtimeInstantiated" - When an {@link @fluidframework/container-definitions#IRuntime} has been instantiated (by
|
|
51
|
-
* calling instantiateRuntime() on the runtime factory), and this._runtime is set.
|
|
52
|
-
*
|
|
53
|
-
* "disposed" - When its dispose() method is called. The {@link ContainerContext} is no longer usable at that point.
|
|
34
|
+
* {@inheritDoc @fluidframework/container-definitions#IContainerContext}
|
|
54
35
|
*/
|
|
55
|
-
type ContextLifecycleEvents = "runtimeInstantiated" | "disposed";
|
|
56
|
-
|
|
57
36
|
export class ContainerContext implements IContainerContext {
|
|
58
|
-
public
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
loader: ILoader,
|
|
67
|
-
submitFn: (type: MessageType, contents: any, batch: boolean, appData: any) => number,
|
|
68
|
-
submitSummaryFn: (summaryOp: ISummaryContent, referenceSequenceNumber?: number) => number,
|
|
69
|
-
submitBatchFn: (batch: IBatchMessage[], referenceSequenceNumber?: number) => number,
|
|
70
|
-
submitSignalFn: (contents: any) => void,
|
|
71
|
-
disposeFn: (error?: ICriticalContainerError) => void,
|
|
72
|
-
closeFn: (error?: ICriticalContainerError) => void,
|
|
73
|
-
version: string,
|
|
74
|
-
updateDirtyContainerState: (dirty: boolean) => void,
|
|
75
|
-
existing: boolean,
|
|
76
|
-
pendingLocalState?: unknown,
|
|
77
|
-
): Promise<ContainerContext> {
|
|
78
|
-
const context = new ContainerContext(
|
|
79
|
-
container,
|
|
80
|
-
scope,
|
|
81
|
-
codeLoader,
|
|
82
|
-
codeDetails,
|
|
83
|
-
baseSnapshot,
|
|
84
|
-
deltaManager,
|
|
85
|
-
quorum,
|
|
86
|
-
loader,
|
|
87
|
-
submitFn,
|
|
88
|
-
submitSummaryFn,
|
|
89
|
-
submitBatchFn,
|
|
90
|
-
submitSignalFn,
|
|
91
|
-
disposeFn,
|
|
92
|
-
closeFn,
|
|
93
|
-
version,
|
|
94
|
-
updateDirtyContainerState,
|
|
95
|
-
existing,
|
|
96
|
-
pendingLocalState,
|
|
97
|
-
);
|
|
98
|
-
await context.instantiateRuntime(existing);
|
|
99
|
-
return context;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
public readonly taggedLogger: ITelemetryLoggerExt;
|
|
103
|
-
public readonly supportedFeatures: ReadonlyMap<string, unknown>;
|
|
37
|
+
public readonly supportedFeatures: ReadonlyMap<string, unknown> = new Map([
|
|
38
|
+
/**
|
|
39
|
+
* This version of the loader accepts `referenceSequenceNumber`, provided by the container runtime,
|
|
40
|
+
* as a parameter to the `submitBatchFn` and `submitSummaryFn` functions.
|
|
41
|
+
* This is then used to set the reference sequence numbers of the submitted ops in the DeltaManager.
|
|
42
|
+
*/
|
|
43
|
+
["referenceSequenceNumbers", true],
|
|
44
|
+
]);
|
|
104
45
|
|
|
105
46
|
public get clientId(): string | undefined {
|
|
106
|
-
return this.
|
|
47
|
+
return this._getClientId();
|
|
107
48
|
}
|
|
108
49
|
|
|
109
50
|
/**
|
|
110
51
|
* DISCLAIMER: this id is only for telemetry purposes. Not suitable for any other usages.
|
|
111
52
|
*/
|
|
112
53
|
public get id(): string {
|
|
113
|
-
return this.
|
|
54
|
+
return this._getContainerDiagnosticId() ?? "";
|
|
114
55
|
}
|
|
115
56
|
|
|
116
|
-
public get clientDetails(): IClientDetails {
|
|
117
|
-
return this.container.clientDetails;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
private _connected: boolean;
|
|
121
57
|
/**
|
|
122
58
|
* When true, ops are free to flow
|
|
123
59
|
* When false, ops should be kept as pending or rejected
|
|
124
60
|
*/
|
|
125
61
|
public get connected(): boolean {
|
|
126
|
-
return this.
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
public get canSummarize(): boolean {
|
|
130
|
-
return "summarize" in this.runtime;
|
|
62
|
+
return this._getConnected();
|
|
131
63
|
}
|
|
132
64
|
|
|
133
65
|
public get serviceConfiguration(): IClientConfiguration | undefined {
|
|
134
|
-
return this.
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
public get audience(): IAudience {
|
|
138
|
-
return this.container.audience;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
public get options(): ILoaderOptions {
|
|
142
|
-
return this.container.options;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
public get baseSnapshot() {
|
|
146
|
-
return this._baseSnapshot;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
public get storage(): IDocumentStorageService {
|
|
150
|
-
return this.container.storage;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
private _runtime: IRuntime | undefined;
|
|
154
|
-
private get runtime() {
|
|
155
|
-
if (this._runtime === undefined) {
|
|
156
|
-
throw new Error("Attempted to access runtime before it was defined");
|
|
157
|
-
}
|
|
158
|
-
return this._runtime;
|
|
66
|
+
return this._getServiceConfiguration();
|
|
159
67
|
}
|
|
160
68
|
|
|
161
69
|
private _disposed = false;
|
|
@@ -164,59 +72,19 @@ export class ContainerContext implements IContainerContext {
|
|
|
164
72
|
return this._disposed;
|
|
165
73
|
}
|
|
166
74
|
|
|
167
|
-
public get codeDetails() {
|
|
168
|
-
return this._codeDetails;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
private readonly _quorum: IQuorum;
|
|
172
75
|
public get quorum(): IQuorumClients {
|
|
173
76
|
return this._quorum;
|
|
174
77
|
}
|
|
175
78
|
|
|
176
|
-
private readonly _fluidModuleP: Promise<IFluidModuleWithDetails>;
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* {@inheritDoc @fluidframework/container-definitions#IContainerContext.getEntryPoint}
|
|
180
|
-
*/
|
|
181
|
-
public async getEntryPoint?(): Promise<FluidObject | undefined> {
|
|
182
|
-
if (this._disposed) {
|
|
183
|
-
throw new UsageError("The context is already disposed");
|
|
184
|
-
}
|
|
185
|
-
if (this._runtime !== undefined) {
|
|
186
|
-
return this._runtime?.getEntryPoint?.();
|
|
187
|
-
}
|
|
188
|
-
return new Promise<FluidObject | undefined>((resolve, reject) => {
|
|
189
|
-
const runtimeInstantiatedHandler = () => {
|
|
190
|
-
assert(
|
|
191
|
-
this._runtime !== undefined,
|
|
192
|
-
0x5a3 /* runtimeInstantiated fired but runtime is still undefined */,
|
|
193
|
-
);
|
|
194
|
-
resolve(this._runtime.getEntryPoint?.());
|
|
195
|
-
this.lifecycleEvents.off("disposed", disposedHandler);
|
|
196
|
-
};
|
|
197
|
-
const disposedHandler = () => {
|
|
198
|
-
reject(new Error("ContainerContext was disposed"));
|
|
199
|
-
this.lifecycleEvents.off("runtimeInstantiated", runtimeInstantiatedHandler);
|
|
200
|
-
};
|
|
201
|
-
this.lifecycleEvents.once("runtimeInstantiated", runtimeInstantiatedHandler);
|
|
202
|
-
this.lifecycleEvents.once("disposed", disposedHandler);
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Emits events about the container context's lifecycle.
|
|
208
|
-
* Use it to coordinate things inside the ContainerContext class.
|
|
209
|
-
*/
|
|
210
|
-
private readonly lifecycleEvents = new TypedEventEmitter<ContextLifecycleEvents>();
|
|
211
|
-
|
|
212
79
|
constructor(
|
|
213
|
-
|
|
80
|
+
public readonly options: ILoaderOptions,
|
|
214
81
|
public readonly scope: FluidObject,
|
|
215
|
-
|
|
216
|
-
private readonly
|
|
217
|
-
private readonly _baseSnapshot: ISnapshotTree | undefined,
|
|
82
|
+
public readonly baseSnapshot: ISnapshotTree | undefined,
|
|
83
|
+
private readonly _version: IVersion | undefined,
|
|
218
84
|
public readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,
|
|
219
|
-
|
|
85
|
+
public readonly storage: IDocumentStorageService,
|
|
86
|
+
private readonly _quorum: IQuorum,
|
|
87
|
+
public readonly audience: IAudience,
|
|
220
88
|
public readonly loader: ILoader,
|
|
221
89
|
public readonly submitFn: (
|
|
222
90
|
type: MessageType,
|
|
@@ -236,28 +104,18 @@ export class ContainerContext implements IContainerContext {
|
|
|
236
104
|
public readonly submitSignalFn: (contents: any) => void,
|
|
237
105
|
public readonly disposeFn: (error?: ICriticalContainerError) => void,
|
|
238
106
|
public readonly closeFn: (error?: ICriticalContainerError) => void,
|
|
239
|
-
public readonly version: string,
|
|
240
107
|
public readonly updateDirtyContainerState: (dirty: boolean) => void,
|
|
108
|
+
public readonly getAbsoluteUrl: (relativeUrl: string) => Promise<string | undefined>,
|
|
109
|
+
private readonly _getContainerDiagnosticId: () => string | undefined,
|
|
110
|
+
private readonly _getClientId: () => string | undefined,
|
|
111
|
+
private readonly _getServiceConfiguration: () => IClientConfiguration | undefined,
|
|
112
|
+
private readonly _getAttachState: () => AttachState,
|
|
113
|
+
private readonly _getConnected: () => boolean,
|
|
114
|
+
public readonly clientDetails: IClientDetails,
|
|
241
115
|
public readonly existing: boolean,
|
|
116
|
+
public readonly taggedLogger: ITelemetryLoggerExt,
|
|
242
117
|
public readonly pendingLocalState?: unknown,
|
|
243
|
-
) {
|
|
244
|
-
this._connected = this.container.connected;
|
|
245
|
-
this._quorum = quorum;
|
|
246
|
-
this.taggedLogger = container.subLogger;
|
|
247
|
-
this._fluidModuleP = new LazyPromise<IFluidModuleWithDetails>(async () =>
|
|
248
|
-
this.loadCodeModule(_codeDetails),
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
this.supportedFeatures = new Map([
|
|
252
|
-
/**
|
|
253
|
-
* This version of the loader accepts `referenceSequenceNumber`, provided by the container runtime,
|
|
254
|
-
* as a parameter to the `submitBatchFn` and `submitSummaryFn` functions.
|
|
255
|
-
* This is then used to set the reference sequence numbers of the submitted ops in the DeltaManager.
|
|
256
|
-
*/
|
|
257
|
-
["referenceSequenceNumbers", true],
|
|
258
|
-
]);
|
|
259
|
-
this.attachListener();
|
|
260
|
-
}
|
|
118
|
+
) {}
|
|
261
119
|
|
|
262
120
|
/**
|
|
263
121
|
* @deprecated Temporary migratory API, to be removed when customers no longer need it.
|
|
@@ -272,158 +130,14 @@ export class ContainerContext implements IContainerContext {
|
|
|
272
130
|
}
|
|
273
131
|
|
|
274
132
|
public dispose(error?: Error): void {
|
|
275
|
-
if (this._disposed) {
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
133
|
this._disposed = true;
|
|
279
|
-
|
|
280
|
-
this.lifecycleEvents.emit("disposed");
|
|
281
|
-
this.runtime.dispose(error);
|
|
282
|
-
this._quorum.dispose();
|
|
283
|
-
this.deltaManager.dispose();
|
|
284
134
|
}
|
|
285
135
|
|
|
286
136
|
public getLoadedFromVersion(): IVersion | undefined {
|
|
287
|
-
return this.
|
|
137
|
+
return this._version;
|
|
288
138
|
}
|
|
289
139
|
|
|
290
140
|
public get attachState(): AttachState {
|
|
291
|
-
return this.
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* Create a summary. Used when attaching or serializing a detached container.
|
|
296
|
-
*
|
|
297
|
-
* @param blobRedirectTable - A table passed during the attach process. While detached, blob upload is supported
|
|
298
|
-
* using IDs generated locally. After attach, these IDs cannot be used, so this table maps the old local IDs to the
|
|
299
|
-
* new storage IDs so requests can be redirected.
|
|
300
|
-
*/
|
|
301
|
-
public createSummary(blobRedirectTable?: Map<string, string>): ISummaryTree {
|
|
302
|
-
return this.runtime.createSummary(blobRedirectTable);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
public setConnectionState(connected: boolean, clientId?: string) {
|
|
306
|
-
const runtime = this.runtime;
|
|
307
|
-
this._connected = connected;
|
|
308
|
-
runtime.setConnectionState(connected, clientId);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
public process(message: ISequencedDocumentMessage, local: boolean) {
|
|
312
|
-
this.runtime.process(message, local);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
public processSignal(message: ISignalMessage, local: boolean) {
|
|
316
|
-
this.runtime.processSignal(message, local);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
public async request(path: IRequest): Promise<IResponse> {
|
|
320
|
-
return this.runtime.request(path);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
public async getAbsoluteUrl(relativeUrl: string): Promise<string | undefined> {
|
|
324
|
-
return this.container.getAbsoluteUrl(relativeUrl);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
public getPendingLocalState(): unknown {
|
|
328
|
-
return this.runtime.getPendingLocalState();
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Determines if the current code details of the context
|
|
333
|
-
* satisfy the incoming constraint code details
|
|
334
|
-
*/
|
|
335
|
-
public async satisfies(constraintCodeDetails: IFluidCodeDetails) {
|
|
336
|
-
const comparers: IFluidCodeDetailsComparer[] = [];
|
|
337
|
-
|
|
338
|
-
const maybeCompareCodeLoader = this.codeLoader;
|
|
339
|
-
if (maybeCompareCodeLoader.IFluidCodeDetailsComparer !== undefined) {
|
|
340
|
-
comparers.push(maybeCompareCodeLoader.IFluidCodeDetailsComparer);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
const moduleWithDetails = await this._fluidModuleP;
|
|
344
|
-
const maybeCompareExport: Partial<IProvideFluidCodeDetailsComparer> | undefined =
|
|
345
|
-
moduleWithDetails.module?.fluidExport;
|
|
346
|
-
if (maybeCompareExport?.IFluidCodeDetailsComparer !== undefined) {
|
|
347
|
-
comparers.push(maybeCompareExport.IFluidCodeDetailsComparer);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// if there are not comparers it is not possible to know
|
|
351
|
-
// if the current satisfy the incoming, so return false,
|
|
352
|
-
// as assuming they do not satisfy is safer .e.g we will
|
|
353
|
-
// reload, rather than potentially running with
|
|
354
|
-
// incompatible code
|
|
355
|
-
if (comparers.length === 0) {
|
|
356
|
-
return false;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
for (const comparer of comparers) {
|
|
360
|
-
const satisfies = await comparer.satisfies(
|
|
361
|
-
moduleWithDetails.details,
|
|
362
|
-
constraintCodeDetails,
|
|
363
|
-
);
|
|
364
|
-
if (satisfies === false) {
|
|
365
|
-
return false;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
return true;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
public async notifyOpReplay(message: ISequencedDocumentMessage): Promise<void> {
|
|
372
|
-
return this.runtime.notifyOpReplay?.(message);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// #region private
|
|
376
|
-
|
|
377
|
-
private async getRuntimeFactory(): Promise<IRuntimeFactory> {
|
|
378
|
-
const fluidExport: FluidObject<IProvideRuntimeFactory> | undefined = (
|
|
379
|
-
await this._fluidModuleP
|
|
380
|
-
).module?.fluidExport;
|
|
381
|
-
const runtimeFactory = fluidExport?.IRuntimeFactory;
|
|
382
|
-
if (runtimeFactory === undefined) {
|
|
383
|
-
throw new Error(PackageNotFactoryError);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
return runtimeFactory;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
private async instantiateRuntime(existing: boolean) {
|
|
390
|
-
const runtimeFactory = await this.getRuntimeFactory();
|
|
391
|
-
this._runtime = await PerformanceEvent.timedExecAsync(
|
|
392
|
-
this.taggedLogger,
|
|
393
|
-
{ eventName: "InstantiateRuntime" },
|
|
394
|
-
async () => runtimeFactory.instantiateRuntime(this, existing),
|
|
395
|
-
);
|
|
396
|
-
this.lifecycleEvents.emit("runtimeInstantiated");
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
private attachListener() {
|
|
400
|
-
this.container.once("attaching", () => {
|
|
401
|
-
this.runtime.setAttachState(AttachState.Attaching);
|
|
402
|
-
});
|
|
403
|
-
this.container.once("attached", () => {
|
|
404
|
-
this.runtime.setAttachState(AttachState.Attached);
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
private async loadCodeModule(codeDetails: IFluidCodeDetails): Promise<IFluidModuleWithDetails> {
|
|
409
|
-
const loadCodeResult = await PerformanceEvent.timedExecAsync(
|
|
410
|
-
this.taggedLogger,
|
|
411
|
-
{ eventName: "CodeLoad" },
|
|
412
|
-
async () => this.codeLoader.load(codeDetails),
|
|
413
|
-
);
|
|
414
|
-
|
|
415
|
-
if ("module" in loadCodeResult) {
|
|
416
|
-
const { module, details } = loadCodeResult;
|
|
417
|
-
return {
|
|
418
|
-
module,
|
|
419
|
-
details: details ?? codeDetails,
|
|
420
|
-
};
|
|
421
|
-
} else {
|
|
422
|
-
// If "module" is not in the result, we are using a legacy ICodeLoader. Fix the result up with details.
|
|
423
|
-
// Once usage drops to 0 we can remove this compat path.
|
|
424
|
-
this.taggedLogger.sendTelemetryEvent({ eventName: "LegacyCodeLoader" });
|
|
425
|
-
return loadCodeResult;
|
|
426
|
-
}
|
|
141
|
+
return this._getAttachState();
|
|
427
142
|
}
|
|
428
|
-
// #endregion
|
|
429
143
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { IDisposable } from "@fluidframework/
|
|
6
|
+
import { IDisposable } from "@fluidframework/core-interfaces";
|
|
7
7
|
import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
|
|
8
8
|
import { assert, bufferToString, stringToBuffer } from "@fluidframework/common-utils";
|
|
9
9
|
import { ISnapshotTreeWithBlobContents } from "@fluidframework/container-definitions";
|
|
@@ -3,13 +3,18 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { assert, Timer } from "@fluidframework/common-utils";
|
|
7
|
-
import { ISequencedDocumentMessage
|
|
8
|
-
import { isRuntimeMessage
|
|
6
|
+
import { assert, Timer, TypedEventEmitter } from "@fluidframework/common-utils";
|
|
7
|
+
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
|
|
8
|
+
import { isRuntimeMessage } from "@fluidframework/driver-utils";
|
|
9
|
+
import { IEvent } from "@fluidframework/common-definitions";
|
|
9
10
|
|
|
10
11
|
const defaultNoopTimeFrequency = 2000;
|
|
11
12
|
const defaultNoopCountFrequency = 50;
|
|
12
13
|
|
|
14
|
+
export interface INoopSenderEvents extends IEvent {
|
|
15
|
+
(event: "wantsNoop", listener: () => void);
|
|
16
|
+
}
|
|
17
|
+
|
|
13
18
|
// Here are key considerations when deciding conditions for when to send non-immediate noops:
|
|
14
19
|
// 1. Sending them too often results in increase in file size and bandwidth, as well as catch up performance
|
|
15
20
|
// 2. Sending too infrequently ensures that collab window is large, and as result Sequence DDS would have
|
|
@@ -29,21 +34,21 @@ const defaultNoopCountFrequency = 50;
|
|
|
29
34
|
// server timeout of 2000ms should be reconsidered to be increased.
|
|
30
35
|
// 2. If there are more than 50 ops received without sending any ops, send noop to keep collab window small.
|
|
31
36
|
// Note that system ops (including noops themselves) are excluded, so it's 1 noop per 50 real ops.
|
|
32
|
-
export class
|
|
33
|
-
private
|
|
37
|
+
export class NoopHeuristic extends TypedEventEmitter<INoopSenderEvents> {
|
|
38
|
+
private opsProcessedSinceOpSent = 0;
|
|
34
39
|
private readonly timer: Timer | undefined;
|
|
35
40
|
|
|
36
41
|
constructor(
|
|
37
|
-
private readonly submit: (type: MessageType) => void,
|
|
38
42
|
NoopTimeFrequency: number = defaultNoopTimeFrequency,
|
|
39
43
|
private readonly NoopCountFrequency: number = defaultNoopCountFrequency,
|
|
40
44
|
) {
|
|
45
|
+
super();
|
|
41
46
|
if (NoopTimeFrequency !== Infinity) {
|
|
42
47
|
this.timer = new Timer(NoopTimeFrequency, () => {
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
if (this.
|
|
46
|
-
this.
|
|
48
|
+
// We allow the timer to expire even if an op is sent or we disconnect.
|
|
49
|
+
// This condition is to guard against trying to send a noop anyway in that case.
|
|
50
|
+
if (this.opsProcessedSinceOpSent !== 0) {
|
|
51
|
+
this.emit("wantsNoop");
|
|
47
52
|
}
|
|
48
53
|
});
|
|
49
54
|
}
|
|
@@ -52,17 +57,7 @@ export class CollabWindowTracker {
|
|
|
52
57
|
/**
|
|
53
58
|
* Schedules as ack to the server to update the reference sequence number
|
|
54
59
|
*/
|
|
55
|
-
public
|
|
56
|
-
message: ISequencedDocumentMessage,
|
|
57
|
-
immediateNoOp: boolean,
|
|
58
|
-
): void {
|
|
59
|
-
// While processing a message, an immediate no-op can be requested.
|
|
60
|
-
// i.e. to expedite approve or commit phase of quorum.
|
|
61
|
-
if (immediateNoOp) {
|
|
62
|
-
this.submitNoop(true /* immediate */);
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
60
|
+
public notifyMessageProcessed(message: ISequencedDocumentMessage): void {
|
|
66
61
|
// We don't acknowledge no-ops to avoid acknowledgement cycles (i.e. ack the MSN
|
|
67
62
|
// update, which updates the MSN, then ack the update, etc...).
|
|
68
63
|
// Intent here is for runtime (and DDSes) not to keep too much tracking state / memory
|
|
@@ -71,22 +66,27 @@ export class CollabWindowTracker {
|
|
|
71
66
|
return;
|
|
72
67
|
}
|
|
73
68
|
|
|
74
|
-
this.
|
|
75
|
-
if (this.
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
69
|
+
this.opsProcessedSinceOpSent++;
|
|
70
|
+
if (this.opsProcessedSinceOpSent === this.NoopCountFrequency) {
|
|
71
|
+
// Wait to send a noop if we are still synchronously processing ops. This guards against two things:
|
|
72
|
+
// 1. If we're processing many ops, we may pass the frequency threshold many times. We only need to send one noop at the very end in this case.
|
|
73
|
+
// 2. We may send another (non-noop) op in response to processing those ops, e.g. an Accept op.
|
|
74
|
+
queueMicrotask(() => {
|
|
75
|
+
if (this.opsProcessedSinceOpSent >= this.NoopCountFrequency) {
|
|
76
|
+
this.emit("wantsNoop");
|
|
77
|
+
assert(
|
|
78
|
+
this.opsProcessedSinceOpSent === 0,
|
|
79
|
+
0x243 /* "Expected a noop to be synchronously sent" */,
|
|
80
|
+
);
|
|
83
81
|
}
|
|
84
82
|
return;
|
|
85
83
|
});
|
|
86
84
|
}
|
|
87
85
|
|
|
88
86
|
if (this.timer !== undefined) {
|
|
89
|
-
if
|
|
87
|
+
// Start the timer if we newly have ops that want a noop.
|
|
88
|
+
// If the timer was already running (e.g. we surpassed the op count and sent a noop) this will reset it to its full duration.
|
|
89
|
+
if (this.opsProcessedSinceOpSent === 1) {
|
|
90
90
|
this.timer.restart();
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -94,23 +94,13 @@ export class CollabWindowTracker {
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
this.submit(immediate ? (MessageType2.Accept as unknown as MessageType) : MessageType.NoOp);
|
|
101
|
-
assert(
|
|
102
|
-
this.opsCountSinceNoop === 0,
|
|
103
|
-
0x243 /* "stopSequenceNumberUpdate should be called as result of sending any op!" */,
|
|
104
|
-
);
|
|
97
|
+
public notifyDisconnect(): void {
|
|
98
|
+
// No need to noop for any ops processed prior to disconnect - we are already removed from MSN calculation.
|
|
99
|
+
this.opsProcessedSinceOpSent = 0;
|
|
105
100
|
}
|
|
106
101
|
|
|
107
|
-
public
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
// keeps sending ops. In most cases it's actually better to let it expire (at most - 4 times per second)
|
|
111
|
-
// for nothing, then have a ton of set/reset cycles.
|
|
112
|
-
// Note that Timer.restart() is smart and will not change timer expiration if we keep extending timer
|
|
113
|
-
// expiration - it will restart the timer instead when it fires with adjusted expiration.
|
|
114
|
-
// this.timer.clear();
|
|
102
|
+
public notifyMessageSent(): void {
|
|
103
|
+
// Sending any message is as good as a noop for updating MSN.
|
|
104
|
+
this.opsProcessedSinceOpSent = 0;
|
|
115
105
|
}
|
|
116
106
|
}
|
package/src/packageVersion.ts
CHANGED
package/src/protocol.ts
CHANGED
|
@@ -11,14 +11,9 @@ import {
|
|
|
11
11
|
} from "@fluidframework/protocol-base";
|
|
12
12
|
import {
|
|
13
13
|
IDocumentAttributes,
|
|
14
|
-
IProcessMessageResult,
|
|
15
|
-
ISequencedClient,
|
|
16
|
-
ISequencedDocumentMessage,
|
|
17
14
|
ISignalClient,
|
|
18
15
|
ISignalMessage,
|
|
19
|
-
MessageType,
|
|
20
16
|
} from "@fluidframework/protocol-definitions";
|
|
21
|
-
import { canBeCoalescedByService } from "@fluidframework/driver-utils";
|
|
22
17
|
|
|
23
18
|
// "term" was an experimental feature that is being removed. The only safe value to use is 1.
|
|
24
19
|
export const OnlyValidTermValue = 1 as const;
|
|
@@ -30,17 +25,6 @@ export enum SignalType {
|
|
|
30
25
|
Clear = "clear", // used only by client for synthetic signals
|
|
31
26
|
}
|
|
32
27
|
|
|
33
|
-
/**
|
|
34
|
-
* ADO: #4277: ConnectionStateHandler can mutate Quorum members, but shouldn't
|
|
35
|
-
* This interface might go away after the above ADO item is done
|
|
36
|
-
*/
|
|
37
|
-
export interface ILocalSequencedClient extends ISequencedClient {
|
|
38
|
-
/**
|
|
39
|
-
* True if the client should have left the quorum, false otherwise
|
|
40
|
-
*/
|
|
41
|
-
shouldHaveLeft?: boolean;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
28
|
/**
|
|
45
29
|
* Function to be used for creating a protocol handler.
|
|
46
30
|
*/
|
|
@@ -82,29 +66,6 @@ export class ProtocolHandler extends ProtocolOpHandler implements IProtocolHandl
|
|
|
82
66
|
}
|
|
83
67
|
}
|
|
84
68
|
|
|
85
|
-
public processMessage(
|
|
86
|
-
message: ISequencedDocumentMessage,
|
|
87
|
-
local: boolean,
|
|
88
|
-
): IProcessMessageResult {
|
|
89
|
-
const client: ILocalSequencedClient | undefined = this.quorum.getMember(message.clientId);
|
|
90
|
-
|
|
91
|
-
// Check and report if we're getting messages from a clientId that we previously
|
|
92
|
-
// flagged as shouldHaveLeft, or from a client that's not in the quorum but should be
|
|
93
|
-
if (message.clientId != null) {
|
|
94
|
-
if (client === undefined && message.type !== MessageType.ClientJoin) {
|
|
95
|
-
// pre-0.58 error message: messageClientIdMissingFromQuorum
|
|
96
|
-
throw new Error("Remote message's clientId is missing from the quorum");
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (client?.shouldHaveLeft === true && !canBeCoalescedByService(message)) {
|
|
100
|
-
// pre-0.58 error message: messageClientIdShouldHaveLeft
|
|
101
|
-
throw new Error("Remote message's clientId already should have left");
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return super.processMessage(message, local);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
69
|
public processSignal(message: ISignalMessage) {
|
|
109
70
|
const innerContent = message.content as { content: any; type: string };
|
|
110
71
|
switch (innerContent.type) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { IDisposable } from "@fluidframework/
|
|
6
|
+
import { IDisposable } from "@fluidframework/core-interfaces";
|
|
7
7
|
import { IDocumentStorageService, ISummaryContext } from "@fluidframework/driver-definitions";
|
|
8
8
|
import { ISummaryTree } from "@fluidframework/protocol-definitions";
|
|
9
9
|
|
|
@@ -18,7 +18,8 @@ import {
|
|
|
18
18
|
ISummaryTree,
|
|
19
19
|
IVersion,
|
|
20
20
|
} from "@fluidframework/protocol-definitions";
|
|
21
|
-
import { IDisposable } from "@fluidframework/
|
|
21
|
+
import { IDisposable } from "@fluidframework/core-interfaces";
|
|
22
|
+
|
|
22
23
|
import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
|
|
23
24
|
import { runWithRetry } from "@fluidframework/driver-utils";
|
|
24
25
|
|