8000 Enums are incorrectly treated upon database load · Issue #372 · eclipse-store/store · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Enums are incorrectly treated upon database load #372

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Bios-Marcel opened this issue Feb 11, 2025 · 8 comments
Open

Enums are incorrectly treated upon database load #372

Bios-Marcel opened this issue Feb 11, 2025 · 8 comments

Comments

@Bios-Marcel
Copy link
Bios-Marcel commented Feb 11, 2025

Environment Details

  • EclipseStore Version: 2.1.1
  • JDK version: 21
  • OS: Windows 11

Describe the bug

Enums seem to be persisted the first time they are encountered, not reflecting any changes on the enum fields.

I have an enum in which I have a collection. I added an item to the collection, but at runtime, its the old collection, without the added item.

If I print it first thing in main, it shows the new value. Once EclipseStore started up, the enum values were replaced with the persisted ones it seemed.

To Reproduce

import org.eclipse.store.storage.embedded.types.EmbeddedStorage;

import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

public class BugMain {
    enum EnumA {
        A1(List.of(EnumB.B1, EnumB.B2)),
        A2(List.of(EnumB.B1 /*, EnumB.B2 UNCOMMENT AFTER FIRST STARTT */)),
        ;
        final List<EnumB> enumBs;

        EnumA(List<EnumB> enumBs) {
            this.enumBs = new ArrayList<>(enumBs);
        }
    }

    enum EnumB {
        B1,
        B2,
        ;
    }

    static class Root {
        EnumA a = null;
    }

    public static void main(String[] args) {
        System.out.println("Before: " + EnumA.A2.enumBs);
        final var manager = EmbeddedStorage.start(Paths.get("./temp_db"));
        if (manager.root() == null) {
            Root newRoot = new Root();
            newRoot.a = EnumA.A2;
            manager.setRoot(newRoot);
            manager.storeRoot();
        }
        System.out.println("After: "+ EnumA.A2.enumBs);

    }
}
  1. Start once
  2. Uncomment EnumA.A2.B2
  3. Start again
  4. Enum is incorrect

Expected behavior

Enum values should always represent the same state as they did before restoring eclipse store data.

@hg-ms
Copy link
Contributor
hg-ms commented Feb 17, 2025

Hi
That’s an interesting scenario.
From my point of view, the behavior is correct.
The enum constants are not modified during loading. It is the referenced list (final List enumBs) content that is restored to its persisted state.

@Bios-Marcel
Copy link
Author

Uh, I am confused.

You are saying that the incorrect enum content is expected behaviour?
I'd say the referenced enum values should only be "pointers", instead of the serialized enum values.

OR even if they are serialized completely, I think it is an error to override the enum instances loaded in the JVM.

@hg-ms
Copy link
Contributor
hg-ms commented Feb 18, 2025

I won’t say that restoring the persisted state of the enums internal values is wrong. That is the state of the enum and all objects referencing that enum when the data had been persisted. This way the persisted state can be restored without been altered by code changes.

Btw there one trick: you can define the List<EnumB> enumBs as transient, then it does not get persisted.

@Bios-Marcel
Copy link
Author

Ah yeah, thanks. Obvious workaround that I didn't think of.

Anyway, let's look at another hypothetical scenario.

Let's say I had two objects.

One is created today (Day A) references Version 1 of the enum and is persisted.

The second object is created tomorrow (Day B) and references Version 2 of the enum and is persisted.

Both objects and therefore both versions of the enum would be in the database.

According to the logic previously explained, both values would be correct, but only one would be loaded.
It would not even be predictable which is loaded first I think?

@Bios-Marcel
Copy link
Author

It seems the storage manager doesn't quite handler this too well :D

Caused by: java.lang.ArrayIndexOutOfBoundsException: Index -1486618539 out of bounds for length 3
	at org.eclipse.serializer.persistence.types.PersistenceLegacyTypeHandler.resolveEnumConstant(PersistenceLegacyTypeHandler.java:69) ~[persistence-2.1.1.jar:?]
	at org.eclipse.serializer.persistence.binary.types.BinaryLegacyTypeHandlerGenericEnumMapped.internalCreate(BinaryLegacyTypeHandlerGenericEnumMapped.java:93) ~[persistence-binary-2.1.1.jar:?]
	at org.eclipse.serializer.persistence.binary.types.AbstractBinaryLegacyTypeHandlerTranslating.create(AbstractBinaryLegacyTypeHandlerTranslating.java:252) ~[persistence-binary-2.1.1.jar:?]
	at org.eclipse.serializer.persistence.binary.types.AbstractBinaryLegacyTypeHandlerTranslating.create(AbstractBinaryLegacyTypeHandlerTranslating.java:34) ~[persistence-binary-2.1.1.jar:?]
	at org.eclipse.serializer.persistence.binary.types.BinaryLoader$Default.createBuildItem(BinaryLoader.java:216) ~[persistence-binary-2.1.1.jar:?]
	at org.eclipse.serializer.persistence.binary.types.BinaryLoader$Default.internalReadBinaryEntities(BinaryLoader.java:169) ~[persistence-binary-2.1.1.jar:?]
	at org.eclipse.serializer.persistence.binary.types.BinaryLoader$Default.readBinaryEntities(BinaryLoader.java:157) ~[persistence-binary-2.1.1.jar:?]
	at org.e
8000
clipse.serializer.persistence.binary.types.ChunksBuffer.iterateEntityDataLocal(ChunksBuffer.java:279) ~[persistence-binary-2.1.1.jar:?]
	at org.eclipse.serializer.persistence.binary.types.ChunksBuffer.iterateEntityData(ChunksBuffer.java:288) ~[persistence-binary-2.1.1.jar:?]
	at org.eclipse.serializer.persistence.binary.types.BinaryLoader$Default.addChunks(BinaryLoader.java:804) ~[persistence-binary-2.1.1.jar:?]
	at org.eclipse.serializer.persistence.binary.types.BinaryLoader$Default.readLoadOidData(BinaryLoader.java:774) ~[persistence-binary-2.1.1.jar:?]
	at org.eclipse.serializer.persistence.binary.types.BinaryLoader$Default.readLoadOnce(BinaryLoader.java:718) ~[persistence-binary-2.1.1.jar:?]
	at org.eclipse.serializer.persistence.binary.types.BinaryLoader$Default.get(BinaryLoader.java:827) ~[persistence-binary-2.1.1.jar:?]
	at org.eclipse.serializer.persistence.binary.types.BinaryLoader$Default.loadRoots(BinaryLoader.java:872) ~[persistence-binary-2.1.1.jar:?]
	at org.eclipse.store.storage.embedded.types.EmbeddedStorageManager$Default.loadExistingRoots(EmbeddedStorageManager.java:352) ~[storage-embedded-2.1.1.jar:?]
	at org.eclipse.store.storage.embedded.types.EmbeddedStorageManager$Default.initialize(EmbeddedStorageManager.java:375) ~[storage-embedded-2.1.1.jar:?]
	at org.eclipse.store.storage.embedded.types.EmbeddedStorageManager$Default.start(EmbeddedStorageManager.java:255) ~[storage-embedded-2.1.1.jar:?]
	at org.eclipse.store.storage.embedded.types.EmbeddedStorageManager$Default.start(EmbeddedStorageManager.java:96) ~[storage-embedded-2.1.1.jar:?]

Shouldn't making a field transient behave the same way as removing a field? Removal would've worked with the heuristic approach afaik? What's different here?

@hg-ms
Copy link
Contributor
hg-ms commented Feb 19, 2025

Never saw that crash with enums. Did you delete the old storage date before making a field transient? There might be some chase the LegacyTypeHandling can’t solve. I couldn’t reproduce that exception.

Regarding enums and legacy type handling:
This is tricky, not all changes to an enum are allowed, adding a new constant is ok, but deleting or reordering will most likely cause problems. If the enum constants are changed in a problematic way, you should get an exception during startup.

@Bios-Marcel
Copy link
Author

I didnt delete the data before making them transient, i want to keep all data afterall.

I also tried to figure out how to write a binary type handler for this case, but couldnt quite figure it out. At this point, I am not sure how to work around this.

Also, if you really dont deem this a bug, which I still believe you should, given this example #372 (comment), maybe the documentation should point this out, to prevent others from running into this, as this is a major pain.

@hg-ms
Copy link
Contributor
hg-ms commented Feb 20, 2025

I guess the legacy type handling reached its limits here. I don’t have a solution for that case, sorry.

Here is a simple typeTandler that persists only the enum constant name for a specific enumeration. Maybe this can help:

class MyEnumHandler extends AbstractBinaryHandlerCustom<EnumA>
{
	public MyEnumHandler() {
		super(EnumA.class, CustomFields(chars("enumConstantName")));
	}

	@Override
	/**
	 * Persists enum constant name as string
	 */
	public void store(Binary data, EnumA instance, long objectId, PersistenceStoreHandler<Binary> handler) {
		data.storeStringSingleValue(this.typeId(), objectId, instance.name());
	}

	@Override
	/**
	 * Use persisted enum constant name to restore enum value.
	 */
	public EnumA create(Binary data, PersistenceLoadHandler handler) {
		return EnumA.valueOf(data.buildString());
	}

	@Override
	/**
	 * Used to register the enum constants as roots.
	 * Return empty array to skip regstering
	 */
	public Object[] collectEnumConstants() {
		return new Object[0];
	}

	@Override
	public void iterateLoadableReferences(Binary data, PersistenceReferenceLoader iterator) {
		//nothing to do
		
	}

	@Override
	public void updateState(Binary data, EnumA instance, PersistenceLoadHandler handler) {
		//nothing to do
	};

}

To setup:

EmbeddedStorage.Foundation(WORKDIR)
	.onConnectionFoundation(f -> f.getCustomTypeHandlerRegistry().registerTypeHandler(
5BB0
new MyEnumHandler()))
	.start();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants
0