From 2a8fbc72de2781d8c99f84722fc4a45f29ed0d94 Mon Sep 17 00:00:00 2001 From: Stepan Usatiuk Date: Tue, 31 Dec 2024 18:05:19 +0100 Subject: [PATCH] slightly hacky versioning --- .../dhfs/objects/JDataVersionedWrapper.java | 11 +++++ .../usatiuk/dhfs/objects/JObjectManager.java | 40 +++++++++---------- .../dhfs/objects/JavaDataSerializer.java | 7 ++-- .../dhfs/objects/ObjectSerializer.java | 3 +- .../transaction/TransactionFactoryImpl.java | 5 ++- .../transaction/TransactionObject.java | 3 +- 6 files changed, 40 insertions(+), 29 deletions(-) create mode 100644 dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/JDataVersionedWrapper.java diff --git a/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/JDataVersionedWrapper.java b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/JDataVersionedWrapper.java new file mode 100644 index 00000000..5194b873 --- /dev/null +++ b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/JDataVersionedWrapper.java @@ -0,0 +1,11 @@ +package com.usatiuk.dhfs.objects; + +import com.usatiuk.objects.common.runtime.JData; +import jakarta.annotation.Nonnull; +import lombok.Builder; + +import java.io.Serializable; + +@Builder +public record JDataVersionedWrapper(@Nonnull T data, long version) implements Serializable { +} diff --git a/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/JObjectManager.java b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/JObjectManager.java index 28e336f9..48b18ca9 100644 --- a/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/JObjectManager.java +++ b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/JObjectManager.java @@ -31,7 +31,7 @@ public class JObjectManager { @Inject ObjectPersistentStore objectStorage; @Inject - ObjectSerializer objectSerializer; + ObjectSerializer objectSerializer; @Inject ObjectAllocator objectAllocator; @Inject @@ -47,12 +47,12 @@ public class JObjectManager { private final ConcurrentHashMap> _objects = new ConcurrentHashMap<>(); private final AtomicLong _txCounter = new AtomicLong(); - private class JDataWrapper extends WeakReference { + private class JDataWrapper extends WeakReference> { private static final Cleaner CLEANER = Cleaner.create(); - public JDataWrapper(T referent) { + public JDataWrapper(JDataVersionedWrapper referent) { super(referent); - var key = referent.getKey(); + var key = referent.data().getKey(); CLEANER.register(referent, () -> { _objects.remove(key, this); }); @@ -66,17 +66,17 @@ public class JObjectManager { } } - private T get(Class type, JObjectKey key) { + private JDataVersionedWrapper get(Class type, JObjectKey key) { while (true) { { var got = _objects.get(key); if (got != null) { var ref = got.get(); - if (type.isInstance(ref)) { - return type.cast(ref); - } else if (ref == null) { + if (ref == null) { _objects.remove(key, got); + } else if (type.isInstance(ref.data())) { + return (JDataVersionedWrapper) ref; } else { throw new IllegalArgumentException("Object type mismatch: " + ref.getClass() + " vs " + type); } @@ -93,11 +93,11 @@ public class JObjectManager { if (read == null) return null; - if (type.isInstance(read)) { - var wrapper = new JDataWrapper<>(type.cast(read)); + if (type.isInstance(read.data())) { + var wrapper = new JDataWrapper<>((JDataVersionedWrapper) read); var old = _objects.put(key, wrapper); assert old == null; - return type.cast(read); + return read; } else { throw new IllegalArgumentException("Object type mismatch: " + read.getClass() + " vs " + type); } @@ -106,12 +106,12 @@ public class JObjectManager { } private record TransactionObjectNoLock - (Optional data) + (Optional> data) implements TransactionObject { } private record TransactionObjectLocked - (Optional data, AutoCloseableNoThrow lock) + (Optional> data, AutoCloseableNoThrow lock) implements TransactionObject { } @@ -210,7 +210,7 @@ public class JObjectManager { if (dep == null) { throw new IllegalStateException("No dependency for " + key); } - yield dep.data.orElse(null); + yield dep.data.map(JDataVersionedWrapper::data).orElse(null); } default -> { throw new IllegalStateException("Unexpected value: " + current.get(key)); @@ -254,7 +254,6 @@ public class JObjectManager { } } - // FIXME: lock leak for (var read : tx.reads().entrySet()) { addDependency.accept(read.getKey()); if (read.getValue() instanceof TransactionObjectLocked locked) { @@ -265,10 +264,10 @@ public class JObjectManager { for (var dep : dependenciesLocked.entrySet()) { Log.trace("Checking dependency " + dep.getKey()); - if (dep.getValue().data.isEmpty()) continue; + if (dep.getValue().data().isEmpty()) continue; - if (dep.getValue().data.get().getVersion() >= tx.getId()) { - throw new IllegalStateException("Serialization hazard: " + dep.getValue().data.get().getVersion() + " vs " + tx.getId()); + if (dep.getValue().data().get().version() >= tx.getId()) { + throw new IllegalStateException("Serialization hazard: " + dep.getValue().data().get().version() + " vs " + tx.getId()); } } @@ -282,9 +281,10 @@ public class JObjectManager { case TxRecord.TxObjectRecordWrite write -> { Log.trace("Flushing object " + action.getKey()); toWrite.add(action.getKey()); - var data = objectSerializer.serialize(write.data()); + var wrapped = new JDataVersionedWrapper<>(write.data(), tx.getId()); + var data = objectSerializer.serialize(wrapped); objectStorage.writeObject(action.getKey(), data); - _objects.put(action.getKey(), new JDataWrapper<>(write.data())); + _objects.put(action.getKey(), new JDataWrapper<>(wrapped)); } case TxRecord.TxObjectRecordDeleted deleted -> { Log.trace("Deleting object " + action.getKey()); diff --git a/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/JavaDataSerializer.java b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/JavaDataSerializer.java index d7ab1597..a42ebc07 100644 --- a/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/JavaDataSerializer.java +++ b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/JavaDataSerializer.java @@ -3,20 +3,19 @@ package com.usatiuk.dhfs.objects; import com.google.protobuf.ByteString; import com.usatiuk.dhfs.utils.SerializationHelper; -import com.usatiuk.objects.common.runtime.JData; import jakarta.enterprise.context.ApplicationScoped; import java.io.Serializable; @ApplicationScoped -public class JavaDataSerializer implements ObjectSerializer { +public class JavaDataSerializer implements ObjectSerializer { @Override - public ByteString serialize(JData obj) { + public ByteString serialize(JDataVersionedWrapper obj) { return SerializationHelper.serialize((Serializable) obj); } @Override - public JData deserialize(ByteString data) { + public JDataVersionedWrapper deserialize(ByteString data) { return SerializationHelper.deserialize(data.toByteArray()); } } diff --git a/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/ObjectSerializer.java b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/ObjectSerializer.java index e5922c67..078dd90f 100644 --- a/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/ObjectSerializer.java +++ b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/ObjectSerializer.java @@ -1,9 +1,8 @@ package com.usatiuk.dhfs.objects; import com.google.protobuf.ByteString; -import com.usatiuk.objects.common.runtime.JData; -public interface ObjectSerializer { +public interface ObjectSerializer { ByteString serialize(T obj); T deserialize(ByteString data); diff --git a/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/TransactionFactoryImpl.java b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/TransactionFactoryImpl.java index fda96766..4384a229 100644 --- a/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/TransactionFactoryImpl.java +++ b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/TransactionFactoryImpl.java @@ -1,5 +1,6 @@ package com.usatiuk.dhfs.objects.transaction; +import com.usatiuk.dhfs.objects.JDataVersionedWrapper; import com.usatiuk.objects.alloc.runtime.ObjectAllocator; import com.usatiuk.objects.common.runtime.JData; import com.usatiuk.objects.common.runtime.JObjectKey; @@ -34,8 +35,8 @@ public class TransactionFactoryImpl implements TransactionFactory { @Override public Optional get(Class type, JObjectKey key, LockingStrategy strategy) { return switch (strategy) { - case OPTIMISTIC -> _source.get(type, key).data(); - case WRITE -> _source.getWriteLocked(type, key).data(); + case OPTIMISTIC -> _source.get(type, key).data().map(JDataVersionedWrapper::data); + case WRITE -> _source.getWriteLocked(type, key).data().map(JDataVersionedWrapper::data); }; } diff --git a/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/TransactionObject.java b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/TransactionObject.java index 0d87926e..1c9fe912 100644 --- a/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/TransactionObject.java +++ b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/TransactionObject.java @@ -1,9 +1,10 @@ package com.usatiuk.dhfs.objects.transaction; +import com.usatiuk.dhfs.objects.JDataVersionedWrapper; import com.usatiuk.objects.common.runtime.JData; import java.util.Optional; public interface TransactionObject { - Optional data(); + Optional> data(); }