This is an automated email from the ASF dual-hosted git repository.
NSAmelchev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 5394a9fa23a IGNITE-28736 Fixed order of fields in
BinaryType#fieldNames (#13411)
5394a9fa23a is described below
commit 5394a9fa23a67754e6fa75f2c2ff601cca1df662
Author: Nikita Amelchev <[email protected]>
AuthorDate: Mon Aug 3 13:58:03 2026 +0300
IGNITE-28736 Fixed order of fields in BinaryType#fieldNames (#13411)
---
.../java/org/apache/ignite/binary/BinaryType.java | 6 ++-
.../ignite/internal/binary/BinaryContext.java | 2 +-
.../ignite/internal/binary/BinaryMetadata.java | 7 ++-
.../internal/binary/BinaryMetadataCollector.java | 6 +--
.../MoveBinaryMetadataCompatibility.java | 23 +++++++---
.../org/apache/ignite/client/IgniteBinaryTest.java | 23 ++++++++++
.../internal/binary/BinaryMarshallerSelfTest.java | 51 +++++++++++++++++++++-
.../ignite/internal/client/thin/ClientUtils.java | 5 +--
8 files changed, 103 insertions(+), 20 deletions(-)
diff --git
a/modules/binary/api/src/main/java/org/apache/ignite/binary/BinaryType.java
b/modules/binary/api/src/main/java/org/apache/ignite/binary/BinaryType.java
index e108349c9fb..147e69ba040 100644
--- a/modules/binary/api/src/main/java/org/apache/ignite/binary/BinaryType.java
+++ b/modules/binary/api/src/main/java/org/apache/ignite/binary/BinaryType.java
@@ -41,7 +41,11 @@ public interface BinaryType {
public int typeId();
/**
- * Gets collection of all field names for this binary type.
+ * Gets collection of all field names for this binary type in the order in
which the fields were initially
+ * registered.
+ * <p>
+ * Older Ignite versions did not guarantee field registration order. For
metadata created or processed by such a
+ * version, the returned order is not guaranteed to match the original
registration order.
*
* @return Collection of all field names for this binary type.
*/
diff --git
a/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryContext.java
b/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryContext.java
index fa4bd41cc2c..f00e26fb26a 100644
---
a/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryContext.java
+++
b/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryContext.java
@@ -264,7 +264,7 @@ public class BinaryContext {
BinaryUtils.binariesFactory.predefinedTypes().forEach(this::registerPredefinedType);
- // BinaryUtils.FIELDS_SORTED_ORDER support, since it uses TreeMap at
BinaryMetadata.
+ // TreeMap and TreeSet binary write-replacement support.
registerBinarilizableSystemClass(BinaryTreeMap.class);
registerBinarilizableSystemClass(TreeMap.class);
registerBinarilizableSystemClass(TreeSet.class);
diff --git
a/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryMetadata.java
b/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryMetadata.java
index 601550ea63b..9fb8a918fa6 100644
---
a/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryMetadata.java
+++
b/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryMetadata.java
@@ -26,7 +26,6 @@ import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
@@ -53,7 +52,7 @@ public class BinaryMetadata implements Externalizable {
@GridToStringInclude(sensitive = true)
private String typeName;
- /** Recorded object fields. */
+ /** Recorded object fields in initial registration order. */
@GridToStringInclude(sensitive = true)
private Map<String, BinaryFieldMetadata> fields;
@@ -117,7 +116,7 @@ public class BinaryMetadata implements Externalizable {
this.typeId = typeId;
this.typeName = typeName;
- this.fields = fields;
+ this.fields = fields == null ? null : new LinkedHashMap<>(fields);
this.affKeyFieldName = affKeyFieldName;
this.schemas = schemas;
@@ -304,7 +303,7 @@ public class BinaryMetadata implements Externalizable {
if (fieldsSize == -1)
fields = null;
else {
- fields = new HashMap<>();
+ fields = new LinkedHashMap<>();
for (int i = 0; i < fieldsSize; i++) {
String fieldName = CommonUtils.readString(in);
diff --git
a/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryMetadataCollector.java
b/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryMetadataCollector.java
index 3700159b573..a946c5724ea 100644
---
a/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryMetadataCollector.java
+++
b/modules/binary/api/src/main/java/org/apache/ignite/internal/binary/BinaryMetadataCollector.java
@@ -25,7 +25,7 @@ import java.sql.Time;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.Date;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import org.apache.ignite.binary.BinaryObjectException;
@@ -46,8 +46,8 @@ class BinaryMetadataCollector implements BinaryWriter {
/** Name mapper. */
private final BinaryInternalMapper mapper;
- /** Collected metadata. */
- private final Map<String, BinaryFieldMetadata> meta = new HashMap<>();
+ /** Collected metadata in field write order. */
+ private final Map<String, BinaryFieldMetadata> meta = new
LinkedHashMap<>();
/** Schema builder. */
private BinarySchema.Builder schemaBuilder =
BinarySchema.Builder.newBuilder();
diff --git
a/modules/compatibility/src/test/java/org/apache/ignite/compatibility/persistence/MoveBinaryMetadataCompatibility.java
b/modules/compatibility/src/test/java/org/apache/ignite/compatibility/persistence/MoveBinaryMetadataCompatibility.java
index 94fe6130a03..8ab80b32ba1 100644
---
a/modules/compatibility/src/test/java/org/apache/ignite/compatibility/persistence/MoveBinaryMetadataCompatibility.java
+++
b/modules/compatibility/src/test/java/org/apache/ignite/compatibility/persistence/MoveBinaryMetadataCompatibility.java
@@ -21,6 +21,7 @@ import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteBinary;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.binary.BinaryObject;
+import org.apache.ignite.binary.BinaryObjectBuilder;
import
org.apache.ignite.compatibility.testframework.junits.SkipTestIfIsJdkNewer;
import org.apache.ignite.configuration.DataRegionConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
@@ -59,14 +60,14 @@ public class MoveBinaryMetadataCompatibility extends
IgnitePersistenceCompatibil
public void test() throws Exception {
String typeName = "TestBinaryType";
- String fieldName = "testField";
-
- String fieldVal = "testVal";
+ String[] fieldNames = {"field9", "field8", "field0", "field1",
"field2"};
String cacheName = "testCache";
String objKey = "obj";
+ String fieldOrderKey = "fieldOrder";
+
String consid = "node1";
// start old version and save some binary object to cache
@@ -83,11 +84,17 @@ public class MoveBinaryMetadataCompatibility extends
IgnitePersistenceCompatibil
IgniteBinary binary = ignite.binary();
- BinaryObject test = binary.builder(typeName).setField(fieldName,
fieldVal).build();
+ BinaryObjectBuilder builder = binary.builder(typeName);
+
+ for (String fieldName : fieldNames)
+ builder.setField(fieldName, fieldName);
+
+ BinaryObject test = builder.build();
IgniteCache<Object, Object> cache =
ignite.getOrCreateCache(cacheName).withKeepBinary();
cache.put(objKey, test);
+ cache.put(fieldOrderKey, String.join(",",
binary.type(typeName).fieldNames()));
});
stopAllGrids();
@@ -103,9 +110,13 @@ public class MoveBinaryMetadataCompatibility extends
IgnitePersistenceCompatibil
// check that binary object is still available in new version of grid
BinaryObject obj = (BinaryObject)cache.get(objKey);
- assertTrue(obj.hasField(fieldName));
+ for (String fieldName : fieldNames) {
+ assertTrue(obj.hasField(fieldName));
+
+ assertEquals(fieldName, obj.field(fieldName));
+ }
- assertEquals(fieldVal, obj.field(fieldName));
+ assertEquals(cache.get(fieldOrderKey), String.join(",",
obj.type().fieldNames()));
newGrid.close();
}
diff --git
a/modules/core/src/test/java/org/apache/ignite/client/IgniteBinaryTest.java
b/modules/core/src/test/java/org/apache/ignite/client/IgniteBinaryTest.java
index 2059d1ee137..8bb6781861b 100644
--- a/modules/core/src/test/java/org/apache/ignite/client/IgniteBinaryTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/client/IgniteBinaryTest.java
@@ -140,6 +140,29 @@ public class IgniteBinaryTest extends
GridCommonAbstractTest {
}
}
+ /** Tests that binary type field order is preserved when metadata is read
by a thin client. */
+ @Test
+ public void testBinaryTypeFieldOrder() throws Exception {
+ String typeName = "OrderedType";
+ String[] fieldNames = {"field9", "field8", "field0", "field1",
"field2"};
+
+ try (Ignite ignite = Ignition.start(Config.getServerConfiguration())) {
+ BinaryObjectBuilder builder = ignite.binary().builder(typeName);
+
+ for (String fieldName : fieldNames)
+ builder.setField(fieldName, 0);
+
+ BinaryType serverType = builder.build().type();
+ Object[] serverFieldNames = serverType.fieldNames().toArray();
+
+ try (IgniteClient client = Ignition.startClient(new
ClientConfiguration().setAddresses(Config.SERVER))) {
+ BinaryType clientType =
client.binary().type(serverType.typeId());
+
+ assertArrayEquals(serverFieldNames,
clientType.fieldNames().toArray());
+ }
+ }
+ }
+
/**
* Tests that {@code
org.apache.ignite.cache.CacheInterceptor#onBeforePut(javax.cache.Cache.Entry,
java.lang.Object)}
* throws correct exception in case while cache operations are called from
thin client. Only BinaryObject`s are
diff --git
a/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java
b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java
index 6f724f526e5..3e52bbc0e35 100644
---
a/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java
+++
b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java
@@ -3770,6 +3770,37 @@ public class BinaryMarshallerSelfTest extends
AbstractBinaryArraysTest {
}
}
+ /** @throws Exception If failed. */
+ @Test
+ public void testFieldOrderByBinarylizable() throws Exception {
+ BinaryObjectImpl binObj = marshal(new FieldOrderBinarylizable(),
binaryMarshaller());
+
+ assertArrayEquals(FieldOrderBinarylizable.FIELD_NAMES,
binObj.type().fieldNames().toArray());
+ }
+
+ /** @throws Exception If failed. */
+ @Test
+ public void testMetadataFieldOrderAfterSerialization() throws Exception {
+ String[] fieldNames = {"field9", "field8", "field0", "field1",
"field2"};
+
+ Map<String, BinaryFieldMetadata> fields = new LinkedHashMap<>();
+
+ for (String fieldName : fieldNames)
+ fields.put(fieldName, new
BinaryFieldMetadata(GridBinaryMarshaller.INT, fieldName.hashCode()));
+
+ BinaryMetadata meta = new BinaryMetadata(1, "type", fields, null,
false, null);
+ BinaryMarshaller marsh = binaryMarshaller();
+ BinaryOutputStream out = BinaryStreams.outputStream(1024);
+
+ meta.writeTo(marsh.binaryMarshaller().writer(out));
+
+ BinaryMetadata restoredMeta = new BinaryMetadata();
+
+
restoredMeta.readFrom(marsh.binaryMarshaller().reader(BinaryStreams.inputStream(out.array())));
+
+ assertArrayEquals(fieldNames, restoredMeta.fields().toArray());
+ }
+
/**
* @param obj Instance of the BinaryObjectImpl to offheap marshalling.
* @param marsh Binary marshaller.
@@ -5065,8 +5096,24 @@ public class BinaryMarshallerSelfTest extends
AbstractBinaryArraysTest {
}
}
- /**
- */
+ /** */
+ private static class FieldOrderBinarylizable implements Binarylizable {
+ /** Field names. */
+ private static final String[] FIELD_NAMES = {"field9", "field8",
"field0", "field1", "field2"};
+
+ /** {@inheritDoc} */
+ @Override public void writeBinary(BinaryWriter writer) throws
BinaryObjectException {
+ for (String fieldName : FIELD_NAMES)
+ writer.writeInt(fieldName, 0);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void readBinary(BinaryReader reader) throws
BinaryObjectException {
+ // No-op.
+ }
+ }
+
+ /** */
private static class CustomSerializedObject1 implements Binarylizable {
/** */
private int val;
diff --git
a/modules/thin-client/impl/src/main/java/org/apache/ignite/internal/client/thin/ClientUtils.java
b/modules/thin-client/impl/src/main/java/org/apache/ignite/internal/client/thin/ClientUtils.java
index 474c4e45709..ac8a9a4c7d3 100644
---
a/modules/thin-client/impl/src/main/java/org/apache/ignite/internal/client/thin/ClientUtils.java
+++
b/modules/thin-client/impl/src/main/java/org/apache/ignite/internal/client/thin/ClientUtils.java
@@ -22,7 +22,6 @@ import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
@@ -146,7 +145,7 @@ public final class ClientUtils {
}
/**
- * @return Deserialized map
+ * @return Deserialized map preserving entry order.
*/
private static <K, V> Map<K, V> map(
BinaryInputStream in,
@@ -155,7 +154,7 @@ public final class ClientUtils {
) {
int cnt = in.readInt();
- Map<K, V> map = new HashMap<>(cnt);
+ Map<K, V> map = new LinkedHashMap<>(cnt);
for (int i = 0; i < cnt; i++)
map.put(keyReader.apply(in), valReader.apply(in));