From aa69ae13a4129fa7ed3938b65751b25391253c16 Mon Sep 17 00:00:00 2001 From: Stepan Usatiuk Date: Wed, 4 Dec 2024 23:25:54 +0100 Subject: [PATCH] object alloc version persistent (but not yet loaded to transaction) --- .../dhfs/objects/CurrentTransaction.java | 5 +++++ .../usatiuk/dhfs/objects/JObjectManager.java | 21 +++++++++---------- .../dhfs/objects/transaction/Transaction.java | 2 ++ ...TransactionObjectAllocVersionProvider.java | 16 ++++++++++++++ .../transaction/TransactionPrivate.java | 1 - .../com/usatiuk/dhfs/objects/ObjectsTest.java | 6 +++--- 6 files changed, 36 insertions(+), 15 deletions(-) create mode 100644 dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/TransactionObjectAllocVersionProvider.java diff --git a/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/CurrentTransaction.java b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/CurrentTransaction.java index bd8dab04..b986b4e4 100644 --- a/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/CurrentTransaction.java +++ b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/CurrentTransaction.java @@ -14,6 +14,11 @@ public class CurrentTransaction implements Transaction { @Inject TransactionManager transactionManager; + @Override + public long getId() { + return transactionManager.current().getId(); + } + @Override public Optional getObject(Class type, JObjectKey key, LockingStrategy strategy) { return transactionManager.current().getObject(type, key, strategy); 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 43a172fe..687a15fa 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 @@ -43,7 +43,6 @@ public class JObjectManager { private static final Cleaner CLEANER = Cleaner.create(); final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); - long lastWriteTx = -1; // FIXME: This should be persistent public JDataWrapper(T referent) { super(referent); @@ -58,7 +57,6 @@ public class JObjectManager { return "JDataWrapper{" + "ref=" + get() + ", lock=" + lock + - ", lastWriteTx=" + lastWriteTx + '}'; } } @@ -155,7 +153,7 @@ public class JObjectManager { public Optional> getWriteLocked(Class type, JObjectKey key) { var got = JObjectManager.this.getLocked(type, key, true); if (got == null) return Optional.empty(); - if (got.wrapper().lastWriteTx >= _txId) { + if (got.obj.getVersion() >= _txId) { got.wrapper().lock.writeLock().unlock(); throw new IllegalStateException("Serialization race"); } @@ -170,7 +168,9 @@ public class JObjectManager { } public void commit(TransactionPrivate tx) { + // This also holds the weak references var toUnlock = new LinkedList(); + var toFlush = new LinkedList>(); var toPut = new LinkedList>(); var toLock = new ArrayList>(); @@ -223,14 +223,13 @@ public class JObjectManager { for (var dep : dependencies) { Log.trace("Checking dependency " + dep.toString()); - var current = _objects.get(dep.data().getKey()); + var current = _objects.get(dep.data().getKey()).get(); - if (current.get() != dep.data()) { - throw new IllegalStateException("Object changed during transaction: " + current.get() + " vs " + dep.data()); - } + // Checked above + assert current == dep.data(); - if (current.lastWriteTx >= tx.getId()) { - throw new IllegalStateException("Serialization hazard: " + current.lastWriteTx + " vs " + tx.getId()); + if (current.getVersion() >= tx.getId()) { + throw new IllegalStateException("Serialization hazard: " + current.getVersion() + " vs " + tx.getId()); } } @@ -252,7 +251,7 @@ public class JObjectManager { var newWrapper = new JDataWrapper<>(record.copy().wrapped()); newWrapper.lock.writeLock().lock(); if (!_objects.replace(record.copy().wrapped().getKey(), current, newWrapper)) { - assert false; + assert false; // Should not happen, as the object is locked throw new IllegalStateException("Object changed during transaction after locking: " + current.get() + " vs " + record.copy().wrapped()); } toUnlock.add(newWrapper.lock.writeLock()::unlock); @@ -266,10 +265,10 @@ public class JObjectManager { // Really flushing to storage written.forEach(obj -> { Log.trace("Flushing object " + obj.getKey()); + assert obj.getVersion() == tx.getId(); var key = obj.getKey(); var data = objectSerializer.serialize(obj); objectStorage.writeObject(key, data); - _objects.get(key).lastWriteTx = tx.getId(); // FIXME: }); Log.tracef("Committing transaction %d to storage", tx.getId()); diff --git a/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/Transaction.java b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/Transaction.java index 544c3a5c..6e7f2228 100644 --- a/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/Transaction.java +++ b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/Transaction.java @@ -7,6 +7,8 @@ import java.util.Optional; // The transaction interface actually used by user code to retrieve objects public interface Transaction { + long getId(); + Optional getObject(Class type, JObjectKey key, LockingStrategy strategy); void putObject(JData obj); diff --git a/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/TransactionObjectAllocVersionProvider.java b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/TransactionObjectAllocVersionProvider.java new file mode 100644 index 00000000..4bb6118e --- /dev/null +++ b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/TransactionObjectAllocVersionProvider.java @@ -0,0 +1,16 @@ +package com.usatiuk.dhfs.objects.transaction; + +import com.usatiuk.objects.common.runtime.JDataAllocVersionProvider; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; + +@Singleton +public class TransactionObjectAllocVersionProvider implements JDataAllocVersionProvider { + @Inject + Transaction transaction; + + public long getVersion() { + return transaction.getId(); + } + +} diff --git a/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/TransactionPrivate.java b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/TransactionPrivate.java index 2d3300bc..d2f12014 100644 --- a/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/TransactionPrivate.java +++ b/dhfs-parent/objects/src/main/java/com/usatiuk/dhfs/objects/transaction/TransactionPrivate.java @@ -4,6 +4,5 @@ import java.util.Collection; // The transaction interface actually used by user code to retrieve objects public interface TransactionPrivate extends Transaction{ - long getId(); Collection> drain(); } diff --git a/dhfs-parent/objects/src/test/java/com/usatiuk/dhfs/objects/ObjectsTest.java b/dhfs-parent/objects/src/test/java/com/usatiuk/dhfs/objects/ObjectsTest.java index 89e2d8d7..ccacd341 100644 --- a/dhfs-parent/objects/src/test/java/com/usatiuk/dhfs/objects/ObjectsTest.java +++ b/dhfs-parent/objects/src/test/java/com/usatiuk/dhfs/objects/ObjectsTest.java @@ -50,19 +50,19 @@ public class ObjectsTest { void createCreateObject() { { txm.begin(); - var newParent = alloc.create(Parent.class, new JObjectKey("Parent")); + var newParent = alloc.create(Parent.class, new JObjectKey("Parent7")); newParent.setLastName("John"); curTx.putObject(newParent); txm.commit(); } Assertions.assertThrows(Exception.class, () -> txm.run(() -> { - var newParent = alloc.create(Parent.class, new JObjectKey("Parent")); + var newParent = alloc.create(Parent.class, new JObjectKey("Parent7")); newParent.setLastName("John2"); curTx.putObject(newParent); })); { txm.begin(); - var parent = curTx.getObject(Parent.class, new JObjectKey("Parent")).orElse(null); + var parent = curTx.getObject(Parent.class, new JObjectKey("Parent7")).orElse(null); Assertions.assertEquals("John", parent.getLastName()); txm.commit(); }