Trees removed inside transaction can automatically come back
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
Akiban Persistit |
Fix Released
|
High
|
Peter Beaman |
Bug Description
Persistit r369
As of the current revision (3.1.8-SNAPSHOT, r269) tree creation is documented as non-transactional and all elements within the tree as transactional.
Since trees lifetime is not handled by MVCC it seems reasonable an application might try to remove any created during a transaction to keep the volume in a known state. However, if a tree is removed inside of a transaction is aborted the tree will be recreated by the CleanupManager.
A distilled test case:
public void treeCheck(Volume v, String msg) throws PersistitException {
System.
for(String tree : v.getTreeNames()) {
Exchange ex = _persistit.
}
}
}
@Test
public void resurrectedTrees() throws PersistitException {
Volume v = _persistit.
Transaction txn = _persistit.
txn.begin();
{
Exchange ex = _persistit.
}
txn.rollback();
txn.end();
treeCheck(v, "Tree Check 1");
_persistit.
treeCheck(v, "Tree Check 2");
}
This will output:
[main] WARNING Transaction <ts=273 tc=ABORTED mvv=1> pruning incomplete at JournalAddress 16,823{273} after rollback
Tree Check 1
Tree Check 2
new_tree1
Note that the claim of the entries being handled correct is true (the scan did not find {1}), but the tree did come back after cleanup. I've not checked into the warning so that may be another bug (e.g. causing journal growth).
Related branches
- Nathan Williams: Needs Fixing
-
Diff: 3507 lines (+1893/-414)37 files modifiedpom.xml (+1/-1)
src/main/java/com/persistit/Buffer.java (+1/-2)
src/main/java/com/persistit/CheckpointManager.java (+1/-0)
src/main/java/com/persistit/ClassIndex.java (+12/-1)
src/main/java/com/persistit/Exchange.java (+60/-38)
src/main/java/com/persistit/JournalManager.java (+5/-0)
src/main/java/com/persistit/MVV.java (+1/-1)
src/main/java/com/persistit/Persistit.java (+93/-43)
src/main/java/com/persistit/RecoveryManager.java (+27/-0)
src/main/java/com/persistit/SharedResource.java (+3/-3)
src/main/java/com/persistit/TimelyResource.java (+505/-0)
src/main/java/com/persistit/TransactionPlayer.java (+107/-68)
src/main/java/com/persistit/Tree.java (+165/-46)
src/main/java/com/persistit/ValueHelper.java (+12/-0)
src/main/java/com/persistit/Version.java (+69/-0)
src/main/java/com/persistit/Volume.java (+4/-1)
src/main/java/com/persistit/VolumeStructure.java (+47/-54)
src/main/java/com/persistit/logging/LogBase.java (+3/-0)
src/main/java/com/persistit/util/SequencerConstants.java (+1/-12)
src/test/java/com/persistit/AccumulatorRecoveryTest.java (+5/-0)
src/test/java/com/persistit/AccumulatorTest.java (+1/-1)
src/test/java/com/persistit/Bug1018526Test.java (+2/-2)
src/test/java/com/persistit/Bug920754Test.java (+2/-1)
src/test/java/com/persistit/Bug932097Test.java (+1/-1)
src/test/java/com/persistit/CorruptVolumeTest.java (+2/-1)
src/test/java/com/persistit/IOFailureTest.java (+4/-1)
src/test/java/com/persistit/IntegrityCheckTest.java (+2/-0)
src/test/java/com/persistit/JournalManagerTest.java (+12/-0)
src/test/java/com/persistit/MVCCPruneBufferTest.java (+9/-4)
src/test/java/com/persistit/PersistitUnitTestCase.java (+11/-0)
src/test/java/com/persistit/RecoveryTest.java (+27/-19)
src/test/java/com/persistit/SplitPolicyTest.java (+1/-1)
src/test/java/com/persistit/TimelyResourceTest.java (+290/-0)
src/test/java/com/persistit/TreeLifetimeTest.java (+26/-88)
src/test/java/com/persistit/TreeTransactionalLifetimeTest.java (+277/-0)
src/test/java/com/persistit/unit/ConcurrentUtil.java (+103/-24)
src/test/java/com/persistit/unit/TransactionTest1.java (+1/-1)
Changed in akiban-persistit: | |
status: | New → Confirmed |
importance: | Undecided → High |
assignee: | nobody → Peter Beaman (pbeaman) |
Changed in akiban-persistit: | |
status: | Confirmed → Fix Released |
status: | Fix Released → Fix Committed |
Changed in akiban-persistit: | |
status: | Fix Committed → Fix Released |
milestone: | none → 3.3.0 |
I think the mechanism is pretty straightforward: the proactive pruning logic uses a TransactionPlayer to read and prune transactions from the journal. TransactionPlay er#getExchange constructs an Exchange for this purpose and sets the create flag to true; this causes the tree to be created again.
A fix would be for the getExchange() method to know whether the exchange is passed to support pruning (in which case the tree should not be created) or reapplying a transaction in recovery (in which case it should be created).