@fluidframework/container-runtime 2.0.0-internal.7.1.0 → 2.0.0-internal.7.1.2
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/api-report/container-runtime.api.md +2 -1
- package/dist/blobManager.d.ts +3 -6
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +17 -42
- package/dist/blobManager.js.map +1 -1
- package/dist/container-runtime-alpha.d.ts +4 -4
- package/dist/container-runtime-beta.d.ts +4 -4
- package/dist/container-runtime-public.d.ts +4 -4
- package/dist/container-runtime.d.ts +4 -4
- package/dist/containerRuntime.d.ts +5 -4
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +33 -8
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +1 -0
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +39 -34
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +0 -16
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +0 -48
- package/dist/dataStores.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +12 -3
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +41 -18
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcConfigs.d.ts +1 -0
- package/dist/gc/gcConfigs.d.ts.map +1 -1
- package/dist/gc/gcConfigs.js +15 -2
- package/dist/gc/gcConfigs.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +28 -9
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js +8 -3
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +12 -6
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +91 -47
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/gc/index.d.ts +2 -2
- package/dist/gc/index.d.ts.map +1 -1
- package/dist/gc/index.js +3 -5
- package/dist/gc/index.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts +10 -2
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +23 -3
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +0 -1
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +2 -1
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/lib/blobManager.d.ts +3 -6
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +18 -43
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +5 -4
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +34 -9
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +1 -0
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +40 -35
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +0 -16
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +2 -50
- package/lib/dataStores.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +12 -3
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +42 -19
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcConfigs.d.ts +1 -0
- package/lib/gc/gcConfigs.d.ts.map +1 -1
- package/lib/gc/gcConfigs.js +17 -4
- package/lib/gc/gcConfigs.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +28 -9
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js +7 -2
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +12 -6
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +92 -48
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/gc/index.d.ts +2 -2
- package/lib/gc/index.d.ts.map +1 -1
- package/lib/gc/index.js +2 -2
- package/lib/gc/index.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts +10 -2
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +23 -3
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +0 -1
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +2 -1
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/package.json +21 -17
- package/src/blobManager.ts +18 -58
- package/src/containerRuntime.ts +50 -18
- package/src/dataStoreContext.ts +17 -8
- package/src/dataStores.ts +2 -80
- package/src/gc/garbageCollection.ts +53 -24
- package/src/gc/gcConfigs.ts +28 -4
- package/src/gc/gcDefinitions.ts +32 -9
- package/src/gc/gcTelemetry.ts +123 -65
- package/src/gc/index.ts +2 -4
- package/src/opLifecycle/opGroupingManager.ts +37 -2
- package/src/opLifecycle/outbox.ts +4 -2
- package/src/packageVersion.ts +1 -1
package/src/gc/gcTelemetry.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
MonitoringContext,
|
|
12
12
|
tagCodeArtifacts,
|
|
13
13
|
} from "@fluidframework/telemetry-utils";
|
|
14
|
+
import { RuntimeHeaderData } from "../containerRuntime";
|
|
14
15
|
import { ICreateContainerMetadata } from "../summary";
|
|
15
16
|
import {
|
|
16
17
|
disableSweepLogKey,
|
|
@@ -19,8 +20,9 @@ import {
|
|
|
19
20
|
IGarbageCollectorConfigs,
|
|
20
21
|
disableTombstoneKey,
|
|
21
22
|
throwOnTombstoneUsageKey,
|
|
22
|
-
|
|
23
|
+
throwOnTombstoneLoadOverrideKey,
|
|
23
24
|
runSweepKey,
|
|
25
|
+
GCFeatureMatrix,
|
|
24
26
|
} from "./gcDefinitions";
|
|
25
27
|
import { UnreferencedStateTracker } from "./gcUnreferencedStateTracker";
|
|
26
28
|
|
|
@@ -32,7 +34,7 @@ interface ICommonProps {
|
|
|
32
34
|
completedGCRuns: number;
|
|
33
35
|
isTombstoned: boolean;
|
|
34
36
|
lastSummaryTime?: number;
|
|
35
|
-
|
|
37
|
+
headers?: RuntimeHeaderData;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
/** The event that is logged when unreferenced node is used after a certain time. */
|
|
@@ -45,6 +47,10 @@ interface IUnreferencedEventProps extends ICreateContainerMetadata, ICommonProps
|
|
|
45
47
|
type: GCNodeType;
|
|
46
48
|
unrefTime: number;
|
|
47
49
|
age: number;
|
|
50
|
+
// Expanding GC feature matrix. Without doing this, the configs cannot be logged in telemetry directly.
|
|
51
|
+
gcConfigs: Omit<IGarbageCollectorConfigs, "persistedGcFeatureMatrix"> & {
|
|
52
|
+
[K in keyof GCFeatureMatrix]: GCFeatureMatrix[K];
|
|
53
|
+
};
|
|
48
54
|
timeout?: number;
|
|
49
55
|
fromId?: {
|
|
50
56
|
value: string;
|
|
@@ -78,12 +84,8 @@ export class GCTelemetryTracker {
|
|
|
78
84
|
|
|
79
85
|
constructor(
|
|
80
86
|
private readonly mc: MonitoringContext,
|
|
81
|
-
private readonly configs:
|
|
82
|
-
IGarbageCollectorConfigs,
|
|
83
|
-
"inactiveTimeoutMs" | "sweepTimeoutMs"
|
|
84
|
-
>,
|
|
87
|
+
private readonly configs: IGarbageCollectorConfigs,
|
|
85
88
|
private readonly isSummarizerClient: boolean,
|
|
86
|
-
private readonly gcTombstoneEnforcementAllowed: boolean,
|
|
87
89
|
private readonly createContainerMetadata: ICreateContainerMetadata,
|
|
88
90
|
private readonly getNodeType: (nodeId: string) => GCNodeType,
|
|
89
91
|
private readonly getNodeStateTracker: (
|
|
@@ -95,12 +97,12 @@ export class GCTelemetryTracker {
|
|
|
95
97
|
) {}
|
|
96
98
|
|
|
97
99
|
/**
|
|
98
|
-
* Returns whether an event should be logged for a node that isn't active anymore.
|
|
99
|
-
*
|
|
100
|
+
* Returns whether an event should be logged for a node that isn't active anymore. This does not apply to
|
|
101
|
+
* tombstoned nodes for which an event is always logged. Some scenarios where we won't log:
|
|
102
|
+
* 1. When a DDS is changed. The corresponding data store's event will be logged instead.
|
|
100
103
|
* 2. An event is logged only once per container instance per event per node.
|
|
101
104
|
*/
|
|
102
105
|
private shouldLogNonActiveEvent(
|
|
103
|
-
nodeId: string,
|
|
104
106
|
nodeType: GCNodeType,
|
|
105
107
|
usageType: NodeUsageType,
|
|
106
108
|
nodeStateTracker: UnreferencedStateTracker,
|
|
@@ -119,6 +121,8 @@ export class GCTelemetryTracker {
|
|
|
119
121
|
return false;
|
|
120
122
|
}
|
|
121
123
|
|
|
124
|
+
// Non-tombstone events are logged once per event per node. A unique id is generated by joining
|
|
125
|
+
// node state (inactive / sweep ready), node's id and usage (loaded / changed / revived).
|
|
122
126
|
if (this.loggedUnreferencedEvents.has(uniqueEventId)) {
|
|
123
127
|
return false;
|
|
124
128
|
}
|
|
@@ -126,24 +130,63 @@ export class GCTelemetryTracker {
|
|
|
126
130
|
}
|
|
127
131
|
|
|
128
132
|
/**
|
|
129
|
-
* Called when a node is used. If the node is not active, log
|
|
133
|
+
* Called when a node is used. If the node is not active or tombstoned, log telemetry indicating object is used
|
|
134
|
+
* when it should not have been.
|
|
130
135
|
*/
|
|
131
136
|
public nodeUsed(nodeUsageProps: INodeUsageProps) {
|
|
132
137
|
// If there is no reference timestamp to work with, no ops have been processed after creation. If so, skip
|
|
133
138
|
// logging as nothing interesting would have happened worth logging.
|
|
134
|
-
|
|
135
|
-
const nodeStateTracker = this.getNodeStateTracker(nodeUsageProps.id);
|
|
136
|
-
if (!nodeStateTracker || nodeUsageProps.currentReferenceTimestampMs === undefined) {
|
|
139
|
+
if (nodeUsageProps.currentReferenceTimestampMs === undefined) {
|
|
137
140
|
return;
|
|
138
141
|
}
|
|
139
142
|
|
|
140
|
-
|
|
141
|
-
// node's id and usage (loaded / changed / revived).
|
|
142
|
-
const uniqueEventId = `${nodeStateTracker.state}-${nodeUsageProps.id}-${nodeUsageProps.usageType}`;
|
|
143
|
+
const nodeStateTracker = this.getNodeStateTracker(nodeUsageProps.id);
|
|
143
144
|
const nodeType = this.getNodeType(nodeUsageProps.id);
|
|
145
|
+
const {
|
|
146
|
+
usageType,
|
|
147
|
+
currentReferenceTimestampMs,
|
|
148
|
+
packagePath,
|
|
149
|
+
id: untaggedId,
|
|
150
|
+
fromId: untaggedFromId,
|
|
151
|
+
...propsToLog
|
|
152
|
+
} = nodeUsageProps;
|
|
153
|
+
const { persistedGcFeatureMatrix, ...configs } = this.configs;
|
|
154
|
+
const unrefEventProps: Omit<IUnreferencedEventProps, "state" | "usageType"> = {
|
|
155
|
+
type: nodeType,
|
|
156
|
+
unrefTime: nodeStateTracker?.unreferencedTimestampMs ?? -1,
|
|
157
|
+
age:
|
|
158
|
+
nodeStateTracker !== undefined
|
|
159
|
+
? nodeUsageProps.currentReferenceTimestampMs -
|
|
160
|
+
nodeStateTracker.unreferencedTimestampMs
|
|
161
|
+
: -1,
|
|
162
|
+
timeout:
|
|
163
|
+
nodeStateTracker?.state === UnreferencedState.Inactive
|
|
164
|
+
? this.configs.inactiveTimeoutMs
|
|
165
|
+
: this.configs.sweepTimeoutMs,
|
|
166
|
+
...tagCodeArtifacts({ id: untaggedId, fromId: untaggedFromId }),
|
|
167
|
+
...propsToLog,
|
|
168
|
+
...this.createContainerMetadata,
|
|
169
|
+
gcConfigs: { ...configs, ...persistedGcFeatureMatrix },
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// If the node that is used is tombstoned, log a tombstone telemetry.
|
|
173
|
+
// Note that this is done before checking if "nodeStateTracker" is undefined below because unreferenced
|
|
174
|
+
// tracking may not have yet been enabled. That happens only after the client transitions to write mode.
|
|
175
|
+
if (nodeUsageProps.isTombstoned) {
|
|
176
|
+
this.logTombstoneUsageTelemetry(nodeUsageProps, unrefEventProps, nodeType, usageType);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// After logging tombstone telemetry, if the node's unreferenced state is not tracked, there is nothing
|
|
180
|
+
// else to log.
|
|
181
|
+
if (nodeStateTracker === undefined) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const state = nodeStateTracker.state;
|
|
186
|
+
const uniqueEventId = `${state}-${nodeUsageProps.id}-${nodeUsageProps.usageType}`;
|
|
187
|
+
|
|
144
188
|
if (
|
|
145
189
|
!this.shouldLogNonActiveEvent(
|
|
146
|
-
nodeUsageProps.id,
|
|
147
190
|
nodeType,
|
|
148
191
|
nodeUsageProps.usageType,
|
|
149
192
|
nodeStateTracker,
|
|
@@ -153,42 +196,9 @@ export class GCTelemetryTracker {
|
|
|
153
196
|
return;
|
|
154
197
|
}
|
|
155
198
|
|
|
156
|
-
// Add the unique event id so that we don't generate a log for this event again in this session
|
|
199
|
+
// Add the unique event id so that we don't generate a log for this event again in this session.
|
|
157
200
|
this.loggedUnreferencedEvents.add(uniqueEventId);
|
|
158
201
|
|
|
159
|
-
const state = nodeStateTracker.state;
|
|
160
|
-
const { usageType, currentReferenceTimestampMs, packagePath, id, fromId, ...propsToLog } =
|
|
161
|
-
nodeUsageProps;
|
|
162
|
-
const eventProps: Omit<IUnreferencedEventProps, "state" | "usageType"> = {
|
|
163
|
-
type: nodeType,
|
|
164
|
-
unrefTime: nodeStateTracker.unreferencedTimestampMs,
|
|
165
|
-
age:
|
|
166
|
-
nodeUsageProps.currentReferenceTimestampMs -
|
|
167
|
-
nodeStateTracker.unreferencedTimestampMs,
|
|
168
|
-
timeout:
|
|
169
|
-
state === UnreferencedState.Inactive
|
|
170
|
-
? this.configs.inactiveTimeoutMs
|
|
171
|
-
: this.configs.sweepTimeoutMs,
|
|
172
|
-
...tagCodeArtifacts({ id, fromId }),
|
|
173
|
-
...propsToLog,
|
|
174
|
-
...this.createContainerMetadata,
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
// This will log the following events:
|
|
178
|
-
// GC_Tombstone_DataStore_Revived, GC_Tombstone_SubDataStore_Revived, GC_Tombstone_Blob_Revived
|
|
179
|
-
if (nodeUsageProps.usageType === "Revived" && nodeUsageProps.isTombstoned) {
|
|
180
|
-
sendGCUnexpectedUsageEvent(
|
|
181
|
-
this.mc,
|
|
182
|
-
{
|
|
183
|
-
eventName: `GC_Tombstone_${nodeType}_Revived`,
|
|
184
|
-
category: "generic",
|
|
185
|
-
...tagCodeArtifacts({ url: id }),
|
|
186
|
-
gcTombstoneEnforcementAllowed: this.gcTombstoneEnforcementAllowed,
|
|
187
|
-
},
|
|
188
|
-
undefined /* packagePath */,
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
202
|
// For summarizer client, queue the event so it is logged the next time GC runs if the event is still valid.
|
|
193
203
|
// For non-summarizer client, log the event now since GC won't run on it. This may result in false positives
|
|
194
204
|
// but it's a good signal nonetheless and we can consume it with a grain of salt.
|
|
@@ -196,7 +206,7 @@ export class GCTelemetryTracker {
|
|
|
196
206
|
// SweepReady errors are usages of Objects that will be deleted by GC Sweep!
|
|
197
207
|
if (this.isSummarizerClient) {
|
|
198
208
|
this.pendingEventsQueue.push({
|
|
199
|
-
...
|
|
209
|
+
...unrefEventProps,
|
|
200
210
|
usageType: nodeUsageProps.usageType,
|
|
201
211
|
state,
|
|
202
212
|
});
|
|
@@ -206,16 +216,16 @@ export class GCTelemetryTracker {
|
|
|
206
216
|
// Events generated:
|
|
207
217
|
// InactiveObject_Loaded, SweepReadyObject_Loaded
|
|
208
218
|
if (nodeUsageProps.usageType === "Loaded") {
|
|
209
|
-
const { id
|
|
219
|
+
const { id, fromId, headers, gcConfigs, ...detailedProps } = unrefEventProps;
|
|
210
220
|
const event = {
|
|
211
221
|
eventName: `${state}Object_${nodeUsageProps.usageType}`,
|
|
212
|
-
|
|
222
|
+
...tagCodeArtifacts({ pkg: nodeUsageProps.packagePath?.join("/") }),
|
|
213
223
|
stack: generateStack(),
|
|
214
|
-
id
|
|
215
|
-
fromId
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
224
|
+
id,
|
|
225
|
+
fromId,
|
|
226
|
+
headers: { ...headers },
|
|
227
|
+
details: detailedProps,
|
|
228
|
+
gcConfigs,
|
|
219
229
|
};
|
|
220
230
|
|
|
221
231
|
// Do not log the inactive object x events as error events as they are not the best signal for
|
|
@@ -229,6 +239,52 @@ export class GCTelemetryTracker {
|
|
|
229
239
|
}
|
|
230
240
|
}
|
|
231
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Logs telemetry when a tombstoned object is changed, revived or loaded.
|
|
244
|
+
*/
|
|
245
|
+
private logTombstoneUsageTelemetry(
|
|
246
|
+
nodeUsageProps: INodeUsageProps,
|
|
247
|
+
unrefEventProps: Omit<IUnreferencedEventProps, "state" | "usageType">,
|
|
248
|
+
nodeType: GCNodeType,
|
|
249
|
+
usageType: NodeUsageType,
|
|
250
|
+
) {
|
|
251
|
+
// This will log the following events:
|
|
252
|
+
// GC_Tombstone_DataStore_Requested, GC_Tombstone_DataStore_Changed, GC_Tombstone_DataStore_Revived
|
|
253
|
+
// GC_Tombstone_SubDataStore_Requested, GC_Tombstone_SubDataStore_Changed, GC_Tombstone_SubDataStore_Revived
|
|
254
|
+
// GC_Tombstone_Blob_Requested, GC_Tombstone_Blob_Changed, GC_Tombstone_Blob_Revived
|
|
255
|
+
const { id, fromId, headers, gcConfigs, ...detailedProps } = unrefEventProps;
|
|
256
|
+
const eventUsageName = usageType === "Loaded" ? "Requested" : usageType;
|
|
257
|
+
const event = {
|
|
258
|
+
eventName: `GC_Tombstone_${nodeType}_${eventUsageName}`,
|
|
259
|
+
pkg: tagCodeArtifacts({ pkg: nodeUsageProps.packagePath?.join("/") }).pkg,
|
|
260
|
+
stack: generateStack(),
|
|
261
|
+
id,
|
|
262
|
+
fromId,
|
|
263
|
+
headers: { ...headers },
|
|
264
|
+
details: detailedProps,
|
|
265
|
+
gcConfigs,
|
|
266
|
+
tombstoneFlags: {
|
|
267
|
+
DisableTombstone: this.mc.config.getBoolean(disableTombstoneKey),
|
|
268
|
+
ThrowOnTombstoneUsage: this.mc.config.getBoolean(throwOnTombstoneUsageKey),
|
|
269
|
+
ThrowOnTombstoneLoad: this.mc.config.getBoolean(throwOnTombstoneLoadOverrideKey),
|
|
270
|
+
},
|
|
271
|
+
sweepFlags: {
|
|
272
|
+
EnableSweepFlag: this.mc.config.getBoolean(runSweepKey),
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
if (
|
|
277
|
+
(usageType === "Loaded" &&
|
|
278
|
+
this.configs.throwOnTombstoneLoad &&
|
|
279
|
+
!headers?.allowTombstone) ||
|
|
280
|
+
(usageType === "Changed" && this.configs.throwOnTombstoneUsage)
|
|
281
|
+
) {
|
|
282
|
+
this.mc.logger.sendErrorEvent(event);
|
|
283
|
+
} else {
|
|
284
|
+
this.mc.logger.sendTelemetryEvent(event);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
232
288
|
/**
|
|
233
289
|
* Log all new references or outbound routes in the current graph that haven't been explicitly notified to GC.
|
|
234
290
|
* The principle is that every new reference or outbound route must be notified to GC via the
|
|
@@ -295,7 +351,9 @@ export class GCTelemetryTracker {
|
|
|
295
351
|
// InactiveObject_Loaded, InactiveObject_Changed, InactiveObject_Revived
|
|
296
352
|
// SweepReadyObject_Loaded, SweepReadyObject_Changed, SweepReadyObject_Revived
|
|
297
353
|
for (const eventProps of this.pendingEventsQueue) {
|
|
298
|
-
const { usageType, state, id, fromId, ...propsToLog } = eventProps;
|
|
354
|
+
// const { usageType, state, id, fromId, ...propsToLog } = eventProps;
|
|
355
|
+
const { usageType, state, id, fromId, headers, gcConfigs, ...detailedProps } =
|
|
356
|
+
eventProps;
|
|
299
357
|
/**
|
|
300
358
|
* Revived event is logged only if the node is active. If the node is not active, the reference to it was
|
|
301
359
|
* from another unreferenced node and this scenario is not interesting to log.
|
|
@@ -313,11 +371,11 @@ export class GCTelemetryTracker {
|
|
|
313
371
|
: undefined;
|
|
314
372
|
const event = {
|
|
315
373
|
eventName: `${state}Object_${usageType}`,
|
|
316
|
-
details: JSON.stringify({
|
|
317
|
-
...propsToLog,
|
|
318
|
-
}),
|
|
319
374
|
id,
|
|
320
375
|
fromId,
|
|
376
|
+
headers: { ...headers },
|
|
377
|
+
details: detailedProps,
|
|
378
|
+
gcConfigs,
|
|
321
379
|
...tagCodeArtifacts({
|
|
322
380
|
pkg: pkg?.join("/"),
|
|
323
381
|
fromPkg: fromPkg?.join("/"),
|
|
@@ -404,7 +462,7 @@ export function sendGCUnexpectedUsageEvent(
|
|
|
404
462
|
event.tombstoneFlags = JSON.stringify({
|
|
405
463
|
DisableTombstone: mc.config.getBoolean(disableTombstoneKey),
|
|
406
464
|
ThrowOnTombstoneUsage: mc.config.getBoolean(throwOnTombstoneUsageKey),
|
|
407
|
-
ThrowOnTombstoneLoad: mc.config.getBoolean(
|
|
465
|
+
ThrowOnTombstoneLoad: mc.config.getBoolean(throwOnTombstoneLoadOverrideKey),
|
|
408
466
|
});
|
|
409
467
|
event.sweepFlags = JSON.stringify({
|
|
410
468
|
EnableSweepFlag: mc.config.getBoolean(runSweepKey),
|
package/src/gc/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ export {
|
|
|
12
12
|
GCNodeType,
|
|
13
13
|
gcTestModeKey,
|
|
14
14
|
gcTombstoneGenerationOptionName,
|
|
15
|
+
gcThrowOnTombstoneLoadOptionName,
|
|
15
16
|
gcSweepGenerationOptionName,
|
|
16
17
|
GCFeatureMatrix,
|
|
17
18
|
GCVersion,
|
|
@@ -31,15 +32,12 @@ export {
|
|
|
31
32
|
stableGCVersion,
|
|
32
33
|
disableAttachmentBlobSweepKey,
|
|
33
34
|
disableDatastoreSweepKey,
|
|
34
|
-
throwOnTombstoneLoadKey,
|
|
35
|
-
throwOnTombstoneUsageKey,
|
|
36
35
|
UnreferencedState,
|
|
36
|
+
throwOnTombstoneLoadOverrideKey,
|
|
37
37
|
} from "./gcDefinitions";
|
|
38
38
|
export {
|
|
39
39
|
cloneGCData,
|
|
40
40
|
concatGarbageCollectionStates,
|
|
41
|
-
shouldAllowGcTombstoneEnforcement,
|
|
42
|
-
shouldAllowGcSweep,
|
|
43
41
|
trimLeadingAndTrailingSlashes,
|
|
44
42
|
unpackChildNodesGCDetails,
|
|
45
43
|
} from "./gcHelpers";
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
import { assert } from "@fluidframework/core-utils";
|
|
7
7
|
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
|
|
8
|
+
import { createChildLogger } from "@fluidframework/telemetry-utils";
|
|
9
|
+
import { ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
|
|
8
10
|
import { ContainerMessageType } from "../messageTypes";
|
|
9
11
|
import { IBatch } from "./definitions";
|
|
10
12
|
|
|
@@ -26,16 +28,38 @@ function isGroupContents(opContents: any): opContents is IGroupedBatchMessageCon
|
|
|
26
28
|
return opContents?.type === OpGroupingManager.groupedBatchOp;
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
export interface OpGroupingManagerConfig {
|
|
32
|
+
readonly groupedBatchingEnabled: boolean;
|
|
33
|
+
readonly opCountThreshold: number;
|
|
34
|
+
readonly reentrantBatchGroupingEnabled: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
29
37
|
export class OpGroupingManager {
|
|
30
38
|
static readonly groupedBatchOp = "groupedBatch";
|
|
39
|
+
private readonly logger;
|
|
31
40
|
|
|
32
|
-
constructor(
|
|
41
|
+
constructor(
|
|
42
|
+
private readonly config: OpGroupingManagerConfig,
|
|
43
|
+
logger: ITelemetryBaseLogger,
|
|
44
|
+
) {
|
|
45
|
+
this.logger = createChildLogger({ logger, namespace: "OpGroupingManager" });
|
|
46
|
+
}
|
|
33
47
|
|
|
34
48
|
public groupBatch(batch: IBatch): IBatch {
|
|
35
|
-
if (
|
|
49
|
+
if (!this.shouldGroup(batch)) {
|
|
36
50
|
return batch;
|
|
37
51
|
}
|
|
38
52
|
|
|
53
|
+
if (batch.content.length >= 1000) {
|
|
54
|
+
this.logger.sendTelemetryEvent({
|
|
55
|
+
eventName: "GroupLargeBatch",
|
|
56
|
+
length: batch.content.length,
|
|
57
|
+
threshold: this.config.opCountThreshold,
|
|
58
|
+
reentrant: batch.hasReentrantOps,
|
|
59
|
+
referenceSequenceNumber: batch.content[0].referenceSequenceNumber,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
39
63
|
for (const message of batch.content) {
|
|
40
64
|
if (message.metadata) {
|
|
41
65
|
const keys = Object.keys(message.metadata);
|
|
@@ -86,4 +110,15 @@ export class OpGroupingManager {
|
|
|
86
110
|
compression: subMessage.compression,
|
|
87
111
|
}));
|
|
88
112
|
}
|
|
113
|
+
|
|
114
|
+
public shouldGroup(batch: IBatch): boolean {
|
|
115
|
+
return (
|
|
116
|
+
// Grouped batching must be enabled
|
|
117
|
+
this.config.groupedBatchingEnabled &&
|
|
118
|
+
// The number of ops in the batch must surpass the configured threshold
|
|
119
|
+
batch.content.length >= this.config.opCountThreshold &&
|
|
120
|
+
// Support for reentrant batches must be explicitly enabled
|
|
121
|
+
(this.config.reentrantBatchGroupingEnabled || batch.hasReentrantOps !== true)
|
|
122
|
+
);
|
|
123
|
+
}
|
|
89
124
|
}
|
|
@@ -30,7 +30,6 @@ export interface IOutboxConfig {
|
|
|
30
30
|
// The maximum size of a batch that we can send over the wire.
|
|
31
31
|
readonly maxBatchSizeInBytes: number;
|
|
32
32
|
readonly disablePartialFlush: boolean;
|
|
33
|
-
readonly enableGroupedBatching: boolean;
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
export interface IOutboxParameters {
|
|
@@ -270,7 +269,10 @@ export class Outbox {
|
|
|
270
269
|
}
|
|
271
270
|
|
|
272
271
|
const rawBatch = batchManager.popBatch();
|
|
273
|
-
if (
|
|
272
|
+
if (
|
|
273
|
+
rawBatch.hasReentrantOps === true &&
|
|
274
|
+
this.params.groupingManager.shouldGroup(rawBatch)
|
|
275
|
+
) {
|
|
274
276
|
assert(!this.rebasing, 0x6fa /* A rebased batch should never have reentrant ops */);
|
|
275
277
|
// If a batch contains reentrant ops (ops created as a result from processing another op)
|
|
276
278
|
// it needs to be rebased so that we can ensure consistent reference sequence numbers
|
package/src/packageVersion.ts
CHANGED