Nikolay Izhikov created IGNITE-29185:
----------------------------------------

             Summary: cpMapSnapshot.bin written by Ignite 2.17/2.18 can't be 
read by current version (generated IDTO serializer changed UUID format)
                 Key: IGNITE-29185
                 URL: https://issues.apache.org/jira/browse/IGNITE-29185
             Project: Ignite
          Issue Type: Bug
    Affects Versions: 2.19
            Reporter: Nikolay Izhikov
         Attachments: IGNITE-29185-reproducer.patch

h3. Problem

{{cpMapSnapshot.bin}} (earliest checkpoint map snapshot, see 
{{CheckpointMarkersStorage#EARLIEST_CP_SNAPSHOT_FILE}}) written by Ignite 2.17 
/ 2.18 can't be deserialized by current master (future 2.19).

The file is an {{EarliestCheckpointMapSnapshot}} marshalled with 
{{JdkMarshaller}}. In 2.17 and 2.18 the payload is written by hand:

{code:java}
U.writeMap(out, data);              // keys (UUID) and values via 
out.writeObject(...)
U.writeCollection(out, checkpointIds); // UUID elements via out.writeObject(...)
{code}

Since IGNITE-27709 the class is serialized by generated code, and since 
IGNITE-27775 the generator emits typed code for collections. The generated 
{{EarliestCheckpointMapSnapshotSerializer}} now writes/reads the outer map keys 
and the {{checkpointIds}} elements with {{U.writeUuid / U.readUuid}} (null flag 
+ two longs) instead of {{writeObject / readObject}} (Java-serialized 
{{java.util.UUID}}). The two formats are not interchangeable, so the file 
produced by a previous version can't be read after upgrade.

The header change made by IGNITE-24260 (protocol version byte removed) is *not* 
a problem: the reader masks the version byte.

h3. Impact

{{CheckpointMarkersStorage#initialize}} catches the failure, logs

{noformat}
Failed to unmarshal earliest checkpoint map snapshot
{noformat}

deletes the file and rebuilds the earliest checkpoint map from checkpoint 
markers. No data is lost, but the optimization is silently lost on the first 
start after an upgrade and the node logs an error. PDS files must stay backward 
compatible, so the old format has to be readable.

h3. Reproducer

{{CheckpointMapSnapshotCompatibilityTest}} in {{ignite-compatibility}} 
(attached): starts a 2.17.0 / 2.18.0 node with persistence, waits for 
{{cpMapSnapshot.bin}}, restarts on the same PDS with the current version and 
asserts the error above isn't logged.

Test output (same for 2.17.0 and 2.18.0):

{noformat}
class org.apache.ignite.IgniteCheckedException: Failed to deserialize object 
with given class loader: null
        at 
org.apache.ignite.marshaller.jdk.JdkMarshallerImpl.unmarshal0(JdkMarshallerImpl.java:128)
        at 
org.apache.ignite.marshaller.jdk.JdkMarshallerImpl.unmarshal0(JdkMarshallerImpl.java:135)
        at 
org.apache.ignite.marshaller.AbstractNodeNameAwareMarshaller.unmarshal(AbstractNodeNameAwareMarshaller.java:74)
        at 
org.apache.ignite.internal.processors.cache.persistence.checkpoint.CheckpointMarkersStorage.initialize(CheckpointMarkersStorage.java:209)
        at 
org.apache.ignite.internal.processors.cache.persistence.checkpoint.CheckpointManager.initializeStorage(CheckpointManager.java:325)
        at 
org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager.readMetastore(GridCacheDatabaseSharedManager.java:854)
        at 
org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager.notifyMetaStorageSubscribersOnReadyForRead(GridCacheDatabaseSharedManager.java:3075)
        at org.apache.ignite.internal.IgniteKernal.start(IgniteKernal.java:1122)
        at 
org.apache.ignite.internal.IgnitionEx$IgniteNamedInstance.start0(IgnitionEx.java:1713)
        at 
org.apache.ignite.internal.IgnitionEx$IgniteNamedInstance.start(IgnitionEx.java:1635)
        at org.apache.ignite.internal.IgnitionEx.start0(IgnitionEx.java:1077)
        at org.apache.ignite.internal.IgnitionEx.start(IgnitionEx.java:587)
        at 
org.apache.ignite.testframework.junits.GridAbstractTest.startGrid(GridAbstractTest.java:1307)
        at 
org.apache.ignite.compatibility.testframework.junits.IgniteCompatibilityAbstractTest.startGrid(IgniteCompatibilityAbstractTest.java:344)
        at 
org.apache.ignite.testframework.junits.GridAbstractTest.startGrid(GridAbstractTest.java:1246)
        at 
org.apache.ignite.testframework.junits.GridAbstractTest.startGrid(GridAbstractTest.java:1222)
        at 
org.apache.ignite.testframework.junits.GridAbstractTest.startGrid(GridAbstractTest.java:1053)
        at 
org.apache.ignite.compatibility.persistence.CheckpointMapSnapshotCompatibilityTest.testSnapshotFileReadableByCurrentVersion(CheckpointMapSnapshotCompatibilityTest.java:121)
        at 
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at 
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at 
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:569)
        at 
org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
        at 
org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
        at 
org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
        at 
org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
        at 
org.apache.ignite.testframework.junits.GridAbstractTest$6.run(GridAbstractTest.java:2464)
        at java.base/java.lang.Thread.run(Thread.java:840)
Caused by: java.io.EOFException
        at 
java.base/java.io.ObjectInputStream$BlockDataInputStream.readBoolean(ObjectInputStream.java:3374)
        at 
java.base/java.io.ObjectInputStream.readBoolean(ObjectInputStream.java:1062)
        at 
org.apache.ignite.internal.dto.IgniteDataTransferObjectInput.readBoolean(IgniteDataTransferObjectInput.java:99)
        at 
org.apache.ignite.internal.util.CommonUtils.readUuid(CommonUtils.java:682)
        at 
org.apache.ignite.internal.processors.cache.persistence.checkpoint.EarliestCheckpointMapSnapshotSerializer.readExternal(EarliestCheckpointMapSnapshotSerializer.java:84)
        at 
org.apache.ignite.internal.processors.cache.persistence.checkpoint.EarliestCheckpointMapSnapshotSerializer.readExternal(EarliestCheckpointMapSnapshotSerializer.java:38)
        at 
org.apache.ignite.internal.dto.IgniteDataTransferObject.readIgniteDataTransferObject(IgniteDataTransferObject.java:84)
        at 
org.apache.ignite.internal.dto.IgniteDataTransferObject.readExternal(IgniteDataTransferObject.java:67)
        at 
java.base/java.io.ObjectInputStream.readExternalData(ObjectInputStream.java:2306)
        at 
java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2255)
        at 
java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1733)
        at 
java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:509)
        at 
java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:467)
        at 
org.apache.ignite.marshaller.jdk.JdkMarshallerImpl.unmarshal0(JdkMarshallerImpl.java:120)
        ... 27 more
{noformat}

h3. Possible fix

Make the generated code (or a hand-written {{readExternalData}} in 
{{EarliestCheckpointMapSnapshot}}) able to read the legacy layout, e.g. keep 
{{writeObject/readObject}} for this class or detect the old format. Add the 
compatibility test to the suite.




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to