From b926a082939c526d80bbab293eee1a2e2b1413a7 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Sat, 13 Apr 2013 22:55:38 +0000 Subject: Index triggers only use "for update" mode when the strict option is supplied. --- .../repo/indexed/DerivedIndexesTrigger.java | 34 +++++++++++--- .../carbonado/repo/indexed/IndexAnalysis.java | 6 ++- .../carbonado/repo/indexed/IndexedRepository.java | 9 +++- .../repo/indexed/IndexedRepositoryBuilder.java | 12 ++++- .../carbonado/repo/indexed/IndexedStorage.java | 10 +++- .../carbonado/repo/indexed/IndexesTrigger.java | 54 ++++++++++++++-------- 6 files changed, 94 insertions(+), 31 deletions(-) diff --git a/src/main/java/com/amazon/carbonado/repo/indexed/DerivedIndexesTrigger.java b/src/main/java/com/amazon/carbonado/repo/indexed/DerivedIndexesTrigger.java index 8f2afde..e18accf 100644 --- a/src/main/java/com/amazon/carbonado/repo/indexed/DerivedIndexesTrigger.java +++ b/src/main/java/com/amazon/carbonado/repo/indexed/DerivedIndexesTrigger.java @@ -75,13 +75,7 @@ class DerivedIndexesTrigger extends Trig } @Override - public Object beforeTryDelete(Transaction txn, S storable) throws PersistException { - return beforeDelete(txn, storable); - } - - @Override - public Object beforeDelete(Transaction txn, S storable) throws PersistException { - txn.setForUpdate(true); + public Object beforeDelete(S storable) throws PersistException { try { if (storable.copy().tryLoad()) { return createDependentIndexEntries(storable); @@ -175,4 +169,30 @@ class DerivedIndexesTrigger extends Trig newIndexEntry.tryInsert(); } } + + /** + * Ensure old storable instance is loaded with an upgradable lock, allowing change to + * proceed without deadlock. + */ + final static class Strict + extends DerivedIndexesTrigger + { + Strict(IndexedRepository repository, + Class sType, ChainedProperty derivedTo) + throws RepositoryException + { + super(repository, sType, derivedTo); + } + + @Override + public Object beforeTryDelete(Transaction txn, S storable) throws PersistException { + return beforeDelete(txn, storable); + } + + @Override + public Object beforeDelete(Transaction txn, S storable) throws PersistException { + txn.setForUpdate(true); + return super.beforeDelete(storable); + } + } } diff --git a/src/main/java/com/amazon/carbonado/repo/indexed/IndexAnalysis.java b/src/main/java/com/amazon/carbonado/repo/indexed/IndexAnalysis.java index e44964b..1ec12ad 100644 --- a/src/main/java/com/amazon/carbonado/repo/indexed/IndexAnalysis.java +++ b/src/main/java/com/amazon/carbonado/repo/indexed/IndexAnalysis.java @@ -278,7 +278,11 @@ class IndexAnalysis { managedIndexes[i++] = managedIndex; } - indexesTrigger = new IndexesTrigger(managedIndexes); + if (repository.isStrictTriggers()) { + indexesTrigger = new IndexesTrigger.Strict(managedIndexes); + } else { + indexesTrigger = new IndexesTrigger(managedIndexes); + } } derivedToDependencies = gatherDerivedToDependencies(info); diff --git a/src/main/java/com/amazon/carbonado/repo/indexed/IndexedRepository.java b/src/main/java/com/amazon/carbonado/repo/indexed/IndexedRepository.java index 91b6615..26b8ea8 100644 --- a/src/main/java/com/amazon/carbonado/repo/indexed/IndexedRepository.java +++ b/src/main/java/com/amazon/carbonado/repo/indexed/IndexedRepository.java @@ -64,6 +64,7 @@ class IndexedRepository implements Repository, private final boolean mIndexRepairEnabled; private final double mIndexThrottle; private final boolean mAllClustered; + private final boolean mStrictTriggers; private final StoragePool mStoragePool; private final IndexAnalysisPool mIndexAnalysisPool; @@ -71,7 +72,8 @@ class IndexedRepository implements Repository, Repository repository, boolean indexRepairEnabled, double indexThrottle, - boolean allClustered) + boolean allClustered, + boolean strictTriggers) { if (repository.getCapability(IndexInfoCapability.class) == null) { throw new UnsupportedOperationException @@ -85,6 +87,7 @@ class IndexedRepository implements Repository, mIndexRepairEnabled = indexRepairEnabled; mIndexThrottle = indexThrottle; mAllClustered = allClustered; + mStrictTriggers = strictTriggers; mIndexAnalysisPool = new IndexAnalysisPool(this); mStoragePool = new StoragePool() { @@ -264,4 +267,8 @@ class IndexedRepository implements Repository, boolean isAllClustered() { return mAllClustered; } + + boolean isStrictTriggers() { + return mStrictTriggers; + } } diff --git a/src/main/java/com/amazon/carbonado/repo/indexed/IndexedRepositoryBuilder.java b/src/main/java/com/amazon/carbonado/repo/indexed/IndexedRepositoryBuilder.java index fa974fd..27b8011 100644 --- a/src/main/java/com/amazon/carbonado/repo/indexed/IndexedRepositoryBuilder.java +++ b/src/main/java/com/amazon/carbonado/repo/indexed/IndexedRepositoryBuilder.java @@ -50,6 +50,7 @@ public class IndexedRepositoryBuilder extends AbstractRepositoryBuilder { private boolean mIndexRepairEnabled = true; private double mIndexThrottle = 1.0; private boolean mAllClustered; + private boolean mStrictTriggers; public IndexedRepositoryBuilder() { } @@ -77,7 +78,8 @@ public class IndexedRepositoryBuilder extends AbstractRepositoryBuilder { Repository repo = new IndexedRepository(rootRef, getName(), wrapped, isIndexRepairEnabled(), getIndexRepairThrottle(), - isAllClustered()); + isAllClustered(), + mStrictTriggers); rootRef.set(repo); return repo; } @@ -190,6 +192,14 @@ public class IndexedRepositoryBuilder extends AbstractRepositoryBuilder { mAllClustered = clustered; } + /** + * Set to true to require that index maintenance triggers use a "for update" transaction, + * avoiding deadlocks and lock upgrade failures. + */ + public void setStrictTriggers(boolean strict) { + mStrictTriggers = strict; + } + @Override public void errorCheck(Collection messages) throws ConfigurationException { super.errorCheck(messages); diff --git a/src/main/java/com/amazon/carbonado/repo/indexed/IndexedStorage.java b/src/main/java/com/amazon/carbonado/repo/indexed/IndexedStorage.java index b00b0b4..eec8522 100644 --- a/src/main/java/com/amazon/carbonado/repo/indexed/IndexedStorage.java +++ b/src/main/java/com/amazon/carbonado/repo/indexed/IndexedStorage.java @@ -112,7 +112,15 @@ class IndexedStorage implements Storage, StorageAccess // Install triggers to manage derived properties in external Storables. if (analysis.derivedToDependencies != null) { for (ChainedProperty derivedTo : analysis.derivedToDependencies) { - addTrigger(new DerivedIndexesTrigger(mRepository, getStorableType(), derivedTo)); + Trigger trigger; + if (mRepository.isStrictTriggers()) { + trigger = new DerivedIndexesTrigger.Strict + (mRepository, getStorableType(), derivedTo); + } else { + trigger = new DerivedIndexesTrigger + (mRepository, getStorableType(), derivedTo); + } + addTrigger(trigger); } } } diff --git a/src/main/java/com/amazon/carbonado/repo/indexed/IndexesTrigger.java b/src/main/java/com/amazon/carbonado/repo/indexed/IndexesTrigger.java index 4c5840a..4b8a380 100644 --- a/src/main/java/com/amazon/carbonado/repo/indexed/IndexesTrigger.java +++ b/src/main/java/com/amazon/carbonado/repo/indexed/IndexesTrigger.java @@ -64,16 +64,7 @@ class IndexesTrigger extends Trigger { } @Override - public Object beforeTryUpdate(Transaction txn, S storable) throws PersistException { - return beforeUpdate(txn, storable); - } - - @Override - public Object beforeUpdate(Transaction txn, S storable) throws PersistException { - // Ensure old storable is loaded with an upgradable lock, allowing - // update to proceed without deadlock. - txn.setForUpdate(true); - + public Object beforeUpdate(S storable) throws PersistException { // Return old storable for afterUpdate. S copy = (S) storable.copy(); try { @@ -98,16 +89,7 @@ class IndexesTrigger extends Trigger { } @Override - public Object beforeTryDelete(Transaction txn, S storable) throws PersistException { - return beforeDelete(txn, storable); - } - - @Override - public Object beforeDelete(Transaction txn, S storable) throws PersistException { - // Ensure old storable is loaded with an upgradable lock, allowing - // delete to proceed without deadlock. - txn.setForUpdate(true); - + public Object beforeDelete(S storable) throws PersistException { // Delete index entries referenced by existing storable. S copy = (S) storable.copy(); try { @@ -130,4 +112,36 @@ class IndexesTrigger extends Trigger { } return null; } + + /** + * Ensure old storable instance is loaded with an upgradable lock, allowing change to + * proceed without deadlock. + */ + final static class Strict extends IndexesTrigger { + Strict(ManagedIndex[] managedIndexes) { + super(managedIndexes); + } + + @Override + public Object beforeTryUpdate(Transaction txn, S storable) throws PersistException { + return beforeUpdate(txn, storable); + } + + @Override + public Object beforeUpdate(Transaction txn, S storable) throws PersistException { + txn.setForUpdate(true); + return super.beforeUpdate(storable); + } + + @Override + public Object beforeTryDelete(Transaction txn, S storable) throws PersistException { + return beforeDelete(txn, storable); + } + + @Override + public Object beforeDelete(Transaction txn, S storable) throws PersistException { + txn.setForUpdate(true); + return super.beforeDelete(storable); + } + } } -- cgit v1.2.3