~pbeaman/akiban-persistit/fix-1046049-keyfilter-wrong

« back to all changes in this revision

Viewing changes to src/test/java/com/persistit/Bug1064565Test.java

merge pbeaman: This proposal fixes https://bugs.launchpad.net/akiban-persistit/+bug/1064565 which is a data-loss bug affecting the state of Accumulator values after system shutdown/restart.

https://code.launchpad.net/~pbeaman/akiban-persistit/fix-accumulator-checkpoint-failure/+merge/129243

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * Copyright © 2012 Akiban Technologies, Inc.  All rights reserved.
 
3
 * 
 
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
 
8
 * 
 
9
 * This program may also be available under different license terms.
 
10
 * For more information, see www.akiban.com or contact licensing@akiban.com.
 
11
 * 
 
12
 * Contributors:
 
13
 * Akiban Technologies, Inc.
 
14
 */
 
15
 
 
16
package com.persistit;
 
17
 
 
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;
 
28
 
 
29
import java.util.concurrent.atomic.AtomicBoolean;
 
30
 
 
31
import org.junit.Test;
 
32
 
 
33
import com.persistit.exception.PersistitException;
 
34
import com.persistit.util.ThreadSequencer.Condition;
 
35
 
 
36
/**
 
37
 * https://bugs.launchpad.net/akiban-persistit/+bug/1064565
 
38
 * 
 
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.
 
42
 * 
 
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.
 
48
 * 
 
49
 * This is a data loss error and is therefore critical.
 
50
 */
 
51
 
 
52
public class Bug1064565Test extends PersistitUnitTestCase {
 
53
 
 
54
    private final static String TREE_NAME = "Bug1064565Test";
 
55
 
 
56
    private Exchange getExchange() throws PersistitException {
 
57
        return _persistit.getExchange(VOLUME_NAME, TREE_NAME, true);
 
58
    }
 
59
 
 
60
    @Test
 
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() {
 
66
            @Override
 
67
            public boolean enabled() {
 
68
                return once.getAndSet(false);
 
69
            }
 
70
        });
 
71
 
 
72
        Exchange exchange = getExchange();
 
73
        Transaction txn = exchange.getTransaction();
 
74
        final Thread t = new Thread(new Runnable() {
 
75
            @Override
 
76
            public void run() {
 
77
                try {
 
78
                    _persistit.checkpoint();
 
79
                } catch (final PersistitException e) {
 
80
                    throw new RuntimeException(e);
 
81
                }
 
82
            }
 
83
        });
 
84
        t.start();
 
85
 
 
86
        txn.begin();
 
87
        Accumulator acc = exchange.getTree().getAccumulator(Accumulator.Type.SUM, 0);
 
88
        acc.update(42, txn);
 
89
        sequence(ACCUMULATOR_CHECKPOINT_B);
 
90
        txn.commit();
 
91
        txn.end();
 
92
        sequence(ACCUMULATOR_CHECKPOINT_C);
 
93
 
 
94
        _persistit.close();
 
95
 
 
96
        final Configuration config = _persistit.getConfiguration();
 
97
        _persistit = new Persistit();
 
98
        _persistit.initialize(config);
 
99
 
 
100
        exchange = getExchange();
 
101
        txn = exchange.getTransaction();
 
102
        txn.begin();
 
103
        acc = exchange.getTree().getAccumulator(Accumulator.Type.SUM, 0);
 
104
        assertEquals("Accumulator state should have been checkpointed", 42, acc.getSnapshotValue(txn));
 
105
        txn.commit();
 
106
        txn.end();
 
107
 
 
108
        _persistit.checkpoint();
 
109
        _persistit.checkpoint();
 
110
        _persistit.checkpoint();
 
111
    }
 
112
 
 
113
    /**
 
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.
 
117
     */
 
118
    @Test
 
119
    public void nathansVersion() throws Exception {
 
120
        Exchange exchange = getExchange();
 
121
        Transaction txn = exchange.getTransaction();
 
122
        txn.begin();
 
123
        Accumulator acc = exchange.getTree().getAccumulator(Accumulator.Type.SUM, 0);
 
124
        acc.update(42, txn);
 
125
        _persistit.checkpoint();
 
126
        txn.commit();
 
127
        txn.end();
 
128
        _persistit.copyBackPages();
 
129
        final Configuration config = _persistit.getConfiguration();
 
130
        _persistit.close();
 
131
        _persistit = new Persistit();
 
132
        _persistit.initialize(config);
 
133
 
 
134
        exchange = getExchange();
 
135
        txn = exchange.getTransaction();
 
136
        txn.begin();
 
137
        acc = exchange.getTree().getAccumulator(Accumulator.Type.SUM, 0);
 
138
        assertEquals("Accumulator state should have been checkpointed", 42, acc.getSnapshotValue(txn));
 
139
        txn.commit();
 
140
        txn.end();
 
141
 
 
142
    }
 
143
}