/**
* Copyright 2011-2012 Akiban Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.persistit;
import static com.persistit.util.SequencerConstants.RECOVERY_PRUNING_SCHEDULE;
import static com.persistit.util.ThreadSequencer.addSchedules;
import static com.persistit.util.ThreadSequencer.disableSequencer;
import static com.persistit.util.ThreadSequencer.enableSequencer;
import static org.junit.Assert.assertNull;
import org.junit.Ignore;
import org.junit.Test;
public class Bug942669Test extends PersistitUnitTestCase {
// See https://bugs.launchpad.net/akiban-persistit/+bug/942669
//
// persistit version: 3.0.5
// server version: 1.0.4
//
// At client site, disk filled up due to journal files not being purged. The
// contents of the akiban data directory looked like:
//
// [root@admindb02 akiban]# ls -ltrh /var/lib/akiban/
// total 55G
// -rw-r--r-- 1 akiban akiban 330M Feb 27 10:33 akiban_journal.000000001346
// -rw-r--r-- 1 akiban akiban 0 Feb 27 10:34 akiban_txn.v0.lck
// -rw-r--r-- 1 akiban akiban 0 Feb 27 10:34 akiban_system.v0.lck
// -rw-r--r-- 1 akiban akiban 0 Feb 27 10:34 akiban_data.v01.lck
// -rw-r--r-- 1 akiban akiban 0 Feb 27 10:34 akiban_journal.000000001345
// -rw-r--r-- 1 akiban akiban 1.0M Feb 27 10:35 akiban_txn.v0
// -rw-r--r-- 1 akiban akiban 48K Feb 27 10:36 akiban_system.v0
// -rw-r--r-- 1 akiban akiban 954M Feb 27 10:44 akiban_journal.000000001347
// -rw-r--r-- 1 akiban akiban 954M Feb 27 10:56 akiban_journal.000000001348
// -rw-r--r-- 1 akiban akiban 954M Feb 27 11:09 akiban_journal.000000001349
/**
* Test for a possible race condition examined in diagnosing 942669. Note
* that file 1535 is empty, suggesting that the JOURNAL_COPIER thread
* decided it was obsolete and deleted it. Hypothesis:
*
* 1. RecoveryManager pruned an aborted transaction
*
* 2. RecoveryManager then set its MVV count to zero.
*
* 3. JOURNAL_COPIER thread pruneObsoleteTransactions then removed the
* TransactionMapItem for it from the live map.
*
* 4. RecoveryManager called writeTransactionToJournal, which reinstated the
* TransactionMapItem.
*
* 5. JOURNAL_COPIER then repeatedly tried to prune the transaction, causing
* an attempt to read file 1345. That attempt caused creation of a new
* zero-length file and then failed with an FileNotFoundException.
*
* 6. Rinse and repeat until the disk fills up.
*
* This code attempts to recreate that scenario.
*
* @throws Exception
*/
// Ignored for now because pruneObsoleteTransactions is now called from
// CleanupManager which hasn't started yet
@Ignore
@Test
public void testRecoveryRace() throws Exception {
/*
* Create a journal with an uncommitted transaction
*/
final Exchange ex = _persistit.getExchange("persistit", "test", true);
final Transaction txn = ex.getTransaction();
txn.begin();
ex.getValue().put(RED_FOX);
for (int k = 1; k < 10; k++) {
ex.clear().append(k).store();
}
_persistit.checkpoint();
txn.commit();
txn.end();
final Configuration config = _persistit.getConfiguration();
_persistit.crash();
_persistit = new Persistit();
_persistit.setConfiguration(config);
enableSequencer(true);
addSchedules(RECOVERY_PRUNING_SCHEDULE);
_persistit.initialize();
_persistit.copyBackPages();
disableSequencer();
}
/**
* Note that journal file 1345 has a length of zero. The issue is that
* JournalManager.pruneObsoleteTransactions is attempting to prune two
* aborted transactions that once upon a time lived in file 1345. Those
* transactions were successfully pruned before the system was shut down,
* and then incorrectly resurrected into the new new epoch. The solution is
* to avoid resurrecting transactions which started before the base address
* during startup. Prior to the fix, this code fails with an
* IllegalArgumentException in the JOURNAL_COPIER thread.
*/
@Test
public void testResurrectedTransactions() throws Exception {
/*
* Create a journal with an uncommitted transaction
*/
final Exchange ex = _persistit.getExchange("persistit", "test", true);
_persistit.flush();
final Transaction txn = ex.getTransaction();
txn.begin();
ex.getValue().put(RED_FOX);
for (int k = 1; k < 10; k++) {
ex.clear().append(k).store();
}
_persistit.checkpoint();
_persistit.getJournalManager().rollover();
txn.rollback();
txn.end();
/*
* copyBackPages creates a checkpoint, prunes obsolete transactions,
* copies pages from the journal back to the volume and then deletes
* obsolete journal files. We need to call it twice so that the results
* of pruning get checkpointed.
*/
_persistit.copyBackPages();
_persistit.copyBackPages();
final Configuration config = _persistit.getConfiguration();
_persistit.close();
_persistit = new Persistit(config);
_persistit.copyBackPages();
assertNull(_persistit.getJournalManager().getLastCopierException());
}
}