@fluidframework/container-runtime 2.1.0-276985 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +71 -18
- package/api-extractor/api-extractor.current.json +5 -0
- package/api-extractor/api-extractor.legacy.json +1 -1
- package/api-extractor.json +1 -1
- package/api-report/container-runtime.legacy.public.api.md +9 -0
- package/container-runtime.test-files.tar +0 -0
- package/dist/blobManager/blobManager.d.ts +10 -0
- package/dist/blobManager/blobManager.d.ts.map +1 -1
- package/dist/blobManager/blobManager.js +19 -0
- package/dist/blobManager/blobManager.js.map +1 -1
- package/dist/channelCollection.d.ts +1 -1
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +40 -8
- package/dist/channelCollection.js.map +1 -1
- package/dist/containerRuntime.d.ts +14 -5
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +151 -99
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +4 -0
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +9 -3
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +1 -1
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +14 -8
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +4 -2
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +12 -0
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +3 -2
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +6 -6
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/legacy.d.ts +1 -1
- package/dist/metadata.d.ts +7 -1
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +6 -0
- package/dist/metadata.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +8 -1
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +37 -16
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/definitions.d.ts +1 -1
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/definitions.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +2 -2
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js +3 -1
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js +12 -8
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +14 -11
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +11 -6
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +22 -6
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +43 -21
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +10 -8
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +39 -15
- package/dist/opLifecycle/remoteMessageProcessor.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/pendingStateManager.d.ts +37 -13
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +95 -45
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/public.d.ts +1 -1
- package/dist/scheduleManager.js +4 -0
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js +2 -0
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js +4 -1
- package/dist/summary/summaryFormat.js.map +1 -1
- package/internal.d.ts +1 -1
- package/legacy.d.ts +1 -1
- package/lib/blobManager/blobManager.d.ts +10 -0
- package/lib/blobManager/blobManager.d.ts.map +1 -1
- package/lib/blobManager/blobManager.js +19 -0
- package/lib/blobManager/blobManager.js.map +1 -1
- package/lib/channelCollection.d.ts +1 -1
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +40 -8
- package/lib/channelCollection.js.map +1 -1
- package/lib/containerRuntime.d.ts +14 -5
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +152 -100
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +4 -0
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +10 -4
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +1 -1
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +14 -8
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +4 -2
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +12 -0
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +3 -2
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +6 -6
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/legacy.d.ts +1 -1
- package/lib/metadata.d.ts +7 -1
- package/lib/metadata.d.ts.map +1 -1
- package/lib/metadata.js +4 -1
- package/lib/metadata.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +8 -1
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +35 -15
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/definitions.d.ts +1 -1
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/definitions.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +2 -2
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +2 -2
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js +12 -8
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +14 -11
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +11 -6
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +22 -6
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +44 -22
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +10 -8
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +37 -14
- package/lib/opLifecycle/remoteMessageProcessor.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/pendingStateManager.d.ts +37 -13
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +95 -45
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/public.d.ts +1 -1
- package/lib/scheduleManager.js +4 -0
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js +2 -0
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js +4 -1
- package/lib/summary/summaryFormat.js.map +1 -1
- package/package.json +46 -31
- package/src/blobManager/blobManager.ts +19 -0
- package/src/channelCollection.ts +48 -11
- package/src/containerRuntime.ts +203 -133
- package/src/dataStoreContext.ts +22 -4
- package/src/gc/garbageCollection.ts +15 -10
- package/src/gc/gcDefinitions.ts +7 -2
- package/src/gc/gcHelpers.ts +18 -6
- package/src/gc/gcTelemetry.ts +20 -8
- package/src/metadata.ts +11 -1
- package/src/opLifecycle/README.md +0 -8
- package/src/opLifecycle/batchManager.ts +49 -16
- package/src/opLifecycle/definitions.ts +1 -1
- package/src/opLifecycle/index.ts +13 -2
- package/src/opLifecycle/opCompressor.ts +12 -8
- package/src/opLifecycle/opGroupingManager.ts +14 -11
- package/src/opLifecycle/opSplitter.ts +10 -6
- package/src/opLifecycle/outbox.ts +64 -26
- package/src/opLifecycle/remoteMessageProcessor.ts +56 -17
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +173 -74
- package/src/scheduleManager.ts +6 -2
- package/src/summary/README.md +81 -0
- package/src/summary/summarizerNode/summarizerNodeUtils.ts +3 -1
- package/src/summary/summaryFormat.ts +3 -1
- package/src/summary/summaryFormats.md +69 -8
- package/tsconfig.json +0 -1
- package/src/summary/images/appTree.png +0 -0
- package/src/summary/images/protocolAndAppTree.png +0 -0
- package/src/summary/images/summaryTree.png +0 -0
package/lib/gc/gcHelpers.js
CHANGED
|
@@ -117,6 +117,8 @@ export function concatGarbageCollectionData(gcData1, gcData2) {
|
|
|
117
117
|
combinedGCData.gcNodes[id] = Array.from(routes);
|
|
118
118
|
}
|
|
119
119
|
else {
|
|
120
|
+
// Non null asserting here since we are checking if combinedGCData.gcNodes[id] is not undefined above.
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
120
122
|
const combinedRoutes = [...routes, ...combinedGCData.gcNodes[id]];
|
|
121
123
|
combinedGCData.gcNodes[id] = [...new Set(combinedRoutes)];
|
|
122
124
|
}
|
|
@@ -134,11 +136,15 @@ export async function getGCDataFromSnapshot(gcSnapshotTree, readAndParseBlob) {
|
|
|
134
136
|
for (const key of Object.keys(gcSnapshotTree.blobs)) {
|
|
135
137
|
// Update deleted nodes blob.
|
|
136
138
|
if (key === gcDeletedBlobKey) {
|
|
139
|
+
// Non null asserting here, we can change this to Object.entries later
|
|
140
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
137
141
|
deletedNodes = await readAndParseBlob(gcSnapshotTree.blobs[key]);
|
|
138
142
|
continue;
|
|
139
143
|
}
|
|
140
144
|
// Update tombstone blob.
|
|
141
145
|
if (key === gcTombstoneBlobKey) {
|
|
146
|
+
// Non null asserting here, we can change this to Object.entries later
|
|
147
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
142
148
|
tombstones = await readAndParseBlob(gcSnapshotTree.blobs[key]);
|
|
143
149
|
continue;
|
|
144
150
|
}
|
|
@@ -175,6 +181,8 @@ export function unpackChildNodesGCDetails(gcDetails) {
|
|
|
175
181
|
continue;
|
|
176
182
|
}
|
|
177
183
|
assert(id.startsWith("/"), 0x5d9 /* node id should always be an absolute route */);
|
|
184
|
+
// TODO Why are we non null asserting here
|
|
185
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
178
186
|
const childId = id.split("/")[1];
|
|
179
187
|
let childGCNodeId = id.slice(childId.length + 1);
|
|
180
188
|
// GC node id always begins with "/". Handle the special case where a child's id in the parent's GC nodes is
|
|
@@ -198,6 +206,8 @@ export function unpackChildNodesGCDetails(gcDetails) {
|
|
|
198
206
|
const usedRoutes = gcDetails.usedRoutes.filter((route) => route !== "" && route !== "/");
|
|
199
207
|
for (const route of usedRoutes) {
|
|
200
208
|
assert(route.startsWith("/"), 0x5db /* Used route should always be an absolute route */);
|
|
209
|
+
// TODO Why are we non null asserting here
|
|
210
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
201
211
|
const childId = route.split("/")[1];
|
|
202
212
|
const childUsedRoute = route.slice(childId.length + 1);
|
|
203
213
|
const childGCDetails = childGCDetailsMap.get(childId);
|
|
@@ -217,6 +227,8 @@ function trimLeadingAndTrailingSlashes(str) {
|
|
|
217
227
|
}
|
|
218
228
|
/** Reformats a request URL to match expected format for a GC node path */
|
|
219
229
|
export function urlToGCNodePath(url) {
|
|
230
|
+
// TODO Why are we non null asserting here
|
|
231
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
220
232
|
return `/${trimLeadingAndTrailingSlashes(url.split("?")[0])}`;
|
|
221
233
|
}
|
|
222
234
|
/**
|
package/lib/gc/gcHelpers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gcHelpers.js","sourceRoot":"","sources":["../../src/gc/gcHelpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAE7D,OAAO,EAEN,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,GAElB,MAAM,8CAA8C,CAAC;AAGtD,OAAO,EAIN,uBAAuB,EACvB,aAAa,EACb,eAAe,GACf,MAAM,oBAAoB,CAAC;AAO5B,MAAM,UAAU,YAAY,CAAC,QAAsB;IAClD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACf,0CAA0C;QAC1C,OAAO,CAAC,CAAC;IACV,CAAC;IACD,OAAO,QAAQ,CAAC,SAAS,IAAI,CAAC,CAAC;AAChC,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,oBAAoB,CAAC,cAA+B;IACnE,yEAAyE;IACzE,OAAO,cAAc,CAAC,UAAU,CAAC,uBAAuB,CAAC,KAAK,IAAI;QACjE,CAAC,CAAC,aAAa;QACf,CAAC,CAAC,eAAe,CAAC;AACpB,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,kBAAkB,CACjC,aAA8B,EAC9B,iBAAqC;IAErC,uEAAuE;IACvE,IAAI,iBAAiB,KAAK,SAAS,EAAE,CAAC;QACrC,OAAO,IAAI,CAAC;IACb,CAAC;IAED,2FAA2F;IAC3F,MAAM,gBAAgB,GAAG,aAAa,CAAC,mBAAmB,IAAI,aAAa,CAAC,YAAY,CAAC;IAEzF,OAAO,iBAAiB,KAAK,gBAAgB,CAAC;AAC/C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CACpC,OAAgC;IAEhC,MAAM,aAAa,GAA2C,MAAM,CAAC,OAAO,CAC3E,OAAO,CAAC,OAAO,CACf,CAAC;IACF,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,MAAM,aAAa,GAA4B,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC/D,KAAK,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,aAAa,EAAE,CAAC;QAChD,QAAQ,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;QAC/B,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC;IAC1C,CAAC;IACD,OAAO,aAAa,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,6BAA6B,CAC5C,QAAiC,EACjC,QAAiC;IAEjC,MAAM,eAAe,GAAiD,EAAE,CAAC;IACzE,KAAK,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACnE,eAAe,CAAC,MAAM,CAAC,GAAG;YACzB,cAAc,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC;YACnD,uBAAuB,EAAE,QAAQ,CAAC,uBAAuB;SACzD,CAAC;IACH,CAAC;IAED,KAAK,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACnE,IAAI,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;QAC9C,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;YACnC,eAAe,GAAG;gBACjB,cAAc,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC;gBACnD,uBAAuB,EAAE,QAAQ,CAAC,uBAAuB;aACzD,CAAC;QACH,CAAC;aAAM,CAAC;YACP,yEAAyE;YACzE,IACC,QAAQ,CAAC,uBAAuB,KAAK,SAAS;gBAC9C,eAAe,CAAC,uBAAuB,KAAK,SAAS,EACpD,CAAC;gBACF,MAAM,CACL,QAAQ,CAAC,uBAAuB,KAAK,eAAe,CAAC,uBAAuB,EAC5E,KAAK,CAAC,4EAA4E,CAClF,CAAC;YACH,CAAC;YACD,eAAe,GAAG;gBACjB,cAAc,EAAE;oBACf,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,cAAc,EAAE,GAAG,eAAe,CAAC,cAAc,CAAC,CAAC;iBAC3E;gBACD,uBAAuB,EACtB,QAAQ,CAAC,uBAAuB,IAAI,eAAe,CAAC,uBAAuB;aAC5E,CAAC;QACH,CAAC;QACD,eAAe,CAAC,MAAM,CAAC,GAAG,eAAe,CAAC;IAC3C,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC;AACrC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,MAA8B;IACzD,MAAM,aAAa,GAA+B,EAAE,CAAC;IACrD,KAAK,MAAM,CAAC,EAAE,EAAE,cAAc,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QACnE,aAAa,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAChD,CAAC;IACD,OAAO;QACN,OAAO,EAAE,aAAa;KACtB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,2BAA2B,CAC1C,OAA+B,EAC/B,OAA+B;IAE/B,MAAM,cAAc,GAA2B,WAAW,CAAC,OAAO,CAAC,CAAC;IACpE,KAAK,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5D,IAAI,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,SAAS,EAAE,CAAC;YAC9C,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjD,CAAC;aAAM,CAAC;YACP,MAAM,cAAc,GAAG,CAAC,GAAG,MAAM,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;YAClE,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC;QAC3D,CAAC;IACF,CAAC;IACD,OAAO,cAAc,CAAC;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAC1C,cAA6B,EAC7B,gBAA+C;IAE/C,IAAI,WAAW,GAA4B,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC3D,IAAI,UAAgC,CAAC;IACrC,IAAI,YAAkC,CAAC;IACvC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;QACrD,6BAA6B;QAC7B,IAAI,GAAG,KAAK,gBAAgB,EAAE,CAAC;YAC9B,YAAY,GAAG,MAAM,gBAAgB,CAAW,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3E,SAAS;QACV,CAAC;QAED,yBAAyB;QACzB,IAAI,GAAG,KAAK,kBAAkB,EAAE,CAAC;YAChC,UAAU,GAAG,MAAM,gBAAgB,CAAW,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;YACzE,SAAS;QACV,CAAC;QAED,mDAAmD;QACnD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YACnC,SAAS;QACV,CAAC;QAED,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACzC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAC1B,SAAS;QACV,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAA0B,MAAM,CAAC,CAAC;QACxE,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE,KAAK,CAAC,mCAAmC,CAAC,CAAC;QACzE,0DAA0D;QAC1D,WAAW,GAAG,6BAA6B,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC;AAC3D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,SAAwC;IACjF,MAAM,iBAAiB,GAA+C,IAAI,GAAG,EAAE,CAAC;IAEhF,yCAAyC;IACzC,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACpC,OAAO,iBAAiB,CAAC;IAC1B,CAAC;IAED,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC;IACzC,KAAK,MAAM,CAAC,EAAE,EAAE,cAAc,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5D,iEAAiE;QACjE,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YAChB,SAAS;QACV,CAAC;QAED,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACnF,MAAM,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACjC,IAAI,aAAa,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACjD,4GAA4G;QAC5G,0GAA0G;QAC1G,IAAI,aAAa,KAAK,EAAE,EAAE,CAAC;YAC1B,aAAa,GAAG,GAAG,CAAC;QACrB,CAAC;QAED,IAAI,cAAc,GAAG,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACpD,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;YAClC,cAAc,GAAG,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;QAC9D,CAAC;QACD,kFAAkF;QAClF,MAAM,CACL,cAAc,CAAC,MAAM,KAAK,SAAS,EACnC,KAAK,CAAC,gDAAgD,CACtD,CAAC;QACF,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC;QAC5E,iBAAiB,CAAC,GAAG,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,SAAS,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACxC,OAAO,iBAAiB,CAAC;IAC1B,CAAC;IAED,oFAAoF;IACpF,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,EAAE,IAAI,KAAK,KAAK,GAAG,CAAC,CAAC;IACzF,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACzF,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,cAAc,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAEvD,MAAM,cAAc,GAAG,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACtD,MAAM,CACL,cAAc,EAAE,UAAU,KAAK,SAAS,EACxC,KAAK,CAAC,kEAAkE,CACxE,CAAC;QAEF,cAAc,CAAC,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC/C,iBAAiB,CAAC,GAAG,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,iBAAiB,CAAC;AAC1B,CAAC;AAED;;;;GAIG;AACH,SAAS,6BAA6B,CAAC,GAAW;IACjD,OAAO,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;AACtC,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,eAAe,CAAC,GAAW;IAC1C,OAAO,IAAI,6BAA6B,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC/D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,gBAAwB;IAC7D,OAAO,IAAI,gBAAgB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC7C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iCAAiC,CAChD,qBAA4B,EAC5B,cAAsD;IAEtD,yDAAyD;IACzD,OAAO,cAAc,KAAK,QAAQ,CAAC;AACpC,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { ISnapshotTree } from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tIGarbageCollectionDetailsBase,\n\tgcBlobPrefix,\n\tgcDeletedBlobKey,\n\tgcTombstoneBlobKey,\n\tIGarbageCollectionData,\n} from \"@fluidframework/runtime-definitions/internal\";\nimport type { IConfigProvider } from \"@fluidframework/telemetry-utils/internal\";\n\nimport {\n\tGCFeatureMatrix,\n\tGCVersion,\n\tIGCMetadata,\n\tgcVersionUpgradeToV4Key,\n\tnextGCVersion,\n\tstableGCVersion,\n} from \"./gcDefinitions.js\";\nimport {\n\tIGarbageCollectionNodeData,\n\tIGarbageCollectionSnapshotData,\n\tIGarbageCollectionState,\n} from \"./gcSummaryDefinitions.js\";\n\nexport function getGCVersion(metadata?: IGCMetadata): GCVersion {\n\tif (!metadata) {\n\t\t// Force to 0/disallowed in prior versions\n\t\treturn 0;\n\t}\n\treturn metadata.gcFeature ?? 0;\n}\n\n/** Indicates what GC version is in effect for new GC data being written in this session */\nexport function getGCVersionInEffect(configProvider: IConfigProvider): number {\n\t// If version upgrade is not enabled, fall back to the stable GC version.\n\treturn configProvider.getBoolean(gcVersionUpgradeToV4Key) === true\n\t\t? nextGCVersion\n\t\t: stableGCVersion;\n}\n\n/**\n * Indicates whether Sweep is allowed for this document based on the persisted GC Feature Matrix and current gcGeneration.\n * This applies to the entire Sweep Phase the same - both Tombstone Enforcement (i.e. should loading a Tombstone fail?) and Deletion.\n *\n * In order to protect old documents that were created at a time when known bugs exist that violate GC's invariants\n * such that enforcing GC Sweep would cause legitimate data loss, the container author may increment the generation value for Sweep\n * such that containers created with a different value will not be subjected to GC Sweep.\n *\n * If no generation is provided, Sweep will be enabled for all documents.\n *\n * For backwards compatibility, the current generation value is also compared against the persisted gcTombstoneGeneration if present.\n *\n * @param featureMatrix - The GC Feature Matrix, containing the persisted generation value\n * @param currentGeneration - The current app-provided gcGeneration value\n * @returns true if GC Sweep should be allowed for this document\n */\nexport function shouldAllowGcSweep(\n\tfeatureMatrix: GCFeatureMatrix,\n\tcurrentGeneration: number | undefined,\n): boolean {\n\t// If no Generation value is provided for this session, default to true\n\tif (currentGeneration === undefined) {\n\t\treturn true;\n\t}\n\n\t// tombstoneGeneration is the predecessor and needs to be supported for back-compat reasons\n\tconst targetGeneration = featureMatrix.tombstoneGeneration ?? featureMatrix.gcGeneration;\n\n\treturn currentGeneration === targetGeneration;\n}\n\n/**\n * Sorts the given GC state as per the id of the GC nodes. It also sorts the outbound routes array of each node.\n */\nexport function generateSortedGCState(\n\tgcState: IGarbageCollectionState,\n): IGarbageCollectionState {\n\tconst sortableArray: [string, IGarbageCollectionNodeData][] = Object.entries(\n\t\tgcState.gcNodes,\n\t);\n\tsortableArray.sort(([a], [b]) => a.localeCompare(b));\n\tconst sortedGCState: IGarbageCollectionState = { gcNodes: {} };\n\tfor (const [nodeId, nodeData] of sortableArray) {\n\t\tnodeData.outboundRoutes.sort();\n\t\tsortedGCState.gcNodes[nodeId] = nodeData;\n\t}\n\treturn sortedGCState;\n}\n\n/**\n * Concatenates the given GC states and returns the concatenated GC state.\n */\nexport function concatGarbageCollectionStates(\n\tgcState1: IGarbageCollectionState,\n\tgcState2: IGarbageCollectionState,\n): IGarbageCollectionState {\n\tconst combinedGCNodes: { [id: string]: IGarbageCollectionNodeData } = {};\n\tfor (const [nodeId, nodeData] of Object.entries(gcState1.gcNodes)) {\n\t\tcombinedGCNodes[nodeId] = {\n\t\t\toutboundRoutes: Array.from(nodeData.outboundRoutes),\n\t\t\tunreferencedTimestampMs: nodeData.unreferencedTimestampMs,\n\t\t};\n\t}\n\n\tfor (const [nodeId, nodeData] of Object.entries(gcState2.gcNodes)) {\n\t\tlet combineNodeData = combinedGCNodes[nodeId];\n\t\tif (combineNodeData === undefined) {\n\t\t\tcombineNodeData = {\n\t\t\t\toutboundRoutes: Array.from(nodeData.outboundRoutes),\n\t\t\t\tunreferencedTimestampMs: nodeData.unreferencedTimestampMs,\n\t\t\t};\n\t\t} else {\n\t\t\t// Validate that same node doesn't have different unreferenced timestamp.\n\t\t\tif (\n\t\t\t\tnodeData.unreferencedTimestampMs !== undefined &&\n\t\t\t\tcombineNodeData.unreferencedTimestampMs !== undefined\n\t\t\t) {\n\t\t\t\tassert(\n\t\t\t\t\tnodeData.unreferencedTimestampMs === combineNodeData.unreferencedTimestampMs,\n\t\t\t\t\t0x5d7 /* Two entries for the same GC node with different unreferenced timestamp */,\n\t\t\t\t);\n\t\t\t}\n\t\t\tcombineNodeData = {\n\t\t\t\toutboundRoutes: [\n\t\t\t\t\t...new Set([...nodeData.outboundRoutes, ...combineNodeData.outboundRoutes]),\n\t\t\t\t],\n\t\t\t\tunreferencedTimestampMs:\n\t\t\t\t\tnodeData.unreferencedTimestampMs ?? combineNodeData.unreferencedTimestampMs,\n\t\t\t};\n\t\t}\n\t\tcombinedGCNodes[nodeId] = combineNodeData;\n\t}\n\treturn { gcNodes: combinedGCNodes };\n}\n\n/**\n * Helper function that clones the GC data.\n * @param gcData - The GC data to clone.\n * @returns a clone of the given GC data.\n */\nexport function cloneGCData(gcData: IGarbageCollectionData): IGarbageCollectionData {\n\tconst clonedGCNodes: { [id: string]: string[] } = {};\n\tfor (const [id, outboundRoutes] of Object.entries(gcData.gcNodes)) {\n\t\tclonedGCNodes[id] = Array.from(outboundRoutes);\n\t}\n\treturn {\n\t\tgcNodes: clonedGCNodes,\n\t};\n}\n\n/**\n * Concatenates the given GC data and returns the concatenated GC data.\n */\nexport function concatGarbageCollectionData(\n\tgcData1: IGarbageCollectionData,\n\tgcData2: IGarbageCollectionData,\n) {\n\tconst combinedGCData: IGarbageCollectionData = cloneGCData(gcData1);\n\tfor (const [id, routes] of Object.entries(gcData2.gcNodes)) {\n\t\tif (combinedGCData.gcNodes[id] === undefined) {\n\t\t\tcombinedGCData.gcNodes[id] = Array.from(routes);\n\t\t} else {\n\t\t\tconst combinedRoutes = [...routes, ...combinedGCData.gcNodes[id]];\n\t\t\tcombinedGCData.gcNodes[id] = [...new Set(combinedRoutes)];\n\t\t}\n\t}\n\treturn combinedGCData;\n}\n\n/**\n * Gets the base garbage collection state from the given snapshot tree. It contains GC state, deleted nodes and\n * tombstones. The GC state may be written into multiple blobs. Merge the GC state from all such blobs into one.\n */\nexport async function getGCDataFromSnapshot(\n\tgcSnapshotTree: ISnapshotTree,\n\treadAndParseBlob: <T>(id: string) => Promise<T>,\n): Promise<IGarbageCollectionSnapshotData> {\n\tlet rootGCState: IGarbageCollectionState = { gcNodes: {} };\n\tlet tombstones: string[] | undefined;\n\tlet deletedNodes: string[] | undefined;\n\tfor (const key of Object.keys(gcSnapshotTree.blobs)) {\n\t\t// Update deleted nodes blob.\n\t\tif (key === gcDeletedBlobKey) {\n\t\t\tdeletedNodes = await readAndParseBlob<string[]>(gcSnapshotTree.blobs[key]);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Update tombstone blob.\n\t\tif (key === gcTombstoneBlobKey) {\n\t\t\ttombstones = await readAndParseBlob<string[]>(gcSnapshotTree.blobs[key]);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Skip blobs that do not start with the GC prefix.\n\t\tif (!key.startsWith(gcBlobPrefix)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst blobId = gcSnapshotTree.blobs[key];\n\t\tif (blobId === undefined) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst gcState = await readAndParseBlob<IGarbageCollectionState>(blobId);\n\t\tassert(gcState !== undefined, 0x5d8 /* GC blob missing from snapshot */);\n\t\t// Merge the GC state of this blob into the root GC state.\n\t\trootGCState = concatGarbageCollectionStates(rootGCState, gcState);\n\t}\n\treturn { gcState: rootGCState, tombstones, deletedNodes };\n}\n\n/**\n * Helper function that unpacks the GC details of the children from a given node's GC details.\n * @param gcDetails - The GC details of a node.\n * @returns A map of GC details of each children of the the given node.\n */\nexport function unpackChildNodesGCDetails(gcDetails: IGarbageCollectionDetailsBase) {\n\tconst childGCDetailsMap: Map<string, IGarbageCollectionDetailsBase> = new Map();\n\n\t// If GC data is not available, bail out.\n\tif (gcDetails.gcData === undefined) {\n\t\treturn childGCDetailsMap;\n\t}\n\n\tconst gcNodes = gcDetails.gcData.gcNodes;\n\tfor (const [id, outboundRoutes] of Object.entries(gcNodes)) {\n\t\t// Skip self-node since only children GC data is to be generated.\n\t\tif (id === \"/\") {\n\t\t\tcontinue;\n\t\t}\n\n\t\tassert(id.startsWith(\"/\"), 0x5d9 /* node id should always be an absolute route */);\n\t\tconst childId = id.split(\"/\")[1];\n\t\tlet childGCNodeId = id.slice(childId.length + 1);\n\t\t// GC node id always begins with \"/\". Handle the special case where a child's id in the parent's GC nodes is\n\t\t// of format `/root`. In this case, the childId is root and childGCNodeId is \"\". Make childGCNodeId = \"/\".\n\t\tif (childGCNodeId === \"\") {\n\t\t\tchildGCNodeId = \"/\";\n\t\t}\n\n\t\tlet childGCDetails = childGCDetailsMap.get(childId);\n\t\tif (childGCDetails === undefined) {\n\t\t\tchildGCDetails = { gcData: { gcNodes: {} }, usedRoutes: [] };\n\t\t}\n\t\t// gcData should not undefined as its always at least initialized as empty above.\n\t\tassert(\n\t\t\tchildGCDetails.gcData !== undefined,\n\t\t\t0x5da /* Child GC data should have been initialized */,\n\t\t);\n\t\tchildGCDetails.gcData.gcNodes[childGCNodeId] = [...new Set(outboundRoutes)];\n\t\tchildGCDetailsMap.set(childId, childGCDetails);\n\t}\n\n\tif (gcDetails.usedRoutes === undefined) {\n\t\treturn childGCDetailsMap;\n\t}\n\n\t// Remove the node's self used route, if any, and generate the children used routes.\n\tconst usedRoutes = gcDetails.usedRoutes.filter((route) => route !== \"\" && route !== \"/\");\n\tfor (const route of usedRoutes) {\n\t\tassert(route.startsWith(\"/\"), 0x5db /* Used route should always be an absolute route */);\n\t\tconst childId = route.split(\"/\")[1];\n\t\tconst childUsedRoute = route.slice(childId.length + 1);\n\n\t\tconst childGCDetails = childGCDetailsMap.get(childId);\n\t\tassert(\n\t\t\tchildGCDetails?.usedRoutes !== undefined,\n\t\t\t0x5dc /* This should have be initialized when generate GC nodes above */,\n\t\t);\n\n\t\tchildGCDetails.usedRoutes.push(childUsedRoute);\n\t\tchildGCDetailsMap.set(childId, childGCDetails);\n\t}\n\treturn childGCDetailsMap;\n}\n\n/**\n * Trims the leading and trailing slashes from the given string.\n * @param str - A string that may contain leading and / or trailing slashes.\n * @returns A new string without leading and trailing slashes.\n */\nfunction trimLeadingAndTrailingSlashes(str: string) {\n\treturn str.replace(/^\\/+|\\/+$/g, \"\");\n}\n\n/** Reformats a request URL to match expected format for a GC node path */\nexport function urlToGCNodePath(url: string): string {\n\treturn `/${trimLeadingAndTrailingSlashes(url.split(\"?\")[0])}`;\n}\n\n/**\n * Pulls out the first path segment and formats it as a GC Node path\n * e.g. \"/dataStoreId/ddsId\" yields \"/dataStoreId\"\n */\nexport function dataStoreNodePathOnly(subDataStorePath: string): string {\n\treturn `/${subDataStorePath.split(\"/\")[1]}`;\n}\n\n/**\n * Utility to implement compat behaviors given an unknown message type\n * The parameters are typed to support compile-time enforcement of handling all known types/behaviors\n *\n * @param _unknownGCMessageType - Typed as never to ensure all known types have been\n * handled before calling this function (e.g. in a switch statement).\n * @param compatBehavior - Typed redundantly with CompatModeBehavior to ensure handling is added when updating that type\n */\nexport function compatBehaviorAllowsGCMessageType(\n\t_unknownGCMessageType: never,\n\tcompatBehavior: \"Ignore\" | \"FailToProcess\" | undefined,\n): boolean {\n\t// undefined defaults to same behavior as \"FailToProcess\"\n\treturn compatBehavior === \"Ignore\";\n}\n"]}
|
|
1
|
+
{"version":3,"file":"gcHelpers.js","sourceRoot":"","sources":["../../src/gc/gcHelpers.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { ISnapshotTree } from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tIGarbageCollectionDetailsBase,\n\tgcBlobPrefix,\n\tgcDeletedBlobKey,\n\tgcTombstoneBlobKey,\n\tIGarbageCollectionData,\n} from \"@fluidframework/runtime-definitions/internal\";\nimport type { IConfigProvider } from \"@fluidframework/telemetry-utils/internal\";\n\nimport {\n\tGCFeatureMatrix,\n\tGCVersion,\n\tIGCMetadata,\n\tgcVersionUpgradeToV4Key,\n\tnextGCVersion,\n\tstableGCVersion,\n} from \"./gcDefinitions.js\";\nimport {\n\tIGarbageCollectionNodeData,\n\tIGarbageCollectionSnapshotData,\n\tIGarbageCollectionState,\n} from \"./gcSummaryDefinitions.js\";\n\nexport function getGCVersion(metadata?: IGCMetadata): GCVersion {\n\tif (!metadata) {\n\t\t// Force to 0/disallowed in prior versions\n\t\treturn 0;\n\t}\n\treturn metadata.gcFeature ?? 0;\n}\n\n/** Indicates what GC version is in effect for new GC data being written in this session */\nexport function getGCVersionInEffect(configProvider: IConfigProvider): number {\n\t// If version upgrade is not enabled, fall back to the stable GC version.\n\treturn configProvider.getBoolean(gcVersionUpgradeToV4Key) === true\n\t\t? nextGCVersion\n\t\t: stableGCVersion;\n}\n\n/**\n * Indicates whether Sweep is allowed for this document based on the persisted GC Feature Matrix and current gcGeneration.\n * This applies to the entire Sweep Phase the same - both Tombstone Enforcement (i.e. should loading a Tombstone fail?) and Deletion.\n *\n * In order to protect old documents that were created at a time when known bugs exist that violate GC's invariants\n * such that enforcing GC Sweep would cause legitimate data loss, the container author may increment the generation value for Sweep\n * such that containers created with a different value will not be subjected to GC Sweep.\n *\n * If no generation is provided, Sweep will be enabled for all documents.\n *\n * For backwards compatibility, the current generation value is also compared against the persisted gcTombstoneGeneration if present.\n *\n * @param featureMatrix - The GC Feature Matrix, containing the persisted generation value\n * @param currentGeneration - The current app-provided gcGeneration value\n * @returns true if GC Sweep should be allowed for this document\n */\nexport function shouldAllowGcSweep(\n\tfeatureMatrix: GCFeatureMatrix,\n\tcurrentGeneration: number | undefined,\n): boolean {\n\t// If no Generation value is provided for this session, default to true\n\tif (currentGeneration === undefined) {\n\t\treturn true;\n\t}\n\n\t// tombstoneGeneration is the predecessor and needs to be supported for back-compat reasons\n\tconst targetGeneration = featureMatrix.tombstoneGeneration ?? featureMatrix.gcGeneration;\n\n\treturn currentGeneration === targetGeneration;\n}\n\n/**\n * Sorts the given GC state as per the id of the GC nodes. It also sorts the outbound routes array of each node.\n */\nexport function generateSortedGCState(\n\tgcState: IGarbageCollectionState,\n): IGarbageCollectionState {\n\tconst sortableArray: [string, IGarbageCollectionNodeData][] = Object.entries(\n\t\tgcState.gcNodes,\n\t);\n\tsortableArray.sort(([a], [b]) => a.localeCompare(b));\n\tconst sortedGCState: IGarbageCollectionState = { gcNodes: {} };\n\tfor (const [nodeId, nodeData] of sortableArray) {\n\t\tnodeData.outboundRoutes.sort();\n\t\tsortedGCState.gcNodes[nodeId] = nodeData;\n\t}\n\treturn sortedGCState;\n}\n\n/**\n * Concatenates the given GC states and returns the concatenated GC state.\n */\nexport function concatGarbageCollectionStates(\n\tgcState1: IGarbageCollectionState,\n\tgcState2: IGarbageCollectionState,\n): IGarbageCollectionState {\n\tconst combinedGCNodes: { [id: string]: IGarbageCollectionNodeData } = {};\n\tfor (const [nodeId, nodeData] of Object.entries(gcState1.gcNodes)) {\n\t\tcombinedGCNodes[nodeId] = {\n\t\t\toutboundRoutes: Array.from(nodeData.outboundRoutes),\n\t\t\tunreferencedTimestampMs: nodeData.unreferencedTimestampMs,\n\t\t};\n\t}\n\n\tfor (const [nodeId, nodeData] of Object.entries(gcState2.gcNodes)) {\n\t\tlet combineNodeData = combinedGCNodes[nodeId];\n\t\tif (combineNodeData === undefined) {\n\t\t\tcombineNodeData = {\n\t\t\t\toutboundRoutes: Array.from(nodeData.outboundRoutes),\n\t\t\t\tunreferencedTimestampMs: nodeData.unreferencedTimestampMs,\n\t\t\t};\n\t\t} else {\n\t\t\t// Validate that same node doesn't have different unreferenced timestamp.\n\t\t\tif (\n\t\t\t\tnodeData.unreferencedTimestampMs !== undefined &&\n\t\t\t\tcombineNodeData.unreferencedTimestampMs !== undefined\n\t\t\t) {\n\t\t\t\tassert(\n\t\t\t\t\tnodeData.unreferencedTimestampMs === combineNodeData.unreferencedTimestampMs,\n\t\t\t\t\t0x5d7 /* Two entries for the same GC node with different unreferenced timestamp */,\n\t\t\t\t);\n\t\t\t}\n\t\t\tcombineNodeData = {\n\t\t\t\toutboundRoutes: [\n\t\t\t\t\t...new Set([...nodeData.outboundRoutes, ...combineNodeData.outboundRoutes]),\n\t\t\t\t],\n\t\t\t\tunreferencedTimestampMs:\n\t\t\t\t\tnodeData.unreferencedTimestampMs ?? combineNodeData.unreferencedTimestampMs,\n\t\t\t};\n\t\t}\n\t\tcombinedGCNodes[nodeId] = combineNodeData;\n\t}\n\treturn { gcNodes: combinedGCNodes };\n}\n\n/**\n * Helper function that clones the GC data.\n * @param gcData - The GC data to clone.\n * @returns a clone of the given GC data.\n */\nexport function cloneGCData(gcData: IGarbageCollectionData): IGarbageCollectionData {\n\tconst clonedGCNodes: { [id: string]: string[] } = {};\n\tfor (const [id, outboundRoutes] of Object.entries(gcData.gcNodes)) {\n\t\tclonedGCNodes[id] = Array.from(outboundRoutes);\n\t}\n\treturn {\n\t\tgcNodes: clonedGCNodes,\n\t};\n}\n\n/**\n * Concatenates the given GC data and returns the concatenated GC data.\n */\nexport function concatGarbageCollectionData(\n\tgcData1: IGarbageCollectionData,\n\tgcData2: IGarbageCollectionData,\n) {\n\tconst combinedGCData: IGarbageCollectionData = cloneGCData(gcData1);\n\tfor (const [id, routes] of Object.entries(gcData2.gcNodes)) {\n\t\tif (combinedGCData.gcNodes[id] === undefined) {\n\t\t\tcombinedGCData.gcNodes[id] = Array.from(routes);\n\t\t} else {\n\t\t\t// Non null asserting here since we are checking if combinedGCData.gcNodes[id] is not undefined above.\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tconst combinedRoutes = [...routes, ...combinedGCData.gcNodes[id]!];\n\t\t\tcombinedGCData.gcNodes[id] = [...new Set(combinedRoutes)];\n\t\t}\n\t}\n\treturn combinedGCData;\n}\n\n/**\n * Gets the base garbage collection state from the given snapshot tree. It contains GC state, deleted nodes and\n * tombstones. The GC state may be written into multiple blobs. Merge the GC state from all such blobs into one.\n */\nexport async function getGCDataFromSnapshot(\n\tgcSnapshotTree: ISnapshotTree,\n\treadAndParseBlob: <T>(id: string) => Promise<T>,\n): Promise<IGarbageCollectionSnapshotData> {\n\tlet rootGCState: IGarbageCollectionState = { gcNodes: {} };\n\tlet tombstones: string[] | undefined;\n\tlet deletedNodes: string[] | undefined;\n\tfor (const key of Object.keys(gcSnapshotTree.blobs)) {\n\t\t// Update deleted nodes blob.\n\t\tif (key === gcDeletedBlobKey) {\n\t\t\t// Non null asserting here, we can change this to Object.entries later\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tdeletedNodes = await readAndParseBlob<string[]>(gcSnapshotTree.blobs[key]!);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Update tombstone blob.\n\t\tif (key === gcTombstoneBlobKey) {\n\t\t\t// Non null asserting here, we can change this to Object.entries later\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\ttombstones = await readAndParseBlob<string[]>(gcSnapshotTree.blobs[key]!);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Skip blobs that do not start with the GC prefix.\n\t\tif (!key.startsWith(gcBlobPrefix)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst blobId = gcSnapshotTree.blobs[key];\n\t\tif (blobId === undefined) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst gcState = await readAndParseBlob<IGarbageCollectionState>(blobId);\n\t\tassert(gcState !== undefined, 0x5d8 /* GC blob missing from snapshot */);\n\t\t// Merge the GC state of this blob into the root GC state.\n\t\trootGCState = concatGarbageCollectionStates(rootGCState, gcState);\n\t}\n\treturn { gcState: rootGCState, tombstones, deletedNodes };\n}\n\n/**\n * Helper function that unpacks the GC details of the children from a given node's GC details.\n * @param gcDetails - The GC details of a node.\n * @returns A map of GC details of each children of the the given node.\n */\nexport function unpackChildNodesGCDetails(gcDetails: IGarbageCollectionDetailsBase) {\n\tconst childGCDetailsMap: Map<string, IGarbageCollectionDetailsBase> = new Map();\n\n\t// If GC data is not available, bail out.\n\tif (gcDetails.gcData === undefined) {\n\t\treturn childGCDetailsMap;\n\t}\n\n\tconst gcNodes = gcDetails.gcData.gcNodes;\n\tfor (const [id, outboundRoutes] of Object.entries(gcNodes)) {\n\t\t// Skip self-node since only children GC data is to be generated.\n\t\tif (id === \"/\") {\n\t\t\tcontinue;\n\t\t}\n\n\t\tassert(id.startsWith(\"/\"), 0x5d9 /* node id should always be an absolute route */);\n\t\t// TODO Why are we non null asserting here\n\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\tconst childId = id.split(\"/\")[1]!;\n\t\tlet childGCNodeId = id.slice(childId.length + 1);\n\t\t// GC node id always begins with \"/\". Handle the special case where a child's id in the parent's GC nodes is\n\t\t// of format `/root`. In this case, the childId is root and childGCNodeId is \"\". Make childGCNodeId = \"/\".\n\t\tif (childGCNodeId === \"\") {\n\t\t\tchildGCNodeId = \"/\";\n\t\t}\n\n\t\tlet childGCDetails = childGCDetailsMap.get(childId);\n\t\tif (childGCDetails === undefined) {\n\t\t\tchildGCDetails = { gcData: { gcNodes: {} }, usedRoutes: [] };\n\t\t}\n\t\t// gcData should not undefined as its always at least initialized as empty above.\n\t\tassert(\n\t\t\tchildGCDetails.gcData !== undefined,\n\t\t\t0x5da /* Child GC data should have been initialized */,\n\t\t);\n\t\tchildGCDetails.gcData.gcNodes[childGCNodeId] = [...new Set(outboundRoutes)];\n\t\tchildGCDetailsMap.set(childId, childGCDetails);\n\t}\n\n\tif (gcDetails.usedRoutes === undefined) {\n\t\treturn childGCDetailsMap;\n\t}\n\n\t// Remove the node's self used route, if any, and generate the children used routes.\n\tconst usedRoutes = gcDetails.usedRoutes.filter((route) => route !== \"\" && route !== \"/\");\n\tfor (const route of usedRoutes) {\n\t\tassert(route.startsWith(\"/\"), 0x5db /* Used route should always be an absolute route */);\n\t\t// TODO Why are we non null asserting here\n\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\tconst childId = route.split(\"/\")[1]!;\n\t\tconst childUsedRoute = route.slice(childId.length + 1);\n\n\t\tconst childGCDetails = childGCDetailsMap.get(childId);\n\t\tassert(\n\t\t\tchildGCDetails?.usedRoutes !== undefined,\n\t\t\t0x5dc /* This should have be initialized when generate GC nodes above */,\n\t\t);\n\n\t\tchildGCDetails.usedRoutes.push(childUsedRoute);\n\t\tchildGCDetailsMap.set(childId, childGCDetails);\n\t}\n\treturn childGCDetailsMap;\n}\n\n/**\n * Trims the leading and trailing slashes from the given string.\n * @param str - A string that may contain leading and / or trailing slashes.\n * @returns A new string without leading and trailing slashes.\n */\nfunction trimLeadingAndTrailingSlashes(str: string) {\n\treturn str.replace(/^\\/+|\\/+$/g, \"\");\n}\n\n/** Reformats a request URL to match expected format for a GC node path */\nexport function urlToGCNodePath(url: string): string {\n\t// TODO Why are we non null asserting here\n\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\treturn `/${trimLeadingAndTrailingSlashes(url.split(\"?\")[0]!)}`;\n}\n\n/**\n * Pulls out the first path segment and formats it as a GC Node path\n * e.g. \"/dataStoreId/ddsId\" yields \"/dataStoreId\"\n */\nexport function dataStoreNodePathOnly(subDataStorePath: string): string {\n\treturn `/${subDataStorePath.split(\"/\")[1]}`;\n}\n\n/**\n * Utility to implement compat behaviors given an unknown message type\n * The parameters are typed to support compile-time enforcement of handling all known types/behaviors\n *\n * @param _unknownGCMessageType - Typed as never to ensure all known types have been\n * handled before calling this function (e.g. in a switch statement).\n * @param compatBehavior - Typed redundantly with CompatModeBehavior to ensure handling is added when updating that type\n */\nexport function compatBehaviorAllowsGCMessageType(\n\t_unknownGCMessageType: never,\n\tcompatBehavior: \"Ignore\" | \"FailToProcess\" | undefined,\n): boolean {\n\t// undefined defaults to same behavior as \"FailToProcess\"\n\treturn compatBehavior === \"Ignore\";\n}\n"]}
|
package/lib/gc/gcTelemetry.d.ts
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import { IGarbageCollectionData } from "@fluidframework/runtime-definitions/internal";
|
|
6
|
-
import { ITelemetryLoggerExt, MonitoringContext, type ITelemetryGenericEventExt } from "@fluidframework/telemetry-utils/internal";
|
|
6
|
+
import { ITelemetryLoggerExt, MonitoringContext, type ITelemetryGenericEventExt, type ITelemetryPropertiesExt } from "@fluidframework/telemetry-utils/internal";
|
|
7
7
|
import { RuntimeHeaderData } from "../containerRuntime.js";
|
|
8
8
|
import { ICreateContainerMetadata } from "../summary/index.js";
|
|
9
9
|
import { GCNodeType, IGarbageCollectorConfigs } from "./gcDefinitions.js";
|
|
10
10
|
import { UnreferencedStateTracker } from "./gcUnreferencedStateTracker.js";
|
|
11
|
-
type NodeUsageType = "Changed" | "Loaded" | "Revived";
|
|
11
|
+
type NodeUsageType = "Changed" | "Loaded" | "Revived" | "Realized";
|
|
12
12
|
/** Properties that are common to IUnreferencedEventProps and INodeUsageProps */
|
|
13
13
|
interface ICommonProps {
|
|
14
14
|
usageType: NodeUsageType;
|
|
@@ -16,6 +16,7 @@ interface ICommonProps {
|
|
|
16
16
|
isTombstoned: boolean;
|
|
17
17
|
lastSummaryTime?: number;
|
|
18
18
|
headers?: RuntimeHeaderData;
|
|
19
|
+
additionalProps?: ITelemetryPropertiesExt;
|
|
19
20
|
}
|
|
20
21
|
/** Properties passed to nodeUsed function when a node is used. */
|
|
21
22
|
interface INodeUsageProps extends ICommonProps {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gcTelemetry.d.ts","sourceRoot":"","sources":["../../src/gc/gcTelemetry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,sBAAsB,EAAE,MAAM,8CAA8C,CAAC;AACtF,OAAO,EACN,mBAAmB,EACnB,iBAAiB,EAGjB,KAAK,yBAAyB,EAC9B,MAAM,0CAA0C,CAAC;AAElD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,OAAO,EAEN,UAAU,EACV,wBAAwB,EAKxB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAE3E,KAAK,aAAa,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"gcTelemetry.d.ts","sourceRoot":"","sources":["../../src/gc/gcTelemetry.ts"],"names":[],"mappings":"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"}
|
package/lib/gc/gcTelemetry.js
CHANGED
|
@@ -134,7 +134,7 @@ export class GCTelemetryTracker {
|
|
|
134
134
|
// Events generated:
|
|
135
135
|
// InactiveObject_Loaded, SweepReadyObject_Loaded
|
|
136
136
|
if (usageType === "Loaded") {
|
|
137
|
-
const { id, fromId, headers, gcConfigs, ...detailedProps } = unrefEventProps;
|
|
137
|
+
const { id, fromId, headers, gcConfigs, additionalProps, ...detailedProps } = unrefEventProps;
|
|
138
138
|
const event = {
|
|
139
139
|
eventName: `${state}Object_${usageType}`,
|
|
140
140
|
...tagCodeArtifacts({ pkg: packagePath?.join("/") }),
|
|
@@ -142,7 +142,7 @@ export class GCTelemetryTracker {
|
|
|
142
142
|
id,
|
|
143
143
|
fromId,
|
|
144
144
|
headers: { ...headers },
|
|
145
|
-
details: detailedProps,
|
|
145
|
+
details: { ...detailedProps, ...additionalProps },
|
|
146
146
|
gcConfigs,
|
|
147
147
|
};
|
|
148
148
|
// These are logged as generic events and not errors because there can be false positives. The Tombstone
|
|
@@ -159,7 +159,7 @@ export class GCTelemetryTracker {
|
|
|
159
159
|
// GC_Tombstone_DataStore_Requested, GC_Tombstone_DataStore_Changed, GC_Tombstone_DataStore_Revived
|
|
160
160
|
// GC_Tombstone_SubDataStore_Requested, GC_Tombstone_SubDataStore_Changed, GC_Tombstone_SubDataStore_Revived
|
|
161
161
|
// GC_Tombstone_Blob_Requested, GC_Tombstone_Blob_Changed, GC_Tombstone_Blob_Revived
|
|
162
|
-
const { id, fromId, headers, gcConfigs, ...detailedProps } = unrefEventProps;
|
|
162
|
+
const { id, fromId, headers, gcConfigs, additionalProps, ...detailedProps } = unrefEventProps;
|
|
163
163
|
const eventUsageName = usageType === "Loaded" ? "Requested" : usageType;
|
|
164
164
|
const event = {
|
|
165
165
|
eventName: `GC_Tombstone_${nodeType}_${eventUsageName}`,
|
|
@@ -168,7 +168,7 @@ export class GCTelemetryTracker {
|
|
|
168
168
|
id,
|
|
169
169
|
fromId,
|
|
170
170
|
headers: { ...headers },
|
|
171
|
-
details: detailedProps, // Also includes some properties from INodeUsageProps type
|
|
171
|
+
details: { ...detailedProps, ...additionalProps }, // Also includes some properties from INodeUsageProps type
|
|
172
172
|
gcConfigs,
|
|
173
173
|
tombstoneFlags: {
|
|
174
174
|
DisableTombstone: this.mc.config.getBoolean(disableTombstoneKey),
|
|
@@ -244,7 +244,7 @@ export class GCTelemetryTracker {
|
|
|
244
244
|
// InactiveObject_Loaded, InactiveObject_Changed, InactiveObject_Revived
|
|
245
245
|
// SweepReadyObject_Loaded, SweepReadyObject_Changed, SweepReadyObject_Revived
|
|
246
246
|
for (const eventProps of this.pendingEventsQueue) {
|
|
247
|
-
const { usageType, state, id, fromId, headers, gcConfigs, ...detailedProps } = eventProps;
|
|
247
|
+
const { usageType, state, id, fromId, headers, gcConfigs, additionalProps, ...detailedProps } = eventProps;
|
|
248
248
|
/**
|
|
249
249
|
* Revived event is logged only if the node is active. If the node is not active, the reference to it was
|
|
250
250
|
* from another unreferenced node and this scenario is not interesting to log.
|
|
@@ -263,7 +263,7 @@ export class GCTelemetryTracker {
|
|
|
263
263
|
id,
|
|
264
264
|
fromId,
|
|
265
265
|
headers: { ...headers },
|
|
266
|
-
details: detailedProps,
|
|
266
|
+
details: { ...detailedProps, ...additionalProps },
|
|
267
267
|
gcConfigs,
|
|
268
268
|
...tagCodeArtifacts({
|
|
269
269
|
pkg: pkg?.join("/"),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gcTelemetry.js","sourceRoot":"","sources":["../../src/gc/gcTelemetry.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { Tagged } from \"@fluidframework/core-interfaces\";\nimport { IGarbageCollectionData } from \"@fluidframework/runtime-definitions/internal\";\nimport {\n\tITelemetryLoggerExt,\n\tMonitoringContext,\n\tgenerateStack,\n\ttagCodeArtifacts,\n\ttype ITelemetryGenericEventExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { RuntimeHeaderData } from \"../containerRuntime.js\";\nimport { ICreateContainerMetadata } from \"../summary/index.js\";\n\nimport {\n\tGCFeatureMatrix,\n\tGCNodeType,\n\tIGarbageCollectorConfigs,\n\tUnreferencedState,\n\tdisableTombstoneKey,\n\tthrowOnTombstoneLoadOverrideKey,\n\tthrowOnTombstoneUsageKey,\n} from \"./gcDefinitions.js\";\nimport { getGCVersionInEffect } from \"./gcHelpers.js\";\nimport { UnreferencedStateTracker } from \"./gcUnreferencedStateTracker.js\";\n\ntype NodeUsageType = \"Changed\" | \"Loaded\" | \"Revived\";\n\n/** Properties that are common to IUnreferencedEventProps and INodeUsageProps */\ninterface ICommonProps {\n\tusageType: NodeUsageType;\n\tcompletedGCRuns: number;\n\tisTombstoned: boolean;\n\tlastSummaryTime?: number;\n\theaders?: RuntimeHeaderData;\n}\n\n/** The event that is logged when unreferenced node is used after a certain time. */\ninterface IUnreferencedEventProps extends ICreateContainerMetadata, ICommonProps {\n\t/** The id that GC uses to track the node. May or may not match id */\n\ttrackedId: string;\n\tstate: UnreferencedState;\n\t/** The full path (in GC Path format) to the node in question */\n\tid: Tagged<string>;\n\tfromId?: Tagged<string>;\n\ttype: GCNodeType;\n\tunrefTime: number;\n\tage: number;\n\t// Expanding GC feature matrix. Without doing this, the configs cannot be logged in telemetry directly.\n\tgcConfigs: Omit<IGarbageCollectorConfigs, \"persistedGcFeatureMatrix\"> & {\n\t\t[K in keyof GCFeatureMatrix]: GCFeatureMatrix[K];\n\t};\n\ttimeout?: number;\n}\n\n/** Properties passed to nodeUsed function when a node is used. */\ninterface INodeUsageProps extends ICommonProps {\n\t/** The full path (in GC Path format) to the node in question */\n\tid: string;\n\t/** Latest timestamp received from the server, as a baseline for computing GC state/age */\n\tcurrentReferenceTimestampMs: number;\n\t/** The package path of the node. This may not be available if the node hasn't been loaded yet */\n\tpackagePath: readonly string[] | undefined;\n\t/** In case of Revived - what node added the reference? */\n\tfromId?: string;\n\t/** In case of Revived - was it revived due to autorecovery? */\n\tautorecovery?: true;\n\t/** URL (including query string) if this usage came from a request */\n\trequestUrl?: string;\n\t/** Original request headers if this usage came from a request or handle.get */\n\trequestHeaders?: string;\n}\n\n/**\n * Encapsulates the logic that tracks the various telemetry logged by the Garbage Collector.\n *\n * These events are not logged as errors, just generic events, since there can be false positives:\n *\n * 1. inactiveObject telemetry - When an inactive node is used - A node that has been unreferenced for inactiveTimeoutMs.\n * 2. tombstoneReadyObject telemetry - When a tombstone-ready node is used - A node that has been unreferenced for tombstoneTimeoutMs.\n * 3. sweepReadyObject telemetry - When a sweep-ready node is used - A node that has been unreferenced for tombstoneTimeoutMs + sweepGracePeriodMs.\n *\n * These events are logged as errors since they are based on the core GC logic:\n *\n * 1. Tombstone telemetry - When a tombstoned node is used - A node that has been marked as tombstone.\n * 2. Unknown outbound reference telemetry - When a node is referenced but GC was not notified of it when the new reference appeared.\n *\n * Note: The telemetry for a Deleted node being used is logged elsewhere in this package.\n */\nexport class GCTelemetryTracker {\n\t// Keeps track of unreferenced events that are logged for a node. This is used to limit the log generation to one\n\t// per event per node.\n\tprivate readonly loggedUnreferencedEvents: Set<string> = new Set();\n\t// Queue for unreferenced events that should be logged the next time GC runs.\n\tprivate pendingEventsQueue: IUnreferencedEventProps[] = [];\n\n\tconstructor(\n\t\tprivate readonly mc: MonitoringContext,\n\t\tprivate readonly configs: IGarbageCollectorConfigs,\n\t\tprivate readonly isSummarizerClient: boolean,\n\t\tprivate readonly createContainerMetadata: ICreateContainerMetadata,\n\t\tprivate readonly getNodeType: (nodeId: string) => GCNodeType,\n\t\tprivate readonly getNodeStateTracker: (\n\t\t\tnodeId: string,\n\t\t) => UnreferencedStateTracker | undefined,\n\t\tprivate readonly getNodePackagePath: (\n\t\t\tnodePath: string,\n\t\t) => Promise<readonly string[] | undefined>,\n\t) {}\n\n\t/**\n\t * Returns whether an event should be logged for a node that isn't active anymore. This does not apply to\n\t * tombstoned nodes for which an event is always logged. Some scenarios where we won't log:\n\t * 1. When a DDS is changed. The corresponding data store's event will be logged instead.\n\t * 2. An event is logged only once per container instance per event per node.\n\t */\n\tprivate shouldLogNonActiveEvent(\n\t\tnodeType: GCNodeType,\n\t\tusageType: NodeUsageType,\n\t\tnodeStateTracker: UnreferencedStateTracker,\n\t\tuniqueEventId: string,\n\t) {\n\t\tif (nodeStateTracker.state === UnreferencedState.Active) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (nodeType === GCNodeType.Other) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// For sub data store (DDS) nodes, if they are changed, its data store will also be changed,\n\t\t// so skip logging to make the telemetry less noisy.\n\t\tif (nodeType === GCNodeType.SubDataStore && usageType === \"Changed\") {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Non-tombstone events are logged once per event per node. A unique id is generated by joining\n\t\t// node state (inactive / sweep ready), node's id and usage (loaded / changed / revived).\n\t\tif (this.loggedUnreferencedEvents.has(uniqueEventId)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Called when a node is used. If the node is inactive or tombstoned, log telemetry indicating object is used\n\t * when it should not have been.\n\t * @param trackedId - The id that GC uses to track the node. For SubDataStore nodes, this should be the DataStore ID.\n\t * @param INodeUsageProps - All kind of details about this event to be logged\n\t */\n\tpublic nodeUsed(\n\t\ttrackedId: string,\n\t\t{\n\t\t\tusageType,\n\t\t\tcurrentReferenceTimestampMs,\n\t\t\tpackagePath,\n\t\t\tid: untaggedId,\n\t\t\tfromId: untaggedFromId,\n\t\t\tisTombstoned,\n\t\t\t...otherNodeUsageProps\n\t\t}: INodeUsageProps,\n\t) {\n\t\t// Note: For SubDataStore Load usage, trackedId will be the DataStore's id, not the full path in question.\n\t\t// This is necessary because the SubDataStore path may be unrecognized by GC (if suited for a custom request handler)\n\t\tconst nodeStateTracker = this.getNodeStateTracker(trackedId);\n\t\tconst nodeType = this.getNodeType(untaggedId);\n\n\t\tconst timeout = (() => {\n\t\t\tswitch (nodeStateTracker?.state) {\n\t\t\t\tcase UnreferencedState.Inactive:\n\t\t\t\t\treturn this.configs.inactiveTimeoutMs;\n\t\t\t\tcase UnreferencedState.TombstoneReady:\n\t\t\t\t\treturn this.configs.tombstoneTimeoutMs;\n\t\t\t\tcase UnreferencedState.SweepReady:\n\t\t\t\t\treturn (\n\t\t\t\t\t\tthis.configs.tombstoneTimeoutMs &&\n\t\t\t\t\t\tthis.configs.tombstoneTimeoutMs + this.configs.sweepGracePeriodMs\n\t\t\t\t\t);\n\t\t\t\tdefault:\n\t\t\t\t\treturn undefined;\n\t\t\t}\n\t\t})();\n\t\tconst { persistedGcFeatureMatrix, ...configs } = this.configs;\n\t\tconst unrefEventProps = {\n\t\t\ttrackedId,\n\t\t\ttype: nodeType,\n\t\t\tunrefTime: nodeStateTracker?.unreferencedTimestampMs ?? -1,\n\t\t\tage:\n\t\t\t\tnodeStateTracker !== undefined\n\t\t\t\t\t? currentReferenceTimestampMs - nodeStateTracker.unreferencedTimestampMs\n\t\t\t\t\t: -1,\n\t\t\ttimeout,\n\t\t\tisTombstoned,\n\t\t\t...tagCodeArtifacts({ id: untaggedId, fromId: untaggedFromId }),\n\t\t\t...otherNodeUsageProps,\n\t\t\t...this.createContainerMetadata,\n\t\t\tgcConfigs: { ...configs, ...persistedGcFeatureMatrix },\n\t\t} satisfies Omit<IUnreferencedEventProps, \"state\" | \"usageType\"> &\n\t\t\ttypeof otherNodeUsageProps;\n\n\t\t// If the node that is used is tombstoned, log a tombstone telemetry.\n\t\tif (isTombstoned) {\n\t\t\tthis.logTombstoneUsageTelemetry(unrefEventProps, nodeType, usageType, packagePath);\n\t\t}\n\n\t\t// After logging tombstone telemetry, if the node's unreferenced state is not tracked, there is nothing\n\t\t// else to log.\n\t\tif (nodeStateTracker === undefined) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst state = nodeStateTracker.state;\n\t\tconst uniqueEventId = `${state}-${untaggedId}-${usageType}`;\n\n\t\tif (!this.shouldLogNonActiveEvent(nodeType, usageType, nodeStateTracker, uniqueEventId)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Add the unique event id so that we don't generate a log for this event again in this session.\n\t\tthis.loggedUnreferencedEvents.add(uniqueEventId);\n\n\t\t// For summarizer client, queue the event so it is logged the next time GC runs if the event is still valid.\n\t\t// For non-summarizer client, log the event now since GC won't run on it. This may result in false positives\n\t\t// but it's a good signal nonetheless and we can consume it with a grain of salt.\n\t\t// Inactive errors are usages of Objects that are unreferenced for at least a period of 7 days.\n\t\t// SweepReady errors are usages of Objects that will be deleted by GC Sweep!\n\t\tif (this.isSummarizerClient) {\n\t\t\tthis.pendingEventsQueue.push({\n\t\t\t\t...unrefEventProps, // Note: Contains some properties from INodeUsageProps as well\n\t\t\t\tusageType,\n\t\t\t\tstate,\n\t\t\t});\n\t\t} else {\n\t\t\t// For non-summarizer clients, only log \"Loaded\" type events since these objects may not be loaded in the\n\t\t\t// summarizer clients if they are based off of user actions (such as scrolling to content for these objects)\n\t\t\t// Events generated:\n\t\t\t// InactiveObject_Loaded, SweepReadyObject_Loaded\n\t\t\tif (usageType === \"Loaded\") {\n\t\t\t\tconst { id, fromId, headers, gcConfigs, ...detailedProps } = unrefEventProps;\n\t\t\t\tconst event = {\n\t\t\t\t\teventName: `${state}Object_${usageType}`,\n\t\t\t\t\t...tagCodeArtifacts({ pkg: packagePath?.join(\"/\") }),\n\t\t\t\t\tstack: generateStack(),\n\t\t\t\t\tid,\n\t\t\t\t\tfromId,\n\t\t\t\t\theaders: { ...headers },\n\t\t\t\t\tdetails: detailedProps,\n\t\t\t\t\tgcConfigs,\n\t\t\t\t};\n\n\t\t\t\t// These are logged as generic events and not errors because there can be false positives. The Tombstone\n\t\t\t\t// and Delete errors are separately logged and are reliable.\n\t\t\t\tthis.mc.logger.sendTelemetryEvent(event);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Logs telemetry when a tombstoned object is changed, revived or loaded.\n\t */\n\tprivate logTombstoneUsageTelemetry(\n\t\tunrefEventProps: Omit<IUnreferencedEventProps, \"state\" | \"usageType\">,\n\t\tnodeType: GCNodeType,\n\t\tusageType: NodeUsageType,\n\t\tpackagePath?: readonly string[],\n\t) {\n\t\t// This will log the following events:\n\t\t// GC_Tombstone_DataStore_Requested, GC_Tombstone_DataStore_Changed, GC_Tombstone_DataStore_Revived\n\t\t// GC_Tombstone_SubDataStore_Requested, GC_Tombstone_SubDataStore_Changed, GC_Tombstone_SubDataStore_Revived\n\t\t// GC_Tombstone_Blob_Requested, GC_Tombstone_Blob_Changed, GC_Tombstone_Blob_Revived\n\t\tconst { id, fromId, headers, gcConfigs, ...detailedProps } = unrefEventProps;\n\t\tconst eventUsageName = usageType === \"Loaded\" ? \"Requested\" : usageType;\n\t\tconst event = {\n\t\t\teventName: `GC_Tombstone_${nodeType}_${eventUsageName}`,\n\t\t\t...tagCodeArtifacts({ pkg: packagePath?.join(\"/\") }),\n\t\t\tstack: generateStack(),\n\t\t\tid,\n\t\t\tfromId,\n\t\t\theaders: { ...headers },\n\t\t\tdetails: detailedProps, // Also includes some properties from INodeUsageProps type\n\t\t\tgcConfigs,\n\t\t\ttombstoneFlags: {\n\t\t\t\tDisableTombstone: this.mc.config.getBoolean(disableTombstoneKey),\n\t\t\t\tThrowOnTombstoneUsage: this.mc.config.getBoolean(throwOnTombstoneUsageKey),\n\t\t\t\tThrowOnTombstoneLoad: this.mc.config.getBoolean(throwOnTombstoneLoadOverrideKey),\n\t\t\t},\n\t\t};\n\n\t\tif (\n\t\t\t(usageType === \"Loaded\" &&\n\t\t\t\tthis.configs.throwOnTombstoneLoad &&\n\t\t\t\t!headers?.allowTombstone) ||\n\t\t\t(usageType === \"Changed\" && this.configs.throwOnTombstoneUsage)\n\t\t) {\n\t\t\tthis.mc.logger.sendErrorEvent(event);\n\t\t} else {\n\t\t\tthis.mc.logger.sendTelemetryEvent(event);\n\t\t}\n\t}\n\n\t/**\n\t * Log all new references or outbound routes in the current graph that haven't been explicitly notified to GC.\n\t * The principle is that every new reference or outbound route must be notified to GC via the\n\t * addedOutboundReference method. It it hasn't, its a bug and we want to identify these scenarios.\n\t *\n\t * In more simple terms:\n\t * Missing Explicit References = Current References - Previous References - Explicitly Added References;\n\t *\n\t * @param currentGCData - The GC data (reference graph) from the current GC run.\n\t * @param previousGCData - The GC data (reference graph) from the previous GC run.\n\t * @param explicitReferences - New references added explicity between the previous and the current run.\n\t */\n\tpublic logIfMissingExplicitReferences(\n\t\tcurrentGCData: IGarbageCollectionData,\n\t\tpreviousGCData: IGarbageCollectionData,\n\t\texplicitReferences: Map<string, string[]>,\n\t\tlogger: ITelemetryLoggerExt,\n\t) {\n\t\tfor (const [nodeId, currentOutboundRoutes] of Object.entries(currentGCData.gcNodes)) {\n\t\t\tconst previousRoutes = previousGCData.gcNodes[nodeId] ?? [];\n\t\t\tconst explicitRoutes = explicitReferences.get(nodeId) ?? [];\n\n\t\t\t/**\n\t\t\t * 1. For routes in the current GC data, routes that were not present in previous GC data and did not have\n\t\t\t * explicit references should be added to missing explicit routes list.\n\t\t\t * 2. Only include data store and blob routes since GC only works for these two.\n\t\t\t * Note: Due to a bug with de-duped blobs, only adding data store routes for now.\n\t\t\t * 3. Ignore DDS routes to their parent datastores since those were added implicitly. So, there won't be\n\t\t\t * explicit routes to them.\n\t\t\t */\n\t\t\tconst missingExplicitRoutes: string[] = [];\n\t\t\tfor (const route of currentOutboundRoutes) {\n\t\t\t\tconst nodeType = this.getNodeType(route);\n\t\t\t\tif (\n\t\t\t\t\t(nodeType === GCNodeType.DataStore || nodeType === GCNodeType.Blob) &&\n\t\t\t\t\t!nodeId.startsWith(route) &&\n\t\t\t\t\t!previousRoutes.includes(route) &&\n\t\t\t\t\t!explicitRoutes.includes(route)\n\t\t\t\t) {\n\t\t\t\t\tmissingExplicitRoutes.push(route);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (missingExplicitRoutes.length > 0) {\n\t\t\t\t// Send as Generic not Error since there are known corner cases where this will fire.\n\t\t\t\t// E.g. If an old client re-references a node via an attach op (that doesn't include GC Data)\n\t\t\t\tlogger.sendTelemetryEvent({\n\t\t\t\t\teventName: \"gcUnknownOutboundReferences\",\n\t\t\t\t\t...tagCodeArtifacts({\n\t\t\t\t\t\tid: nodeId,\n\t\t\t\t\t\troutes: JSON.stringify(missingExplicitRoutes),\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Log events that are pending in pendingEventsQueue. This is called after GC runs in the summarizer client\n\t * so that the state of an unreferenced node is updated.\n\t */\n\tpublic async logPendingEvents(logger: ITelemetryLoggerExt) {\n\t\t// Events sent come only from the summarizer client. In between summaries, events are pushed to a queue and at\n\t\t// summary time they are then logged.\n\t\t// Events generated:\n\t\t// InactiveObject_Loaded, InactiveObject_Changed, InactiveObject_Revived\n\t\t// SweepReadyObject_Loaded, SweepReadyObject_Changed, SweepReadyObject_Revived\n\t\tfor (const eventProps of this.pendingEventsQueue) {\n\t\t\tconst { usageType, state, id, fromId, headers, gcConfigs, ...detailedProps } =\n\t\t\t\teventProps;\n\t\t\t/**\n\t\t\t * Revived event is logged only if the node is active. If the node is not active, the reference to it was\n\t\t\t * from another unreferenced node and this scenario is not interesting to log.\n\t\t\t * Loaded and Changed events are logged only if the node is not active. If the node is active, it was\n\t\t\t * revived and a Revived event will be logged for it.\n\t\t\t */\n\t\t\tconst nodeStateTracker = this.getNodeStateTracker(detailedProps.trackedId); // Note: This is never SubDataStore path\n\t\t\tconst active =\n\t\t\t\tnodeStateTracker === undefined || nodeStateTracker.state === UnreferencedState.Active;\n\t\t\tif ((usageType === \"Revived\") === active) {\n\t\t\t\tconst pkg = await this.getNodePackagePath(eventProps.id.value);\n\t\t\t\tconst fromPkg = eventProps.fromId\n\t\t\t\t\t? await this.getNodePackagePath(eventProps.fromId.value)\n\t\t\t\t\t: undefined;\n\t\t\t\tconst event = {\n\t\t\t\t\teventName: `${state}Object_${usageType}`,\n\t\t\t\t\tid,\n\t\t\t\t\tfromId,\n\t\t\t\t\theaders: { ...headers },\n\t\t\t\t\tdetails: detailedProps,\n\t\t\t\t\tgcConfigs,\n\t\t\t\t\t...tagCodeArtifacts({\n\t\t\t\t\t\tpkg: pkg?.join(\"/\"),\n\t\t\t\t\t\tfromPkg: fromPkg?.join(\"/\"),\n\t\t\t\t\t}),\n\t\t\t\t};\n\n\t\t\t\t// These are logged as generic events and not errors because there can be false positives. The Tombstone\n\t\t\t\t// and Delete errors are separately logged and are reliable.\n\t\t\t\tlogger.sendTelemetryEvent(event);\n\t\t\t}\n\t\t}\n\t\tthis.pendingEventsQueue = [];\n\t}\n}\n\n/**\n * Consolidates info / logic for logging when we encounter unexpected usage of GC'd objects. For example, when a\n * tombstoned or deleted object is loaded.\n */\nexport function sendGCUnexpectedUsageEvent(\n\tmc: MonitoringContext,\n\tevent: ITelemetryGenericEventExt & {\n\t\tcategory: \"error\" | \"generic\";\n\t\tgcTombstoneEnforcementAllowed: boolean | undefined;\n\t},\n\tpackagePath: readonly string[] | undefined,\n\terror?: unknown,\n) {\n\tevent.pkg = tagCodeArtifacts({ pkg: packagePath?.join(\"/\") })?.pkg;\n\tevent.tombstoneFlags = JSON.stringify({\n\t\tDisableTombstone: mc.config.getBoolean(disableTombstoneKey),\n\t\tThrowOnTombstoneUsage: mc.config.getBoolean(throwOnTombstoneUsageKey),\n\t\tThrowOnTombstoneLoad: mc.config.getBoolean(throwOnTombstoneLoadOverrideKey),\n\t});\n\tevent.gcVersion = getGCVersionInEffect(mc.config);\n\n\tmc.logger.sendTelemetryEvent(event, error);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"gcTelemetry.js","sourceRoot":"","sources":["../../src/gc/gcTelemetry.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { Tagged } from \"@fluidframework/core-interfaces\";\nimport { IGarbageCollectionData } from \"@fluidframework/runtime-definitions/internal\";\nimport {\n\tITelemetryLoggerExt,\n\tMonitoringContext,\n\tgenerateStack,\n\ttagCodeArtifacts,\n\ttype ITelemetryGenericEventExt,\n\ttype ITelemetryPropertiesExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { RuntimeHeaderData } from \"../containerRuntime.js\";\nimport { ICreateContainerMetadata } from \"../summary/index.js\";\n\nimport {\n\tGCFeatureMatrix,\n\tGCNodeType,\n\tIGarbageCollectorConfigs,\n\tUnreferencedState,\n\tdisableTombstoneKey,\n\tthrowOnTombstoneLoadOverrideKey,\n\tthrowOnTombstoneUsageKey,\n} from \"./gcDefinitions.js\";\nimport { getGCVersionInEffect } from \"./gcHelpers.js\";\nimport { UnreferencedStateTracker } from \"./gcUnreferencedStateTracker.js\";\n\ntype NodeUsageType = \"Changed\" | \"Loaded\" | \"Revived\" | \"Realized\";\n\n/** Properties that are common to IUnreferencedEventProps and INodeUsageProps */\ninterface ICommonProps {\n\tusageType: NodeUsageType;\n\tcompletedGCRuns: number;\n\tisTombstoned: boolean;\n\tlastSummaryTime?: number;\n\theaders?: RuntimeHeaderData;\n\tadditionalProps?: ITelemetryPropertiesExt;\n}\n\n/** The event that is logged when unreferenced node is used after a certain time. */\ninterface IUnreferencedEventProps extends ICreateContainerMetadata, ICommonProps {\n\t/** The id that GC uses to track the node. May or may not match id */\n\ttrackedId: string;\n\tstate: UnreferencedState;\n\t/** The full path (in GC Path format) to the node in question */\n\tid: Tagged<string>;\n\tfromId?: Tagged<string>;\n\ttype: GCNodeType;\n\tunrefTime: number;\n\tage: number;\n\t// Expanding GC feature matrix. Without doing this, the configs cannot be logged in telemetry directly.\n\tgcConfigs: Omit<IGarbageCollectorConfigs, \"persistedGcFeatureMatrix\"> & {\n\t\t[K in keyof GCFeatureMatrix]: GCFeatureMatrix[K];\n\t};\n\ttimeout?: number;\n}\n\n/** Properties passed to nodeUsed function when a node is used. */\ninterface INodeUsageProps extends ICommonProps {\n\t/** The full path (in GC Path format) to the node in question */\n\tid: string;\n\t/** Latest timestamp received from the server, as a baseline for computing GC state/age */\n\tcurrentReferenceTimestampMs: number;\n\t/** The package path of the node. This may not be available if the node hasn't been loaded yet */\n\tpackagePath: readonly string[] | undefined;\n\t/** In case of Revived - what node added the reference? */\n\tfromId?: string;\n\t/** In case of Revived - was it revived due to autorecovery? */\n\tautorecovery?: true;\n\t/** URL (including query string) if this usage came from a request */\n\trequestUrl?: string;\n\t/** Original request headers if this usage came from a request or handle.get */\n\trequestHeaders?: string;\n}\n\n/**\n * Encapsulates the logic that tracks the various telemetry logged by the Garbage Collector.\n *\n * These events are not logged as errors, just generic events, since there can be false positives:\n *\n * 1. inactiveObject telemetry - When an inactive node is used - A node that has been unreferenced for inactiveTimeoutMs.\n * 2. tombstoneReadyObject telemetry - When a tombstone-ready node is used - A node that has been unreferenced for tombstoneTimeoutMs.\n * 3. sweepReadyObject telemetry - When a sweep-ready node is used - A node that has been unreferenced for tombstoneTimeoutMs + sweepGracePeriodMs.\n *\n * These events are logged as errors since they are based on the core GC logic:\n *\n * 1. Tombstone telemetry - When a tombstoned node is used - A node that has been marked as tombstone.\n * 2. Unknown outbound reference telemetry - When a node is referenced but GC was not notified of it when the new reference appeared.\n *\n * Note: The telemetry for a Deleted node being used is logged elsewhere in this package.\n */\nexport class GCTelemetryTracker {\n\t// Keeps track of unreferenced events that are logged for a node. This is used to limit the log generation to one\n\t// per event per node.\n\tprivate readonly loggedUnreferencedEvents: Set<string> = new Set();\n\t// Queue for unreferenced events that should be logged the next time GC runs.\n\tprivate pendingEventsQueue: IUnreferencedEventProps[] = [];\n\n\tconstructor(\n\t\tprivate readonly mc: MonitoringContext,\n\t\tprivate readonly configs: IGarbageCollectorConfigs,\n\t\tprivate readonly isSummarizerClient: boolean,\n\t\tprivate readonly createContainerMetadata: ICreateContainerMetadata,\n\t\tprivate readonly getNodeType: (nodeId: string) => GCNodeType,\n\t\tprivate readonly getNodeStateTracker: (\n\t\t\tnodeId: string,\n\t\t) => UnreferencedStateTracker | undefined,\n\t\tprivate readonly getNodePackagePath: (\n\t\t\tnodePath: string,\n\t\t) => Promise<readonly string[] | undefined>,\n\t) {}\n\n\t/**\n\t * Returns whether an event should be logged for a node that isn't active anymore. This does not apply to\n\t * tombstoned nodes for which an event is always logged. Some scenarios where we won't log:\n\t * 1. When a DDS is changed. The corresponding data store's event will be logged instead.\n\t * 2. An event is logged only once per container instance per event per node.\n\t */\n\tprivate shouldLogNonActiveEvent(\n\t\tnodeType: GCNodeType,\n\t\tusageType: NodeUsageType,\n\t\tnodeStateTracker: UnreferencedStateTracker,\n\t\tuniqueEventId: string,\n\t) {\n\t\tif (nodeStateTracker.state === UnreferencedState.Active) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (nodeType === GCNodeType.Other) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// For sub data store (DDS) nodes, if they are changed, its data store will also be changed,\n\t\t// so skip logging to make the telemetry less noisy.\n\t\tif (nodeType === GCNodeType.SubDataStore && usageType === \"Changed\") {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Non-tombstone events are logged once per event per node. A unique id is generated by joining\n\t\t// node state (inactive / sweep ready), node's id and usage (loaded / changed / revived).\n\t\tif (this.loggedUnreferencedEvents.has(uniqueEventId)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Called when a node is used. If the node is inactive or tombstoned, log telemetry indicating object is used\n\t * when it should not have been.\n\t * @param trackedId - The id that GC uses to track the node. For SubDataStore nodes, this should be the DataStore ID.\n\t * @param INodeUsageProps - All kind of details about this event to be logged\n\t */\n\tpublic nodeUsed(\n\t\ttrackedId: string,\n\t\t{\n\t\t\tusageType,\n\t\t\tcurrentReferenceTimestampMs,\n\t\t\tpackagePath,\n\t\t\tid: untaggedId,\n\t\t\tfromId: untaggedFromId,\n\t\t\tisTombstoned,\n\t\t\t...otherNodeUsageProps\n\t\t}: INodeUsageProps,\n\t) {\n\t\t// Note: For SubDataStore Load usage, trackedId will be the DataStore's id, not the full path in question.\n\t\t// This is necessary because the SubDataStore path may be unrecognized by GC (if suited for a custom request handler)\n\t\tconst nodeStateTracker = this.getNodeStateTracker(trackedId);\n\t\tconst nodeType = this.getNodeType(untaggedId);\n\n\t\tconst timeout = (() => {\n\t\t\tswitch (nodeStateTracker?.state) {\n\t\t\t\tcase UnreferencedState.Inactive:\n\t\t\t\t\treturn this.configs.inactiveTimeoutMs;\n\t\t\t\tcase UnreferencedState.TombstoneReady:\n\t\t\t\t\treturn this.configs.tombstoneTimeoutMs;\n\t\t\t\tcase UnreferencedState.SweepReady:\n\t\t\t\t\treturn (\n\t\t\t\t\t\tthis.configs.tombstoneTimeoutMs &&\n\t\t\t\t\t\tthis.configs.tombstoneTimeoutMs + this.configs.sweepGracePeriodMs\n\t\t\t\t\t);\n\t\t\t\tdefault:\n\t\t\t\t\treturn undefined;\n\t\t\t}\n\t\t})();\n\t\tconst { persistedGcFeatureMatrix, ...configs } = this.configs;\n\t\tconst unrefEventProps = {\n\t\t\ttrackedId,\n\t\t\ttype: nodeType,\n\t\t\tunrefTime: nodeStateTracker?.unreferencedTimestampMs ?? -1,\n\t\t\tage:\n\t\t\t\tnodeStateTracker !== undefined\n\t\t\t\t\t? currentReferenceTimestampMs - nodeStateTracker.unreferencedTimestampMs\n\t\t\t\t\t: -1,\n\t\t\ttimeout,\n\t\t\tisTombstoned,\n\t\t\t...tagCodeArtifacts({ id: untaggedId, fromId: untaggedFromId }),\n\t\t\t...otherNodeUsageProps,\n\t\t\t...this.createContainerMetadata,\n\t\t\tgcConfigs: { ...configs, ...persistedGcFeatureMatrix },\n\t\t} satisfies Omit<IUnreferencedEventProps, \"state\" | \"usageType\"> &\n\t\t\ttypeof otherNodeUsageProps;\n\n\t\t// If the node that is used is tombstoned, log a tombstone telemetry.\n\t\tif (isTombstoned) {\n\t\t\tthis.logTombstoneUsageTelemetry(unrefEventProps, nodeType, usageType, packagePath);\n\t\t}\n\n\t\t// After logging tombstone telemetry, if the node's unreferenced state is not tracked, there is nothing\n\t\t// else to log.\n\t\tif (nodeStateTracker === undefined) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst state = nodeStateTracker.state;\n\t\tconst uniqueEventId = `${state}-${untaggedId}-${usageType}`;\n\n\t\tif (!this.shouldLogNonActiveEvent(nodeType, usageType, nodeStateTracker, uniqueEventId)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Add the unique event id so that we don't generate a log for this event again in this session.\n\t\tthis.loggedUnreferencedEvents.add(uniqueEventId);\n\n\t\t// For summarizer client, queue the event so it is logged the next time GC runs if the event is still valid.\n\t\t// For non-summarizer client, log the event now since GC won't run on it. This may result in false positives\n\t\t// but it's a good signal nonetheless and we can consume it with a grain of salt.\n\t\t// Inactive errors are usages of Objects that are unreferenced for at least a period of 7 days.\n\t\t// SweepReady errors are usages of Objects that will be deleted by GC Sweep!\n\t\tif (this.isSummarizerClient) {\n\t\t\tthis.pendingEventsQueue.push({\n\t\t\t\t...unrefEventProps, // Note: Contains some properties from INodeUsageProps as well\n\t\t\t\tusageType,\n\t\t\t\tstate,\n\t\t\t});\n\t\t} else {\n\t\t\t// For non-summarizer clients, only log \"Loaded\" type events since these objects may not be loaded in the\n\t\t\t// summarizer clients if they are based off of user actions (such as scrolling to content for these objects)\n\t\t\t// Events generated:\n\t\t\t// InactiveObject_Loaded, SweepReadyObject_Loaded\n\t\t\tif (usageType === \"Loaded\") {\n\t\t\t\tconst { id, fromId, headers, gcConfigs, additionalProps, ...detailedProps } =\n\t\t\t\t\tunrefEventProps;\n\t\t\t\tconst event = {\n\t\t\t\t\teventName: `${state}Object_${usageType}`,\n\t\t\t\t\t...tagCodeArtifacts({ pkg: packagePath?.join(\"/\") }),\n\t\t\t\t\tstack: generateStack(),\n\t\t\t\t\tid,\n\t\t\t\t\tfromId,\n\t\t\t\t\theaders: { ...headers },\n\t\t\t\t\tdetails: { ...detailedProps, ...additionalProps },\n\t\t\t\t\tgcConfigs,\n\t\t\t\t};\n\n\t\t\t\t// These are logged as generic events and not errors because there can be false positives. The Tombstone\n\t\t\t\t// and Delete errors are separately logged and are reliable.\n\t\t\t\tthis.mc.logger.sendTelemetryEvent(event);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Logs telemetry when a tombstoned object is changed, revived or loaded.\n\t */\n\tprivate logTombstoneUsageTelemetry(\n\t\tunrefEventProps: Omit<IUnreferencedEventProps, \"state\" | \"usageType\">,\n\t\tnodeType: GCNodeType,\n\t\tusageType: NodeUsageType,\n\t\tpackagePath?: readonly string[],\n\t) {\n\t\t// This will log the following events:\n\t\t// GC_Tombstone_DataStore_Requested, GC_Tombstone_DataStore_Changed, GC_Tombstone_DataStore_Revived\n\t\t// GC_Tombstone_SubDataStore_Requested, GC_Tombstone_SubDataStore_Changed, GC_Tombstone_SubDataStore_Revived\n\t\t// GC_Tombstone_Blob_Requested, GC_Tombstone_Blob_Changed, GC_Tombstone_Blob_Revived\n\t\tconst { id, fromId, headers, gcConfigs, additionalProps, ...detailedProps } =\n\t\t\tunrefEventProps;\n\t\tconst eventUsageName = usageType === \"Loaded\" ? \"Requested\" : usageType;\n\t\tconst event = {\n\t\t\teventName: `GC_Tombstone_${nodeType}_${eventUsageName}`,\n\t\t\t...tagCodeArtifacts({ pkg: packagePath?.join(\"/\") }),\n\t\t\tstack: generateStack(),\n\t\t\tid,\n\t\t\tfromId,\n\t\t\theaders: { ...headers },\n\t\t\tdetails: { ...detailedProps, ...additionalProps }, // Also includes some properties from INodeUsageProps type\n\t\t\tgcConfigs,\n\t\t\ttombstoneFlags: {\n\t\t\t\tDisableTombstone: this.mc.config.getBoolean(disableTombstoneKey),\n\t\t\t\tThrowOnTombstoneUsage: this.mc.config.getBoolean(throwOnTombstoneUsageKey),\n\t\t\t\tThrowOnTombstoneLoad: this.mc.config.getBoolean(throwOnTombstoneLoadOverrideKey),\n\t\t\t},\n\t\t};\n\n\t\tif (\n\t\t\t(usageType === \"Loaded\" &&\n\t\t\t\tthis.configs.throwOnTombstoneLoad &&\n\t\t\t\t!headers?.allowTombstone) ||\n\t\t\t(usageType === \"Changed\" && this.configs.throwOnTombstoneUsage)\n\t\t) {\n\t\t\tthis.mc.logger.sendErrorEvent(event);\n\t\t} else {\n\t\t\tthis.mc.logger.sendTelemetryEvent(event);\n\t\t}\n\t}\n\n\t/**\n\t * Log all new references or outbound routes in the current graph that haven't been explicitly notified to GC.\n\t * The principle is that every new reference or outbound route must be notified to GC via the\n\t * addedOutboundReference method. It it hasn't, its a bug and we want to identify these scenarios.\n\t *\n\t * In more simple terms:\n\t * Missing Explicit References = Current References - Previous References - Explicitly Added References;\n\t *\n\t * @param currentGCData - The GC data (reference graph) from the current GC run.\n\t * @param previousGCData - The GC data (reference graph) from the previous GC run.\n\t * @param explicitReferences - New references added explicity between the previous and the current run.\n\t */\n\tpublic logIfMissingExplicitReferences(\n\t\tcurrentGCData: IGarbageCollectionData,\n\t\tpreviousGCData: IGarbageCollectionData,\n\t\texplicitReferences: Map<string, string[]>,\n\t\tlogger: ITelemetryLoggerExt,\n\t) {\n\t\tfor (const [nodeId, currentOutboundRoutes] of Object.entries(currentGCData.gcNodes)) {\n\t\t\tconst previousRoutes = previousGCData.gcNodes[nodeId] ?? [];\n\t\t\tconst explicitRoutes = explicitReferences.get(nodeId) ?? [];\n\n\t\t\t/**\n\t\t\t * 1. For routes in the current GC data, routes that were not present in previous GC data and did not have\n\t\t\t * explicit references should be added to missing explicit routes list.\n\t\t\t * 2. Only include data store and blob routes since GC only works for these two.\n\t\t\t * Note: Due to a bug with de-duped blobs, only adding data store routes for now.\n\t\t\t * 3. Ignore DDS routes to their parent datastores since those were added implicitly. So, there won't be\n\t\t\t * explicit routes to them.\n\t\t\t */\n\t\t\tconst missingExplicitRoutes: string[] = [];\n\t\t\tfor (const route of currentOutboundRoutes) {\n\t\t\t\tconst nodeType = this.getNodeType(route);\n\t\t\t\tif (\n\t\t\t\t\t(nodeType === GCNodeType.DataStore || nodeType === GCNodeType.Blob) &&\n\t\t\t\t\t!nodeId.startsWith(route) &&\n\t\t\t\t\t!previousRoutes.includes(route) &&\n\t\t\t\t\t!explicitRoutes.includes(route)\n\t\t\t\t) {\n\t\t\t\t\tmissingExplicitRoutes.push(route);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (missingExplicitRoutes.length > 0) {\n\t\t\t\t// Send as Generic not Error since there are known corner cases where this will fire.\n\t\t\t\t// E.g. If an old client re-references a node via an attach op (that doesn't include GC Data)\n\t\t\t\tlogger.sendTelemetryEvent({\n\t\t\t\t\teventName: \"gcUnknownOutboundReferences\",\n\t\t\t\t\t...tagCodeArtifacts({\n\t\t\t\t\t\tid: nodeId,\n\t\t\t\t\t\troutes: JSON.stringify(missingExplicitRoutes),\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Log events that are pending in pendingEventsQueue. This is called after GC runs in the summarizer client\n\t * so that the state of an unreferenced node is updated.\n\t */\n\tpublic async logPendingEvents(logger: ITelemetryLoggerExt) {\n\t\t// Events sent come only from the summarizer client. In between summaries, events are pushed to a queue and at\n\t\t// summary time they are then logged.\n\t\t// Events generated:\n\t\t// InactiveObject_Loaded, InactiveObject_Changed, InactiveObject_Revived\n\t\t// SweepReadyObject_Loaded, SweepReadyObject_Changed, SweepReadyObject_Revived\n\t\tfor (const eventProps of this.pendingEventsQueue) {\n\t\t\tconst {\n\t\t\t\tusageType,\n\t\t\t\tstate,\n\t\t\t\tid,\n\t\t\t\tfromId,\n\t\t\t\theaders,\n\t\t\t\tgcConfigs,\n\t\t\t\tadditionalProps,\n\t\t\t\t...detailedProps\n\t\t\t} = eventProps;\n\t\t\t/**\n\t\t\t * Revived event is logged only if the node is active. If the node is not active, the reference to it was\n\t\t\t * from another unreferenced node and this scenario is not interesting to log.\n\t\t\t * Loaded and Changed events are logged only if the node is not active. If the node is active, it was\n\t\t\t * revived and a Revived event will be logged for it.\n\t\t\t */\n\t\t\tconst nodeStateTracker = this.getNodeStateTracker(detailedProps.trackedId); // Note: This is never SubDataStore path\n\t\t\tconst active =\n\t\t\t\tnodeStateTracker === undefined || nodeStateTracker.state === UnreferencedState.Active;\n\t\t\tif ((usageType === \"Revived\") === active) {\n\t\t\t\tconst pkg = await this.getNodePackagePath(eventProps.id.value);\n\t\t\t\tconst fromPkg = eventProps.fromId\n\t\t\t\t\t? await this.getNodePackagePath(eventProps.fromId.value)\n\t\t\t\t\t: undefined;\n\t\t\t\tconst event = {\n\t\t\t\t\teventName: `${state}Object_${usageType}`,\n\t\t\t\t\tid,\n\t\t\t\t\tfromId,\n\t\t\t\t\theaders: { ...headers },\n\t\t\t\t\tdetails: { ...detailedProps, ...additionalProps },\n\t\t\t\t\tgcConfigs,\n\t\t\t\t\t...tagCodeArtifacts({\n\t\t\t\t\t\tpkg: pkg?.join(\"/\"),\n\t\t\t\t\t\tfromPkg: fromPkg?.join(\"/\"),\n\t\t\t\t\t}),\n\t\t\t\t};\n\n\t\t\t\t// These are logged as generic events and not errors because there can be false positives. The Tombstone\n\t\t\t\t// and Delete errors are separately logged and are reliable.\n\t\t\t\tlogger.sendTelemetryEvent(event);\n\t\t\t}\n\t\t}\n\t\tthis.pendingEventsQueue = [];\n\t}\n}\n\n/**\n * Consolidates info / logic for logging when we encounter unexpected usage of GC'd objects. For example, when a\n * tombstoned or deleted object is loaded.\n */\nexport function sendGCUnexpectedUsageEvent(\n\tmc: MonitoringContext,\n\tevent: ITelemetryGenericEventExt & {\n\t\tcategory: \"error\" | \"generic\";\n\t\tgcTombstoneEnforcementAllowed: boolean | undefined;\n\t},\n\tpackagePath: readonly string[] | undefined,\n\terror?: unknown,\n) {\n\tevent.pkg = tagCodeArtifacts({ pkg: packagePath?.join(\"/\") })?.pkg;\n\tevent.tombstoneFlags = JSON.stringify({\n\t\tDisableTombstone: mc.config.getBoolean(disableTombstoneKey),\n\t\tThrowOnTombstoneUsage: mc.config.getBoolean(throwOnTombstoneUsageKey),\n\t\tThrowOnTombstoneLoad: mc.config.getBoolean(throwOnTombstoneLoadOverrideKey),\n\t});\n\tevent.gcVersion = getGCVersionInEffect(mc.config);\n\n\tmc.logger.sendTelemetryEvent(event, error);\n}\n"]}
|
package/lib/legacy.d.ts
CHANGED
package/lib/metadata.d.ts
CHANGED
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
+
import type { BatchId } from "./opLifecycle/index.js";
|
|
6
|
+
/** Syntactic sugar for casting */
|
|
7
|
+
export declare function asBatchMetadata(metadata: unknown): IBatchMetadata | undefined;
|
|
5
8
|
/**
|
|
6
|
-
*
|
|
9
|
+
* Properties put on the op metadata object for batch tracking
|
|
7
10
|
*/
|
|
8
11
|
export interface IBatchMetadata {
|
|
12
|
+
/** Set on first/last messages of a multi-message batch, to true/false respectively */
|
|
9
13
|
batch?: boolean;
|
|
14
|
+
/** Maybe set on first message of a batch, to the batchId generated when resubmitting (set/fixed on first resubmit) */
|
|
15
|
+
batchId?: BatchId;
|
|
10
16
|
}
|
|
11
17
|
/**
|
|
12
18
|
* Blob handling makes assumptions about what might be on the metadata. This interface codifies those assumptions, but does not validate them.
|
package/lib/metadata.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,KAAK,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEtD,kCAAkC;AAClC,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAE7E;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,sFAAsF;IACtF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,sHAAsH;IACtH,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB"}
|
package/lib/metadata.js
CHANGED
|
@@ -2,5 +2,8 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
|
|
5
|
+
/** Syntactic sugar for casting */
|
|
6
|
+
export function asBatchMetadata(metadata) {
|
|
7
|
+
return metadata;
|
|
8
|
+
}
|
|
6
9
|
//# sourceMappingURL=metadata.js.map
|
package/lib/metadata.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,kCAAkC;AAClC,MAAM,UAAU,eAAe,CAAC,QAAiB;IAChD,OAAO,QAAsC,CAAC;AAC/C,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { BatchId } from \"./opLifecycle/index.js\";\n\n/** Syntactic sugar for casting */\nexport function asBatchMetadata(metadata: unknown): IBatchMetadata | undefined {\n\treturn metadata as IBatchMetadata | undefined;\n}\n\n/**\n * Properties put on the op metadata object for batch tracking\n */\nexport interface IBatchMetadata {\n\t/** Set on first/last messages of a multi-message batch, to true/false respectively */\n\tbatch?: boolean;\n\t/** Maybe set on first message of a batch, to the batchId generated when resubmitting (set/fixed on first resubmit) */\n\tbatchId?: BatchId;\n}\n\n/**\n * Blob handling makes assumptions about what might be on the metadata. This interface codifies those assumptions, but does not validate them.\n */\nexport interface IBlobMetadata {\n\tblobId?: string;\n\tlocalId?: string;\n}\n\n/**\n * ContainerRuntime needs to know if this is a replayed savedOp as those need to be skipped in stashed ops scenarios.\n */\nexport interface ISavedOpMetadata {\n\tsavedOp?: boolean;\n}\n"]}
|
|
@@ -16,6 +16,10 @@ export interface BatchSequenceNumbers {
|
|
|
16
16
|
referenceSequenceNumber?: number;
|
|
17
17
|
clientSequenceNumber?: number;
|
|
18
18
|
}
|
|
19
|
+
/** Type alias for the batchId stored in batch metadata */
|
|
20
|
+
export type BatchId = string;
|
|
21
|
+
/** Compose original client ID and client sequence number into BatchId to stamp on the message during reconnect */
|
|
22
|
+
export declare function generateBatchId(originalClientId: string, batchStartCsn: number): BatchId;
|
|
19
23
|
/**
|
|
20
24
|
* Helper class that manages partial batch & rollback.
|
|
21
25
|
*/
|
|
@@ -36,7 +40,10 @@ export declare class BatchManager {
|
|
|
36
40
|
constructor(options: IBatchManagerOptions);
|
|
37
41
|
push(message: BatchMessage, reentrant: boolean, currentClientSequenceNumber?: number): boolean;
|
|
38
42
|
get empty(): boolean;
|
|
39
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Gets the pending batch and clears state for the next batch.
|
|
45
|
+
*/
|
|
46
|
+
popBatch(batchId?: BatchId): IBatch;
|
|
40
47
|
/**
|
|
41
48
|
* Capture the pending state at this point
|
|
42
49
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batchManager.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/batchManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"batchManager.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/batchManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AAGpE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAE1E,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,0BAA0B,CAAC;IAEzD;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACpC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,0DAA0D;AAC1D,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B,kHAAkH;AAClH,wBAAgB,eAAe,CAAC,gBAAgB,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAExF;AAQD;;GAEG;AACH,qBAAa,YAAY;aAiCI,OAAO,EAAE,oBAAoB;IAhCzD,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,eAAe,CAAS;IAEhC,IAAW,MAAM,WAEhB;IACD,IAAW,kBAAkB,WAE5B;IAED,IAAW,eAAe,IAAI,oBAAoB,CAKjD;IAED,OAAO,KAAK,uBAAuB,GAMlC;IAED;;;OAGG;IACH,OAAO,CAAC,oBAAoB,CAAqB;gBAErB,OAAO,EAAE,oBAAoB;IAElD,IAAI,CACV,OAAO,EAAE,YAAY,EACrB,SAAS,EAAE,OAAO,EAClB,2BAA2B,CAAC,EAAE,MAAM,GAClC,OAAO;IAyBV,IAAW,KAAK,YAEf;IAED;;OAEG;IACI,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM;IAgB1C;;OAEG;IACI,UAAU,IAAI,gBAAgB;CAiBrC;AAgCD;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,UAAW,MAAM,KAAG,MAElD,CAAC;AAEF,eAAO,MAAM,oBAAoB,YACvB,oBAAoB,gBACf,oBAAoB,KAChC,OASF,CAAC"}
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
+
import { assert } from "@fluidframework/core-utils/internal";
|
|
6
|
+
/** Compose original client ID and client sequence number into BatchId to stamp on the message during reconnect */
|
|
7
|
+
export function generateBatchId(originalClientId, batchStartCsn) {
|
|
8
|
+
return `${originalClientId}_[${batchStartCsn}]`;
|
|
9
|
+
}
|
|
5
10
|
/**
|
|
6
11
|
* Estimated size of the stringification overhead for an op accumulated
|
|
7
12
|
* from runtime to loader to the service.
|
|
@@ -26,7 +31,9 @@ export class BatchManager {
|
|
|
26
31
|
get referenceSequenceNumber() {
|
|
27
32
|
return this.pendingBatch.length === 0
|
|
28
33
|
? undefined
|
|
29
|
-
:
|
|
34
|
+
: // Non null asserting here since we are checking the length above
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
36
|
+
this.pendingBatch[this.pendingBatch.length - 1].referenceSequenceNumber;
|
|
30
37
|
}
|
|
31
38
|
constructor(options) {
|
|
32
39
|
this.options = options;
|
|
@@ -57,9 +64,12 @@ export class BatchManager {
|
|
|
57
64
|
get empty() {
|
|
58
65
|
return this.pendingBatch.length === 0;
|
|
59
66
|
}
|
|
60
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Gets the pending batch and clears state for the next batch.
|
|
69
|
+
*/
|
|
70
|
+
popBatch(batchId) {
|
|
61
71
|
const batch = {
|
|
62
|
-
|
|
72
|
+
messages: this.pendingBatch,
|
|
63
73
|
contentSizeInBytes: this.batchContentSize,
|
|
64
74
|
referenceSequenceNumber: this.referenceSequenceNumber,
|
|
65
75
|
hasReentrantOps: this.hasReentrantOps,
|
|
@@ -68,7 +78,7 @@ export class BatchManager {
|
|
|
68
78
|
this.batchContentSize = 0;
|
|
69
79
|
this.clientSequenceNumber = undefined;
|
|
70
80
|
this.hasReentrantOps = false;
|
|
71
|
-
return addBatchMetadata(batch);
|
|
81
|
+
return addBatchMetadata(batch, batchId);
|
|
72
82
|
}
|
|
73
83
|
/**
|
|
74
84
|
* Capture the pending state at this point
|
|
@@ -79,6 +89,8 @@ export class BatchManager {
|
|
|
79
89
|
rollback: (process) => {
|
|
80
90
|
for (let i = this.pendingBatch.length; i > startPoint;) {
|
|
81
91
|
i--;
|
|
92
|
+
// Non null asserting here since we are iterating though pendingBatch
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
82
94
|
const message = this.pendingBatch[i];
|
|
83
95
|
this.batchContentSize -= message.contents?.length ?? 0;
|
|
84
96
|
process(message);
|
|
@@ -88,16 +100,24 @@ export class BatchManager {
|
|
|
88
100
|
};
|
|
89
101
|
}
|
|
90
102
|
}
|
|
91
|
-
const addBatchMetadata = (batch) => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
103
|
+
const addBatchMetadata = (batch, batchId) => {
|
|
104
|
+
const batchEnd = batch.messages.length - 1;
|
|
105
|
+
const firstMsg = batch.messages[0];
|
|
106
|
+
const lastMsg = batch.messages[batchEnd];
|
|
107
|
+
assert(firstMsg !== undefined && lastMsg !== undefined, 0x9d1 /* expected non-empty batch */);
|
|
108
|
+
const firstMetadata = firstMsg.metadata ?? {};
|
|
109
|
+
const lastMetadata = lastMsg.metadata ?? {};
|
|
110
|
+
// Multi-message batches: mark the first and last messages with the "batch" flag indicating batch start/end
|
|
111
|
+
if (batch.messages.length > 1) {
|
|
112
|
+
firstMetadata.batch = true;
|
|
113
|
+
lastMetadata.batch = false;
|
|
114
|
+
firstMsg.metadata = firstMetadata;
|
|
115
|
+
lastMsg.metadata = lastMetadata;
|
|
116
|
+
}
|
|
117
|
+
// If batchId is provided (e.g. in case of resubmit): stamp it on the first message
|
|
118
|
+
if (batchId !== undefined) {
|
|
119
|
+
firstMetadata.batchId = batchId;
|
|
120
|
+
firstMsg.metadata = firstMetadata;
|
|
101
121
|
}
|
|
102
122
|
return batch;
|
|
103
123
|
};
|
|
@@ -110,7 +130,7 @@ const addBatchMetadata = (batch) => {
|
|
|
110
130
|
* @returns An estimate of the payload size in bytes which will be produced when the batch is sent over the wire
|
|
111
131
|
*/
|
|
112
132
|
export const estimateSocketSize = (batch) => {
|
|
113
|
-
return batch.contentSizeInBytes + opOverhead * batch.
|
|
133
|
+
return batch.contentSizeInBytes + opOverhead * batch.messages.length;
|
|
114
134
|
};
|
|
115
135
|
export const sequenceNumbersMatch = (seqNums, otherSeqNums) => {
|
|
116
136
|
return ((seqNums.referenceSequenceNumber === undefined ||
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batchManager.js","sourceRoot":"","sources":["../../src/opLifecycle/batchManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ICompressionRuntimeOptions } from \"../containerRuntime.js\";\n\nimport { BatchMessage, IBatch, IBatchCheckpoint } from \"./definitions.js\";\n\nexport interface IBatchManagerOptions {\n\treadonly hardLimit: number;\n\treadonly compressionOptions?: ICompressionRuntimeOptions;\n\n\t/**\n\t * If true, the outbox is allowed to rebase the batch during flushing.\n\t */\n\treadonly canRebase: boolean;\n}\n\nexport interface BatchSequenceNumbers {\n\treferenceSequenceNumber?: number;\n\tclientSequenceNumber?: number;\n}\n\n/**\n * Estimated size of the stringification overhead for an op accumulated\n * from runtime to loader to the service.\n */\nconst opOverhead = 200;\n\n/**\n * Helper class that manages partial batch & rollback.\n */\nexport class BatchManager {\n\tprivate pendingBatch: BatchMessage[] = [];\n\tprivate batchContentSize = 0;\n\tprivate hasReentrantOps = false;\n\n\tpublic get length() {\n\t\treturn this.pendingBatch.length;\n\t}\n\tpublic get contentSizeInBytes() {\n\t\treturn this.batchContentSize;\n\t}\n\n\tpublic get sequenceNumbers(): BatchSequenceNumbers {\n\t\treturn {\n\t\t\treferenceSequenceNumber: this.referenceSequenceNumber,\n\t\t\tclientSequenceNumber: this.clientSequenceNumber,\n\t\t};\n\t}\n\n\tprivate get referenceSequenceNumber(): number | undefined {\n\t\treturn this.pendingBatch.length === 0\n\t\t\t? undefined\n\t\t\t: this.pendingBatch[this.pendingBatch.length - 1].referenceSequenceNumber;\n\t}\n\n\t/**\n\t * The last-processed CSN when this batch started.\n\t * This is used to ensure that while the batch is open, no incoming ops are processed.\n\t */\n\tprivate clientSequenceNumber: number | undefined;\n\n\tconstructor(public readonly options: IBatchManagerOptions) {}\n\n\tpublic push(\n\t\tmessage: BatchMessage,\n\t\treentrant: boolean,\n\t\tcurrentClientSequenceNumber?: number,\n\t): boolean {\n\t\tconst contentSize = this.batchContentSize + (message.contents?.length ?? 0);\n\t\tconst opCount = this.pendingBatch.length;\n\t\tthis.hasReentrantOps = this.hasReentrantOps || reentrant;\n\n\t\t// Attempt to estimate batch size, aka socket message size.\n\t\t// Each op has pretty large envelope, estimating to be 200 bytes.\n\t\t// Also content will be strigified, and that adds a lot of overhead due to a lot of escape characters.\n\t\t// Not taking it into account, as compression work should help there - compressed payload will be\n\t\t// initially stored as base64, and that requires only 2 extra escape characters.\n\t\tconst socketMessageSize = contentSize + opOverhead * opCount;\n\n\t\tif (socketMessageSize >= this.options.hardLimit) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (this.pendingBatch.length === 0) {\n\t\t\tthis.clientSequenceNumber = currentClientSequenceNumber;\n\t\t}\n\n\t\tthis.batchContentSize = contentSize;\n\t\tthis.pendingBatch.push(message);\n\t\treturn true;\n\t}\n\n\tpublic get empty() {\n\t\treturn this.pendingBatch.length === 0;\n\t}\n\n\tpublic popBatch(): IBatch {\n\t\tconst batch: IBatch = {\n\t\t\tcontent: this.pendingBatch,\n\t\t\tcontentSizeInBytes: this.batchContentSize,\n\t\t\treferenceSequenceNumber: this.referenceSequenceNumber,\n\t\t\thasReentrantOps: this.hasReentrantOps,\n\t\t};\n\n\t\tthis.pendingBatch = [];\n\t\tthis.batchContentSize = 0;\n\t\tthis.clientSequenceNumber = undefined;\n\t\tthis.hasReentrantOps = false;\n\n\t\treturn addBatchMetadata(batch);\n\t}\n\n\t/**\n\t * Capture the pending state at this point\n\t */\n\tpublic checkpoint(): IBatchCheckpoint {\n\t\tconst startPoint = this.pendingBatch.length;\n\t\treturn {\n\t\t\trollback: (process: (message: BatchMessage) => void) => {\n\t\t\t\tfor (let i = this.pendingBatch.length; i > startPoint; ) {\n\t\t\t\t\ti--;\n\t\t\t\t\tconst message = this.pendingBatch[i];\n\t\t\t\t\tthis.batchContentSize -= message.contents?.length ?? 0;\n\t\t\t\t\tprocess(message);\n\t\t\t\t}\n\n\t\t\t\tthis.pendingBatch.length = startPoint;\n\t\t\t},\n\t\t};\n\t}\n}\n\nconst addBatchMetadata = (batch: IBatch): IBatch => {\n\tif (batch.content.length > 1) {\n\t\tbatch.content[0].metadata = {\n\t\t\t...batch.content[0].metadata,\n\t\t\tbatch: true,\n\t\t};\n\t\tbatch.content[batch.content.length - 1].metadata = {\n\t\t\t...batch.content[batch.content.length - 1].metadata,\n\t\t\tbatch: false,\n\t\t};\n\t}\n\n\treturn batch;\n};\n\n/**\n * Estimates the real size in bytes on the socket for a given batch. It assumes that\n * the envelope size (and the size of an empty op) is 200 bytes, taking into account\n * extra overhead from stringification.\n *\n * @param batch - the batch to inspect\n * @returns An estimate of the payload size in bytes which will be produced when the batch is sent over the wire\n */\nexport const estimateSocketSize = (batch: IBatch): number => {\n\treturn batch.contentSizeInBytes + opOverhead * batch.content.length;\n};\n\nexport const sequenceNumbersMatch = (\n\tseqNums: BatchSequenceNumbers,\n\totherSeqNums: BatchSequenceNumbers,\n): boolean => {\n\treturn (\n\t\t(seqNums.referenceSequenceNumber === undefined ||\n\t\t\totherSeqNums.referenceSequenceNumber === undefined ||\n\t\t\tseqNums.referenceSequenceNumber === otherSeqNums.referenceSequenceNumber) &&\n\t\t(seqNums.clientSequenceNumber === undefined ||\n\t\t\totherSeqNums.clientSequenceNumber === undefined ||\n\t\t\tseqNums.clientSequenceNumber === otherSeqNums.clientSequenceNumber)\n\t);\n};\n"]}
|
|
1
|
+
{"version":3,"file":"batchManager.js","sourceRoot":"","sources":["../../src/opLifecycle/batchManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/core-utils/internal\";\n\nimport { ICompressionRuntimeOptions } from \"../containerRuntime.js\";\nimport { type IBatchMetadata } from \"../metadata.js\";\n\nimport { BatchMessage, IBatch, IBatchCheckpoint } from \"./definitions.js\";\n\nexport interface IBatchManagerOptions {\n\treadonly hardLimit: number;\n\treadonly compressionOptions?: ICompressionRuntimeOptions;\n\n\t/**\n\t * If true, the outbox is allowed to rebase the batch during flushing.\n\t */\n\treadonly canRebase: boolean;\n}\n\nexport interface BatchSequenceNumbers {\n\treferenceSequenceNumber?: number;\n\tclientSequenceNumber?: number;\n}\n\n/** Type alias for the batchId stored in batch metadata */\nexport type BatchId = string;\n\n/** Compose original client ID and client sequence number into BatchId to stamp on the message during reconnect */\nexport function generateBatchId(originalClientId: string, batchStartCsn: number): BatchId {\n\treturn `${originalClientId}_[${batchStartCsn}]`;\n}\n\n/**\n * Estimated size of the stringification overhead for an op accumulated\n * from runtime to loader to the service.\n */\nconst opOverhead = 200;\n\n/**\n * Helper class that manages partial batch & rollback.\n */\nexport class BatchManager {\n\tprivate pendingBatch: BatchMessage[] = [];\n\tprivate batchContentSize = 0;\n\tprivate hasReentrantOps = false;\n\n\tpublic get length() {\n\t\treturn this.pendingBatch.length;\n\t}\n\tpublic get contentSizeInBytes() {\n\t\treturn this.batchContentSize;\n\t}\n\n\tpublic get sequenceNumbers(): BatchSequenceNumbers {\n\t\treturn {\n\t\t\treferenceSequenceNumber: this.referenceSequenceNumber,\n\t\t\tclientSequenceNumber: this.clientSequenceNumber,\n\t\t};\n\t}\n\n\tprivate get referenceSequenceNumber(): number | undefined {\n\t\treturn this.pendingBatch.length === 0\n\t\t\t? undefined\n\t\t\t: // Non null asserting here since we are checking the length above\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\tthis.pendingBatch[this.pendingBatch.length - 1]!.referenceSequenceNumber;\n\t}\n\n\t/**\n\t * The last-processed CSN when this batch started.\n\t * This is used to ensure that while the batch is open, no incoming ops are processed.\n\t */\n\tprivate clientSequenceNumber: number | undefined;\n\n\tconstructor(public readonly options: IBatchManagerOptions) {}\n\n\tpublic push(\n\t\tmessage: BatchMessage,\n\t\treentrant: boolean,\n\t\tcurrentClientSequenceNumber?: number,\n\t): boolean {\n\t\tconst contentSize = this.batchContentSize + (message.contents?.length ?? 0);\n\t\tconst opCount = this.pendingBatch.length;\n\t\tthis.hasReentrantOps = this.hasReentrantOps || reentrant;\n\n\t\t// Attempt to estimate batch size, aka socket message size.\n\t\t// Each op has pretty large envelope, estimating to be 200 bytes.\n\t\t// Also content will be strigified, and that adds a lot of overhead due to a lot of escape characters.\n\t\t// Not taking it into account, as compression work should help there - compressed payload will be\n\t\t// initially stored as base64, and that requires only 2 extra escape characters.\n\t\tconst socketMessageSize = contentSize + opOverhead * opCount;\n\n\t\tif (socketMessageSize >= this.options.hardLimit) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (this.pendingBatch.length === 0) {\n\t\t\tthis.clientSequenceNumber = currentClientSequenceNumber;\n\t\t}\n\n\t\tthis.batchContentSize = contentSize;\n\t\tthis.pendingBatch.push(message);\n\t\treturn true;\n\t}\n\n\tpublic get empty() {\n\t\treturn this.pendingBatch.length === 0;\n\t}\n\n\t/**\n\t * Gets the pending batch and clears state for the next batch.\n\t */\n\tpublic popBatch(batchId?: BatchId): IBatch {\n\t\tconst batch: IBatch = {\n\t\t\tmessages: this.pendingBatch,\n\t\t\tcontentSizeInBytes: this.batchContentSize,\n\t\t\treferenceSequenceNumber: this.referenceSequenceNumber,\n\t\t\thasReentrantOps: this.hasReentrantOps,\n\t\t};\n\n\t\tthis.pendingBatch = [];\n\t\tthis.batchContentSize = 0;\n\t\tthis.clientSequenceNumber = undefined;\n\t\tthis.hasReentrantOps = false;\n\n\t\treturn addBatchMetadata(batch, batchId);\n\t}\n\n\t/**\n\t * Capture the pending state at this point\n\t */\n\tpublic checkpoint(): IBatchCheckpoint {\n\t\tconst startPoint = this.pendingBatch.length;\n\t\treturn {\n\t\t\trollback: (process: (message: BatchMessage) => void) => {\n\t\t\t\tfor (let i = this.pendingBatch.length; i > startPoint; ) {\n\t\t\t\t\ti--;\n\t\t\t\t\t// Non null asserting here since we are iterating though pendingBatch\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\t\tconst message = this.pendingBatch[i]!;\n\t\t\t\t\tthis.batchContentSize -= message.contents?.length ?? 0;\n\t\t\t\t\tprocess(message);\n\t\t\t\t}\n\n\t\t\t\tthis.pendingBatch.length = startPoint;\n\t\t\t},\n\t\t};\n\t}\n}\n\nconst addBatchMetadata = (batch: IBatch, batchId?: BatchId): IBatch => {\n\tconst batchEnd = batch.messages.length - 1;\n\n\tconst firstMsg = batch.messages[0];\n\tconst lastMsg = batch.messages[batchEnd];\n\tassert(\n\t\tfirstMsg !== undefined && lastMsg !== undefined,\n\t\t0x9d1 /* expected non-empty batch */,\n\t);\n\n\tconst firstMetadata: Partial<IBatchMetadata> = firstMsg.metadata ?? {};\n\tconst lastMetadata: Partial<IBatchMetadata> = lastMsg.metadata ?? {};\n\n\t// Multi-message batches: mark the first and last messages with the \"batch\" flag indicating batch start/end\n\tif (batch.messages.length > 1) {\n\t\tfirstMetadata.batch = true;\n\t\tlastMetadata.batch = false;\n\t\tfirstMsg.metadata = firstMetadata;\n\t\tlastMsg.metadata = lastMetadata;\n\t}\n\n\t// If batchId is provided (e.g. in case of resubmit): stamp it on the first message\n\tif (batchId !== undefined) {\n\t\tfirstMetadata.batchId = batchId;\n\t\tfirstMsg.metadata = firstMetadata;\n\t}\n\n\treturn batch;\n};\n\n/**\n * Estimates the real size in bytes on the socket for a given batch. It assumes that\n * the envelope size (and the size of an empty op) is 200 bytes, taking into account\n * extra overhead from stringification.\n *\n * @param batch - the batch to inspect\n * @returns An estimate of the payload size in bytes which will be produced when the batch is sent over the wire\n */\nexport const estimateSocketSize = (batch: IBatch): number => {\n\treturn batch.contentSizeInBytes + opOverhead * batch.messages.length;\n};\n\nexport const sequenceNumbersMatch = (\n\tseqNums: BatchSequenceNumbers,\n\totherSeqNums: BatchSequenceNumbers,\n): boolean => {\n\treturn (\n\t\t(seqNums.referenceSequenceNumber === undefined ||\n\t\t\totherSeqNums.referenceSequenceNumber === undefined ||\n\t\t\tseqNums.referenceSequenceNumber === otherSeqNums.referenceSequenceNumber) &&\n\t\t(seqNums.clientSequenceNumber === undefined ||\n\t\t\totherSeqNums.clientSequenceNumber === undefined ||\n\t\t\tseqNums.clientSequenceNumber === otherSeqNums.clientSequenceNumber)\n\t);\n};\n"]}
|