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;
19
import static com.persistit.util.SequencerConstants.ACCUMULATOR_CHECKPOINT_A;
20
import static com.persistit.util.SequencerConstants.ACCUMULATOR_CHECKPOINT_B;
21
import static com.persistit.util.SequencerConstants.ACCUMULATOR_CHECKPOINT_C;
22
import static com.persistit.util.SequencerConstants.ACCUMULATOR_CHECKPOINT_SCHEDULED;
23
import static com.persistit.util.ThreadSequencer.addSchedules;
24
import static com.persistit.util.ThreadSequencer.enableSequencer;
25
import static com.persistit.util.ThreadSequencer.sequence;
26
import static com.persistit.util.ThreadSequencer.setCondition;
27
import static org.junit.Assert.assertEquals;
29
import java.util.concurrent.atomic.AtomicBoolean;
31
import org.junit.Test;
33
import com.persistit.exception.PersistitException;
34
import com.persistit.util.ThreadSequencer.Condition;
37
* https://bugs.launchpad.net/akiban-persistit/+bug/1064565
39
* The state of an Accumulator is sometimes incorrect after shutting down and
40
* restarting Persistit and as a result an application can read a count or value
41
* that is inconsistent with the history of committed transactions.
43
* The bug mechanism is a race between the CheckpointManager#createCheckpoint
44
* method and the Accumulator#update method in which an update which occurs in a
45
* transaction that starts immediately after the checkpoint begins its
46
* transaction can be lost. The probability of failure is low but may be
47
* increased by intense I/O activity.
49
* This is a data loss error and is therefore critical.
52
public class Bug1064565Test extends PersistitUnitTestCase {
54
private final static String TREE_NAME = "Bug1064565Test";
56
private Exchange getExchange() throws PersistitException {
57
return _persistit.getExchange(VOLUME_NAME, TREE_NAME, true);
61
public void accumulatorRace() throws Exception {
62
enableSequencer(false);
63
addSchedules(ACCUMULATOR_CHECKPOINT_SCHEDULED);
64
final AtomicBoolean once = new AtomicBoolean(true);
65
setCondition(ACCUMULATOR_CHECKPOINT_A, new Condition() {
67
public boolean enabled() {
68
return once.getAndSet(false);
72
Exchange exchange = getExchange();
73
Transaction txn = exchange.getTransaction();
74
final Thread t = new Thread(new Runnable() {
78
_persistit.checkpoint();
79
} catch (final PersistitException e) {
80
throw new RuntimeException(e);
87
Accumulator acc = exchange.getTree().getAccumulator(Accumulator.Type.SUM, 0);
89
sequence(ACCUMULATOR_CHECKPOINT_B);
92
sequence(ACCUMULATOR_CHECKPOINT_C);
96
final Configuration config = _persistit.getConfiguration();
97
_persistit = new Persistit();
98
_persistit.initialize(config);
100
exchange = getExchange();
101
txn = exchange.getTransaction();
103
acc = exchange.getTree().getAccumulator(Accumulator.Type.SUM, 0);
104
assertEquals("Accumulator state should have been checkpointed", 42, acc.getSnapshotValue(txn));
108
_persistit.checkpoint();
109
_persistit.checkpoint();
110
_persistit.checkpoint();
114
* ThreadSequencer is not even needed: this sequence shows how setting
115
* checkpointNeeded inside of the main transaction is not correctly
116
* sequenced against the checkpoint.
119
public void nathansVersion() throws Exception {
120
Exchange exchange = getExchange();
121
Transaction txn = exchange.getTransaction();
123
Accumulator acc = exchange.getTree().getAccumulator(Accumulator.Type.SUM, 0);
125
_persistit.checkpoint();
128
_persistit.copyBackPages();
129
final Configuration config = _persistit.getConfiguration();
131
_persistit = new Persistit();
132
_persistit.initialize(config);
134
exchange = getExchange();
135
txn = exchange.getTransaction();
137
acc = exchange.getTree().getAccumulator(Accumulator.Type.SUM, 0);
138
assertEquals("Accumulator state should have been checkpointed", 42, acc.getSnapshotValue(txn));