From 5b2b2bab46aca3b75d966e2c0c3cc058d7b73da8 Mon Sep 17 00:00:00 2001 From: catbref Date: Sun, 17 Jan 2021 15:33:59 +0000 Subject: [PATCH] Two-pronged fix for HSQLDB 'serialization failure' errors when receiving multiple PRESENCE transactions -- reported by marracc --- .../qortal/transaction/PresenceTransaction.java | 5 +++-- .../org/qortal/transaction/Transaction.java | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/qortal/transaction/PresenceTransaction.java b/src/main/java/org/qortal/transaction/PresenceTransaction.java index 772928e9..1869f043 100644 --- a/src/main/java/org/qortal/transaction/PresenceTransaction.java +++ b/src/main/java/org/qortal/transaction/PresenceTransaction.java @@ -234,9 +234,10 @@ public class PresenceTransaction extends Transaction { if (creatorsPresenceTransactions.isEmpty()) return; - // List should contain oldest transaction first, so remove all but last from repository. - creatorsPresenceTransactions.remove(creatorsPresenceTransactions.size() - 1); for (TransactionData transactionData : creatorsPresenceTransactions) { + if (transactionData.getTimestamp() >= this.transactionData.getTimestamp()) + continue; + LOGGER.info(() -> String.format("Deleting older PRESENCE transaction %s", Base58.encode(transactionData.getSignature()))); this.repository.getTransactionRepository().delete(transactionData); } diff --git a/src/main/java/org/qortal/transaction/Transaction.java b/src/main/java/org/qortal/transaction/Transaction.java index 2236e353..3ed02f8d 100644 --- a/src/main/java/org/qortal/transaction/Transaction.java +++ b/src/main/java/org/qortal/transaction/Transaction.java @@ -814,6 +814,23 @@ public abstract class Transaction { return ValidationResult.OK; } finally { + /* + * We call discardChanges() to restart repository 'transaction', discarding any + * transactional table locks, hence reducing possibility of deadlock or + * "serialization failure" with HSQLDB due to reads. + * + * "Serialization failure" most likely caused by existing transaction check above, + * where multiple threads are importing transactions + * and one thread finds existing an transaction, returns (unlocking blockchain lock), + * then another thread immediately obtains lock, tries to delete above existing transaction + * (e.g. older PRESENCE transaction) but can't because first thread's repository + * session still has row-lock on existing transaction and hasn't yet closed + * repository session. Deadlock caused by race condition. + * + * Hence we clear any repository-based locks before releasing blockchain lock. + */ + repository.discardChanges(); + blockchainLock.unlock(); } }