@fluidframework/container-runtime 2.0.0-internal.2.2.1 → 2.0.0-internal.2.3.1

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 (195) hide show
  1. package/.eslintrc.js +19 -8
  2. package/dist/batchTracker.d.ts +1 -2
  3. package/dist/batchTracker.d.ts.map +1 -1
  4. package/dist/batchTracker.js.map +1 -1
  5. package/dist/blobManager.d.ts +45 -34
  6. package/dist/blobManager.d.ts.map +1 -1
  7. package/dist/blobManager.js +135 -102
  8. package/dist/blobManager.js.map +1 -1
  9. package/dist/containerRuntime.d.ts +54 -8
  10. package/dist/containerRuntime.d.ts.map +1 -1
  11. package/dist/containerRuntime.js +143 -72
  12. package/dist/containerRuntime.js.map +1 -1
  13. package/dist/dataStoreContext.d.ts +1 -1
  14. package/dist/dataStoreContext.d.ts.map +1 -1
  15. package/dist/dataStoreContext.js +6 -8
  16. package/dist/dataStoreContext.js.map +1 -1
  17. package/dist/dataStores.d.ts +12 -9
  18. package/dist/dataStores.d.ts.map +1 -1
  19. package/dist/dataStores.js +41 -35
  20. package/dist/dataStores.js.map +1 -1
  21. package/dist/garbageCollection.d.ts +41 -20
  22. package/dist/garbageCollection.d.ts.map +1 -1
  23. package/dist/garbageCollection.js +205 -150
  24. package/dist/garbageCollection.js.map +1 -1
  25. package/dist/garbageCollectionConstants.d.ts +7 -3
  26. package/dist/garbageCollectionConstants.d.ts.map +1 -1
  27. package/dist/garbageCollectionConstants.js +10 -8
  28. package/dist/garbageCollectionConstants.js.map +1 -1
  29. package/dist/garbageCollectionTombstoneUtils.d.ts +14 -0
  30. package/dist/garbageCollectionTombstoneUtils.d.ts.map +1 -0
  31. package/dist/garbageCollectionTombstoneUtils.js +23 -0
  32. package/dist/garbageCollectionTombstoneUtils.js.map +1 -0
  33. package/dist/index.d.ts +1 -2
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +3 -5
  36. package/dist/index.js.map +1 -1
  37. package/dist/opLifecycle/batchManager.d.ts +13 -1
  38. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  39. package/dist/opLifecycle/batchManager.js +35 -1
  40. package/dist/opLifecycle/batchManager.js.map +1 -1
  41. package/dist/opLifecycle/definitions.d.ts +25 -1
  42. package/dist/opLifecycle/definitions.d.ts.map +1 -1
  43. package/dist/opLifecycle/definitions.js.map +1 -1
  44. package/dist/opLifecycle/index.d.ts +2 -2
  45. package/dist/opLifecycle/index.d.ts.map +1 -1
  46. package/dist/opLifecycle/index.js +2 -1
  47. package/dist/opLifecycle/index.js.map +1 -1
  48. package/dist/opLifecycle/opCompressor.d.ts +1 -1
  49. package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
  50. package/dist/opLifecycle/opCompressor.js +24 -10
  51. package/dist/opLifecycle/opCompressor.js.map +1 -1
  52. package/dist/opLifecycle/opDecompressor.d.ts +2 -1
  53. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
  54. package/dist/opLifecycle/opDecompressor.js +30 -17
  55. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  56. package/dist/opLifecycle/opSplitter.d.ts +34 -2
  57. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  58. package/dist/opLifecycle/opSplitter.js +114 -5
  59. package/dist/opLifecycle/opSplitter.js.map +1 -1
  60. package/dist/opLifecycle/outbox.d.ts +5 -0
  61. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  62. package/dist/opLifecycle/outbox.js +24 -14
  63. package/dist/opLifecycle/outbox.js.map +1 -1
  64. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  65. package/dist/opLifecycle/remoteMessageProcessor.js +17 -2
  66. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  67. package/dist/packageVersion.d.ts +1 -1
  68. package/dist/packageVersion.js +1 -1
  69. package/dist/packageVersion.js.map +1 -1
  70. package/dist/runningSummarizer.d.ts.map +1 -1
  71. package/dist/runningSummarizer.js +0 -1
  72. package/dist/runningSummarizer.js.map +1 -1
  73. package/dist/scheduleManager.d.ts +0 -1
  74. package/dist/scheduleManager.d.ts.map +1 -1
  75. package/dist/scheduleManager.js +9 -20
  76. package/dist/scheduleManager.js.map +1 -1
  77. package/dist/summarizer.d.ts +0 -1
  78. package/dist/summarizer.d.ts.map +1 -1
  79. package/dist/summarizer.js +2 -1
  80. package/dist/summarizer.js.map +1 -1
  81. package/dist/summarizerTypes.d.ts +1 -0
  82. package/dist/summarizerTypes.d.ts.map +1 -1
  83. package/dist/summarizerTypes.js.map +1 -1
  84. package/dist/summaryFormat.d.ts.map +1 -1
  85. package/dist/summaryFormat.js +1 -2
  86. package/dist/summaryFormat.js.map +1 -1
  87. package/lib/batchTracker.d.ts +1 -2
  88. package/lib/batchTracker.d.ts.map +1 -1
  89. package/lib/batchTracker.js.map +1 -1
  90. package/lib/blobManager.d.ts +45 -34
  91. package/lib/blobManager.d.ts.map +1 -1
  92. package/lib/blobManager.js +137 -104
  93. package/lib/blobManager.js.map +1 -1
  94. package/lib/containerRuntime.d.ts +54 -8
  95. package/lib/containerRuntime.d.ts.map +1 -1
  96. package/lib/containerRuntime.js +140 -69
  97. package/lib/containerRuntime.js.map +1 -1
  98. package/lib/dataStoreContext.d.ts +1 -1
  99. package/lib/dataStoreContext.d.ts.map +1 -1
  100. package/lib/dataStoreContext.js +7 -9
  101. package/lib/dataStoreContext.js.map +1 -1
  102. package/lib/dataStores.d.ts +12 -9
  103. package/lib/dataStores.d.ts.map +1 -1
  104. package/lib/dataStores.js +44 -38
  105. package/lib/dataStores.js.map +1 -1
  106. package/lib/garbageCollection.d.ts +41 -20
  107. package/lib/garbageCollection.d.ts.map +1 -1
  108. package/lib/garbageCollection.js +201 -146
  109. package/lib/garbageCollection.js.map +1 -1
  110. package/lib/garbageCollectionConstants.d.ts +7 -3
  111. package/lib/garbageCollectionConstants.d.ts.map +1 -1
  112. package/lib/garbageCollectionConstants.js +9 -7
  113. package/lib/garbageCollectionConstants.js.map +1 -1
  114. package/lib/garbageCollectionTombstoneUtils.d.ts +14 -0
  115. package/lib/garbageCollectionTombstoneUtils.d.ts.map +1 -0
  116. package/lib/garbageCollectionTombstoneUtils.js +19 -0
  117. package/lib/garbageCollectionTombstoneUtils.js.map +1 -0
  118. package/lib/index.d.ts +1 -2
  119. package/lib/index.d.ts.map +1 -1
  120. package/lib/index.js +1 -2
  121. package/lib/index.js.map +1 -1
  122. package/lib/opLifecycle/batchManager.d.ts +13 -1
  123. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  124. package/lib/opLifecycle/batchManager.js +35 -1
  125. package/lib/opLifecycle/batchManager.js.map +1 -1
  126. package/lib/opLifecycle/definitions.d.ts +25 -1
  127. package/lib/opLifecycle/definitions.d.ts.map +1 -1
  128. package/lib/opLifecycle/definitions.js.map +1 -1
  129. package/lib/opLifecycle/index.d.ts +2 -2
  130. package/lib/opLifecycle/index.d.ts.map +1 -1
  131. package/lib/opLifecycle/index.js +1 -1
  132. package/lib/opLifecycle/index.js.map +1 -1
  133. package/lib/opLifecycle/opCompressor.d.ts +1 -1
  134. package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
  135. package/lib/opLifecycle/opCompressor.js +24 -10
  136. package/lib/opLifecycle/opCompressor.js.map +1 -1
  137. package/lib/opLifecycle/opDecompressor.d.ts +2 -1
  138. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
  139. package/lib/opLifecycle/opDecompressor.js +30 -17
  140. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  141. package/lib/opLifecycle/opSplitter.d.ts +34 -2
  142. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  143. package/lib/opLifecycle/opSplitter.js +112 -4
  144. package/lib/opLifecycle/opSplitter.js.map +1 -1
  145. package/lib/opLifecycle/outbox.d.ts +5 -0
  146. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  147. package/lib/opLifecycle/outbox.js +24 -14
  148. package/lib/opLifecycle/outbox.js.map +1 -1
  149. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  150. package/lib/opLifecycle/remoteMessageProcessor.js +17 -2
  151. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  152. package/lib/packageVersion.d.ts +1 -1
  153. package/lib/packageVersion.js +1 -1
  154. package/lib/packageVersion.js.map +1 -1
  155. package/lib/runningSummarizer.d.ts.map +1 -1
  156. package/lib/runningSummarizer.js +0 -1
  157. package/lib/runningSummarizer.js.map +1 -1
  158. package/lib/scheduleManager.d.ts +0 -1
  159. package/lib/scheduleManager.d.ts.map +1 -1
  160. package/lib/scheduleManager.js +9 -20
  161. package/lib/scheduleManager.js.map +1 -1
  162. package/lib/summarizer.d.ts +0 -1
  163. package/lib/summarizer.d.ts.map +1 -1
  164. package/lib/summarizer.js +2 -1
  165. package/lib/summarizer.js.map +1 -1
  166. package/lib/summarizerTypes.d.ts +1 -0
  167. package/lib/summarizerTypes.d.ts.map +1 -1
  168. package/lib/summarizerTypes.js.map +1 -1
  169. package/lib/summaryFormat.d.ts.map +1 -1
  170. package/lib/summaryFormat.js +1 -2
  171. package/lib/summaryFormat.js.map +1 -1
  172. package/package.json +20 -19
  173. package/src/batchTracker.ts +1 -1
  174. package/src/blobManager.ts +159 -111
  175. package/src/containerRuntime.ts +202 -73
  176. package/src/dataStoreContext.ts +15 -16
  177. package/src/dataStores.ts +61 -45
  178. package/src/garbageCollection.ts +258 -183
  179. package/src/garbageCollectionConstants.ts +10 -7
  180. package/src/garbageCollectionTombstoneUtils.ts +28 -0
  181. package/src/index.ts +2 -5
  182. package/src/opLifecycle/batchManager.ts +59 -1
  183. package/src/opLifecycle/definitions.ts +27 -1
  184. package/src/opLifecycle/index.ts +2 -1
  185. package/src/opLifecycle/opCompressor.ts +29 -12
  186. package/src/opLifecycle/opDecompressor.ts +39 -18
  187. package/src/opLifecycle/opSplitter.ts +141 -7
  188. package/src/opLifecycle/outbox.ts +32 -16
  189. package/src/opLifecycle/remoteMessageProcessor.ts +19 -3
  190. package/src/packageVersion.ts +1 -1
  191. package/src/runningSummarizer.ts +0 -1
  192. package/src/scheduleManager.ts +19 -30
  193. package/src/summarizer.ts +1 -1
  194. package/src/summarizerTypes.ts +1 -0
  195. package/src/summaryFormat.ts +1 -2
@@ -3,6 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
+ import { ITelemetryLogger } from "@fluidframework/common-definitions";
6
7
  import { assert } from "@fluidframework/common-utils";
7
8
  import { IContainerContext } from "@fluidframework/container-definitions";
8
9
  import { GenericError } from "@fluidframework/container-utils";
@@ -12,11 +13,13 @@ import { PendingStateManager } from "../pendingStateManager";
12
13
  import { BatchManager } from "./batchManager";
13
14
  import { BatchMessage, IBatch } from "./definitions";
14
15
  import { OpCompressor } from "./opCompressor";
16
+ import { OpSplitter } from "./opSplitter";
15
17
 
16
18
  export interface IOutboxConfig {
17
19
  readonly compressionOptions: ICompressionRuntimeOptions;
18
20
  // The maximum size of a batch that we can send over the wire.
19
21
  readonly maxBatchSizeInBytes: number;
22
+ readonly enableOpReentryCheck?: boolean;
20
23
  };
21
24
 
22
25
  export interface IOutboxParameters {
@@ -25,6 +28,8 @@ export interface IOutboxParameters {
25
28
  readonly containerContext: IContainerContext,
26
29
  readonly config: IOutboxConfig,
27
30
  readonly compressor: OpCompressor;
31
+ readonly splitter: OpSplitter;
32
+ readonly logger: ITelemetryLogger;
28
33
  }
29
34
 
30
35
  export class Outbox {
@@ -41,10 +46,12 @@ export class Outbox {
41
46
  this.attachFlowBatch = new BatchManager({
42
47
  hardLimit,
43
48
  softLimit,
44
- });
49
+ enableOpReentryCheck: params.config.enableOpReentryCheck,
50
+ }, params.logger);
45
51
  this.mainBatch = new BatchManager({
46
- hardLimit
47
- });
52
+ hardLimit,
53
+ enableOpReentryCheck: params.config.enableOpReentryCheck,
54
+ }, params.logger);
48
55
  }
49
56
 
50
57
  public get isEmpty(): boolean {
@@ -113,20 +120,25 @@ export class Outbox {
113
120
  }
114
121
 
115
122
  const compressedBatch = this.params.compressor.compressBatch(batch);
116
- if (compressedBatch.contentSizeInBytes > this.params.config.maxBatchSizeInBytes) {
117
- throw new GenericError(
118
- "BatchTooLarge",
119
- /* error */ undefined,
120
- {
121
- opSize: batch.contentSizeInBytes,
122
- count: batch.content.length,
123
- limit: this.params.config.maxBatchSizeInBytes,
124
- compressed: true,
125
- });
123
+ if (compressedBatch.contentSizeInBytes <= this.params.config.maxBatchSizeInBytes) {
124
+ // If we don't reach the maximum supported size of a batch, it can safely be sent as is
125
+ return compressedBatch;
126
+ }
127
+
128
+ if (this.params.splitter.isBatchChunkingEnabled) {
129
+ return this.params.splitter.splitCompressedBatch(compressedBatch);
126
130
  }
127
131
 
128
- // If we don't reach the maximum supported size of a batch, it safe to be sent as is
129
- return compressedBatch;
132
+ // If we've reached this point, the runtime would attempt to send a batch larger than the allowed size
133
+ throw new GenericError(
134
+ "BatchTooLarge",
135
+ /* error */ undefined,
136
+ {
137
+ opSize: batch.contentSizeInBytes,
138
+ count: batch.content.length,
139
+ limit: this.params.config.maxBatchSizeInBytes,
140
+ compressed: true,
141
+ });
130
142
  }
131
143
 
132
144
  /**
@@ -166,7 +178,11 @@ export class Outbox {
166
178
  } else {
167
179
  // returns clientSequenceNumber of last message in a batch
168
180
  clientSequenceNumber = this.params.containerContext.submitBatchFn(
169
- batch.content.map((message) => ({ contents: message.contents, metadata: message.metadata })));
181
+ batch.content.map((message) => ({
182
+ contents: message.contents,
183
+ metadata: message.metadata,
184
+ compression: message.compression,
185
+ })));
170
186
  }
171
187
 
172
188
  // Convert from clientSequenceNumber of last message in the batch to clientSequenceNumber of first message.
@@ -24,11 +24,27 @@ export class RemoteMessageProcessor {
24
24
 
25
25
  public process(remoteMessage: ISequencedDocumentMessage): ISequencedDocumentMessage {
26
26
  let message = copy(remoteMessage);
27
-
28
- message = this.opDecompressor.processMessage(message);
27
+ message = this.opDecompressor.processMessage(message).message;
29
28
  unpackRuntimeMessage(message);
30
- message = this.opSplitter.processRemoteMessage(message);
31
29
 
30
+ const chunkProcessingResult = this.opSplitter.processRemoteMessage(message);
31
+ message = chunkProcessingResult.message;
32
+ if (chunkProcessingResult.state !== "Processed") {
33
+ // If the message is not chunked or if the splitter is still rebuilding the original message,
34
+ // there is no need to continue processing
35
+ return message;
36
+ }
37
+
38
+ const decompressionAfterChunking = this.opDecompressor.processMessage(message);
39
+ message = decompressionAfterChunking.message;
40
+ if (decompressionAfterChunking.state === "Skipped") {
41
+ // After chunking, if the original message was not compressed,
42
+ // there is no need to continue processing
43
+ return message;
44
+ }
45
+
46
+ // The message needs to be unpacked after chunking + decompression
47
+ unpack(message);
32
48
  return message;
33
49
  }
34
50
  }
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-runtime";
9
- export const pkgVersion = "2.0.0-internal.2.2.1";
9
+ export const pkgVersion = "2.0.0-internal.2.3.1";
@@ -279,7 +279,6 @@ export class RunningSummarizer implements IDisposable {
279
279
  }
280
280
 
281
281
  private nonRuntimeOpCanTriggerSummary(): boolean {
282
- // eslint-disable-next-line max-len
283
282
  const opsSinceLastAck = this.heuristicData.lastOpSequenceNumber - this.heuristicData.lastSuccessfulSummary.refSequenceNumber;
284
283
  return this.configuration.state === "enabled"
285
284
  && (this.configuration.nonRuntimeHeuristicThreshold === undefined
@@ -16,7 +16,6 @@ import {
16
16
  } from "@fluidframework/container-utils";
17
17
  import { DeltaScheduler } from "./deltaScheduler";
18
18
  import { pkgVersion } from "./packageVersion";
19
- import { latencyThreshold } from "./connectionTelemetry";
20
19
 
21
20
  type IRuntimeMessageMetadata = undefined | {
22
21
  batch?: boolean;
@@ -212,16 +211,6 @@ class ScheduleManagerCore {
212
211
 
213
212
  this.localPaused = false;
214
213
 
215
- // Random round number - we want to know when batch waiting paused op processing.
216
- if (duration !== undefined && duration > latencyThreshold) {
217
- this.logger.sendErrorEvent({
218
- eventName: "MaxBatchWaitTimeExceeded",
219
- duration,
220
- sequenceNumber: endBatch,
221
- length: endBatch - startBatch,
222
- });
223
- }
224
-
225
214
  this.deltaManager.inbound.resume();
226
215
  }
227
216
 
@@ -236,6 +225,8 @@ class ScheduleManagerCore {
236
225
  0x299 /* "non-synchronized state" */);
237
226
 
238
227
  const metadata = message.metadata as IRuntimeMessageMetadata;
228
+ // batchMetadata will be true for the message that starts a batch, false for the one that ends it, and
229
+ // undefined for all other messages.
239
230
  const batchMetadata = metadata?.batch;
240
231
 
241
232
  // Protocol messages are never part of a runtime batch of messages
@@ -265,30 +256,28 @@ class ScheduleManagerCore {
265
256
  return;
266
257
  }
267
258
 
268
- // If the client ID changes then we can move the pause point. If it stayed the same then we need to check.
269
- // If batchMetadata is not undefined then if it's true we've begun a new batch - if false we've ended
270
- // the previous one
271
- if (this.currentBatchClientId !== undefined || batchMetadata === false) {
272
- if (this.currentBatchClientId !== message.clientId) {
273
- // "Batch not closed, yet message from another client!"
274
- throw new DataCorruptionError(
275
- "OpBatchIncomplete",
276
- {
277
- runtimeVersion: pkgVersion,
278
- batchClientId: this.currentBatchClientId,
279
- pauseSequenceNumber: this.pauseSequenceNumber,
280
- localBatch: this.currentBatchClientId === this.getClientId(),
281
- localMessage: message.clientId === this.getClientId(),
282
- ...extractSafePropertiesFromMessage(message),
283
- });
284
- }
259
+ // If we got here, the message is part of a batch. Either starting, in progress, or ending.
260
+
261
+ // If this is not the start of the batch, error out if the message was sent by a client other than the one that
262
+ // started the current batch (it should not be possible for ops from other clients to get interleaved with a batch).
263
+ if (this.currentBatchClientId !== undefined && this.currentBatchClientId !== message.clientId) {
264
+ throw new DataCorruptionError(
265
+ "OpBatchIncomplete",
266
+ {
267
+ runtimeVersion: pkgVersion,
268
+ batchClientId: this.currentBatchClientId,
269
+ pauseSequenceNumber: this.pauseSequenceNumber,
270
+ localBatch: this.currentBatchClientId === this.getClientId(),
271
+ localMessage: message.clientId === this.getClientId(),
272
+ ...extractSafePropertiesFromMessage(message),
273
+ });
285
274
  }
286
275
 
287
276
  // The queue is
288
277
  // 1. paused only when the next message to be processed is the beginning of a batch. Done in two places:
289
278
  // - in afterOpProcessing() - processing ops until reaching start of incomplete batch
290
- // - here (batchMetadata == false below), when queue was empty and start of batch showed up.
291
- // 2. resumed when batch end comes in (batchMetadata === true case below)
279
+ // - here, when queue was empty and start of batch showed up (batchMetadata === true below).
280
+ // 2. resumed when batch end comes in (batchMetadata === false below)
292
281
 
293
282
  if (batchMetadata) {
294
283
  assert(this.currentBatchClientId === undefined, 0x29e /* "there can't be active batch" */);
package/src/summarizer.ts CHANGED
@@ -160,7 +160,7 @@ export class Summarizer extends EventEmitter implements ISummarizer {
160
160
  // This will result in "summarizerClientDisconnected" stop reason recorded in telemetry,
161
161
  // unless stop() was called earlier
162
162
  this.dispose();
163
- this.runtime.closeFn();
163
+ (this.runtime.disposeFn ?? this.runtime.closeFn)()
164
164
  }
165
165
 
166
166
  private async runCore(onBehalfOf: string): Promise<SummarizerStopReason> {
@@ -94,6 +94,7 @@ export interface ISummarizerRuntime extends IConnectableRuntime {
94
94
  readonly logger: ITelemetryLogger;
95
95
  /** clientId of parent (non-summarizing) container that owns summarizer container */
96
96
  readonly summarizerClientId: string | undefined;
97
+ disposeFn?(): void;
97
98
  closeFn(): void;
98
99
  /** @deprecated 1.0, please remove all implementations and usage */
99
100
  on(event: "batchEnd", listener: (error: any, op: ISequencedDocumentMessage) => void): this;
@@ -7,8 +7,7 @@ import { assert } from "@fluidframework/common-utils";
7
7
  import { IDocumentStorageService } from "@fluidframework/driver-definitions";
8
8
  import { readAndParse } from "@fluidframework/driver-utils";
9
9
  import { ISequencedDocumentMessage, ISnapshotTree, SummaryType } from "@fluidframework/protocol-definitions";
10
- import { channelsTreeName, ISummaryTreeWithStats } from "@fluidframework/runtime-definitions";
11
- import { gcTreeKey } from "./garbageCollectionConstants";
10
+ import { channelsTreeName, gcTreeKey, ISummaryTreeWithStats } from "@fluidframework/runtime-definitions";
12
11
 
13
12
  type OmitAttributesVersions<T> = Omit<T, "snapshotFormatVersion" | "summaryFormatVersion">;
14
13
  interface IFluidDataStoreAttributes0 {