Merge lp:~pbeaman/akiban-persistit/remove_fast_index_ratio into lp:akiban-persistit

Proposed by Peter Beaman
Status: Merged
Merged at revision: 327
Proposed branch: lp:~pbeaman/akiban-persistit/remove_fast_index_ratio
Merge into: lp:akiban-persistit
Diff against target: 810 lines (+234/-246)
8 files modified
src/main/java/com/persistit/Buffer.java (+23/-61)
src/main/java/com/persistit/BufferPool.java (+1/-56)
src/main/java/com/persistit/Exchange.java (+0/-6)
src/main/java/com/persistit/FastIndex.java (+3/-28)
src/main/java/com/persistit/Volume.java (+22/-2)
src/test/java/com/persistit/BufferTest2.java (+69/-0)
src/test/java/com/persistit/FastIndexTest.java (+94/-85)
src/test/java/com/persistit/VolumeTest.java (+22/-8)
To merge this branch: bzr merge lp:~pbeaman/akiban-persistit/remove_fast_index_ratio
Reviewer Review Type Date Requested Status
Akiban Build User Needs Fixing
Nathan Williams Approve
Review via email: mp+110391@code.launchpad.net

Description of the change

While testing another branch I detected an anomalous state in the association of a Buffer with its FastIndex. This branch eliminates that possibility.

The code originally allowed there to be fewer FastIndex instances than Buffer instances and would shuttle FastIndexes between Buffers as needed. But after reducing the size of the FastIndex to 12.5% of the buffer and benchmarks that prove having a 1:1 ratio is optimal, we set the FAST_INDEX_RATIO in BufferPool to 1.0f and have no inclination to change it. This is equivalent to permanently assigning a FastIndex instance to each Buffer.

That's what this branch does: it allocates a unique, permanent FastIndex when a Buffer is constructed and removes all the code that permitting the reallocation of these structures on the fly.

Advantages: simpler, faster, less prone to bugs due to complex code path.

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

Makes sense.

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 360: http://172.16.20.104:8080/job/persistit-build/360/

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

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

New constructor for FastIndex was off by one.

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

Yup, there was an actual bug in the modified version of Buffer. As a consequence, FastIndexTest#testFastIndexRecompute throw an AssertionError. But FastIndexTest was not written defensively to remove buffer claims even in the event of an error, so the tearDown() method, which calls Persistit#close() hung due to inability to claim the page.

Three issues to resolve:

1. New bug in Buffer (rewrite failed to recompute an invalid FastIndex in the getFastIndex() method).

2. FastIndexTest needs try/finally blocks to ensure the buffer gets released.

3. Volume#close() needs to time out if unable to invalidate the volume within a reasonable length of time.

Adding this changes and resubmitting.

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

The timeout in Volume#close() is probably a good thing to do in general and the other changes make sense as well.

Third times the charm.

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

There were 3 failures during build/test:

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

* view must-pass failed: server-build is blue_anime

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

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

Real failure, but in an unrelated area (TransactionTest2WithInterrupts, add a few revs ago). Trying again.

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

Well, this has failed twice now in the server histogramsIT test with output like:

com.akiban.server.error.PersistitAdapterException: PERSISTIT_ERROR: Persistit Data Layer error: Volume akiban_data(/tmp/akserver-junit/akserver-unitdata7395450818281228504/akiban_data.v01) level=0 page=16 key=<{"Bob",null,"0245",null}> is before left edge

http://172.16.20.104:8080/job/server-build/2333/
http://172.16.20.104:8080/job/server-build/2334/

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

Ouch. Thanks for passing this on. I don't have vpn access.
On Jun 16, 2012 9:57 AM, "Nathan Williams" <email address hidden> wrote:

> Review: Needs Fixing
>
> Well, this has failed twice now in the server histogramsIT test with
> output like:
>
> com.akiban.server.error.PersistitAdapterException: PERSISTIT_ERROR:
> Persistit Data Layer error: Volume
> akiban_data(/tmp/akserver-junit/akserver-unitdata7395450818281228504/akiban_data.v01)
> level=0 page=16 key=<{"Bob",null,"0245",null}> is before left edge
>
> http://172.16.20.104:8080/job/server-build/2333/
> http://172.16.20.104:8080/job/server-build/2334/
>
>
> --
>
> https://code.launchpad.net/~pbeaman/akiban-persistit/remove_fast_index_ratio/+merge/110391
> You are the owner of lp:~pbeaman/akiban-persistit/remove_fast_index_ratio.
>

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

The failures were due to two subtle bugs in the branch. In Buffer#split there was a conditional statement missing a condition needed to correctly set a flag. The incorrectly set flag caused FastIndex#insertKeyBlock to misbehave.

But, the other bug is that the FastIndex should not have been recomputed in Buffer#putValue. The previous version of this branch did so by using a call to getFastIndex() rather than simply using the field value.

Finally, these issues were discovered in IndexHistogramIT because that test inserts both null- and String-valued key segments. This behavior leads to a sequence of keys having different data types and therefore differeing in their first byte. We need to add unit and stress tests to cover this; at the moment all Persistit tests have keys with the same data type in their first key segment.

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

> We need to add unit and stress tests to cover this; at the
> moment all Persistit tests have keys with the same data type
> in their first key segment.

Any reason not to add that before merging this?

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

Added unit test BufferTest2#splitAndJoinBuffersWithZeroEbcKeys().

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

Thanks for the new test.

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

There was one failure during build/test:

* bzr error: These branches have diverged. Use the missing command to see how.
Use the merge command to reconcile them.

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

Looks like /build got out of sync with trunk.

Luckily it only looks like 2 revs are off and build is in order so I'll push it up to trunk.

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/Buffer.java'
2--- src/main/java/com/persistit/Buffer.java 2012-06-08 00:06:12 +0000
3+++ src/main/java/com/persistit/Buffer.java 2012-06-25 15:43:24 +0000
4@@ -269,7 +269,7 @@
5 public final static int MAX_KEY_RATIO = 16;
6
7 private final static int BINARY_SEARCH_THRESHOLD = 6;
8-
9+
10 abstract static class VerifyVisitor {
11
12 protected void visitPage(long timestamp, Volume volume, long page, int type, int bufferSize, int keyBlockStart,
13@@ -318,13 +318,23 @@
14 /**
15 * A ByteBuffer facade for this Buffer used in NIO operations
16 */
17- private ByteBuffer _byteBuffer;
18+ private final ByteBuffer _byteBuffer;
19+
20+ /**
21+ * The size of this buffer
22+ */
23+ private final int _bufferSize;
24
25 /**
26 * The bytes in this buffer. Note these bytes are also the backing store of
27 * _byteBuffer.
28 */
29 private byte[] _bytes;
30+
31+ /**
32+ * FastIndex structure used for rapid page searching
33+ */
34+ private final FastIndex _fastIndex;
35
36 /**
37 * The right sibling page address
38@@ -332,16 +342,6 @@
39 private volatile long _rightSibling;
40
41 /**
42- * The size of this buffer
43- */
44- private int _bufferSize;
45-
46- /**
47- * FastIndex structure used for rapid page searching
48- */
49- private FastIndex _fastIndex;
50-
51- /**
52 * Type code for this page.
53 */
54 private volatile int _type;
55@@ -405,6 +405,7 @@
56 _byteBuffer = ByteBuffer.allocate(size);
57 _bytes = _byteBuffer.array();
58 _bufferSize = size;
59+ _fastIndex = new FastIndex(this, 1 + (size - HEADER_SIZE) / MAX_KEY_RATIO);
60 }
61
62 Buffer(Buffer original) {
63@@ -423,20 +424,6 @@
64 System.arraycopy(original._bytes, 0, _bytes, 0, _bytes.length);
65 }
66
67- Buffer(Persistit persistit, Volume vol, long page, byte[] bytes) throws InvalidPageStructureException,
68- PersistitInterruptedException {
69- this(bytes.length, -1, persistit.getBufferPool(bytes.length), persistit);
70- System.arraycopy(bytes, 0, _bytes, 0, bytes.length);
71- _vol = vol;
72- _page = page;
73- claim(true);
74- try {
75- load();
76- } finally {
77- release();
78- }
79- }
80-
81 /**
82 * Initializes the buffer so that it contains no keys or data.
83 */
84@@ -508,11 +495,11 @@
85 } else if (isIndexPage()) {
86 _tailHeaderSize = TAILBLOCK_HDR_SIZE_INDEX;
87 }
88- invalidateFastIndex();
89 }
90 } else {
91 _type = PAGE_TYPE_HEAD;
92 }
93+ invalidateFastIndex();
94 bumpGeneration();
95 }
96
97@@ -1406,7 +1393,6 @@
98 if (Debug.ENABLED) {
99 assertVerify();
100 }
101- final FastIndex fastIndex = _fastIndex;
102
103 boolean exactMatch = (foundAt & EXACT_MASK) > 0;
104 int p = foundAt & P_MASK;
105@@ -1522,11 +1508,10 @@
106 _bytes.length); // TODO limit
107 incCountIfMvv(_bytes, newTail + _tailHeaderSize + klength, storedLength & MVV.STORE_LENGTH_MASK);
108 }
109-
110- if (fastIndex != null) {
111- fastIndex.insertKeyBlock(p, ebcSuccessor, fixupSuccessor);
112- }
113-
114+ //
115+ // Correct not to call getFastIndex()
116+ //
117+ _fastIndex.insertKeyBlock(p, ebcSuccessor, fixupSuccessor);
118 bumpGeneration();
119
120 if (p > KEY_BLOCK_START) {
121@@ -2294,7 +2279,7 @@
122 if (foundAtPosition >= splitAtPosition && (!lastLeft || foundAtPosition > splitAtPosition)) {
123 foundAt -= (splitAtPosition - KEY_BLOCK_START);
124 if (firstRight && !fixupSuccessor) {
125- foundAt = (foundAt & P_MASK) | FIXUP_MASK | (ebc << DEPTH_SHIFT);
126+ foundAt = (foundAt & P_MASK) | (ebc > 0 ? FIXUP_MASK : 0) | (ebc << DEPTH_SHIFT);
127 }
128 final int t = rightSibling.putValue(key, valueHelper, foundAt, true);
129 whereInserted = -foundAt;
130@@ -2313,8 +2298,7 @@
131 }
132 //
133 // It is really bad if whereInserted is less than 0. Means that we
134- // failed
135- // to replace the value.
136+ // failed to replace the value.
137 //
138 Debug.$assert0.t(whereInserted > 0);
139 if (whereInserted <= 0) {
140@@ -2830,38 +2814,17 @@
141
142 }
143
144- void invalidateFastIndex() {
145- if (_fastIndex != null) {
146+ synchronized void invalidateFastIndex() {
147 _fastIndex.invalidate();
148- }
149 }
150
151- synchronized FastIndex getFastIndex() throws PersistitInterruptedException {
152- // TODO - replace synchronized with CAS instructions
153- if (_fastIndex == null) {
154- _fastIndex = _pool.allocFastIndex();
155- _fastIndex.setBuffer(this);
156- }
157+ synchronized FastIndex getFastIndex() {
158 if (!_fastIndex.isValid()) {
159 _fastIndex.recompute();
160 }
161- _fastIndex.setTouched();
162 return _fastIndex;
163 }
164
165- synchronized void takeFastIndex() {
166- _fastIndex = null;
167- }
168-
169- /**
170- * Only for unit tests.
171- *
172- * @param fastIndex
173- */
174- void setFastIndex(final FastIndex fastIndex) {
175- _fastIndex = fastIndex;
176- }
177-
178 private void reduceEbc(int p, int newEbc, byte[] indexKeyBytes) {
179 int kbData = getInt(p);
180 int oldDb = decodeKeyBlockDb(kbData);
181@@ -3305,8 +3268,7 @@
182
183 static int bufferSizeWithOverhead(final int bufferSize) {
184 int fastIndexSize = ((bufferSize - HEADER_SIZE) / MAX_KEY_RATIO) * FastIndex.BYTES_PER_ENTRY;
185- int fastIndexOverhead = (int) (fastIndexSize * BufferPool.FAST_INDEX_RATIO);
186- return bufferSize + fastIndexOverhead + ESTIMATED_FIXED_BUFFER_OVERHEAD;
187+ return bufferSize + fastIndexSize + ESTIMATED_FIXED_BUFFER_OVERHEAD;
188 }
189
190 /**
191
192=== modified file 'src/main/java/com/persistit/BufferPool.java'
193--- src/main/java/com/persistit/BufferPool.java 2012-05-25 18:50:59 +0000
194+++ src/main/java/com/persistit/BufferPool.java 2012-06-25 15:43:24 +0000
195@@ -76,10 +76,6 @@
196 * Maximum number of buffers this pool may have
197 */
198 public final static int MAXIMUM_POOL_COUNT = Integer.MAX_VALUE;
199- /**
200- * Ratio of FastIndex to buffers
201- */
202- final static float FAST_INDEX_RATIO = 1.0f;
203
204 /**
205 * The maximum number of lock buckets
206@@ -126,15 +122,6 @@
207 private final int _bufferSize;
208
209 /**
210- * Count of FastIndex instances, computed as a fraction of buffer count
211- */
212- private final int _fastIndexCount;
213- /**
214- * FastIndex array
215- */
216- private final FastIndex[] _fastIndexes;
217-
218- /**
219 * Bit map for invalidated pages. Elements in this array, one bit per page,
220 * indicate buffers that have been invalidated and are therefore able to be
221 * allocated without evicting a valid page.
222@@ -154,11 +141,6 @@
223 private final AtomicInteger _clock = new AtomicInteger();
224
225 /**
226- * Pointer to next FastIndex to allocate
227- */
228- private final AtomicInteger _fastIndexClock = new AtomicInteger();
229-
230- /**
231 * Count of buffer pool misses (buffer not found in pool)
232 */
233 private final AtomicLong _missCounter = new AtomicLong();
234@@ -260,15 +242,12 @@
235 _hashTable = new Buffer[_bufferCount * HASH_MULTIPLE];
236 _hashLocks = new ReentrantLock[HASH_LOCKS];
237 _maxKeys = (_bufferSize - Buffer.HEADER_SIZE) / Buffer.MAX_KEY_RATIO;
238- _fastIndexCount = (int) (count * FAST_INDEX_RATIO);
239- _fastIndexes = new FastIndex[_fastIndexCount];
240
241 for (int index = 0; index < HASH_LOCKS; index++) {
242 _hashLocks[index] = new ReentrantLock();
243 }
244
245 int buffers = 0;
246- int fastIndexes = 0;
247 //
248 // Allocate this here so that in the event of an OOME we can release it
249 // to free enough memory to write the error information out.
250@@ -280,11 +259,6 @@
251 _buffers[index] = buffer;
252 buffers++;
253 }
254- for (int index = 0; index < _fastIndexCount; index++) {
255- _fastIndexes[index] = new FastIndex(_maxKeys + 1);
256- _fastIndexes[index].setBuffer(_buffers[index]);
257- fastIndexes++;
258- }
259 } catch (OutOfMemoryError e) {
260 //
261 // Note: written this way to try to avoid another OOME.
262@@ -297,12 +271,7 @@
263 System.err.print(buffers);
264 System.err.print("/");
265 System.err.print(_bufferCount);
266- System.err.print(" buffers and ");
267- System.err.print(fastIndexes);
268- System.err.print("/");
269- System.err.print(_fastIndexCount);
270- System.err.print(" fast indexes ");
271- System.err.print(" from maximum heap ");
272+ System.err.print(" buffers from maximum heap ");
273 System.err.println(_persistit.getAvailableHeap());
274 throw e;
275 }
276@@ -1016,30 +985,6 @@
277 throw new IllegalStateException("No available Buffers");
278 }
279
280- FastIndex allocFastIndex() throws PersistitInterruptedException {
281- for (int retry = 0; retry < _fastIndexCount * 2;) {
282- int clock = _fastIndexClock.get();
283- if (!_fastIndexClock.compareAndSet(clock, (clock + 1) % _fastIndexCount)) {
284- continue;
285- }
286- FastIndex findex = _fastIndexes[clock];
287-
288- if (findex.testTouched()) {
289- findex.clearTouched();
290- } else {
291- Buffer buffer = findex.getBuffer();
292- if (buffer.claim(true, 0)) {
293- buffer.takeFastIndex();
294- buffer.release();
295- findex.invalidate();
296- return findex;
297- }
298- }
299- retry++;
300- }
301- throw new IllegalStateException("No FastIndex buffers available");
302- }
303-
304 enum Result {
305 WRITTEN, UNAVAILABLE, ERROR
306 };
307
308=== modified file 'src/main/java/com/persistit/Exchange.java'
309--- src/main/java/com/persistit/Exchange.java 2012-06-08 00:06:12 +0000
310+++ src/main/java/com/persistit/Exchange.java 2012-06-25 15:43:24 +0000
311@@ -23,15 +23,9 @@
312 import static com.persistit.Buffer.EXACT_MASK;
313 import static com.persistit.Buffer.HEADER_SIZE;
314 import static com.persistit.Buffer.KEYBLOCK_LENGTH;
315-import static com.persistit.Buffer.LONGREC_PREFIX_OFFSET;
316-import static com.persistit.Buffer.LONGREC_PREFIX_SIZE;
317-import static com.persistit.Buffer.LONGREC_SIZE;
318-import static com.persistit.Buffer.LONGREC_TYPE;
319-import static com.persistit.Buffer.MAX_LONG_RECORD_CHAIN;
320 import static com.persistit.Buffer.MAX_VALID_PAGE_ADDR;
321 import static com.persistit.Buffer.PAGE_TYPE_DATA;
322 import static com.persistit.Buffer.PAGE_TYPE_INDEX_MIN;
323-import static com.persistit.Buffer.PAGE_TYPE_LONG_RECORD;
324 import static com.persistit.Buffer.P_MASK;
325 import static com.persistit.Buffer.TAILBLOCK_HDR_SIZE_INDEX;
326 import static com.persistit.Key.AFTER;
327
328=== modified file 'src/main/java/com/persistit/FastIndex.java'
329--- src/main/java/com/persistit/FastIndex.java 2012-05-25 18:50:59 +0000
330+++ src/main/java/com/persistit/FastIndex.java 2012-06-25 15:43:24 +0000
331@@ -24,7 +24,6 @@
332
333 import com.persistit.util.Debug;
334
335-
336 /**
337 * FastIndex is a wrapper for an array of integers that contain information used
338 * in searching for a key in a page. There is one meaningful element in that
339@@ -154,11 +153,6 @@
340 */
341 private final short[] _findexElements;
342 /**
343- * AtomicInteger used to house the "touched" bit for the clock replacement
344- * algorithm in BufferPool.
345- */
346- private final AtomicInteger _bits = new AtomicInteger();
347- /**
348 * Indicates whether the _findexElements array is valid
349 */
350 private boolean _isValid;
351@@ -166,9 +160,10 @@
352 /**
353 * The buffer this fast index is associated with.
354 */
355- private Buffer _buffer;
356+ private final Buffer _buffer;
357
358- FastIndex(int elementSize) {
359+ FastIndex(Buffer buffer, int elementSize) {
360+ _buffer = buffer;
361 _findexElements = new short[elementSize];
362 _isValid = false;
363 }
364@@ -607,26 +602,6 @@
365 return start - end + 1;
366 }
367
368- void setBuffer(final Buffer buffer) {
369- _buffer = buffer;
370- }
371-
372- Buffer getBuffer() {
373- return _buffer;
374- }
375-
376- void setTouched() {
377- _bits.set(1);
378- }
379-
380- void clearTouched() {
381- _bits.set(0);
382- }
383-
384- boolean testTouched() {
385- return (_bits.get()) != 0;
386- }
387-
388 // TODO - unnecessary after debugging
389 public String toString() {
390 StringBuilder sb = new StringBuilder();
391
392=== modified file 'src/main/java/com/persistit/Volume.java'
393--- src/main/java/com/persistit/Volume.java 2012-05-25 18:50:59 +0000
394+++ src/main/java/com/persistit/Volume.java 2012-06-25 15:43:24 +0000
395@@ -144,19 +144,36 @@
396 /**
397 * Release all resources for this <code>Volume</code> and invalidate all its
398 * buffers in the {@link BufferPool}. Exchanges based on this
399+ * <code>Volume</code> may no longer be used after this call. Waits up to
400+ * {@value com.persistit.SharedResource#DEFAULT_MAX_WAIT_TIME} milliseconds
401+ * for other threads to relinquish access to the volume.
402+ *
403+ * @throws PersistitException
404+ */
405+ public void close() throws PersistitException {
406+ close(SharedResource.DEFAULT_MAX_WAIT_TIME);
407+ }
408+
409+ /**
410+ * Release all resources for this <code>Volume</code> and invalidate all its
411+ * buffers in the {@link BufferPool}. Exchanges based on this
412 * <code>Volume</code> may no longer be used after this call.
413 *
414+ * @param timeout
415+ * Maximum time in milliseconds to wait for other threads to
416+ * relinquish access to the volume.
417 * @throws PersistitException
418 */
419- public void close() throws PersistitException {
420+ public void close(final long timeout) throws PersistitException {
421 closing();
422+ final long expiration = System.currentTimeMillis() + timeout;
423 for (;;) {
424 //
425 // Prevents read/write operations from starting while the
426 // volume is being closed.
427 //
428 final VolumeStorage storage = getStorage();
429- if (!storage.claim(true)) {
430+ if (!storage.claim(true, timeout)) {
431 throw new InUseException("Unable to acquire claim on " + this);
432 }
433 try {
434@@ -174,6 +191,9 @@
435 } finally {
436 storage.release();
437 }
438+ if (System.currentTimeMillis() >= expiration) {
439+ throw new InUseException("Unable invalidate all pages on " + this);
440+ }
441 Util.sleep(Persistit.SHORT_DELAY);
442 }
443 }
444
445=== modified file 'src/test/java/com/persistit/BufferTest2.java'
446--- src/test/java/com/persistit/BufferTest2.java 2012-05-25 18:50:59 +0000
447+++ src/test/java/com/persistit/BufferTest2.java 2012-06-25 15:43:24 +0000
448@@ -29,6 +29,8 @@
449 import static org.junit.Assert.assertTrue;
450 import static org.junit.Assert.fail;
451
452+import java.util.Arrays;
453+
454 import org.junit.Test;
455
456 import com.persistit.ValueHelper.RawValueWriter;
457@@ -331,6 +333,53 @@
458 b1.removeKeys(foundAt1 & ~EXACT_MASK, foundAt2, ex.getAuxiliaryKey1());
459 }
460
461+ @Test
462+ public void splitAndJoinBuffersWithZeroEbcKeys() throws Exception {
463+ b1.init(Buffer.PAGE_TYPE_DATA);
464+ b2.init(Buffer.PAGE_TYPE_DATA);
465+ setUpValue(true, b1.getBufferSize() / 100);
466+
467+ int a;
468+ for (a = 1;; a++) {
469+ setUpShallowKey(a);
470+ if (b1.putValue(ex.getKey(), vw) == -1) {
471+ break;
472+ }
473+ }
474+ setUpShallowKey(a / 2);
475+ ex.getKey().getEncodedBytes()[6] = 0x7F;
476+ ex.getKey().setEncodedSize(7);
477+ int foundAt = b1.findKey(ex.getKey());
478+ int splitAt = b1.split(b2, ex.getKey(), vw, foundAt, ex.getAuxiliaryKey1(), Exchange.Sequence.NONE,
479+ SplitPolicy.EVEN_BIAS);
480+
481+ assertTrue("Split failed", splitAt != -1);
482+ assertTrue("Verify failed", b1.verify(null, null) == null);
483+ assertTrue("Verify failed", b2.verify(null, null) == null);
484+
485+ for (int b = 1; b < a; b++) {
486+ setUpShallowKey(b);
487+ if (b <= a / 2) {
488+ b1.fetch(b1.findKey(ex.getKey()), ex.getValue());
489+ } else {
490+ b2.fetch(b2.findKey(ex.getKey()), ex.getValue());
491+ }
492+ assertTrue("Value should be defined", ex.getValue().isDefined());
493+ }
494+
495+ setUpShallowKey(a / 2);
496+ ex.getKey().getEncodedBytes()[6] = 0x7F;
497+ ex.getKey().setEncodedSize(7);
498+ int foundAt1 = b1.findKey(ex.getKey());
499+ int foundAt2 = b2.findKey(ex.getKey());
500+ assertTrue("Should have found exact match", (foundAt2 & EXACT_MASK) > 0);
501+ foundAt2 = b2.nextKeyBlock(foundAt2);
502+ boolean rebalanced = b1.join(b2, foundAt1, foundAt2, ex.getKey(), ex.getAuxiliaryKey1(), JoinPolicy.EVEN_BIAS);
503+ assertTrue("Should have joined pages", !rebalanced);
504+ assertTrue("Verify failed", b1.verify(null, null) == null);
505+ assertTrue("Verify failed", b2.verify(null, null) == null);
506+ }
507+
508 private void setUpPrettyFullBufferWithChangingEbc(final int valueLength) throws PersistitException {
509 b1.init(Buffer.PAGE_TYPE_DATA);
510 setUpValue(false, valueLength);
511@@ -401,6 +450,26 @@
512 ex.getValue().setPointerValue(pointer);
513 }
514
515+ private void setUpShallowKey(final int a) {
516+ setUpShallowKey(ex, a, 0);
517+ }
518+
519+ private void setUpShallowKey(Exchange ex, int value, long pointer) {
520+ byte[] bytes = ex.getKey().clear().getEncodedBytes();
521+ Arrays.fill(bytes, (byte) 0);
522+ /*
523+ * Convert int to little-endian form so that byte 0 varies fastest. 7
524+ * bits per byte, no zeroes.
525+ */
526+ bytes[0] = (byte) (((value >>> 0) & 0x7F) + 1);
527+ bytes[1] = (byte) (((value >>> 7) & 0x7F) + 1);
528+ bytes[2] = (byte) (((value >>> 14) & 0x7F) + 1);
529+ bytes[3] = (byte) (((value >>> 21) & 0x7F) + 1);
530+ bytes[5] = (byte) (((value >>> 28) & 0x7F) + 1);
531+ ex.getKey().setEncodedSize(6);
532+ ex.getValue().setPointerValue(pointer);
533+ }
534+
535 String keyString(char fill, int length, int prefix, int width, int k) {
536 StringBuilder sb = new StringBuilder();
537 for (int i = 0; i < prefix && i < length; i++) {
538
539=== modified file 'src/test/java/com/persistit/FastIndexTest.java'
540--- src/test/java/com/persistit/FastIndexTest.java 2012-05-25 18:50:59 +0000
541+++ src/test/java/com/persistit/FastIndexTest.java 2012-06-25 15:43:24 +0000
542@@ -39,78 +39,88 @@
543 @Test
544 public void testIndexSize() throws Exception {
545 final Buffer b1 = getABuffer();
546- FastIndex fi = b1.getFastIndex();
547- /*
548- * BUFFER_SIZE - HEADER_SIZE / MAX_KEY_RATIO BUFFER_SIZE = 16384
549- * HEADER_SIZE = 32 MAX_KEY_RATIO = 16 16384 - 32 / 16 = 16352 / 16 =
550- * 1022 size passed to FastIndex constructor is 1022 + 1 when parameters
551- * are as listed above.
552- */
553- final int expectedSize = (16384 - Buffer.HEADER_SIZE) / Buffer.MAX_KEY_RATIO;
554- assertEquals(expectedSize + 1, fi.size());
555- b1.release();
556+ try {
557+ FastIndex fi = b1.getFastIndex();
558+ /*
559+ * BUFFER_SIZE - HEADER_SIZE / MAX_KEY_RATIO BUFFER_SIZE = 16384
560+ * HEADER_SIZE = 32 MAX_KEY_RATIO = 16 16384 - 32 / 16 = 16352 / 16
561+ * = 1022 size passed to FastIndex constructor is 1022 + 1 when
562+ * parameters are as listed above.
563+ */
564+ final int expectedSize = (16384 - Buffer.HEADER_SIZE) / Buffer.MAX_KEY_RATIO;
565+ assertEquals(expectedSize + 1, fi.size());
566+ } finally {
567+ b1.release();
568+ }
569 }
570
571 @Test
572 public void testIndexValidity() throws Exception {
573 final Buffer b1 = getABuffer();
574- b1.init(Buffer.PAGE_TYPE_GARBAGE);
575- FastIndex fi = b1.getFastIndex();
576- fi.invalidate();
577- assertEquals(false, fi.isValid());
578- fi.recompute();
579- assertEquals(false, fi.isValid());
580- b1.init(Buffer.PAGE_TYPE_DATA);
581- fi.recompute();
582- assertEquals(true, fi.isValid());
583- assertEquals(true, fi.verify());
584- fi.invalidate();
585- assertEquals(false, fi.isValid());
586- assertEquals(false, fi.verify());
587- b1.release();
588+ try {
589+ b1.init(Buffer.PAGE_TYPE_GARBAGE);
590+ FastIndex fi = b1.getFastIndex();
591+ fi.invalidate();
592+ assertEquals(false, fi.isValid());
593+ fi.recompute();
594+ assertEquals(false, fi.isValid());
595+ b1.init(Buffer.PAGE_TYPE_DATA);
596+ fi.recompute();
597+ assertEquals(true, fi.isValid());
598+ assertEquals(true, fi.verify());
599+ fi.invalidate();
600+ assertEquals(false, fi.isValid());
601+ assertEquals(false, fi.verify());
602+ } finally {
603+ b1.release();
604+ }
605 }
606
607 @Test
608 public void testFastIndexRecompute() throws Exception {
609 Buffer b1 = getABuffer();
610- b1.init(Buffer.PAGE_TYPE_DATA);
611- Key key = new Key(_persistit);
612- Value value = new Value(_persistit);
613- RawValueWriter vwriter = new RawValueWriter();
614- vwriter.init(value);
615- fakeKey(key, "A");
616- b1.putValue(key, vwriter);
617- fakeKey(key, "ABC");
618- b1.putValue(key, vwriter);
619- fakeKey(key, "ABK");
620- b1.putValue(key, vwriter);
621- fakeKey(key, "ABZ");
622- b1.putValue(key, vwriter);
623- fakeKey(key, "AC");
624- b1.putValue(key, vwriter);
625- fakeKey(key, "C");
626- b1.putValue(key, vwriter);
627- fakeKey(key, "B");
628- b1.putValue(key, vwriter);
629- fakeKey(key, "E");
630- b1.putValue(key, vwriter);
631- fakeKey(key, "D");
632- b1.putValue(key, vwriter);
633- fakeKey(key, "DA");
634- b1.putValue(key, vwriter);
635- fakeKey(key, "ABB");
636- b1.putValue(key, vwriter);
637- fakeKey(key, "ABA");
638- b1.putValue(key, vwriter);
639- fakeKey(key, "ABJ");
640- b1.putValue(key, vwriter);
641+ try {
642+ b1.init(Buffer.PAGE_TYPE_DATA);
643+ Key key = new Key(_persistit);
644+ Value value = new Value(_persistit);
645+ RawValueWriter vwriter = new RawValueWriter();
646+ FastIndex fi = b1.getFastIndex();
647+ fi.recompute();
648+ vwriter.init(value);
649+ fakeKey(key, "A");
650+ b1.putValue(key, vwriter);
651+ fakeKey(key, "ABC");
652+ b1.putValue(key, vwriter);
653+ fakeKey(key, "ABK");
654+ b1.putValue(key, vwriter);
655+ fakeKey(key, "ABZ");
656+ b1.putValue(key, vwriter);
657+ fakeKey(key, "AC");
658+ b1.putValue(key, vwriter);
659+ fakeKey(key, "C");
660+ b1.putValue(key, vwriter);
661+ fakeKey(key, "B");
662+ b1.putValue(key, vwriter);
663+ fakeKey(key, "E");
664+ b1.putValue(key, vwriter);
665+ fakeKey(key, "D");
666+ b1.putValue(key, vwriter);
667+ fakeKey(key, "DA");
668+ b1.putValue(key, vwriter);
669+ fakeKey(key, "ABB");
670+ b1.putValue(key, vwriter);
671+ fakeKey(key, "ABA");
672+ b1.putValue(key, vwriter);
673+ fakeKey(key, "ABJ");
674+ b1.putValue(key, vwriter);
675
676- FastIndex fi = b1.getFastIndex();
677- String inserteds = fi.toString();
678- fi.recompute();
679- String computeds = fi.toString();
680- assertEquals(inserteds, computeds);
681- b1.release();
682+ String inserteds = fi.toString();
683+ fi.recompute();
684+ String computeds = fi.toString();
685+ assertEquals(inserteds, computeds);
686+ } finally {
687+ b1.release();
688+ }
689 }
690
691 private void fakeKey(final Key key, final String v) {
692@@ -123,31 +133,30 @@
693 public void testRandomInsert() throws Exception {
694 Random random = new Random(3);
695 Buffer b1 = getABuffer();
696- FastIndex f1 = b1.getFastIndex();
697-
698- b1.init(Buffer.PAGE_TYPE_DATA);
699- Key key = new Key(_persistit);
700- Value value = new Value(_persistit);
701- for (int i = 0; i < 1000; i++) {
702- int size = random.nextInt(10) + 2;
703- byte[] bytes = new byte[size];
704- random.nextBytes(bytes);
705- System.arraycopy(bytes, 0, key.getEncodedBytes(), 0, size);
706- key.setEncodedSize(size);
707- RawValueWriter vwriter = new RawValueWriter();
708- vwriter.init(value);
709- b1.putValue(key, vwriter);
710-
711- String s1 = f1.toString();
712- f1.recompute();
713- String s2 = f1.toString();
714- assertEquals(s1, s2);
715+ try {
716+ FastIndex f1 = b1.getFastIndex();
717+
718+ b1.init(Buffer.PAGE_TYPE_DATA);
719+ Key key = new Key(_persistit);
720+ Value value = new Value(_persistit);
721+ for (int i = 0; i < 1000; i++) {
722+ int size = random.nextInt(10) + 2;
723+ byte[] bytes = new byte[size];
724+ random.nextBytes(bytes);
725+ System.arraycopy(bytes, 0, key.getEncodedBytes(), 0, size);
726+ key.setEncodedSize(size);
727+ RawValueWriter vwriter = new RawValueWriter();
728+ vwriter.init(value);
729+ b1.putValue(key, vwriter);
730+
731+ String s1 = f1.toString();
732+ f1.recompute();
733+ String s2 = f1.toString();
734+ assertEquals(s1, s2);
735+ }
736+ } finally {
737+ b1.release();
738 }
739- b1.release();
740- }
741-
742- @Override
743- public void runAllTests() {
744 }
745
746 }
747
748=== modified file 'src/test/java/com/persistit/VolumeTest.java'
749--- src/test/java/com/persistit/VolumeTest.java 2012-05-25 18:50:59 +0000
750+++ src/test/java/com/persistit/VolumeTest.java 2012-06-25 15:43:24 +0000
751@@ -35,6 +35,7 @@
752 import org.junit.Test;
753
754 import com.persistit.exception.CorruptVolumeException;
755+import com.persistit.exception.InUseException;
756 import com.persistit.exception.InvalidVolumeSpecificationException;
757 import com.persistit.exception.VolumeFullException;
758 import com.persistit.unit.PersistitUnitTestCase;
759@@ -184,12 +185,12 @@
760 final Volume vol2 = _persistit.loadVolume(vs);
761 final long statTimestamp = vol2.getStatistics().getLastGlobalTimestamp();
762 // Greater than is ok (other activity may have occurred)
763- if(statTimestamp < MARKER) {
764+ if (statTimestamp < MARKER) {
765 assertEquals("Saved and loaded timestamp", MARKER, statTimestamp);
766 }
767 }
768
769- @Test(expected=CorruptVolumeException.class)
770+ @Test(expected = CorruptVolumeException.class)
771 public void volumeFromFutureIsRejected() throws Exception {
772 final int RECORDS = 100;
773
774@@ -227,6 +228,25 @@
775 _persistit.initialize(properties);
776 }
777
778+ @Test
779+ public void timeoutWhenPageIsInUse() throws Exception {
780+ final Exchange exchange = _persistit.getExchange(UnitTestProperties.VOLUME_NAME, "VolumeTest", true);
781+ final Buffer buffer = exchange.getBufferPool().get(exchange.getVolume(), 1, false, true);
782+ try {
783+ final long start = System.currentTimeMillis();
784+ try {
785+ exchange.getVolume().close(5000);
786+ fail("Expect an InUseException");
787+ } catch (InUseException e) {
788+ final long elapsed = System.currentTimeMillis() - start;
789+ assertTrue("Expected InUseException to happen at 5000 ms but was " + elapsed, elapsed > 4000
790+ && elapsed < 10000);
791+ }
792+ } finally {
793+ buffer.release();
794+ }
795+ }
796+
797 private VolumeSpecification validVolumeSpecification(final String specification) throws Exception {
798 try {
799 return _persistit.getConfiguration().volumeSpecification(specification);
800@@ -245,10 +265,4 @@
801 }
802 }
803
804- @Override
805- public void runAllTests() throws Exception {
806- // TODO Auto-generated method stub
807-
808- }
809-
810 }

Subscribers

People subscribed via source and target branches