2
* Copyright © 2012 Akiban Technologies, Inc. All rights reserved.
4
* This program and the accompanying materials are made available
5
* under the terms of the Eclipse Public License v1.0 which
6
* accompanies this distribution, and is available at
7
* http://www.eclipse.org/legal/epl-v10.html
9
* This program may also be available under different license terms.
10
* For more information, see www.akiban.com or contact licensing@akiban.com.
13
* Akiban Technologies, Inc.
16
package com.persistit;
18
import static com.persistit.unit.UnitTestProperties.VOLUME_NAME;
21
import java.io.IOException;
22
import java.io.RandomAccessFile;
24
import org.junit.Test;
26
import com.persistit.exception.PersistitException;
29
* https://bugs.launchpad.net/akiban-persistit/+bug/1065677
31
* Doing a long delete in OLB_latest dataset, my Mac crashed, taking my VM with
32
* it. The Vm was running akiban. It was via a local psql client.
35
* INFO 15:14:05,258 Starting services.
36
* Exception in thread "main" java.lang.AssertionError
37
* at com.persistit.JournalManager$PageNode.setPrevious(JournalManager.java:1918)
38
* at com.persistit.RecoveryManager.scanLoadPageMap(RecoveryManager.java:1150)
39
* at com.persistit.RecoveryManager.scanOneRecord(RecoveryManager.java:937)
40
* at com.persistit.RecoveryManager.findAndValidateKeystone(RecoveryManager.java:784)
41
* at com.persistit.RecoveryManager.buildRecoveryPlan(RecoveryManager.java:
46
public class Bug1065677Test extends PersistitUnitTestCase {
48
private final static String TREE_NAME = "Bug1065677Test";
50
private Exchange getExchange() throws PersistitException {
51
return _persistit.getExchange(VOLUME_NAME, TREE_NAME, true);
55
* This method tries to recreate the state at which the journal files in bug
56
* 1065677 arrived. Plan:
58
* 1. Write a transaction that updates multiple pages.
60
* 2. Flush all buffers so that there are pages to recover and then crash
61
* Persistit so there is no checkpoint.
63
* 3. Restart Persistit, but advance the system timestamp to simulate
64
* somewhat chaotic ordering of page writes created by reapplying a huge
65
* delete operation in the transaction.
67
* 4. Crash once again. This will leave pages from the original epoch in the
68
* page map and will add versions of those pages with larger timestamps.
69
* This crash simulates the JVM being killed during recovery processing. In
70
* the actual case it appears there is no post-recovery checkpoint, merely
71
* lots of dirty pages. To simulate this outcome we truncate about 50K of
72
* bytes form the end of the journal file as a surrogate for the system
73
* having not completed recovery.
75
* 5. Restart the system once again. Now do some normal processing work,
76
* simulated here by adding another transaction.
78
* 6. Magic happens here: we now perform a rollover, which writes both the
79
* branch map and the page map into a single PM record. Because the PM
80
* record is written by two separate loops, some page P found in both the
81
* branch map and the page map is written into two separate PM sub-records.
82
* As it turns out, the version in the branch map has a smaller timestamp
83
* than the one in the page map. This sets up the AssertionError.
85
* 7. Restart Persistit to exploit the bug during scanLoadPageMap.
90
public void breakTimestampSequence() throws Exception {
93
final Configuration config = _persistit.getConfiguration();
94
final long lastTimestamp = _persistit.getCurrentTimestamp();
98
_persistit = new Persistit();
99
_persistit.getTimestampAllocator().updateTimestamp(lastTimestamp + 1000);
100
_persistit.initialize(config);
104
_persistit = new Persistit();
105
config.setAppendOnly(true);
106
_persistit.initialize(config);
110
final JournalManager jman = _persistit.getJournalManager();
114
_persistit = new Persistit();
115
_persistit.initialize(config);
120
private void doTransaction() throws Exception {
121
final Exchange ex = getExchange();
122
final Transaction txn = ex.getTransaction();
124
ex.getValue().put(RED_FOX);
125
for (int i = 0; i < 10000; i++) {
132
private void truncate() throws IOException {
133
final JournalManager jman = _persistit.getJournalManager();
134
final long lastAddress = jman.getCurrentAddress();
135
final File file = jman.addressToFile(lastAddress);
136
final RandomAccessFile raf = new RandomAccessFile(file, "rw");
138
final long length = raf.length();
139
raf.setLength(length - 50000);