@fluidframework/container-loader 2.0.0-internal.1.1.2 → 2.0.0-internal.1.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.
Files changed (92) hide show
  1. package/dist/catchUpMonitor.d.ts +6 -17
  2. package/dist/catchUpMonitor.d.ts.map +1 -1
  3. package/dist/catchUpMonitor.js +5 -36
  4. package/dist/catchUpMonitor.js.map +1 -1
  5. package/dist/collabWindowTracker.d.ts +1 -1
  6. package/dist/collabWindowTracker.d.ts.map +1 -1
  7. package/dist/collabWindowTracker.js +2 -1
  8. package/dist/collabWindowTracker.js.map +1 -1
  9. package/dist/connectionManager.d.ts +1 -1
  10. package/dist/connectionManager.d.ts.map +1 -1
  11. package/dist/connectionManager.js +8 -11
  12. package/dist/connectionManager.js.map +1 -1
  13. package/dist/connectionStateHandler.d.ts +80 -26
  14. package/dist/connectionStateHandler.d.ts.map +1 -1
  15. package/dist/connectionStateHandler.js +170 -89
  16. package/dist/connectionStateHandler.js.map +1 -1
  17. package/dist/container.d.ts +22 -11
  18. package/dist/container.d.ts.map +1 -1
  19. package/dist/container.js +130 -142
  20. package/dist/container.js.map +1 -1
  21. package/dist/containerContext.d.ts +18 -7
  22. package/dist/containerContext.d.ts.map +1 -1
  23. package/dist/containerContext.js +18 -8
  24. package/dist/containerContext.js.map +1 -1
  25. package/dist/containerStorageAdapter.d.ts +10 -24
  26. package/dist/containerStorageAdapter.d.ts.map +1 -1
  27. package/dist/containerStorageAdapter.js +50 -16
  28. package/dist/containerStorageAdapter.js.map +1 -1
  29. package/dist/deltaManager.d.ts +1 -1
  30. package/dist/deltaManager.d.ts.map +1 -1
  31. package/dist/deltaManager.js +18 -6
  32. package/dist/deltaManager.js.map +1 -1
  33. package/dist/loader.d.ts +1 -1
  34. package/dist/loader.js.map +1 -1
  35. package/dist/packageVersion.d.ts +1 -1
  36. package/dist/packageVersion.js +1 -1
  37. package/dist/packageVersion.js.map +1 -1
  38. package/dist/protocol.d.ts.map +1 -1
  39. package/dist/protocol.js +2 -1
  40. package/dist/protocol.js.map +1 -1
  41. package/lib/catchUpMonitor.d.ts +6 -17
  42. package/lib/catchUpMonitor.d.ts.map +1 -1
  43. package/lib/catchUpMonitor.js +5 -35
  44. package/lib/catchUpMonitor.js.map +1 -1
  45. package/lib/collabWindowTracker.d.ts +1 -1
  46. package/lib/collabWindowTracker.d.ts.map +1 -1
  47. package/lib/collabWindowTracker.js +3 -2
  48. package/lib/collabWindowTracker.js.map +1 -1
  49. package/lib/connectionManager.d.ts +1 -1
  50. package/lib/connectionManager.d.ts.map +1 -1
  51. package/lib/connectionManager.js +9 -14
  52. package/lib/connectionManager.js.map +1 -1
  53. package/lib/connectionStateHandler.d.ts +80 -26
  54. package/lib/connectionStateHandler.d.ts.map +1 -1
  55. package/lib/connectionStateHandler.js +170 -90
  56. package/lib/connectionStateHandler.js.map +1 -1
  57. package/lib/container.d.ts +22 -11
  58. package/lib/container.d.ts.map +1 -1
  59. package/lib/container.js +133 -145
  60. package/lib/container.js.map +1 -1
  61. package/lib/containerContext.d.ts +18 -7
  62. package/lib/containerContext.d.ts.map +1 -1
  63. package/lib/containerContext.js +19 -9
  64. package/lib/containerContext.js.map +1 -1
  65. package/lib/containerStorageAdapter.d.ts +10 -24
  66. package/lib/containerStorageAdapter.d.ts.map +1 -1
  67. package/lib/containerStorageAdapter.js +50 -15
  68. package/lib/containerStorageAdapter.js.map +1 -1
  69. package/lib/deltaManager.d.ts +1 -1
  70. package/lib/deltaManager.d.ts.map +1 -1
  71. package/lib/deltaManager.js +18 -6
  72. package/lib/deltaManager.js.map +1 -1
  73. package/lib/loader.d.ts +1 -1
  74. package/lib/loader.js.map +1 -1
  75. package/lib/packageVersion.d.ts +1 -1
  76. package/lib/packageVersion.js +1 -1
  77. package/lib/packageVersion.js.map +1 -1
  78. package/lib/protocol.d.ts.map +1 -1
  79. package/lib/protocol.js +2 -1
  80. package/lib/protocol.js.map +1 -1
  81. package/package.json +13 -13
  82. package/src/catchUpMonitor.ts +7 -47
  83. package/src/collabWindowTracker.ts +4 -3
  84. package/src/connectionManager.ts +9 -11
  85. package/src/connectionStateHandler.ts +231 -106
  86. package/src/container.ts +156 -168
  87. package/src/containerContext.ts +22 -8
  88. package/src/containerStorageAdapter.ts +64 -15
  89. package/src/deltaManager.ts +20 -7
  90. package/src/loader.ts +1 -1
  91. package/src/packageVersion.ts +1 -1
  92. package/src/protocol.ts +2 -1
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
- import { assert, LazyPromise } from "@fluidframework/common-utils";
7
+ import { LazyPromise } from "@fluidframework/common-utils";
8
8
  import {
9
9
  IAudience,
10
10
  IContainerContext,
@@ -22,6 +22,7 @@ import {
22
22
  ICodeDetailsLoader,
23
23
  IFluidModuleWithDetails,
24
24
  ISnapshotTreeWithBlobContents,
25
+ IBatchMessage,
25
26
  } from "@fluidframework/container-definitions";
26
27
  import {
27
28
  IRequest,
@@ -42,6 +43,7 @@ import {
42
43
  ISummaryTree,
43
44
  IVersion,
44
45
  MessageType,
46
+ ISummaryContent,
45
47
  } from "@fluidframework/protocol-definitions";
46
48
  import { PerformanceEvent } from "@fluidframework/telemetry-utils";
47
49
  import { Container } from "./container";
@@ -59,6 +61,8 @@ export class ContainerContext implements IContainerContext {
59
61
  quorum: IQuorum,
60
62
  loader: ILoader,
61
63
  submitFn: (type: MessageType, contents: any, batch: boolean, appData: any) => number,
64
+ submitSummaryFn: (summaryOp: ISummaryContent) => number,
65
+ submitBatchFn: (batch: IBatchMessage[]) => number,
62
66
  submitSignalFn: (contents: any) => void,
63
67
  closeFn: (error?: ICriticalContainerError) => void,
64
68
  version: string,
@@ -76,6 +80,8 @@ export class ContainerContext implements IContainerContext {
76
80
  quorum,
77
81
  loader,
78
82
  submitFn,
83
+ submitSummaryFn,
84
+ submitBatchFn,
79
85
  submitSignalFn,
80
86
  closeFn,
81
87
  version,
@@ -107,8 +113,13 @@ export class ContainerContext implements IContainerContext {
107
113
  return this.container.clientDetails;
108
114
  }
109
115
 
116
+ private _connected: boolean;
117
+ /**
118
+ * When true, ops are free to flow
119
+ * When false, ops should be kept as pending or rejected
120
+ */
110
121
  public get connected(): boolean {
111
- return this.container.connected;
122
+ return this._connected;
112
123
  }
113
124
 
114
125
  public get canSummarize(): boolean {
@@ -166,6 +177,9 @@ export class ContainerContext implements IContainerContext {
166
177
  quorum: IQuorum,
167
178
  public readonly loader: ILoader,
168
179
  public readonly submitFn: (type: MessageType, contents: any, batch: boolean, appData: any) => number,
180
+ public readonly submitSummaryFn: (summaryOp: ISummaryContent) => number,
181
+ /** @returns clientSequenceNumber of last message in a batch */
182
+ public readonly submitBatchFn: (batch: IBatchMessage[]) => number,
169
183
  public readonly submitSignalFn: (contents: any) => void,
170
184
  public readonly closeFn: (error?: ICriticalContainerError) => void,
171
185
  public readonly version: string,
@@ -174,6 +188,7 @@ export class ContainerContext implements IContainerContext {
174
188
  public readonly pendingLocalState?: unknown,
175
189
 
176
190
  ) {
191
+ this._connected = this.container.connected;
177
192
  this._quorum = quorum;
178
193
  this.taggedLogger = container.subLogger;
179
194
  this._fluidModuleP = new LazyPromise<IFluidModuleWithDetails>(
@@ -183,9 +198,10 @@ export class ContainerContext implements IContainerContext {
183
198
  }
184
199
 
185
200
  /**
186
- * @deprecated - Temporary migratory API, to be removed when customers no longer need it. When removed,
187
- * ContainerContext should only take an IQuorumClients rather than an IQuorum. See IContainerContext for more
188
- * details.
201
+ * @deprecated Temporary migratory API, to be removed when customers no longer need it.
202
+ * When removed, `ContainerContext` should only take an {@link @fluidframework/container-definitions#IQuorumClients}
203
+ * rather than an {@link @fluidframework/protocol-definitions#IQuorum}.
204
+ * See {@link @fluidframework/container-definitions#IContainerContext} for more details.
189
205
  */
190
206
  public getSpecifiedCodeDetails(): IFluidCodeDetails | undefined {
191
207
  return (this._quorum.get("code") ?? this._quorum.get("code2")) as IFluidCodeDetails | undefined;
@@ -223,9 +239,7 @@ export class ContainerContext implements IContainerContext {
223
239
 
224
240
  public setConnectionState(connected: boolean, clientId?: string) {
225
241
  const runtime = this.runtime;
226
-
227
- assert(connected === this.connected, 0x0de /* "Mismatch in connection state while setting" */);
228
-
242
+ this._connected = connected;
229
243
  runtime.setConnectionState(connected, clientId);
230
244
  }
231
245
 
@@ -3,14 +3,17 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { ITelemetryLogger } from "@fluidframework/common-definitions";
6
+ import { IDisposable, ITelemetryLogger } from "@fluidframework/common-definitions";
7
+ import { assert } from "@fluidframework/common-utils";
7
8
  import { ISnapshotTreeWithBlobContents } from "@fluidframework/container-definitions";
8
9
  import {
9
10
  FetchSource,
11
+ IDocumentService,
10
12
  IDocumentStorageService,
11
13
  IDocumentStorageServicePolicies,
12
14
  ISummaryContext,
13
15
  } from "@fluidframework/driver-definitions";
16
+ import { UsageError } from "@fluidframework/driver-utils";
14
17
  import {
15
18
  ICreateBlobResponse,
16
19
  ISnapshotTree,
@@ -19,14 +22,52 @@ import {
19
22
  IVersion,
20
23
  } from "@fluidframework/protocol-definitions";
21
24
  import { IDetachedBlobStorage } from "./loader";
25
+ import { ProtocolTreeStorageService } from "./protocolTreeDocumentStorageService";
26
+ import { RetriableDocumentStorageService } from "./retriableDocumentStorageService";
22
27
 
23
28
  /**
24
29
  * This class wraps the actual storage and make sure no wrong apis are called according to
25
30
  * container attach state.
26
31
  */
27
- export class ContainerStorageAdapter implements IDocumentStorageService {
32
+ export class ContainerStorageAdapter implements IDocumentStorageService, IDisposable {
28
33
  private readonly blobContents: { [id: string]: ArrayBufferLike; } = {};
29
- constructor(private readonly storageGetter: () => IDocumentStorageService) {}
34
+ private _storageService: IDocumentStorageService & Partial<IDisposable>;
35
+
36
+ constructor(
37
+ detachedBlobStorage: IDetachedBlobStorage | undefined,
38
+ private readonly logger: ITelemetryLogger,
39
+ private readonly captureProtocolSummary?: () => ISummaryTree,
40
+ ) {
41
+ this._storageService = new BlobOnlyStorage(detachedBlobStorage, logger);
42
+ }
43
+
44
+ disposed: boolean = false;
45
+ dispose(error?: Error): void {
46
+ this._storageService?.dispose?.(error);
47
+ this.disposed = true;
48
+ }
49
+
50
+ public async connectToService(service: IDocumentService): Promise<void> {
51
+ if (!(this._storageService instanceof BlobOnlyStorage)) {
52
+ return;
53
+ }
54
+
55
+ const storageService = await service.connectToStorage();
56
+ const retriableStorage = this._storageService =
57
+ new RetriableDocumentStorageService(
58
+ storageService,
59
+ this.logger);
60
+
61
+ if (this.captureProtocolSummary !== undefined) {
62
+ this.logger.sendTelemetryEvent({ eventName: "summarizeProtocolTreeEnabled" });
63
+ this._storageService =
64
+ new ProtocolTreeStorageService(retriableStorage, this.captureProtocolSummary);
65
+ }
66
+
67
+ // ensure we did not lose that policy in the process of wrapping
68
+ assert(storageService.policies?.minBlobSize === this._storageService.policies?.minBlobSize,
69
+ 0x0e0 /* "lost minBlobSize policy" */);
70
+ }
30
71
 
31
72
  public loadSnapshotForRehydratingContainer(snapshotTree: ISnapshotTreeWithBlobContents) {
32
73
  this.getBlobContents(snapshotTree);
@@ -45,17 +86,17 @@ export class ContainerStorageAdapter implements IDocumentStorageService {
45
86
  // back-compat 0.40 containerRuntime requests policies even in detached container if storage is present
46
87
  // and storage is always present in >=0.41.
47
88
  try {
48
- return this.storageGetter().policies;
89
+ return this._storageService.policies;
49
90
  } catch (e) {}
50
91
  return undefined;
51
92
  }
52
93
 
53
94
  public get repositoryUrl(): string {
54
- return this.storageGetter().repositoryUrl;
95
+ return this._storageService.repositoryUrl;
55
96
  }
56
97
 
57
98
  public async getSnapshotTree(version?: IVersion, scenarioName?: string): Promise<ISnapshotTree | null> {
58
- return this.storageGetter().getSnapshotTree(version, scenarioName);
99
+ return this._storageService.getSnapshotTree(version, scenarioName);
59
100
  }
60
101
 
61
102
  public async readBlob(id: string): Promise<ArrayBufferLike> {
@@ -63,7 +104,7 @@ export class ContainerStorageAdapter implements IDocumentStorageService {
63
104
  if (blob !== undefined) {
64
105
  return blob;
65
106
  }
66
- return this.storageGetter().readBlob(id);
107
+ return this._storageService.readBlob(id);
67
108
  }
68
109
 
69
110
  public async getVersions(
@@ -72,19 +113,19 @@ export class ContainerStorageAdapter implements IDocumentStorageService {
72
113
  scenarioName?: string,
73
114
  fetchSource?: FetchSource,
74
115
  ): Promise<IVersion[]> {
75
- return this.storageGetter().getVersions(versionId, count, scenarioName, fetchSource);
116
+ return this._storageService.getVersions(versionId, count, scenarioName, fetchSource);
76
117
  }
77
118
 
78
119
  public async uploadSummaryWithContext(summary: ISummaryTree, context: ISummaryContext): Promise<string> {
79
- return this.storageGetter().uploadSummaryWithContext(summary, context);
120
+ return this._storageService.uploadSummaryWithContext(summary, context);
80
121
  }
81
122
 
82
123
  public async downloadSummary(handle: ISummaryHandle): Promise<ISummaryTree> {
83
- return this.storageGetter().downloadSummary(handle);
124
+ return this._storageService.downloadSummary(handle);
84
125
  }
85
126
 
86
127
  public async createBlob(file: ArrayBufferLike): Promise<ICreateBlobResponse> {
87
- return this.storageGetter().createBlob(file);
128
+ return this._storageService.createBlob(file);
88
129
  }
89
130
  }
90
131
 
@@ -92,18 +133,25 @@ export class ContainerStorageAdapter implements IDocumentStorageService {
92
133
  * Storage which only supports createBlob() and readBlob(). This is used with IDetachedBlobStorage to support
93
134
  * blobs in detached containers.
94
135
  */
95
- export class BlobOnlyStorage implements IDocumentStorageService {
136
+ class BlobOnlyStorage implements IDocumentStorageService {
96
137
  constructor(
97
- private readonly blobStorage: IDetachedBlobStorage,
138
+ private readonly detachedStorage: IDetachedBlobStorage | undefined,
98
139
  private readonly logger: ITelemetryLogger,
99
140
  ) { }
100
141
 
101
142
  public async createBlob(content: ArrayBufferLike): Promise<ICreateBlobResponse> {
102
- return this.blobStorage.createBlob(content);
143
+ return this.verifyStorage().createBlob(content);
103
144
  }
104
145
 
105
146
  public async readBlob(blobId: string): Promise<ArrayBufferLike> {
106
- return this.blobStorage.readBlob(blobId);
147
+ return this.verifyStorage().readBlob(blobId);
148
+ }
149
+
150
+ private verifyStorage(): IDetachedBlobStorage {
151
+ if (this.detachedStorage === undefined) {
152
+ throw new UsageError("Real storage calls not allowed in Unattached container");
153
+ }
154
+ return this.detachedStorage;
107
155
  }
108
156
 
109
157
  public get policies(): IDocumentStorageServicePolicies | undefined {
@@ -123,6 +171,7 @@ export class BlobOnlyStorage implements IDocumentStorageService {
123
171
  /* eslint-enable @typescript-eslint/unbound-method */
124
172
 
125
173
  private notCalled(): never {
174
+ this.verifyStorage();
126
175
  try {
127
176
  // some browsers may not populate stack unless exception is thrown
128
177
  throw new Error("BlobOnlyStorage not implemented method used");
@@ -199,12 +199,12 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
199
199
  public get readOnlyInfo() { return this.connectionManager.readOnlyInfo; }
200
200
  public get clientDetails() { return this.connectionManager.clientDetails; }
201
201
 
202
- public submit(type: MessageType, contents: any, batch = false, metadata?: any) {
202
+ public submit(type: MessageType, contents?: string, batch = false, metadata?: any) {
203
203
  if (this.currentlyProcessingOps && this.preventConcurrentOpSend) {
204
204
  this.close(new UsageError("Making changes to data model is disallowed while processing ops."));
205
205
  }
206
206
  const messagePartial: Omit<IDocumentMessage, "clientSequenceNumber"> = {
207
- contents: JSON.stringify(contents),
207
+ contents,
208
208
  metadata,
209
209
  referenceSequenceNumber: this.lastProcessedSequenceNumber,
210
210
  type,
@@ -218,7 +218,9 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
218
218
  return -1;
219
219
  }
220
220
 
221
- this.opsSize += message.contents.length;
221
+ if (contents !== undefined) {
222
+ this.opsSize += contents.length;
223
+ }
222
224
 
223
225
  this.messageBuffer.push(message);
224
226
 
@@ -233,15 +235,26 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
233
235
  public submitSignal(content: any) { return this.connectionManager.submitSignal(content); }
234
236
 
235
237
  public flush() {
236
- if (this.messageBuffer.length === 0) {
238
+ const batch = this.messageBuffer;
239
+ if (batch.length === 0) {
237
240
  return;
238
241
  }
239
242
 
243
+ this.messageBuffer = [];
244
+
240
245
  // The prepareFlush event allows listeners to append metadata to the batch prior to submission.
241
- this.emit("prepareSend", this.messageBuffer);
246
+ this.emit("prepareSend", batch);
242
247
 
243
- this.connectionManager.sendMessages(this.messageBuffer);
244
- this.messageBuffer = [];
248
+ if (batch.length === 1) {
249
+ assert(batch[0].metadata?.batch === undefined, 0x3c9 /* no batch markup on single message */);
250
+ } else {
251
+ assert(batch[0].metadata?.batch === true, 0x3ca /* no start batch markup */);
252
+ assert(batch[batch.length - 1].metadata?.batch === false, 0x3cb /* no end batch markup */);
253
+ }
254
+
255
+ this.connectionManager.sendMessages(batch);
256
+
257
+ assert(this.messageBuffer.length === 0, 0x3cc /* reentrancy */);
245
258
  }
246
259
 
247
260
  public get connectionProps(): ITelemetryProperties {
package/src/loader.ts CHANGED
@@ -133,7 +133,7 @@ export interface ILoaderOptions extends ILoaderOptions1 {
133
133
 
134
134
  /**
135
135
  * @deprecated IFluidModuleWithDetails interface is moved to
136
- * {@link @fluidframework/container-definition#IFluidModuleWithDetails}
136
+ * {@link @fluidframework/container-definitions#IFluidModuleWithDetails}
137
137
  * to have all the code loading modules in one package. #8193
138
138
  * Encapsulates a module entry point with corresponding code details.
139
139
  */
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-loader";
9
- export const pkgVersion = "2.0.0-internal.1.1.2";
9
+ export const pkgVersion = "2.0.0-internal.1.2.0";
package/src/protocol.ts CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  ISignalMessage,
19
19
  MessageType,
20
20
  } from "@fluidframework/protocol-definitions";
21
+ import { canBeCoalescedByService } from "@fluidframework/driver-utils";
21
22
 
22
23
  /**
23
24
  * Function to be used for creating a protocol handler.
@@ -68,7 +69,7 @@ export class ProtocolHandler extends ProtocolOpHandler implements IProtocolHandl
68
69
  throw new Error("Remote message's clientId is missing from the quorum");
69
70
  }
70
71
 
71
- if (client?.shouldHaveLeft === true && message.type !== MessageType.NoOp) {
72
+ if (client?.shouldHaveLeft === true && !canBeCoalescedByService(message)) {
72
73
  // pre-0.58 error message: messageClientIdShouldHaveLeft
73
74
  throw new Error("Remote message's clientId already should have left");
74
75
  }