@fluidframework/datastore 2.41.0-338401 → 2.42.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/.eslintrc.cjs +1 -4
- package/CHANGELOG.md +8 -0
- package/dist/channelContext.d.ts +7 -5
- package/dist/channelContext.d.ts.map +1 -1
- package/dist/channelContext.js +3 -1
- package/dist/channelContext.js.map +1 -1
- package/dist/channelDeltaConnection.d.ts +5 -5
- package/dist/channelDeltaConnection.d.ts.map +1 -1
- package/dist/channelDeltaConnection.js +7 -6
- package/dist/channelDeltaConnection.js.map +1 -1
- package/dist/channelStorageService.js +2 -2
- package/dist/channelStorageService.js.map +1 -1
- package/dist/dataStoreRuntime.d.ts +20 -0
- package/dist/dataStoreRuntime.d.ts.map +1 -1
- package/dist/dataStoreRuntime.js +125 -48
- package/dist/dataStoreRuntime.js.map +1 -1
- package/dist/fluidHandle.d.ts.map +1 -1
- package/dist/fluidHandle.js +6 -2
- package/dist/fluidHandle.js.map +1 -1
- package/dist/localChannelContext.d.ts +8 -6
- package/dist/localChannelContext.d.ts.map +1 -1
- package/dist/localChannelContext.js +8 -6
- package/dist/localChannelContext.js.map +1 -1
- package/dist/localChannelStorageService.d.ts.map +1 -1
- package/dist/localChannelStorageService.js +6 -4
- package/dist/localChannelStorageService.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/remoteChannelContext.d.ts +7 -5
- package/dist/remoteChannelContext.d.ts.map +1 -1
- package/dist/remoteChannelContext.js +5 -4
- package/dist/remoteChannelContext.js.map +1 -1
- package/lib/channelContext.d.ts +7 -5
- package/lib/channelContext.d.ts.map +1 -1
- package/lib/channelContext.js +3 -1
- package/lib/channelContext.js.map +1 -1
- package/lib/channelDeltaConnection.d.ts +5 -5
- package/lib/channelDeltaConnection.d.ts.map +1 -1
- package/lib/channelDeltaConnection.js +7 -6
- package/lib/channelDeltaConnection.js.map +1 -1
- package/lib/channelStorageService.js +2 -2
- package/lib/channelStorageService.js.map +1 -1
- package/lib/dataStoreRuntime.d.ts +20 -0
- package/lib/dataStoreRuntime.d.ts.map +1 -1
- package/lib/dataStoreRuntime.js +125 -48
- package/lib/dataStoreRuntime.js.map +1 -1
- package/lib/fluidHandle.d.ts.map +1 -1
- package/lib/fluidHandle.js +6 -2
- package/lib/fluidHandle.js.map +1 -1
- package/lib/localChannelContext.d.ts +8 -6
- package/lib/localChannelContext.d.ts.map +1 -1
- package/lib/localChannelContext.js +8 -6
- package/lib/localChannelContext.js.map +1 -1
- package/lib/localChannelStorageService.d.ts.map +1 -1
- package/lib/localChannelStorageService.js +6 -4
- package/lib/localChannelStorageService.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/remoteChannelContext.d.ts +7 -5
- package/lib/remoteChannelContext.d.ts.map +1 -1
- package/lib/remoteChannelContext.js +5 -4
- package/lib/remoteChannelContext.js.map +1 -1
- package/package.json +16 -16
- package/src/channelContext.ts +7 -5
- package/src/channelDeltaConnection.ts +19 -19
- package/src/channelStorageService.ts +3 -3
- package/src/dataStoreRuntime.ts +174 -75
- package/src/fluidHandle.ts +7 -3
- package/src/localChannelContext.ts +18 -16
- package/src/localChannelStorageService.ts +6 -4
- package/src/packageVersion.ts +1 -1
- package/src/remoteChannelContext.ts +19 -19
package/src/dataStoreRuntime.ts
CHANGED
|
@@ -27,6 +27,8 @@ import {
|
|
|
27
27
|
IFluidDataStoreRuntime,
|
|
28
28
|
IFluidDataStoreRuntimeEvents,
|
|
29
29
|
type IDeltaManagerErased,
|
|
30
|
+
// eslint-disable-next-line import/no-deprecated
|
|
31
|
+
type IFluidDataStoreRuntimeExperimental,
|
|
30
32
|
} from "@fluidframework/datastore-definitions/internal";
|
|
31
33
|
import {
|
|
32
34
|
IClientDetails,
|
|
@@ -148,6 +150,22 @@ const defaultPolicies: IFluidDataStorePolicies = {
|
|
|
148
150
|
readonlyInStagingMode: true,
|
|
149
151
|
};
|
|
150
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Set up the boxed pendingOpCount value.
|
|
155
|
+
*/
|
|
156
|
+
function initializePendingOpCount(): { value: number } {
|
|
157
|
+
let value = 0;
|
|
158
|
+
return {
|
|
159
|
+
get value() {
|
|
160
|
+
return value;
|
|
161
|
+
},
|
|
162
|
+
set value(newValue: number) {
|
|
163
|
+
assert(newValue >= 0, 0xbbd /* pendingOpCount must be non-negative */);
|
|
164
|
+
value = newValue;
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
151
169
|
/**
|
|
152
170
|
* Base data store class
|
|
153
171
|
* @legacy
|
|
@@ -201,22 +219,25 @@ export class FluidDataStoreRuntime
|
|
|
201
219
|
return this.dataStoreContext.idCompressor;
|
|
202
220
|
}
|
|
203
221
|
|
|
204
|
-
|
|
222
|
+
// TODO: the methods below should have more specific return typing, per the interfaces they are implementing.
|
|
223
|
+
// Doing so would be a breaking change.
|
|
224
|
+
|
|
225
|
+
public get IFluidHandleContext(): this {
|
|
205
226
|
return this;
|
|
206
227
|
}
|
|
207
228
|
|
|
208
|
-
public get rootRoutingContext() {
|
|
229
|
+
public get rootRoutingContext(): this {
|
|
209
230
|
return this;
|
|
210
231
|
}
|
|
211
|
-
public get channelsRoutingContext() {
|
|
232
|
+
public get channelsRoutingContext(): this {
|
|
212
233
|
return this;
|
|
213
234
|
}
|
|
214
|
-
public get objectsRoutingContext() {
|
|
235
|
+
public get objectsRoutingContext(): this {
|
|
215
236
|
return this;
|
|
216
237
|
}
|
|
217
238
|
|
|
218
239
|
private _disposed = false;
|
|
219
|
-
public get disposed() {
|
|
240
|
+
public get disposed(): boolean {
|
|
220
241
|
return this._disposed;
|
|
221
242
|
}
|
|
222
243
|
|
|
@@ -234,6 +255,8 @@ export class FluidDataStoreRuntime
|
|
|
234
255
|
|
|
235
256
|
public readonly id: string;
|
|
236
257
|
|
|
258
|
+
// TODO: use something other than `any` here (breaking change)
|
|
259
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
237
260
|
public readonly options: Record<string | number, any>;
|
|
238
261
|
public readonly deltaManagerInternal: IDeltaManager<
|
|
239
262
|
ISequencedDocumentMessage,
|
|
@@ -337,10 +360,10 @@ export class FluidDataStoreRuntime
|
|
|
337
360
|
|
|
338
361
|
// Must always receive the data store type inside of the attributes
|
|
339
362
|
if (tree?.trees !== undefined) {
|
|
340
|
-
Object.entries(tree.trees)
|
|
363
|
+
for (const [path, subtree] of Object.entries(tree.trees)) {
|
|
341
364
|
// Issue #4414
|
|
342
365
|
if (path === "_search") {
|
|
343
|
-
|
|
366
|
+
continue;
|
|
344
367
|
}
|
|
345
368
|
|
|
346
369
|
let channelContext: RemoteChannelContext | RehydratedLocalChannelContext;
|
|
@@ -353,10 +376,10 @@ export class FluidDataStoreRuntime
|
|
|
353
376
|
// data store, if the data store is loaded after the container is attached, then we missed making
|
|
354
377
|
// the channel visible. So do it now. Otherwise, add it to local channel context queue, so
|
|
355
378
|
// that it can be make it visible later with the data store.
|
|
356
|
-
if (dataStoreContext.attachState
|
|
357
|
-
channelContext.makeVisible();
|
|
358
|
-
} else {
|
|
379
|
+
if (dataStoreContext.attachState === AttachState.Detached) {
|
|
359
380
|
this.localChannelContextQueue.set(path, channelContext);
|
|
381
|
+
} else {
|
|
382
|
+
channelContext.makeVisible();
|
|
360
383
|
}
|
|
361
384
|
} else {
|
|
362
385
|
channelContext = new RemoteChannelContext(
|
|
@@ -376,7 +399,7 @@ export class FluidDataStoreRuntime
|
|
|
376
399
|
}
|
|
377
400
|
|
|
378
401
|
this.contexts.set(path, channelContext);
|
|
379
|
-
}
|
|
402
|
+
}
|
|
380
403
|
}
|
|
381
404
|
|
|
382
405
|
this.entryPoint = new FluidObjectHandle<FluidObject>(
|
|
@@ -416,17 +439,30 @@ export class FluidDataStoreRuntime
|
|
|
416
439
|
this.localChangesTelemetryCount =
|
|
417
440
|
this.mc.config.getNumber("Fluid.Telemetry.LocalChangesTelemetryCount") ?? 10;
|
|
418
441
|
|
|
419
|
-
//
|
|
420
|
-
|
|
442
|
+
// Reference these properties to avoid unused private member errors.
|
|
443
|
+
// They're accessed via IFluidDataStoreRuntimeExperimental interface.
|
|
444
|
+
// eslint-disable-next-line no-void
|
|
445
|
+
void [this.inStagingMode, this.isDirty];
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Implementation of IFluidDataStoreRuntimeExperimental.inStagingMode
|
|
450
|
+
*/
|
|
451
|
+
// eslint-disable-next-line import/no-deprecated
|
|
452
|
+
private get inStagingMode(): IFluidDataStoreRuntimeExperimental["inStagingMode"] {
|
|
453
|
+
return (
|
|
421
454
|
// eslint-disable-next-line import/no-deprecated
|
|
422
|
-
this.dataStoreContext.containerRuntime
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
455
|
+
(this.dataStoreContext.containerRuntime as IContainerRuntimeBaseExperimental)
|
|
456
|
+
?.inStagingMode
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Implementation of IFluidDataStoreRuntimeExperimental.isDirty
|
|
462
|
+
*/
|
|
463
|
+
// eslint-disable-next-line import/no-deprecated
|
|
464
|
+
private get isDirty(): IFluidDataStoreRuntimeExperimental["isDirty"] {
|
|
465
|
+
return this.pendingOpCount.value > 0;
|
|
430
466
|
}
|
|
431
467
|
|
|
432
468
|
get deltaManager(): IDeltaManagerErased {
|
|
@@ -508,7 +544,7 @@ export class FluidDataStoreRuntime
|
|
|
508
544
|
* IDs cannot start with "_" as it could result in collision of IDs with auto-assigned (by FF) short IDs.
|
|
509
545
|
* @param id - channel ID.
|
|
510
546
|
*/
|
|
511
|
-
protected validateChannelId(id: string) {
|
|
547
|
+
protected validateChannelId(id: string): void {
|
|
512
548
|
if (id.includes("/")) {
|
|
513
549
|
throw new UsageError(`Id cannot contain slashes: ${id}`);
|
|
514
550
|
}
|
|
@@ -546,10 +582,7 @@ export class FluidDataStoreRuntime
|
|
|
546
582
|
public createChannel(idArg: string | undefined, type: string): IChannel {
|
|
547
583
|
let id: string;
|
|
548
584
|
|
|
549
|
-
if (idArg
|
|
550
|
-
id = idArg;
|
|
551
|
-
this.validateChannelId(id);
|
|
552
|
-
} else {
|
|
585
|
+
if (idArg === undefined) {
|
|
553
586
|
/**
|
|
554
587
|
* Return uuid if short-ids are explicitly disabled via feature flags.
|
|
555
588
|
*/
|
|
@@ -562,18 +595,21 @@ export class FluidDataStoreRuntime
|
|
|
562
595
|
// - uuids
|
|
563
596
|
// In first two cases we will encode result as strings in more compact form, with leading underscore,
|
|
564
597
|
// to ensure no overlap with user-provided DDS names (see validateChannelId())
|
|
565
|
-
if (this.visibilityState
|
|
566
|
-
// container is detached, only one client observes content, no way to hit collisions with other clients.
|
|
567
|
-
id = encodeCompactIdToString(2 * this.contexts.size, "_");
|
|
568
|
-
} else {
|
|
598
|
+
if (this.visibilityState === VisibilityState.GloballyVisible) {
|
|
569
599
|
// Due to back-compat, we could not depend yet on generateDocumentUniqueId() being there.
|
|
570
600
|
// We can remove the need to leverage uuid() as fall-back in couple releases.
|
|
571
601
|
const res =
|
|
572
602
|
this.dataStoreContext.containerRuntime.generateDocumentUniqueId?.() ?? uuid();
|
|
573
603
|
id = typeof res === "number" ? encodeCompactIdToString(2 * res + 1, "_") : res;
|
|
604
|
+
} else {
|
|
605
|
+
// container is detached, only one client observes content, no way to hit collisions with other clients.
|
|
606
|
+
id = encodeCompactIdToString(2 * this.contexts.size, "_");
|
|
574
607
|
}
|
|
575
608
|
}
|
|
576
609
|
assert(!id.includes("/"), 0x8fc /* slash */);
|
|
610
|
+
} else {
|
|
611
|
+
id = idArg;
|
|
612
|
+
this.validateChannelId(id);
|
|
577
613
|
}
|
|
578
614
|
|
|
579
615
|
this.verifyNotClosed();
|
|
@@ -592,7 +628,7 @@ export class FluidDataStoreRuntime
|
|
|
592
628
|
return channel;
|
|
593
629
|
}
|
|
594
630
|
|
|
595
|
-
private createChannelContext(channel: IChannel) {
|
|
631
|
+
private createChannelContext(channel: IChannel): void {
|
|
596
632
|
this.notBoundedChannelContextSet.add(channel.id);
|
|
597
633
|
const context = new LocalChannelContext(
|
|
598
634
|
channel,
|
|
@@ -610,7 +646,7 @@ export class FluidDataStoreRuntime
|
|
|
610
646
|
id: string,
|
|
611
647
|
tree: ISnapshotTree,
|
|
612
648
|
flatBlobs?: Map<string, ArrayBufferLike>,
|
|
613
|
-
) {
|
|
649
|
+
): RehydratedLocalChannelContext {
|
|
614
650
|
return new RehydratedLocalChannelContext(
|
|
615
651
|
id,
|
|
616
652
|
this.sharedObjectRegistry,
|
|
@@ -672,15 +708,15 @@ export class FluidDataStoreRuntime
|
|
|
672
708
|
* visible, it will mark us globally visible. Otherwise, it will mark us globally visible when it becomes
|
|
673
709
|
* globally visible.
|
|
674
710
|
*/
|
|
675
|
-
public makeVisibleAndAttachGraph() {
|
|
711
|
+
public makeVisibleAndAttachGraph(): void {
|
|
676
712
|
if (this.visibilityState !== VisibilityState.NotVisible) {
|
|
677
713
|
return;
|
|
678
714
|
}
|
|
679
715
|
this.visibilityState = VisibilityState.LocallyVisible;
|
|
680
716
|
|
|
681
|
-
this.pendingHandlesToMakeVisible
|
|
717
|
+
for (const handle of this.pendingHandlesToMakeVisible) {
|
|
682
718
|
handle.attachGraph();
|
|
683
|
-
}
|
|
719
|
+
}
|
|
684
720
|
this.pendingHandlesToMakeVisible.clear();
|
|
685
721
|
this.dataStoreContext.makeLocallyVisible();
|
|
686
722
|
}
|
|
@@ -688,7 +724,7 @@ export class FluidDataStoreRuntime
|
|
|
688
724
|
/**
|
|
689
725
|
* This function is called when a handle to this data store is added to a visible DDS.
|
|
690
726
|
*/
|
|
691
|
-
public attachGraph() {
|
|
727
|
+
public attachGraph(): void {
|
|
692
728
|
this.makeVisibleAndAttachGraph();
|
|
693
729
|
}
|
|
694
730
|
|
|
@@ -701,7 +737,7 @@ export class FluidDataStoreRuntime
|
|
|
701
737
|
this.pendingHandlesToMakeVisible.add(toFluidHandleInternal(handle));
|
|
702
738
|
}
|
|
703
739
|
|
|
704
|
-
public setConnectionState(connected: boolean, clientId?: string) {
|
|
740
|
+
public setConnectionState(connected: boolean, clientId?: string): void {
|
|
705
741
|
this.verifyNotClosed();
|
|
706
742
|
|
|
707
743
|
for (const [, object] of this.contexts) {
|
|
@@ -745,7 +781,7 @@ export class FluidDataStoreRuntime
|
|
|
745
781
|
private createRemoteChannelContext(
|
|
746
782
|
attachMessage: IAttachMessage,
|
|
747
783
|
summarizerNodeParams: CreateChildSummarizerNodeParam,
|
|
748
|
-
) {
|
|
784
|
+
): RemoteChannelContext {
|
|
749
785
|
const flatBlobs = new Map<string, ArrayBufferLike>();
|
|
750
786
|
const snapshotTree = buildSnapshotTree(attachMessage.snapshot.entries, flatBlobs);
|
|
751
787
|
|
|
@@ -773,7 +809,7 @@ export class FluidDataStoreRuntime
|
|
|
773
809
|
* store.
|
|
774
810
|
* @param messageCollection - The collection of messages to process.
|
|
775
811
|
*/
|
|
776
|
-
private processChannelMessages(messageCollection: IRuntimeMessageCollection) {
|
|
812
|
+
private processChannelMessages(messageCollection: IRuntimeMessageCollection): void {
|
|
777
813
|
this.verifyNotClosed();
|
|
778
814
|
|
|
779
815
|
/*
|
|
@@ -783,9 +819,9 @@ export class FluidDataStoreRuntime
|
|
|
783
819
|
*/
|
|
784
820
|
let currentAddress: string | undefined;
|
|
785
821
|
let currentMessagesContent: IRuntimeMessagesContent[] = [];
|
|
786
|
-
const { messagesContent, local } = messageCollection;
|
|
822
|
+
const { messagesContent, local, envelope } = messageCollection;
|
|
787
823
|
|
|
788
|
-
const sendBunchedMessages = () => {
|
|
824
|
+
const sendBunchedMessages = (): void => {
|
|
789
825
|
// Current address will be undefined for the first message in the list.
|
|
790
826
|
if (currentAddress === undefined) {
|
|
791
827
|
return;
|
|
@@ -796,7 +832,7 @@ export class FluidDataStoreRuntime
|
|
|
796
832
|
assert(!!channelContext, 0xa6b /* Channel context not found */);
|
|
797
833
|
|
|
798
834
|
channelContext.processMessages({
|
|
799
|
-
envelope
|
|
835
|
+
envelope,
|
|
800
836
|
messagesContent: currentMessagesContent,
|
|
801
837
|
local,
|
|
802
838
|
});
|
|
@@ -823,7 +859,7 @@ export class FluidDataStoreRuntime
|
|
|
823
859
|
sendBunchedMessages();
|
|
824
860
|
}
|
|
825
861
|
|
|
826
|
-
private processAttachMessages(messageCollection: IRuntimeMessageCollection) {
|
|
862
|
+
private processAttachMessages(messageCollection: IRuntimeMessageCollection): void {
|
|
827
863
|
const { envelope, messagesContent, local } = messageCollection;
|
|
828
864
|
for (const { contents } of messagesContent) {
|
|
829
865
|
const attachMessage = contents as IAttachMessage;
|
|
@@ -868,15 +904,22 @@ export class FluidDataStoreRuntime
|
|
|
868
904
|
public processMessages(messageCollection: IRuntimeMessageCollection): void {
|
|
869
905
|
this.verifyNotClosed();
|
|
870
906
|
|
|
871
|
-
const { envelope, messagesContent } = messageCollection;
|
|
907
|
+
const { envelope, local, messagesContent } = messageCollection;
|
|
908
|
+
|
|
909
|
+
if (local) {
|
|
910
|
+
this.pendingOpCount.value -= messagesContent.length;
|
|
911
|
+
}
|
|
912
|
+
|
|
872
913
|
try {
|
|
873
914
|
switch (envelope.type) {
|
|
874
|
-
case DataStoreMessageType.ChannelOp:
|
|
915
|
+
case DataStoreMessageType.ChannelOp: {
|
|
875
916
|
this.processChannelMessages(messageCollection);
|
|
876
917
|
break;
|
|
877
|
-
|
|
918
|
+
}
|
|
919
|
+
case DataStoreMessageType.Attach: {
|
|
878
920
|
this.processAttachMessages(messageCollection);
|
|
879
921
|
break;
|
|
922
|
+
}
|
|
880
923
|
default:
|
|
881
924
|
}
|
|
882
925
|
} catch (error) {
|
|
@@ -892,7 +935,7 @@ export class FluidDataStoreRuntime
|
|
|
892
935
|
}
|
|
893
936
|
}
|
|
894
937
|
|
|
895
|
-
public processSignal(message: IInboundSignalMessage, local: boolean) {
|
|
938
|
+
public processSignal(message: IInboundSignalMessage, local: boolean): void {
|
|
896
939
|
this.emit("signal", message, local);
|
|
897
940
|
}
|
|
898
941
|
|
|
@@ -929,7 +972,7 @@ export class FluidDataStoreRuntime
|
|
|
929
972
|
* - Adds a node for this channel.
|
|
930
973
|
* @param builder - The builder that contains the GC nodes for this channel's children.
|
|
931
974
|
*/
|
|
932
|
-
private updateGCNodes(builder: GCDataBuilder) {
|
|
975
|
+
private updateGCNodes(builder: GCDataBuilder): void {
|
|
933
976
|
// Add a back route to self in each child's GC nodes. If any child is referenced, then its parent should
|
|
934
977
|
// be considered referenced as well.
|
|
935
978
|
builder.addRouteToAllNodes(this.absolutePath);
|
|
@@ -993,7 +1036,7 @@ export class FluidDataStoreRuntime
|
|
|
993
1036
|
* update their used routes.
|
|
994
1037
|
* @param usedRoutes - The routes that are used in all contexts in this channel.
|
|
995
1038
|
*/
|
|
996
|
-
public updateUsedRoutes(usedRoutes: string[]) {
|
|
1039
|
+
public updateUsedRoutes(usedRoutes: string[]): void {
|
|
997
1040
|
// Get a map of channel ids to routes used in it.
|
|
998
1041
|
const usedContextRoutes = unpackChildNodesUsedRoutes(usedRoutes);
|
|
999
1042
|
|
|
@@ -1132,7 +1175,13 @@ export class FluidDataStoreRuntime
|
|
|
1132
1175
|
}
|
|
1133
1176
|
}
|
|
1134
1177
|
|
|
1135
|
-
public submitMessage(
|
|
1178
|
+
public submitMessage(
|
|
1179
|
+
type: DataStoreMessageType,
|
|
1180
|
+
// TODO: use something other than `any` here (breaking change)
|
|
1181
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
|
|
1182
|
+
content: any,
|
|
1183
|
+
localOpMetadata: unknown,
|
|
1184
|
+
): void {
|
|
1136
1185
|
this.submit(type, content, localOpMetadata);
|
|
1137
1186
|
}
|
|
1138
1187
|
|
|
@@ -1142,9 +1191,9 @@ export class FluidDataStoreRuntime
|
|
|
1142
1191
|
* @param content - Content of the signal. Should be a JSON serializable object or primitive.
|
|
1143
1192
|
* @param targetClientId - When specified, the signal is only sent to the provided client id.
|
|
1144
1193
|
*/
|
|
1145
|
-
public submitSignal(type: string, content: unknown, targetClientId?: string) {
|
|
1194
|
+
public submitSignal(type: string, content: unknown, targetClientId?: string): void {
|
|
1146
1195
|
this.verifyNotClosed();
|
|
1147
|
-
|
|
1196
|
+
this.dataStoreContext.submitSignal(type, content, targetClientId);
|
|
1148
1197
|
}
|
|
1149
1198
|
|
|
1150
1199
|
/**
|
|
@@ -1201,18 +1250,25 @@ export class FluidDataStoreRuntime
|
|
|
1201
1250
|
context.makeVisible();
|
|
1202
1251
|
}
|
|
1203
1252
|
|
|
1204
|
-
private submitChannelOp(address: string, contents:
|
|
1253
|
+
private submitChannelOp(address: string, contents: unknown, localOpMetadata: unknown): void {
|
|
1205
1254
|
const envelope: IEnvelope = { address, contents };
|
|
1206
1255
|
this.submit(DataStoreMessageType.ChannelOp, envelope, localOpMetadata);
|
|
1207
1256
|
}
|
|
1208
1257
|
|
|
1258
|
+
/**
|
|
1259
|
+
* Count of pending ops that have been submitted but not yet ack'd.
|
|
1260
|
+
* Used to compute {@link FluidDataStoreRuntime.isDirty}
|
|
1261
|
+
*/
|
|
1262
|
+
private readonly pendingOpCount: { value: number } = initializePendingOpCount();
|
|
1263
|
+
|
|
1209
1264
|
private submit(
|
|
1210
1265
|
type: DataStoreMessageType,
|
|
1211
|
-
content:
|
|
1266
|
+
content: unknown,
|
|
1212
1267
|
localOpMetadata: unknown = undefined,
|
|
1213
1268
|
): void {
|
|
1214
1269
|
this.verifyNotClosed();
|
|
1215
1270
|
this.dataStoreContext.submitMessage(type, content, localOpMetadata);
|
|
1271
|
+
++this.pendingOpCount.value;
|
|
1216
1272
|
}
|
|
1217
1273
|
|
|
1218
1274
|
/**
|
|
@@ -1224,27 +1280,36 @@ export class FluidDataStoreRuntime
|
|
|
1224
1280
|
*/
|
|
1225
1281
|
public reSubmit(
|
|
1226
1282
|
type: DataStoreMessageType,
|
|
1283
|
+
// TODO: use something other than `any` here (breaking change)
|
|
1284
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
|
|
1227
1285
|
content: any,
|
|
1228
1286
|
localOpMetadata: unknown,
|
|
1229
1287
|
squash?: boolean,
|
|
1230
|
-
) {
|
|
1288
|
+
): void {
|
|
1231
1289
|
this.verifyNotClosed();
|
|
1232
1290
|
|
|
1291
|
+
// The op being resubmitted was not / will not be submitted, so decrement the count.
|
|
1292
|
+
// The calls below may result in one or more ops submitted again, which will increment the count (or not if nothing needs to be submitted anymore).
|
|
1293
|
+
--this.pendingOpCount.value;
|
|
1294
|
+
|
|
1233
1295
|
switch (type) {
|
|
1234
1296
|
case DataStoreMessageType.ChannelOp: {
|
|
1235
1297
|
// For Operations, find the right channel and trigger resubmission on it.
|
|
1236
1298
|
const envelope = content as IEnvelope;
|
|
1237
1299
|
const channelContext = this.contexts.get(envelope.address);
|
|
1238
1300
|
assert(!!channelContext, 0x183 /* "There should be a channel context for the op" */);
|
|
1301
|
+
|
|
1239
1302
|
channelContext.reSubmit(envelope.contents, localOpMetadata, squash);
|
|
1240
1303
|
break;
|
|
1241
1304
|
}
|
|
1242
|
-
case DataStoreMessageType.Attach:
|
|
1305
|
+
case DataStoreMessageType.Attach: {
|
|
1243
1306
|
// For Attach messages, just submit them again.
|
|
1244
1307
|
this.submit(type, content, localOpMetadata);
|
|
1245
1308
|
break;
|
|
1246
|
-
|
|
1309
|
+
}
|
|
1310
|
+
default: {
|
|
1247
1311
|
unreachableCase(type);
|
|
1312
|
+
}
|
|
1248
1313
|
}
|
|
1249
1314
|
}
|
|
1250
1315
|
|
|
@@ -1253,27 +1318,46 @@ export class FluidDataStoreRuntime
|
|
|
1253
1318
|
* @param content - The content of the original message.
|
|
1254
1319
|
* @param localOpMetadata - The local metadata associated with the original message.
|
|
1255
1320
|
*/
|
|
1256
|
-
public rollback?(
|
|
1321
|
+
public rollback?(
|
|
1322
|
+
type: DataStoreMessageType,
|
|
1323
|
+
// TODO: use something other than `any` here (breaking change)
|
|
1324
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
|
|
1325
|
+
content: any,
|
|
1326
|
+
localOpMetadata: unknown,
|
|
1327
|
+
): void {
|
|
1257
1328
|
this.verifyNotClosed();
|
|
1258
1329
|
|
|
1330
|
+
// The op being rolled back was not/will not be submitted, so decrement the count.
|
|
1331
|
+
--this.pendingOpCount.value;
|
|
1332
|
+
|
|
1259
1333
|
switch (type) {
|
|
1260
1334
|
case DataStoreMessageType.ChannelOp: {
|
|
1261
1335
|
// For Operations, find the right channel and trigger resubmission on it.
|
|
1262
1336
|
const envelope = content as IEnvelope;
|
|
1263
1337
|
const channelContext = this.contexts.get(envelope.address);
|
|
1264
1338
|
assert(!!channelContext, 0x2ed /* "There should be a channel context for the op" */);
|
|
1339
|
+
|
|
1265
1340
|
channelContext.rollback(envelope.contents, localOpMetadata);
|
|
1266
1341
|
break;
|
|
1267
1342
|
}
|
|
1268
|
-
default:
|
|
1343
|
+
default: {
|
|
1269
1344
|
throw new LoggingError(`Can't rollback ${type} message`);
|
|
1345
|
+
}
|
|
1270
1346
|
}
|
|
1271
1347
|
}
|
|
1272
1348
|
|
|
1349
|
+
// TODO: use something other than `any` here
|
|
1350
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
|
|
1273
1351
|
public async applyStashedOp(content: any): Promise<unknown> {
|
|
1352
|
+
// The op being applied may have been submitted in a previous session, so we increment the count here.
|
|
1353
|
+
// Either the ack will arrive and be processed, or that previous session's connection will end, at which point the op will be resubmitted.
|
|
1354
|
+
++this.pendingOpCount.value;
|
|
1355
|
+
|
|
1356
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
1274
1357
|
const type = content?.type as DataStoreMessageType;
|
|
1275
1358
|
switch (type) {
|
|
1276
1359
|
case DataStoreMessageType.Attach: {
|
|
1360
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
1277
1361
|
const attachMessage = content.content as IAttachMessage;
|
|
1278
1362
|
|
|
1279
1363
|
const flatBlobs = new Map<string, ArrayBufferLike>();
|
|
@@ -1295,39 +1379,50 @@ export class FluidDataStoreRuntime
|
|
|
1295
1379
|
return;
|
|
1296
1380
|
}
|
|
1297
1381
|
case DataStoreMessageType.ChannelOp: {
|
|
1382
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
1298
1383
|
const envelope = content.content as IEnvelope;
|
|
1299
1384
|
const channelContext = this.contexts.get(envelope.address);
|
|
1300
1385
|
assert(!!channelContext, 0x184 /* "There should be a channel context for the op" */);
|
|
1301
1386
|
await channelContext.getChannel();
|
|
1302
1387
|
return channelContext.applyStashedOp(envelope.contents);
|
|
1303
1388
|
}
|
|
1304
|
-
default:
|
|
1389
|
+
default: {
|
|
1305
1390
|
unreachableCase(type);
|
|
1391
|
+
}
|
|
1306
1392
|
}
|
|
1307
1393
|
}
|
|
1308
1394
|
|
|
1395
|
+
/**
|
|
1396
|
+
* Indicates the given channel is dirty from Summarizer's point of view,
|
|
1397
|
+
* i.e. it has local changes that need to be included in the summary.
|
|
1398
|
+
*
|
|
1399
|
+
* @remarks - If a channel's changes are rolled back or rebased away, we won't
|
|
1400
|
+
* clear the dirty flag set here.
|
|
1401
|
+
*/
|
|
1309
1402
|
private setChannelDirty(address: string): void {
|
|
1310
1403
|
this.verifyNotClosed();
|
|
1311
1404
|
this.dataStoreContext.setChannelDirty(address);
|
|
1312
1405
|
}
|
|
1313
1406
|
|
|
1314
|
-
private attachListener() {
|
|
1407
|
+
private attachListener(): void {
|
|
1315
1408
|
this.setMaxListeners(Number.MAX_SAFE_INTEGER);
|
|
1316
1409
|
|
|
1317
1410
|
// back-compat, to be removed in the future.
|
|
1318
1411
|
// Added in "2.0.0-rc.2.0.0" timeframe.
|
|
1412
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
|
|
1319
1413
|
(this.dataStoreContext as any).once?.("attaching", () => {
|
|
1320
1414
|
this.setAttachState(AttachState.Attaching);
|
|
1321
1415
|
});
|
|
1322
1416
|
|
|
1323
1417
|
// back-compat, to be removed in the future.
|
|
1324
1418
|
// Added in "2.0.0-rc.2.0.0" timeframe.
|
|
1419
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
|
|
1325
1420
|
(this.dataStoreContext as any).once?.("attached", () => {
|
|
1326
1421
|
this.setAttachState(AttachState.Attached);
|
|
1327
1422
|
});
|
|
1328
1423
|
}
|
|
1329
1424
|
|
|
1330
|
-
private verifyNotClosed() {
|
|
1425
|
+
private verifyNotClosed(): void {
|
|
1331
1426
|
if (this._disposed) {
|
|
1332
1427
|
throw new LoggingError("Runtime is closed");
|
|
1333
1428
|
}
|
|
@@ -1342,7 +1437,7 @@ export class FluidDataStoreRuntime
|
|
|
1342
1437
|
eventName: string,
|
|
1343
1438
|
channelId: string,
|
|
1344
1439
|
channelType: string,
|
|
1345
|
-
) {
|
|
1440
|
+
): void {
|
|
1346
1441
|
if (this.clientDetails.type !== "summarizer" || this.localChangesTelemetryCount <= 0) {
|
|
1347
1442
|
return;
|
|
1348
1443
|
}
|
|
@@ -1365,7 +1460,7 @@ export class FluidDataStoreRuntime
|
|
|
1365
1460
|
|
|
1366
1461
|
public setAttachState(attachState: AttachState.Attaching | AttachState.Attached): void {
|
|
1367
1462
|
switch (attachState) {
|
|
1368
|
-
case AttachState.Attaching:
|
|
1463
|
+
case AttachState.Attaching: {
|
|
1369
1464
|
/**
|
|
1370
1465
|
* back-compat 0.59.1000 - Ideally, attachGraph() should have already been called making the data store
|
|
1371
1466
|
* locally visible. However, before visibility state was added, this may not have been the case and data
|
|
@@ -1386,16 +1481,17 @@ export class FluidDataStoreRuntime
|
|
|
1386
1481
|
|
|
1387
1482
|
// Mark the data store globally visible and make its child channels visible as well.
|
|
1388
1483
|
this.visibilityState = VisibilityState.GloballyVisible;
|
|
1389
|
-
this.localChannelContextQueue
|
|
1484
|
+
for (const [, channel] of this.localChannelContextQueue) {
|
|
1390
1485
|
channel.makeVisible();
|
|
1391
|
-
}
|
|
1486
|
+
}
|
|
1392
1487
|
this.localChannelContextQueue.clear();
|
|
1393
1488
|
|
|
1394
1489
|
// This promise resolution will be moved to attached event once we fix the scheduler.
|
|
1395
1490
|
this.deferredAttached.resolve();
|
|
1396
1491
|
this.emit("attaching");
|
|
1397
1492
|
break;
|
|
1398
|
-
|
|
1493
|
+
}
|
|
1494
|
+
case AttachState.Attached: {
|
|
1399
1495
|
assert(
|
|
1400
1496
|
this.visibilityState === VisibilityState.GloballyVisible,
|
|
1401
1497
|
0x2d2 /* "Data store should be globally visible when its attached." */,
|
|
@@ -1403,8 +1499,10 @@ export class FluidDataStoreRuntime
|
|
|
1403
1499
|
this._attachState = AttachState.Attached;
|
|
1404
1500
|
this.emit("attached");
|
|
1405
1501
|
break;
|
|
1406
|
-
|
|
1502
|
+
}
|
|
1503
|
+
default: {
|
|
1407
1504
|
unreachableCase(attachState, "unreached");
|
|
1505
|
+
}
|
|
1408
1506
|
}
|
|
1409
1507
|
}
|
|
1410
1508
|
}
|
|
@@ -1420,9 +1518,9 @@ export class FluidDataStoreRuntime
|
|
|
1420
1518
|
export const mixinRequestHandler = (
|
|
1421
1519
|
requestHandler: (request: IRequest, runtime: FluidDataStoreRuntime) => Promise<IResponse>,
|
|
1422
1520
|
Base: typeof FluidDataStoreRuntime = FluidDataStoreRuntime,
|
|
1423
|
-
) =>
|
|
1521
|
+
): typeof FluidDataStoreRuntime =>
|
|
1424
1522
|
class RuntimeWithRequestHandler extends Base {
|
|
1425
|
-
public async request(request: IRequest) {
|
|
1523
|
+
public async request(request: IRequest): Promise<IResponse> {
|
|
1426
1524
|
const response = await super.request(request);
|
|
1427
1525
|
if (response.status === 404) {
|
|
1428
1526
|
return requestHandler(request, this);
|
|
@@ -1444,9 +1542,9 @@ export const mixinSummaryHandler = (
|
|
|
1444
1542
|
runtime: FluidDataStoreRuntime,
|
|
1445
1543
|
) => Promise<{ path: string[]; content: string } | undefined>,
|
|
1446
1544
|
Base: typeof FluidDataStoreRuntime = FluidDataStoreRuntime,
|
|
1447
|
-
) =>
|
|
1545
|
+
): typeof FluidDataStoreRuntime =>
|
|
1448
1546
|
class RuntimeWithSummarizerHandler extends Base {
|
|
1449
|
-
private addBlob(summary: ISummaryTreeWithStats, path: string[], content: string) {
|
|
1547
|
+
private addBlob(summary: ISummaryTreeWithStats, path: string[], content: string): void {
|
|
1450
1548
|
const firstName = path.shift();
|
|
1451
1549
|
if (firstName === undefined) {
|
|
1452
1550
|
throw new LoggingError("Path can't be empty");
|
|
@@ -1469,7 +1567,8 @@ export const mixinSummaryHandler = (
|
|
|
1469
1567
|
summary.summary.tree[firstName] = blob;
|
|
1470
1568
|
}
|
|
1471
1569
|
|
|
1472
|
-
async summarize(...args: any[]) {
|
|
1570
|
+
async summarize(...args: any[]): Promise<ISummaryTreeWithStats> {
|
|
1571
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
1473
1572
|
const summary = await super.summarize(...args);
|
|
1474
1573
|
|
|
1475
1574
|
try {
|
|
@@ -1477,9 +1576,9 @@ export const mixinSummaryHandler = (
|
|
|
1477
1576
|
if (content !== undefined) {
|
|
1478
1577
|
this.addBlob(summary, content.path, content.content);
|
|
1479
1578
|
}
|
|
1480
|
-
} catch (
|
|
1579
|
+
} catch (error) {
|
|
1481
1580
|
// Any error coming from app-provided handler should be marked as DataProcessingError
|
|
1482
|
-
throw DataProcessingError.wrapIfUnrecognized(
|
|
1581
|
+
throw DataProcessingError.wrapIfUnrecognized(error, "mixinSummaryHandler");
|
|
1483
1582
|
}
|
|
1484
1583
|
|
|
1485
1584
|
return summary;
|
package/src/fluidHandle.ts
CHANGED
|
@@ -77,6 +77,9 @@ export class FluidObjectHandle<
|
|
|
77
77
|
/**
|
|
78
78
|
* {@inheritDoc @fluidframework/core-interfaces#IFluidHandle.get}
|
|
79
79
|
*/
|
|
80
|
+
// TODO: Return `Promise<T>` instead of `Promise<any>`.
|
|
81
|
+
// This was clearly the intended typing of this API, but fixing it would be a breaking change.
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
80
83
|
public async get(): Promise<any> {
|
|
81
84
|
// Note that this return works whether we received a T or a Promise<T> for this.value in the constructor.
|
|
82
85
|
return this.value;
|
|
@@ -91,18 +94,19 @@ export class FluidObjectHandle<
|
|
|
91
94
|
}
|
|
92
95
|
|
|
93
96
|
this.locallyVisible = true;
|
|
94
|
-
this.pendingHandlesToMakeVisible
|
|
97
|
+
for (const handle of this.pendingHandlesToMakeVisible) {
|
|
95
98
|
handle.attachGraph();
|
|
96
|
-
}
|
|
99
|
+
}
|
|
97
100
|
this.pendingHandlesToMakeVisible.clear();
|
|
98
101
|
this.routeContext.attachGraph();
|
|
99
102
|
}
|
|
100
103
|
|
|
104
|
+
// eslint-disable-next-line jsdoc/require-description
|
|
101
105
|
/**
|
|
102
106
|
* @deprecated No replacement provided. Arbitrary handles may not serve as a bind source.
|
|
103
107
|
* @privateRemarks This implementation will be moved to SharedObjectHandle once this is removed.
|
|
104
108
|
*/
|
|
105
|
-
public bind(handle: IFluidHandleInternal) {
|
|
109
|
+
public bind(handle: IFluidHandleInternal): void {
|
|
106
110
|
// If this handle is visible, attach the graph of the incoming handle as well.
|
|
107
111
|
if (this.visible) {
|
|
108
112
|
handle.attachGraph();
|