Merge lp:~pbeaman/akiban-persistit/fix-1056489-mvv-step-visibility into lp:akiban-persistit

Proposed by Peter Beaman
Status: Merged
Approved by: Peter Beaman
Approved revision: 370
Merged at revision: 371
Proposed branch: lp:~pbeaman/akiban-persistit/fix-1056489-mvv-step-visibility
Merge into: lp:akiban-persistit
Diff against target: 231 lines (+148/-18)
3 files modified
src/main/java/com/persistit/MVV.java (+17/-5)
src/test/java/com/persistit/Bug1056489Test.java (+125/-0)
src/test/java/com/persistit/MVVTest.java (+6/-13)
To merge this branch: bzr merge lp:~pbeaman/akiban-persistit/fix-1056489-mvv-step-visibility
Reviewer Review Type Date Requested Status
Akiban Build User Needs Fixing
Nathan Williams Approve
Review via email: mp+126522@code.launchpad.net

Description of the change

Fix bug 1056489 MVV and step visibility and pruning errors. This bug arises to incorrect handling of MVV versions when a single transaction performs two or more updates at different step numbers, and when those updates are not ordered by increasing step value. For example, inserting a value with step=2 and then removing the value with step=1 will result in an MVV that sometimes yields the correct result and sometimes appears to have no value.

The assumption in MVV#storeVersion that versions could only arrive in increasing version-handle order was wrong in this case. The primary change is to allow step #01 to be inserted into the MVV at the correct location with respect to step #02 rather than at the end. A new unit test based on Nathan's original investigation code has also been added.

To post a comment you must log in.
Revision history for this message
Nathan Williams (nwilliams) wrote :

Keeping the mvv array in strict order is probably simplest. Looks good.

review: Approve
Revision history for this message
Akiban Build User (build-akiban) wrote :

There were 2 failures during build/test:

* job persistit-build failed at build number 433: http://172.16.20.104:8080/job/persistit-build/433/

* view must-pass failed: persistit-build is red

review: Needs Fixing
Revision history for this message
Nathan Williams (nwilliams) wrote :

Jenkins setup of AMI failed, apache mirror down?

Revision history for this message
Akiban Build User (build-akiban) wrote :

There were 2 failures during build/test:

* job persistit-build failed at build number 438: http://172.16.20.104:8080/job/persistit-build/438/

* view must-pass failed: persistit-build is yellow

review: Needs Fixing
Revision history for this message
Nathan Williams (nwilliams) wrote :

Real failures in MVVTest that time.

371. By Peter Beaman

Modify MVVTest contants to match reordering now done by MVV.storeVersion

Revision history for this message
Peter Beaman (pbeaman) wrote :

MVVTest contains constants that no longer match MVV.storeVersion. Fixed the constants.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/main/java/com/persistit/MVV.java'
2--- src/main/java/com/persistit/MVV.java 2012-08-24 13:57:19 +0000
3+++ src/main/java/com/persistit/MVV.java 2012-09-27 17:48:28 +0000
4@@ -252,6 +252,9 @@
5 final int sourceLength) {
6 int existedMask = 0;
7 int to = targetOffset;
8+ int remainder = 0;
9+ int end = targetOffset + targetLength;
10+
11 if (targetLimit > target.length) {
12 throw new IllegalArgumentException("Target limit exceed target array length: " + targetLimit + " > "
13 + target.length);
14@@ -314,8 +317,7 @@
15 */
16 to++;
17 int next = to;
18- int end = targetOffset + targetLength;
19-
20+
21 while (next < end) {
22 final long curVersion = getVersion(target, to);
23 final int vlength = getLength(target, to);
24@@ -340,20 +342,30 @@
25 end -= (next - to);
26 next = to;
27 }
28+ } else if (curVersion > versionHandle) {
29+ if (vh2ts(versionHandle) != vh2ts(curVersion)) {
30+ throw new IllegalStateException("Versions out of order");
31+ }
32+ remainder = end - to;
33+ break;
34 }
35 to = next;
36 }
37 }
38- assertCapacity(targetLimit, to + LENGTH_PER_VERSION + sourceLength);
39+ assertCapacity(targetLimit, end + LENGTH_PER_VERSION + sourceLength);
40+ // Move same-transaction steps that are higher
41+ if (remainder > 0) {
42+ System.arraycopy(target, to, target, to + sourceLength + LENGTH_PER_VERSION, remainder);
43+ }
44 // Append new value
45 putVersion(target, to, versionHandle);
46 putLength(target, to, sourceLength);
47 to += LENGTH_PER_VERSION;
48 System.arraycopy(source, sourceOffset, target, to, sourceLength);
49 to += sourceLength;
50- Debug.$assert0.t(verify(target, targetOffset, to - targetOffset));
51+ Debug.$assert0.t(verify(target, targetOffset, to - targetOffset + remainder));
52
53- return (to - targetOffset) | existedMask;
54+ return (to - targetOffset + remainder) | existedMask;
55 }
56
57 /**
58
59=== added file 'src/test/java/com/persistit/Bug1056489Test.java'
60--- src/test/java/com/persistit/Bug1056489Test.java 1970-01-01 00:00:00 +0000
61+++ src/test/java/com/persistit/Bug1056489Test.java 2012-09-27 17:48:28 +0000
62@@ -0,0 +1,125 @@
63+/**
64+ * Copyright © 2012 Akiban Technologies, Inc. All rights reserved.
65+ *
66+ * This program and the accompanying materials are made available
67+ * under the terms of the Eclipse Public License v1.0 which
68+ * accompanies this distribution, and is available at
69+ * http://www.eclipse.org/legal/epl-v10.html
70+ *
71+ * This program may also be available under different license terms.
72+ * For more information, see www.akiban.com or contact licensing@akiban.com.
73+ *
74+ * Contributors:
75+ * Akiban Technologies, Inc.
76+ */
77+
78+package com.persistit;
79+
80+import static org.junit.Assert.assertEquals;
81+import static org.junit.Assert.assertTrue;
82+
83+import org.junit.Test;
84+
85+import com.persistit.exception.PersistitException;
86+import com.persistit.unit.UnitTestProperties;
87+
88+/**
89+ * https://bugs.launchpad.net/akiban-persistit/+bug/1056489
90+ *
91+ * When utilizing the step capability of transactions there appear to be
92+ * inconsistencies between reading, writing, removing, and pruning. Particularly
93+ * when steps of a single transaction appear out of order in a single MVV.
94+ *
95+ * An invariant in MVV#storeVersion is that each version added to an MVV must
96+ * have a larger ts than any version already present in the MVV. This follows
97+ * from ww-dependency validation; if there's a version tsv on the MVV and my ts
98+ * is before that tsv, by definition I'm concurrent with it and can't write.
99+ *
100+ * But, the step handling code breaks that. A transaction can't conflict with
101+ * itself, so no code prevents insertion of versions with step numbers out of
102+ * sequence. MVV#storeVersion does not rearrange the MVV in step order, and the
103+ * pruning code simply keeps the last value found for each concurrent
104+ * transaction. Therefore in the test case for key={2}, the AntiValue for the
105+ * remove is stored after the value 200 in the MVV. The MVV looks like this:
106+ *
107+ * [277<277>:20,282#02<UNCOMMITTED>:200,282#01<UNCOMMITTED>:AntiValue{}]
108+ *
109+ * Pruning will then remove the value at 282#2 and keep 282#1, the AntiValue
110+ * from the remove.
111+ *
112+ */
113+
114+public class Bug1056489Test extends PersistitUnitTestCase {
115+
116+ public void update(final Exchange ex, final int k, final int v) throws Exception {
117+ _persistit.getTransaction().setStep(2);
118+ ex.getKey().clear().append(k);
119+ ex.getValue().clear().put(v);
120+ ex.store();
121+ }
122+
123+ public void remove(final Exchange ex, final int k) throws Exception {
124+ _persistit.getTransaction().setStep(1);
125+ ex.getKey().clear().append(k);
126+ ex.remove();
127+ }
128+
129+ @Test
130+ public void mvvStepCheck() throws Exception {
131+ final Transaction txn = _persistit.getTransaction();
132+ final Exchange ex = _persistit.getExchange(UnitTestProperties.VOLUME_NAME, "new_tree1", true);
133+
134+ txn.begin();
135+ for (int i = 1; i <= 3; ++i) {
136+ ex.getKey().clear().append(i);
137+ ex.getValue().clear().put(i * 10);
138+ ex.store();
139+ }
140+ txn.commit();
141+ txn.end();
142+ treeCheck(ex, "Initial", 10, 20, 30);
143+
144+ txn.begin();
145+ for (int i = 1; i <= 3; ++i) {
146+ ex.clear().append(i);
147+ if (i == 2) {
148+ update(ex, i, i * 100);
149+ remove(ex, i);
150+ } else {
151+ remove(ex, i);
152+ update(ex, i, i * 100);
153+ }
154+ }
155+ treeCheck(ex, "Post update, pre commit", 100, 200, 300);
156+ txn.commit();
157+ txn.end();
158+
159+ treeCheck(ex, "Updated, committed", 100, 200, 300);
160+
161+ while (_persistit.getCleanupManager().getPerformedCount() < _persistit.getCleanupManager().getAcceptedCount()) {
162+ Thread.sleep(100);
163+ }
164+
165+ treeCheck(ex, "Updated, committed, pruned", 100, 200, 300);
166+
167+ _persistit.getTransactionIndex().cleanup();
168+ txn.begin();
169+
170+ treeCheck(ex, "Updated, committed, TI cleaned", 100, 200, 300);
171+ ex.clear().append(2);
172+ assertTrue("Removed should find key {2}", ex.remove());
173+ txn.commit();
174+ txn.end();
175+ treeCheck(ex, "Updated, committed, TI cleaned, removed", 100, 300);
176+ }
177+
178+ private void treeCheck(final Exchange ex, final String message, final int... expected) throws PersistitException {
179+ ex.clear();
180+ int index = 0;
181+ while (ex.next(true)) {
182+ assertTrue("Too many keys", index < expected.length);
183+ assertEquals("Wrong value", expected[index++], ex.getValue().getInt());
184+
185+ }
186+ }
187+}
188
189=== modified file 'src/test/java/com/persistit/MVVTest.java'
190--- src/test/java/com/persistit/MVVTest.java 2012-08-24 13:57:19 +0000
191+++ src/test/java/com/persistit/MVVTest.java 2012-09-27 17:48:28 +0000
192@@ -179,8 +179,8 @@
193 storedLength &= STORE_LENGTH_MASK;
194 assertEquals(targetLength - 1, storedLength);
195 assertArrayEqualsLen(
196- newArray(TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, vh1, 0, 2, 0x4, 0x5, 0, 0, 0, 0, 0, 0, 0, vh3, 0, 4, 0x6, 0x7,
197- 0x8, 0x9, 0, 0, 0, 0, 0, 0, 0, vh2, 0, 2, 0xD, 0xE), target, storedLength);
198+ newArray(TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, vh1, 0, 2, 0x4, 0x5, 0, 0, 0, 0, 0, 0, 0, vh2, 0, 2, 0xD, 0xE,
199+ 0, 0, 0, 0, 0, 0, 0, vh3, 0, 4, 0x6, 0x7, 0x8, 0x9), target, storedLength);
200 }
201
202 @Test
203@@ -197,8 +197,8 @@
204
205 assertEquals(targetLength + 1, storedLength);
206 assertArrayEqualsLen(
207- newArray(TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, vh1, 0, 2, 0x4, 0x5, 0, 0, 0, 0, 0, 0, 0, vh3, 0, 4, 0x6, 0x7,
208- 0x8, 0x9, 0, 0, 0, 0, 0, 0, 0, vh2, 0, 4, 0xC, 0xD, 0xE, 0xF), target, storedLength);
209+ newArray(TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, vh1, 0, 2, 0x4, 0x5, 0, 0, 0, 0, 0, 0, 0, vh2, 0, 4, 0xC, 0xD,
210+ 0xE, 0xF, 0, 0, 0, 0, 0, 0, 0, vh3, 0, 4, 0x6, 0x7, 0x8, 0x9), target, storedLength);
211 }
212
213 @Test
214@@ -220,15 +220,8 @@
215
216 @Test
217 public void storeBigVersions() {
218- final long versions[] = { Integer.MAX_VALUE, 10, 1844674407370955161L /*
219- * MAX
220- * /
221- * 5
222- * -
223- * ish
224- */, Short.MAX_VALUE, Long.MAX_VALUE,
225- 8301034833169298227L /* MAX*0.9-ish */
226- };
227+ final long versions[] = { 10, Short.MAX_VALUE, Integer.MAX_VALUE, 1844674407370955161L, 8301034833169298227L,
228+ Long.MAX_VALUE };
229 final byte contents[][] = { newArray(0xA0), newArray(0xB0, 0xB1), newArray(0xC0, 0xC1, 0xC2),
230 newArray(0xD0, 0xD1, 0xD2, 0xD3), newArray(0xE0, 0xE1, 0xE2, 0xE3, 0xE4),
231 newArray(0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5) };

Subscribers

People subscribed via source and target branches