xiangfu0 commented on code in PR #19738:
URL: https://github.com/apache/pinot/pull/19738#discussion_r4290444930


##########
pinot-common/src/main/java/org/apache/pinot/common/utils/helix/HelixHelper.java:
##########
@@ -78,9 +78,31 @@ private HelixHelper() {
 
   public static final String BROKER_RESOURCE = 
CommonConstants.Helix.BROKER_RESOURCE_INSTANCE;
 
+  /// Returns a deep copy of the IdealState with the same content as cloning 
it through a serialization round trip of
+  /// its ZNRecord: id, simple fields, map fields, list fields and raw 
payload, with the same container types and
+  /// iteration order. Like the round trip, the ZK stat fields (e.g. version) 
are not copied. Unlike the round trip, the
+  /// serializer's write policies (list field bound, size limit) are not 
applied; they still apply when the IdealState
+  /// is written.
+  ///
+  /// Copying the fields directly avoids serializing (and compressing) the 
whole ZNRecord and parsing it back, which
+  /// takes seconds for an IdealState with hundreds of thousands of segments 
and widens the window for version
+  /// conflicts when updating it.
   public static IdealState cloneIdealState(IdealState idealState) {
-    return new IdealState(
-        (ZNRecord) 
ZN_RECORD_SERIALIZER.deserialize(ZN_RECORD_SERIALIZER.serialize(idealState.getRecord())));
+    ZNRecord record = idealState.getRecord();
+    IdealState copy = new IdealState(record.getId());
+    ZNRecord copyRecord = copy.getRecord();
+    copyRecord.setSimpleFields(new TreeMap<>(record.getSimpleFields()));
+    TreeMap<String, Map<String, String>> mapFields = new 
TreeMap<>(record.getMapFields());
+    mapFields.replaceAll((key, value) -> value != null ? new 
LinkedHashMap<>(value) : null);
+    copyRecord.setMapFields(mapFields);
+    TreeMap<String, List<String>> listFields = new 
TreeMap<>(record.getListFields());
+    listFields.replaceAll((key, value) -> value != null ? new 
ArrayList<>(value) : null);
+    copyRecord.setListFields(listFields);
+    byte[] rawPayload = record.getRawPayload();
+    if (rawPayload != null) {
+      copyRecord.setRawPayload(rawPayload.clone());
+    }
+    return copy;

Review Comment:
   Nice — the deep copy looks correct here. The outer `new TreeMap<>(...)` plus 
`replaceAll` with fresh `LinkedHashMap`/`ArrayList` gives genuinely independent 
maps and lists, so no mutable state is shared back to the source record. It 
also matches what the old round trip produced (sorted TreeMap outer maps, 
LinkedHashMap inner maps, ArrayList lists), which the test confirms.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to