@fluidframework/container-runtime 2.0.0-internal.2.3.1 → 2.0.0-internal.2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blobManager.d.ts +3 -1
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +35 -2
- package/dist/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +33 -29
- package/dist/containerRuntime.js.map +1 -1
- package/dist/garbageCollection.d.ts +17 -9
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +101 -41
- package/dist/garbageCollection.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/summarizer.js.map +1 -1
- package/lib/blobManager.d.ts +3 -1
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +35 -2
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +33 -29
- package/lib/containerRuntime.js.map +1 -1
- package/lib/garbageCollection.d.ts +17 -9
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +102 -42
- package/lib/garbageCollection.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/summarizer.js.map +1 -1
- package/package.json +20 -19
- package/src/blobManager.ts +41 -2
- package/src/containerRuntime.ts +67 -47
- package/src/garbageCollection.ts +109 -42
- package/src/packageVersion.ts +1 -1
- package/src/summarizer.ts +1 -1
package/src/garbageCollection.ts
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
IGarbageCollectionNodeData,
|
|
31
31
|
IGarbageCollectionSummaryDetailsLegacy,
|
|
32
32
|
ISummaryTreeWithStats,
|
|
33
|
+
gcDeletedBlobKey,
|
|
33
34
|
} from "@fluidframework/runtime-definitions";
|
|
34
35
|
import {
|
|
35
36
|
mergeStats,
|
|
@@ -157,9 +158,8 @@ export interface IGarbageCollector {
|
|
|
157
158
|
getBaseGCDetails(): Promise<IGarbageCollectionDetailsBase>;
|
|
158
159
|
/** Called when the latest summary of the system has been refreshed. */
|
|
159
160
|
refreshLatestSummary(
|
|
160
|
-
result: RefreshSummaryResult,
|
|
161
161
|
proposalHandle: string | undefined,
|
|
162
|
-
|
|
162
|
+
result: RefreshSummaryResult,
|
|
163
163
|
readAndParseBlob: ReadAndParseBlob,
|
|
164
164
|
): Promise<void>;
|
|
165
165
|
/** Called when a node is updated. Used to detect and log when an inactive node is changed or loaded. */
|
|
@@ -172,6 +172,8 @@ export interface IGarbageCollector {
|
|
|
172
172
|
): void;
|
|
173
173
|
/** Called when a reference is added to a node. Used to identify nodes that were referenced between summaries. */
|
|
174
174
|
addedOutboundReference(fromNodePath: string, toNodePath: string): void;
|
|
175
|
+
/** Returns true if this node has been deleted by GC during sweep phase. */
|
|
176
|
+
isNodeDeleted(nodePath: string): boolean;
|
|
175
177
|
setConnectionState(connected: boolean, clientId?: string): void;
|
|
176
178
|
dispose(): void;
|
|
177
179
|
}
|
|
@@ -226,6 +228,7 @@ interface IUnreferencedEventProps {
|
|
|
226
228
|
interface IGCSummaryTrackingData {
|
|
227
229
|
serializedGCState: string | undefined;
|
|
228
230
|
serializedTombstones: string | undefined;
|
|
231
|
+
serializedDeletedNodes: string | undefined;
|
|
229
232
|
}
|
|
230
233
|
|
|
231
234
|
/**
|
|
@@ -419,7 +422,10 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
419
422
|
// Keeps a list of references (edges in the GC graph) between GC runs. Each entry has a node id and a list of
|
|
420
423
|
// outbound routes from that node.
|
|
421
424
|
private readonly newReferencesSinceLastRun: Map<string, string[]> = new Map();
|
|
425
|
+
// A list of nodes that have been tombstoned.
|
|
422
426
|
private tombstones: string[] = [];
|
|
427
|
+
// A list of nodes that have been deleted during sweep phase.
|
|
428
|
+
private deletedNodes: Set<string> = new Set();
|
|
423
429
|
|
|
424
430
|
/**
|
|
425
431
|
* Keeps track of the GC data from the latest summary successfully submitted to and acked from the server.
|
|
@@ -565,8 +571,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
565
571
|
// flag in GC options to false.
|
|
566
572
|
this.gcEnabled = this.gcOptions.gcAllowed !== false;
|
|
567
573
|
// The sweep phase has to be explicitly enabled by setting the sweepAllowed flag in GC options to true.
|
|
568
|
-
|
|
569
|
-
this.sweepEnabled = this.gcOptions.sweepAllowed === true || testOverrideSweepTimeoutMs !== undefined;
|
|
574
|
+
this.sweepEnabled = this.gcOptions.sweepAllowed === true;
|
|
570
575
|
|
|
571
576
|
// Set the Session Expiry only if the flag is enabled and GC is enabled.
|
|
572
577
|
if (this.mc.config.getBoolean(runSessionExpiryKey) && this.gcEnabled) {
|
|
@@ -640,8 +645,9 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
640
645
|
|
|
641
646
|
// Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.
|
|
642
647
|
this.testMode = this.mc.config.getBoolean(gcTestModeKey) ?? this.gcOptions.runGCInTestMode === true;
|
|
643
|
-
// Whether we are running in tombstone mode. This is
|
|
644
|
-
|
|
648
|
+
// Whether we are running in tombstone mode. This is enabled by default if sweep won't run. It can be disabled
|
|
649
|
+
// via feature flags.
|
|
650
|
+
this.tombstoneMode = !this.shouldRunSweep && this.mc.config.getBoolean(disableTombstoneKey) !== true;
|
|
645
651
|
|
|
646
652
|
// If GC ran in the container that generated the base snapshot, it will have a GC tree.
|
|
647
653
|
this.wasGCRunInLatestSummary = baseSnapshot?.trees[gcTreeKey] !== undefined;
|
|
@@ -704,7 +710,9 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
704
710
|
}
|
|
705
711
|
// If there is only one node (root node just added above), either GC is disabled or we are loading from
|
|
706
712
|
// the first summary generated by detached container. In both cases, GC was not run - return undefined.
|
|
707
|
-
return Object.keys(gcState.gcNodes).length === 1
|
|
713
|
+
return Object.keys(gcState.gcNodes).length === 1
|
|
714
|
+
? undefined
|
|
715
|
+
: { gcState, tombstones: undefined, deletedNodes: undefined };
|
|
708
716
|
} catch (error) {
|
|
709
717
|
const dpe = DataProcessingError.wrapIfUnrecognized(
|
|
710
718
|
error,
|
|
@@ -782,24 +790,33 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
782
790
|
}
|
|
783
791
|
|
|
784
792
|
/**
|
|
785
|
-
* Called during container initialization. Initialize the tombstone state
|
|
786
|
-
*
|
|
787
|
-
* in use or not.
|
|
793
|
+
* Called during container initialization. Initialize from the tombstone state in the base snapshot. This is done
|
|
794
|
+
* during initialization so that deleted or tombstoned objects are marked as such before they are loaded or used.
|
|
788
795
|
*/
|
|
789
796
|
public async initializeBaseState(): Promise<void> {
|
|
790
797
|
const baseSnapshotData = await this.baseSnapshotDataP;
|
|
791
798
|
/**
|
|
792
|
-
* The base snapshot data
|
|
799
|
+
* The base snapshot data will not be present if the container is loaded from:
|
|
793
800
|
* 1. The first summary created by the detached container.
|
|
794
801
|
* 2. A summary that was generated with GC disabled.
|
|
795
802
|
* 3. A summary that was generated before GC even existed.
|
|
796
|
-
* 4. A summary that was generated with tombstone feature disabled.
|
|
797
803
|
*/
|
|
798
|
-
if (
|
|
804
|
+
if (baseSnapshotData === undefined) {
|
|
799
805
|
return;
|
|
800
806
|
}
|
|
801
|
-
|
|
802
|
-
|
|
807
|
+
|
|
808
|
+
// Initialize the deleted nodes from the snapshot. This is done irrespective of whether sweep is enabled or not
|
|
809
|
+
// to identify deleted nodes' usage.
|
|
810
|
+
if (baseSnapshotData.deletedNodes !== undefined) {
|
|
811
|
+
this.deletedNodes = new Set(baseSnapshotData.deletedNodes);
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// If running in tombstone mode, initialize the tombstone state from the snapshot. Also, notify the runtime of
|
|
815
|
+
// tombstone routes.
|
|
816
|
+
if (this.tombstoneMode && baseSnapshotData.tombstones !== undefined) {
|
|
817
|
+
this.tombstones = Array.from(baseSnapshotData.tombstones);
|
|
818
|
+
this.runtime.updateTombstonedRoutes(this.tombstones);
|
|
819
|
+
}
|
|
803
820
|
}
|
|
804
821
|
|
|
805
822
|
/**
|
|
@@ -831,9 +848,29 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
831
848
|
};
|
|
832
849
|
this.unreferencedNodesState.clear();
|
|
833
850
|
|
|
834
|
-
// If
|
|
835
|
-
//
|
|
836
|
-
|
|
851
|
+
// If running sweep, the tombstone state represents the list of nodes that have been deleted during sweep.
|
|
852
|
+
// If running in tombstone mode, the tombstone state represents the list of nodes that have been marked as
|
|
853
|
+
// tombstones.
|
|
854
|
+
// If this call is because we are refreshing from a snapshot due to an ack, it is likely that the GC state
|
|
855
|
+
// in the snapshot is newer than this client's. And so, the deleted / tombstone nodes need to be updated.
|
|
856
|
+
if (this.shouldRunSweep) {
|
|
857
|
+
const snapshotDeletedNodes = snapshotData?.tombstones ? new Set(snapshotData.tombstones) : undefined;
|
|
858
|
+
// If the snapshot contains deleted nodes that are not yet deleted by this client, ask the runtime to
|
|
859
|
+
// delete them.
|
|
860
|
+
if (snapshotDeletedNodes !== undefined) {
|
|
861
|
+
const newDeletedNodes: string[] = [];
|
|
862
|
+
for (const nodeId of snapshotDeletedNodes) {
|
|
863
|
+
if (!this.deletedNodes.has(nodeId)) {
|
|
864
|
+
newDeletedNodes.push(nodeId);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
if (newDeletedNodes.length > 0) {
|
|
868
|
+
// Call container runtime to delete these nodes and add deleted nodes to this.deletedNodes.
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
} else if (this.tombstoneMode) {
|
|
872
|
+
// The snapshot may contain more or fewer tombstone nodes than this client. Update tombstone state and
|
|
873
|
+
// notify the runtime to update its state as well.
|
|
837
874
|
this.tombstones = snapshotData?.tombstones ? Array.from(snapshotData.tombstones) : [];
|
|
838
875
|
this.runtime.updateTombstonedRoutes(this.tombstones);
|
|
839
876
|
}
|
|
@@ -869,6 +906,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
869
906
|
this.latestSummaryData = {
|
|
870
907
|
serializedGCState: JSON.stringify(generateSortedGCState(snapshotData.gcState)),
|
|
871
908
|
serializedTombstones: JSON.stringify(snapshotData.tombstones),
|
|
909
|
+
serializedDeletedNodes: JSON.stringify(snapshotData.deletedNodes),
|
|
872
910
|
};
|
|
873
911
|
}
|
|
874
912
|
}
|
|
@@ -1020,18 +1058,26 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1020
1058
|
}
|
|
1021
1059
|
|
|
1022
1060
|
const serializedGCState = JSON.stringify(generateSortedGCState(gcState));
|
|
1061
|
+
// Serialize and write deleted nodes, if any. This is done irrespective of whether sweep is enabled or not so
|
|
1062
|
+
// to identify deleted nodes' usage.
|
|
1063
|
+
const serializedDeletedNodes = this.deletedNodes.size > 0
|
|
1064
|
+
? JSON.stringify(Array.from(this.deletedNodes).sort())
|
|
1065
|
+
: undefined;
|
|
1066
|
+
// If running in tombstone mode, serialize and write tombstones, if any.
|
|
1023
1067
|
const serializedTombstones = this.tombstoneMode
|
|
1024
1068
|
? (this.tombstones.length > 0 ? JSON.stringify(this.tombstones.sort()) : undefined)
|
|
1025
1069
|
: undefined;
|
|
1026
1070
|
|
|
1027
1071
|
/**
|
|
1028
|
-
* Incremental summary of GC data - If
|
|
1029
|
-
*
|
|
1072
|
+
* Incremental summary of GC data - If none of GC state, deleted nodes or tombstones changed since last summary,
|
|
1073
|
+
* write summary handle instead of summary tree for GC.
|
|
1074
|
+
* Otherwise, write the GC summary tree. In the tree, for each of these that changed, write a summary blob and
|
|
1075
|
+
* for each of these that did not change, write a summary handle.
|
|
1030
1076
|
*/
|
|
1031
1077
|
if (this.trackGCState) {
|
|
1032
|
-
this.pendingSummaryData = { serializedGCState, serializedTombstones };
|
|
1078
|
+
this.pendingSummaryData = { serializedGCState, serializedTombstones, serializedDeletedNodes };
|
|
1033
1079
|
if (trackState && !fullTree && this.latestSummaryData !== undefined) {
|
|
1034
|
-
// If
|
|
1080
|
+
// If nothing changed since last summary, send a summary handle for the entire GC data.
|
|
1035
1081
|
if (this.latestSummaryData.serializedGCState === serializedGCState
|
|
1036
1082
|
&& this.latestSummaryData.serializedTombstones === serializedTombstones) {
|
|
1037
1083
|
const stats = mergeStats();
|
|
@@ -1046,26 +1092,30 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1046
1092
|
};
|
|
1047
1093
|
}
|
|
1048
1094
|
|
|
1049
|
-
// If
|
|
1050
|
-
return this.buildGCSummaryTree(
|
|
1095
|
+
// If some state changed, build a GC summary tree.
|
|
1096
|
+
return this.buildGCSummaryTree(
|
|
1097
|
+
serializedGCState, serializedTombstones, serializedDeletedNodes, true /* trackState */);
|
|
1051
1098
|
}
|
|
1052
1099
|
}
|
|
1053
1100
|
// If not tracking GC state, build a GC summary tree without any summary handles.
|
|
1054
|
-
return this.buildGCSummaryTree(
|
|
1101
|
+
return this.buildGCSummaryTree(
|
|
1102
|
+
serializedGCState, serializedTombstones, serializedDeletedNodes, false /* trackState */);
|
|
1055
1103
|
}
|
|
1056
1104
|
|
|
1057
1105
|
/**
|
|
1058
|
-
* Builds the GC summary tree which contains GC state and
|
|
1059
|
-
* If trackState is false,
|
|
1060
|
-
* If trackState is true,
|
|
1106
|
+
* Builds the GC summary tree which contains GC state, deleted nodes and tombstones.
|
|
1107
|
+
* If trackState is false, all of GC state, deleted nodes and tombstones are written as summary blobs.
|
|
1108
|
+
* If trackState is true, only states that changed are written. Rest are written as handles.
|
|
1061
1109
|
* @param serializedGCState - The GC state serialized as string.
|
|
1062
|
-
* @param serializedTombstones -
|
|
1110
|
+
* @param serializedTombstones - The tombstone state serialized as string.
|
|
1111
|
+
* @param serializedDeletedNodes - Deleted nodes serialized as string.
|
|
1063
1112
|
* @param trackState - Whether we are tracking GC state across summaries.
|
|
1064
1113
|
* @returns the GC summary tree.
|
|
1065
1114
|
*/
|
|
1066
1115
|
private buildGCSummaryTree(
|
|
1067
1116
|
serializedGCState: string,
|
|
1068
1117
|
serializedTombstones: string | undefined,
|
|
1118
|
+
serializedDeletedNodes: string | undefined,
|
|
1069
1119
|
trackState: boolean,
|
|
1070
1120
|
): ISummaryTreeWithStats {
|
|
1071
1121
|
const gcStateBlobKey = `${gcBlobPrefix}_root`;
|
|
@@ -1078,16 +1128,26 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1078
1128
|
builder.addBlob(gcStateBlobKey, serializedGCState);
|
|
1079
1129
|
}
|
|
1080
1130
|
|
|
1081
|
-
// If
|
|
1082
|
-
|
|
1131
|
+
// If tombstones exist, write a summary handle if it hasn't changed. If it has changed, write a
|
|
1132
|
+
// summary blob.
|
|
1133
|
+
if (serializedTombstones !== undefined) {
|
|
1134
|
+
if (this.latestSummaryData?.serializedTombstones === serializedTombstones && trackState) {
|
|
1135
|
+
builder.addHandle(gcTombstoneBlobKey, SummaryType.Blob, `/${gcTreeKey}/${gcTombstoneBlobKey}`);
|
|
1136
|
+
} else {
|
|
1137
|
+
builder.addBlob(gcTombstoneBlobKey, serializedTombstones);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// If there are no deleted nodes, return the summary tree.
|
|
1142
|
+
if (serializedDeletedNodes === undefined) {
|
|
1083
1143
|
return builder.getSummaryTree();
|
|
1084
1144
|
}
|
|
1085
1145
|
|
|
1086
|
-
// If the
|
|
1087
|
-
if (this.latestSummaryData?.
|
|
1088
|
-
builder.addHandle(
|
|
1146
|
+
// If the deleted nodes hasn't changed, write a summary handle, else write a summary blob for it.
|
|
1147
|
+
if (this.latestSummaryData?.serializedDeletedNodes === serializedDeletedNodes && trackState) {
|
|
1148
|
+
builder.addHandle(gcDeletedBlobKey, SummaryType.Blob, `/${gcTreeKey}/${gcDeletedBlobKey}`);
|
|
1089
1149
|
} else {
|
|
1090
|
-
builder.addBlob(
|
|
1150
|
+
builder.addBlob(gcDeletedBlobKey, serializedDeletedNodes);
|
|
1091
1151
|
}
|
|
1092
1152
|
return builder.getSummaryTree();
|
|
1093
1153
|
}
|
|
@@ -1118,9 +1178,8 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1118
1178
|
* is downloaded and should be used to update the state.
|
|
1119
1179
|
*/
|
|
1120
1180
|
public async refreshLatestSummary(
|
|
1121
|
-
result: RefreshSummaryResult,
|
|
1122
1181
|
proposalHandle: string | undefined,
|
|
1123
|
-
|
|
1182
|
+
result: RefreshSummaryResult,
|
|
1124
1183
|
readAndParseBlob: ReadAndParseBlob,
|
|
1125
1184
|
): Promise<void> {
|
|
1126
1185
|
// If the latest summary was updated and the summary was tracked, this client is the one that generated this
|
|
@@ -1147,8 +1206,8 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1147
1206
|
}
|
|
1148
1207
|
|
|
1149
1208
|
// If the summary was not tracked by this client, the state should be updated from the downloaded snapshot.
|
|
1150
|
-
const
|
|
1151
|
-
const metadataBlobId =
|
|
1209
|
+
const snapshotTree = result.snapshotTree;
|
|
1210
|
+
const metadataBlobId = snapshotTree.blobs[metadataBlobName];
|
|
1152
1211
|
if (metadataBlobId) {
|
|
1153
1212
|
const metadata = await readAndParseBlob<IContainerRuntimeMetadata>(metadataBlobId);
|
|
1154
1213
|
this.latestSummaryGCVersion = getGCVersion(metadata);
|
|
@@ -1162,10 +1221,10 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1162
1221
|
"No reference timestamp when updating GC state from snapshot",
|
|
1163
1222
|
"refreshLatestSummary",
|
|
1164
1223
|
undefined,
|
|
1165
|
-
{ proposalHandle, summaryRefSeq, details: JSON.stringify(this.configs) },
|
|
1224
|
+
{ proposalHandle, summaryRefSeq: result.summaryRefSeq, details: JSON.stringify(this.configs) },
|
|
1166
1225
|
);
|
|
1167
1226
|
}
|
|
1168
|
-
const gcSnapshotTree =
|
|
1227
|
+
const gcSnapshotTree = snapshotTree.trees[gcTreeKey];
|
|
1169
1228
|
// If GC ran in the container that generated this snapshot, it will have a GC tree.
|
|
1170
1229
|
this.wasGCRunInLatestSummary = gcSnapshotTree !== undefined;
|
|
1171
1230
|
let latestGCData: IGarbageCollectionSnapshotData | undefined;
|
|
@@ -1257,6 +1316,14 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1257
1316
|
}
|
|
1258
1317
|
}
|
|
1259
1318
|
|
|
1319
|
+
/**
|
|
1320
|
+
* Returns whether a node with the given path has been deleted or not. This can be used by the runtime to identify
|
|
1321
|
+
* cases where objects are used after they are deleted and throw / log errors accordingly.
|
|
1322
|
+
*/
|
|
1323
|
+
public isNodeDeleted(nodePath: string): boolean {
|
|
1324
|
+
return this.deletedNodes.has(nodePath);
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1260
1327
|
public dispose(): void {
|
|
1261
1328
|
this.sessionExpiryTimer?.clear();
|
|
1262
1329
|
this.sessionExpiryTimer = undefined;
|
|
@@ -1640,7 +1707,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1640
1707
|
}
|
|
1641
1708
|
}
|
|
1642
1709
|
|
|
1643
|
-
// If SweepReady Usage Detection is
|
|
1710
|
+
// If SweepReady Usage Detection is enabled, the handler may close the interactive container.
|
|
1644
1711
|
// Once Sweep is fully implemented, this will be removed since the objects will be gone
|
|
1645
1712
|
// and errors will arise elsewhere in the runtime
|
|
1646
1713
|
if (state === UnreferencedState.SweepReady) {
|
package/src/packageVersion.ts
CHANGED
package/src/summarizer.ts
CHANGED
|
@@ -160,7 +160,7 @@ export class Summarizer extends EventEmitter implements ISummarizer {
|
|
|
160
160
|
// This will result in "summarizerClientDisconnected" stop reason recorded in telemetry,
|
|
161
161
|
// unless stop() was called earlier
|
|
162
162
|
this.dispose();
|
|
163
|
-
(this.runtime.disposeFn ?? this.runtime.closeFn)()
|
|
163
|
+
(this.runtime.disposeFn ?? this.runtime.closeFn)();
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
private async runCore(onBehalfOf: string): Promise<SummarizerStopReason> {
|