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
=== modified file 'src/main/java/com/persistit/Buffer.java'
--- src/main/java/com/persistit/Buffer.java 2012-06-08 00:06:12 +0000
+++ src/main/java/com/persistit/Buffer.java 2012-06-25 15:43:24 +0000
@@ -269,7 +269,7 @@
269 public final static int MAX_KEY_RATIO = 16;269 public final static int MAX_KEY_RATIO = 16;
270270
271 private final static int BINARY_SEARCH_THRESHOLD = 6;271 private final static int BINARY_SEARCH_THRESHOLD = 6;
272272
273 abstract static class VerifyVisitor {273 abstract static class VerifyVisitor {
274274
275 protected void visitPage(long timestamp, Volume volume, long page, int type, int bufferSize, int keyBlockStart,275 protected void visitPage(long timestamp, Volume volume, long page, int type, int bufferSize, int keyBlockStart,
@@ -318,13 +318,23 @@
318 /**318 /**
319 * A ByteBuffer facade for this Buffer used in NIO operations319 * A ByteBuffer facade for this Buffer used in NIO operations
320 */320 */
321 private ByteBuffer _byteBuffer;321 private final ByteBuffer _byteBuffer;
322
323 /**
324 * The size of this buffer
325 */
326 private final int _bufferSize;
322327
323 /**328 /**
324 * The bytes in this buffer. Note these bytes are also the backing store of329 * The bytes in this buffer. Note these bytes are also the backing store of
325 * _byteBuffer.330 * _byteBuffer.
326 */331 */
327 private byte[] _bytes;332 private byte[] _bytes;
333
334 /**
335 * FastIndex structure used for rapid page searching
336 */
337 private final FastIndex _fastIndex;
328338
329 /**339 /**
330 * The right sibling page address340 * The right sibling page address
@@ -332,16 +342,6 @@
332 private volatile long _rightSibling;342 private volatile long _rightSibling;
333343
334 /**344 /**
335 * The size of this buffer
336 */
337 private int _bufferSize;
338
339 /**
340 * FastIndex structure used for rapid page searching
341 */
342 private FastIndex _fastIndex;
343
344 /**
345 * Type code for this page.345 * Type code for this page.
346 */346 */
347 private volatile int _type;347 private volatile int _type;
@@ -405,6 +405,7 @@
405 _byteBuffer = ByteBuffer.allocate(size);405 _byteBuffer = ByteBuffer.allocate(size);
406 _bytes = _byteBuffer.array();406 _bytes = _byteBuffer.array();
407 _bufferSize = size;407 _bufferSize = size;
408 _fastIndex = new FastIndex(this, 1 + (size - HEADER_SIZE) / MAX_KEY_RATIO);
408 }409 }
409410
410 Buffer(Buffer original) {411 Buffer(Buffer original) {
@@ -423,20 +424,6 @@
423 System.arraycopy(original._bytes, 0, _bytes, 0, _bytes.length);424 System.arraycopy(original._bytes, 0, _bytes, 0, _bytes.length);
424 }425 }
425426
426 Buffer(Persistit persistit, Volume vol, long page, byte[] bytes) throws InvalidPageStructureException,
427 PersistitInterruptedException {
428 this(bytes.length, -1, persistit.getBufferPool(bytes.length), persistit);
429 System.arraycopy(bytes, 0, _bytes, 0, bytes.length);
430 _vol = vol;
431 _page = page;
432 claim(true);
433 try {
434 load();
435 } finally {
436 release();
437 }
438 }
439
440 /**427 /**
441 * Initializes the buffer so that it contains no keys or data.428 * Initializes the buffer so that it contains no keys or data.
442 */429 */
@@ -508,11 +495,11 @@
508 } else if (isIndexPage()) {495 } else if (isIndexPage()) {
509 _tailHeaderSize = TAILBLOCK_HDR_SIZE_INDEX;496 _tailHeaderSize = TAILBLOCK_HDR_SIZE_INDEX;
510 }497 }
511 invalidateFastIndex();
512 }498 }
513 } else {499 } else {
514 _type = PAGE_TYPE_HEAD;500 _type = PAGE_TYPE_HEAD;
515 }501 }
502 invalidateFastIndex();
516 bumpGeneration();503 bumpGeneration();
517 }504 }
518505
@@ -1406,7 +1393,6 @@
1406 if (Debug.ENABLED) {1393 if (Debug.ENABLED) {
1407 assertVerify();1394 assertVerify();
1408 }1395 }
1409 final FastIndex fastIndex = _fastIndex;
14101396
1411 boolean exactMatch = (foundAt & EXACT_MASK) > 0;1397 boolean exactMatch = (foundAt & EXACT_MASK) > 0;
1412 int p = foundAt & P_MASK;1398 int p = foundAt & P_MASK;
@@ -1522,11 +1508,10 @@
1522 _bytes.length); // TODO limit1508 _bytes.length); // TODO limit
1523 incCountIfMvv(_bytes, newTail + _tailHeaderSize + klength, storedLength & MVV.STORE_LENGTH_MASK);1509 incCountIfMvv(_bytes, newTail + _tailHeaderSize + klength, storedLength & MVV.STORE_LENGTH_MASK);
1524 }1510 }
15251511 //
1526 if (fastIndex != null) {1512 // Correct not to call getFastIndex()
1527 fastIndex.insertKeyBlock(p, ebcSuccessor, fixupSuccessor);1513 //
1528 }1514 _fastIndex.insertKeyBlock(p, ebcSuccessor, fixupSuccessor);
1529
1530 bumpGeneration();1515 bumpGeneration();
15311516
1532 if (p > KEY_BLOCK_START) {1517 if (p > KEY_BLOCK_START) {
@@ -2294,7 +2279,7 @@
2294 if (foundAtPosition >= splitAtPosition && (!lastLeft || foundAtPosition > splitAtPosition)) {2279 if (foundAtPosition >= splitAtPosition && (!lastLeft || foundAtPosition > splitAtPosition)) {
2295 foundAt -= (splitAtPosition - KEY_BLOCK_START);2280 foundAt -= (splitAtPosition - KEY_BLOCK_START);
2296 if (firstRight && !fixupSuccessor) {2281 if (firstRight && !fixupSuccessor) {
2297 foundAt = (foundAt & P_MASK) | FIXUP_MASK | (ebc << DEPTH_SHIFT);2282 foundAt = (foundAt & P_MASK) | (ebc > 0 ? FIXUP_MASK : 0) | (ebc << DEPTH_SHIFT);
2298 }2283 }
2299 final int t = rightSibling.putValue(key, valueHelper, foundAt, true);2284 final int t = rightSibling.putValue(key, valueHelper, foundAt, true);
2300 whereInserted = -foundAt;2285 whereInserted = -foundAt;
@@ -2313,8 +2298,7 @@
2313 }2298 }
2314 //2299 //
2315 // It is really bad if whereInserted is less than 0. Means that we2300 // It is really bad if whereInserted is less than 0. Means that we
2316 // failed2301 // failed to replace the value.
2317 // to replace the value.
2318 //2302 //
2319 Debug.$assert0.t(whereInserted > 0);2303 Debug.$assert0.t(whereInserted > 0);
2320 if (whereInserted <= 0) {2304 if (whereInserted <= 0) {
@@ -2830,38 +2814,17 @@
28302814
2831 }2815 }
28322816
2833 void invalidateFastIndex() {2817 synchronized void invalidateFastIndex() {
2834 if (_fastIndex != null) {
2835 _fastIndex.invalidate();2818 _fastIndex.invalidate();
2836 }
2837 }2819 }
28382820
2839 synchronized FastIndex getFastIndex() throws PersistitInterruptedException {2821 synchronized FastIndex getFastIndex() {
2840 // TODO - replace synchronized with CAS instructions
2841 if (_fastIndex == null) {
2842 _fastIndex = _pool.allocFastIndex();
2843 _fastIndex.setBuffer(this);
2844 }
2845 if (!_fastIndex.isValid()) {2822 if (!_fastIndex.isValid()) {
2846 _fastIndex.recompute();2823 _fastIndex.recompute();
2847 }2824 }
2848 _fastIndex.setTouched();
2849 return _fastIndex;2825 return _fastIndex;
2850 }2826 }
28512827
2852 synchronized void takeFastIndex() {
2853 _fastIndex = null;
2854 }
2855
2856 /**
2857 * Only for unit tests.
2858 *
2859 * @param fastIndex
2860 */
2861 void setFastIndex(final FastIndex fastIndex) {
2862 _fastIndex = fastIndex;
2863 }
2864
2865 private void reduceEbc(int p, int newEbc, byte[] indexKeyBytes) {2828 private void reduceEbc(int p, int newEbc, byte[] indexKeyBytes) {
2866 int kbData = getInt(p);2829 int kbData = getInt(p);
2867 int oldDb = decodeKeyBlockDb(kbData);2830 int oldDb = decodeKeyBlockDb(kbData);
@@ -3305,8 +3268,7 @@
33053268
3306 static int bufferSizeWithOverhead(final int bufferSize) {3269 static int bufferSizeWithOverhead(final int bufferSize) {
3307 int fastIndexSize = ((bufferSize - HEADER_SIZE) / MAX_KEY_RATIO) * FastIndex.BYTES_PER_ENTRY;3270 int fastIndexSize = ((bufferSize - HEADER_SIZE) / MAX_KEY_RATIO) * FastIndex.BYTES_PER_ENTRY;
3308 int fastIndexOverhead = (int) (fastIndexSize * BufferPool.FAST_INDEX_RATIO);3271 return bufferSize + fastIndexSize + ESTIMATED_FIXED_BUFFER_OVERHEAD;
3309 return bufferSize + fastIndexOverhead + ESTIMATED_FIXED_BUFFER_OVERHEAD;
3310 }3272 }
33113273
3312 /**3274 /**
33133275
=== modified file 'src/main/java/com/persistit/BufferPool.java'
--- src/main/java/com/persistit/BufferPool.java 2012-05-25 18:50:59 +0000
+++ src/main/java/com/persistit/BufferPool.java 2012-06-25 15:43:24 +0000
@@ -76,10 +76,6 @@
76 * Maximum number of buffers this pool may have76 * Maximum number of buffers this pool may have
77 */77 */
78 public final static int MAXIMUM_POOL_COUNT = Integer.MAX_VALUE;78 public final static int MAXIMUM_POOL_COUNT = Integer.MAX_VALUE;
79 /**
80 * Ratio of FastIndex to buffers
81 */
82 final static float FAST_INDEX_RATIO = 1.0f;
8379
84 /**80 /**
85 * The maximum number of lock buckets81 * The maximum number of lock buckets
@@ -126,15 +122,6 @@
126 private final int _bufferSize;122 private final int _bufferSize;
127123
128 /**124 /**
129 * Count of FastIndex instances, computed as a fraction of buffer count
130 */
131 private final int _fastIndexCount;
132 /**
133 * FastIndex array
134 */
135 private final FastIndex[] _fastIndexes;
136
137 /**
138 * Bit map for invalidated pages. Elements in this array, one bit per page,125 * Bit map for invalidated pages. Elements in this array, one bit per page,
139 * indicate buffers that have been invalidated and are therefore able to be126 * indicate buffers that have been invalidated and are therefore able to be
140 * allocated without evicting a valid page.127 * allocated without evicting a valid page.
@@ -154,11 +141,6 @@
154 private final AtomicInteger _clock = new AtomicInteger();141 private final AtomicInteger _clock = new AtomicInteger();
155142
156 /**143 /**
157 * Pointer to next FastIndex to allocate
158 */
159 private final AtomicInteger _fastIndexClock = new AtomicInteger();
160
161 /**
162 * Count of buffer pool misses (buffer not found in pool)144 * Count of buffer pool misses (buffer not found in pool)
163 */145 */
164 private final AtomicLong _missCounter = new AtomicLong();146 private final AtomicLong _missCounter = new AtomicLong();
@@ -260,15 +242,12 @@
260 _hashTable = new Buffer[_bufferCount * HASH_MULTIPLE];242 _hashTable = new Buffer[_bufferCount * HASH_MULTIPLE];
261 _hashLocks = new ReentrantLock[HASH_LOCKS];243 _hashLocks = new ReentrantLock[HASH_LOCKS];
262 _maxKeys = (_bufferSize - Buffer.HEADER_SIZE) / Buffer.MAX_KEY_RATIO;244 _maxKeys = (_bufferSize - Buffer.HEADER_SIZE) / Buffer.MAX_KEY_RATIO;
263 _fastIndexCount = (int) (count * FAST_INDEX_RATIO);
264 _fastIndexes = new FastIndex[_fastIndexCount];
265245
266 for (int index = 0; index < HASH_LOCKS; index++) {246 for (int index = 0; index < HASH_LOCKS; index++) {
267 _hashLocks[index] = new ReentrantLock();247 _hashLocks[index] = new ReentrantLock();
268 }248 }
269249
270 int buffers = 0;250 int buffers = 0;
271 int fastIndexes = 0;
272 //251 //
273 // Allocate this here so that in the event of an OOME we can release it252 // Allocate this here so that in the event of an OOME we can release it
274 // to free enough memory to write the error information out.253 // to free enough memory to write the error information out.
@@ -280,11 +259,6 @@
280 _buffers[index] = buffer;259 _buffers[index] = buffer;
281 buffers++;260 buffers++;
282 }261 }
283 for (int index = 0; index < _fastIndexCount; index++) {
284 _fastIndexes[index] = new FastIndex(_maxKeys + 1);
285 _fastIndexes[index].setBuffer(_buffers[index]);
286 fastIndexes++;
287 }
288 } catch (OutOfMemoryError e) {262 } catch (OutOfMemoryError e) {
289 //263 //
290 // Note: written this way to try to avoid another OOME.264 // Note: written this way to try to avoid another OOME.
@@ -297,12 +271,7 @@
297 System.err.print(buffers);271 System.err.print(buffers);
298 System.err.print("/");272 System.err.print("/");
299 System.err.print(_bufferCount);273 System.err.print(_bufferCount);
300 System.err.print(" buffers and ");274 System.err.print(" buffers from maximum heap ");
301 System.err.print(fastIndexes);
302 System.err.print("/");
303 System.err.print(_fastIndexCount);
304 System.err.print(" fast indexes ");
305 System.err.print(" from maximum heap ");
306 System.err.println(_persistit.getAvailableHeap());275 System.err.println(_persistit.getAvailableHeap());
307 throw e;276 throw e;
308 }277 }
@@ -1016,30 +985,6 @@
1016 throw new IllegalStateException("No available Buffers");985 throw new IllegalStateException("No available Buffers");
1017 }986 }
1018987
1019 FastIndex allocFastIndex() throws PersistitInterruptedException {
1020 for (int retry = 0; retry < _fastIndexCount * 2;) {
1021 int clock = _fastIndexClock.get();
1022 if (!_fastIndexClock.compareAndSet(clock, (clock + 1) % _fastIndexCount)) {
1023 continue;
1024 }
1025 FastIndex findex = _fastIndexes[clock];
1026
1027 if (findex.testTouched()) {
1028 findex.clearTouched();
1029 } else {
1030 Buffer buffer = findex.getBuffer();
1031 if (buffer.claim(true, 0)) {
1032 buffer.takeFastIndex();
1033 buffer.release();
1034 findex.invalidate();
1035 return findex;
1036 }
1037 }
1038 retry++;
1039 }
1040 throw new IllegalStateException("No FastIndex buffers available");
1041 }
1042
1043 enum Result {988 enum Result {
1044 WRITTEN, UNAVAILABLE, ERROR989 WRITTEN, UNAVAILABLE, ERROR
1045 };990 };
1046991
=== modified file 'src/main/java/com/persistit/Exchange.java'
--- src/main/java/com/persistit/Exchange.java 2012-06-08 00:06:12 +0000
+++ src/main/java/com/persistit/Exchange.java 2012-06-25 15:43:24 +0000
@@ -23,15 +23,9 @@
23import static com.persistit.Buffer.EXACT_MASK;23import static com.persistit.Buffer.EXACT_MASK;
24import static com.persistit.Buffer.HEADER_SIZE;24import static com.persistit.Buffer.HEADER_SIZE;
25import static com.persistit.Buffer.KEYBLOCK_LENGTH;25import static com.persistit.Buffer.KEYBLOCK_LENGTH;
26import static com.persistit.Buffer.LONGREC_PREFIX_OFFSET;
27import static com.persistit.Buffer.LONGREC_PREFIX_SIZE;
28import static com.persistit.Buffer.LONGREC_SIZE;
29import static com.persistit.Buffer.LONGREC_TYPE;
30import static com.persistit.Buffer.MAX_LONG_RECORD_CHAIN;
31import static com.persistit.Buffer.MAX_VALID_PAGE_ADDR;26import static com.persistit.Buffer.MAX_VALID_PAGE_ADDR;
32import static com.persistit.Buffer.PAGE_TYPE_DATA;27import static com.persistit.Buffer.PAGE_TYPE_DATA;
33import static com.persistit.Buffer.PAGE_TYPE_INDEX_MIN;28import static com.persistit.Buffer.PAGE_TYPE_INDEX_MIN;
34import static com.persistit.Buffer.PAGE_TYPE_LONG_RECORD;
35import static com.persistit.Buffer.P_MASK;29import static com.persistit.Buffer.P_MASK;
36import static com.persistit.Buffer.TAILBLOCK_HDR_SIZE_INDEX;30import static com.persistit.Buffer.TAILBLOCK_HDR_SIZE_INDEX;
37import static com.persistit.Key.AFTER;31import static com.persistit.Key.AFTER;
3832
=== modified file 'src/main/java/com/persistit/FastIndex.java'
--- src/main/java/com/persistit/FastIndex.java 2012-05-25 18:50:59 +0000
+++ src/main/java/com/persistit/FastIndex.java 2012-06-25 15:43:24 +0000
@@ -24,7 +24,6 @@
2424
25import com.persistit.util.Debug;25import com.persistit.util.Debug;
2626
27
28/**27/**
29 * FastIndex is a wrapper for an array of integers that contain information used28 * FastIndex is a wrapper for an array of integers that contain information used
30 * in searching for a key in a page. There is one meaningful element in that29 * in searching for a key in a page. There is one meaningful element in that
@@ -154,11 +153,6 @@
154 */153 */
155 private final short[] _findexElements;154 private final short[] _findexElements;
156 /**155 /**
157 * AtomicInteger used to house the "touched" bit for the clock replacement
158 * algorithm in BufferPool.
159 */
160 private final AtomicInteger _bits = new AtomicInteger();
161 /**
162 * Indicates whether the _findexElements array is valid156 * Indicates whether the _findexElements array is valid
163 */157 */
164 private boolean _isValid;158 private boolean _isValid;
@@ -166,9 +160,10 @@
166 /**160 /**
167 * The buffer this fast index is associated with.161 * The buffer this fast index is associated with.
168 */162 */
169 private Buffer _buffer;163 private final Buffer _buffer;
170164
171 FastIndex(int elementSize) {165 FastIndex(Buffer buffer, int elementSize) {
166 _buffer = buffer;
172 _findexElements = new short[elementSize];167 _findexElements = new short[elementSize];
173 _isValid = false;168 _isValid = false;
174 }169 }
@@ -607,26 +602,6 @@
607 return start - end + 1;602 return start - end + 1;
608 }603 }
609604
610 void setBuffer(final Buffer buffer) {
611 _buffer = buffer;
612 }
613
614 Buffer getBuffer() {
615 return _buffer;
616 }
617
618 void setTouched() {
619 _bits.set(1);
620 }
621
622 void clearTouched() {
623 _bits.set(0);
624 }
625
626 boolean testTouched() {
627 return (_bits.get()) != 0;
628 }
629
630 // TODO - unnecessary after debugging605 // TODO - unnecessary after debugging
631 public String toString() {606 public String toString() {
632 StringBuilder sb = new StringBuilder();607 StringBuilder sb = new StringBuilder();
633608
=== modified file 'src/main/java/com/persistit/Volume.java'
--- src/main/java/com/persistit/Volume.java 2012-05-25 18:50:59 +0000
+++ src/main/java/com/persistit/Volume.java 2012-06-25 15:43:24 +0000
@@ -144,19 +144,36 @@
144 /**144 /**
145 * Release all resources for this <code>Volume</code> and invalidate all its145 * Release all resources for this <code>Volume</code> and invalidate all its
146 * buffers in the {@link BufferPool}. Exchanges based on this146 * buffers in the {@link BufferPool}. Exchanges based on this
147 * <code>Volume</code> may no longer be used after this call. Waits up to
148 * {@value com.persistit.SharedResource#DEFAULT_MAX_WAIT_TIME} milliseconds
149 * for other threads to relinquish access to the volume.
150 *
151 * @throws PersistitException
152 */
153 public void close() throws PersistitException {
154 close(SharedResource.DEFAULT_MAX_WAIT_TIME);
155 }
156
157 /**
158 * Release all resources for this <code>Volume</code> and invalidate all its
159 * buffers in the {@link BufferPool}. Exchanges based on this
147 * <code>Volume</code> may no longer be used after this call.160 * <code>Volume</code> may no longer be used after this call.
148 * 161 *
162 * @param timeout
163 * Maximum time in milliseconds to wait for other threads to
164 * relinquish access to the volume.
149 * @throws PersistitException165 * @throws PersistitException
150 */166 */
151 public void close() throws PersistitException {167 public void close(final long timeout) throws PersistitException {
152 closing();168 closing();
169 final long expiration = System.currentTimeMillis() + timeout;
153 for (;;) {170 for (;;) {
154 //171 //
155 // Prevents read/write operations from starting while the172 // Prevents read/write operations from starting while the
156 // volume is being closed.173 // volume is being closed.
157 //174 //
158 final VolumeStorage storage = getStorage();175 final VolumeStorage storage = getStorage();
159 if (!storage.claim(true)) {176 if (!storage.claim(true, timeout)) {
160 throw new InUseException("Unable to acquire claim on " + this);177 throw new InUseException("Unable to acquire claim on " + this);
161 }178 }
162 try {179 try {
@@ -174,6 +191,9 @@
174 } finally {191 } finally {
175 storage.release();192 storage.release();
176 }193 }
194 if (System.currentTimeMillis() >= expiration) {
195 throw new InUseException("Unable invalidate all pages on " + this);
196 }
177 Util.sleep(Persistit.SHORT_DELAY);197 Util.sleep(Persistit.SHORT_DELAY);
178 }198 }
179 }199 }
180200
=== modified file 'src/test/java/com/persistit/BufferTest2.java'
--- src/test/java/com/persistit/BufferTest2.java 2012-05-25 18:50:59 +0000
+++ src/test/java/com/persistit/BufferTest2.java 2012-06-25 15:43:24 +0000
@@ -29,6 +29,8 @@
29import static org.junit.Assert.assertTrue;29import static org.junit.Assert.assertTrue;
30import static org.junit.Assert.fail;30import static org.junit.Assert.fail;
3131
32import java.util.Arrays;
33
32import org.junit.Test;34import org.junit.Test;
3335
34import com.persistit.ValueHelper.RawValueWriter;36import com.persistit.ValueHelper.RawValueWriter;
@@ -331,6 +333,53 @@
331 b1.removeKeys(foundAt1 & ~EXACT_MASK, foundAt2, ex.getAuxiliaryKey1());333 b1.removeKeys(foundAt1 & ~EXACT_MASK, foundAt2, ex.getAuxiliaryKey1());
332 }334 }
333335
336 @Test
337 public void splitAndJoinBuffersWithZeroEbcKeys() throws Exception {
338 b1.init(Buffer.PAGE_TYPE_DATA);
339 b2.init(Buffer.PAGE_TYPE_DATA);
340 setUpValue(true, b1.getBufferSize() / 100);
341
342 int a;
343 for (a = 1;; a++) {
344 setUpShallowKey(a);
345 if (b1.putValue(ex.getKey(), vw) == -1) {
346 break;
347 }
348 }
349 setUpShallowKey(a / 2);
350 ex.getKey().getEncodedBytes()[6] = 0x7F;
351 ex.getKey().setEncodedSize(7);
352 int foundAt = b1.findKey(ex.getKey());
353 int splitAt = b1.split(b2, ex.getKey(), vw, foundAt, ex.getAuxiliaryKey1(), Exchange.Sequence.NONE,
354 SplitPolicy.EVEN_BIAS);
355
356 assertTrue("Split failed", splitAt != -1);
357 assertTrue("Verify failed", b1.verify(null, null) == null);
358 assertTrue("Verify failed", b2.verify(null, null) == null);
359
360 for (int b = 1; b < a; b++) {
361 setUpShallowKey(b);
362 if (b <= a / 2) {
363 b1.fetch(b1.findKey(ex.getKey()), ex.getValue());
364 } else {
365 b2.fetch(b2.findKey(ex.getKey()), ex.getValue());
366 }
367 assertTrue("Value should be defined", ex.getValue().isDefined());
368 }
369
370 setUpShallowKey(a / 2);
371 ex.getKey().getEncodedBytes()[6] = 0x7F;
372 ex.getKey().setEncodedSize(7);
373 int foundAt1 = b1.findKey(ex.getKey());
374 int foundAt2 = b2.findKey(ex.getKey());
375 assertTrue("Should have found exact match", (foundAt2 & EXACT_MASK) > 0);
376 foundAt2 = b2.nextKeyBlock(foundAt2);
377 boolean rebalanced = b1.join(b2, foundAt1, foundAt2, ex.getKey(), ex.getAuxiliaryKey1(), JoinPolicy.EVEN_BIAS);
378 assertTrue("Should have joined pages", !rebalanced);
379 assertTrue("Verify failed", b1.verify(null, null) == null);
380 assertTrue("Verify failed", b2.verify(null, null) == null);
381 }
382
334 private void setUpPrettyFullBufferWithChangingEbc(final int valueLength) throws PersistitException {383 private void setUpPrettyFullBufferWithChangingEbc(final int valueLength) throws PersistitException {
335 b1.init(Buffer.PAGE_TYPE_DATA);384 b1.init(Buffer.PAGE_TYPE_DATA);
336 setUpValue(false, valueLength);385 setUpValue(false, valueLength);
@@ -401,6 +450,26 @@
401 ex.getValue().setPointerValue(pointer);450 ex.getValue().setPointerValue(pointer);
402 }451 }
403452
453 private void setUpShallowKey(final int a) {
454 setUpShallowKey(ex, a, 0);
455 }
456
457 private void setUpShallowKey(Exchange ex, int value, long pointer) {
458 byte[] bytes = ex.getKey().clear().getEncodedBytes();
459 Arrays.fill(bytes, (byte) 0);
460 /*
461 * Convert int to little-endian form so that byte 0 varies fastest. 7
462 * bits per byte, no zeroes.
463 */
464 bytes[0] = (byte) (((value >>> 0) & 0x7F) + 1);
465 bytes[1] = (byte) (((value >>> 7) & 0x7F) + 1);
466 bytes[2] = (byte) (((value >>> 14) & 0x7F) + 1);
467 bytes[3] = (byte) (((value >>> 21) & 0x7F) + 1);
468 bytes[5] = (byte) (((value >>> 28) & 0x7F) + 1);
469 ex.getKey().setEncodedSize(6);
470 ex.getValue().setPointerValue(pointer);
471 }
472
404 String keyString(char fill, int length, int prefix, int width, int k) {473 String keyString(char fill, int length, int prefix, int width, int k) {
405 StringBuilder sb = new StringBuilder();474 StringBuilder sb = new StringBuilder();
406 for (int i = 0; i < prefix && i < length; i++) {475 for (int i = 0; i < prefix && i < length; i++) {
407476
=== modified file 'src/test/java/com/persistit/FastIndexTest.java'
--- src/test/java/com/persistit/FastIndexTest.java 2012-05-25 18:50:59 +0000
+++ src/test/java/com/persistit/FastIndexTest.java 2012-06-25 15:43:24 +0000
@@ -39,78 +39,88 @@
39 @Test39 @Test
40 public void testIndexSize() throws Exception {40 public void testIndexSize() throws Exception {
41 final Buffer b1 = getABuffer();41 final Buffer b1 = getABuffer();
42 FastIndex fi = b1.getFastIndex();42 try {
43 /*43 FastIndex fi = b1.getFastIndex();
44 * BUFFER_SIZE - HEADER_SIZE / MAX_KEY_RATIO BUFFER_SIZE = 1638444 /*
45 * HEADER_SIZE = 32 MAX_KEY_RATIO = 16 16384 - 32 / 16 = 16352 / 16 =45 * BUFFER_SIZE - HEADER_SIZE / MAX_KEY_RATIO BUFFER_SIZE = 16384
46 * 1022 size passed to FastIndex constructor is 1022 + 1 when parameters46 * HEADER_SIZE = 32 MAX_KEY_RATIO = 16 16384 - 32 / 16 = 16352 / 16
47 * are as listed above.47 * = 1022 size passed to FastIndex constructor is 1022 + 1 when
48 */48 * parameters are as listed above.
49 final int expectedSize = (16384 - Buffer.HEADER_SIZE) / Buffer.MAX_KEY_RATIO;49 */
50 assertEquals(expectedSize + 1, fi.size());50 final int expectedSize = (16384 - Buffer.HEADER_SIZE) / Buffer.MAX_KEY_RATIO;
51 b1.release();51 assertEquals(expectedSize + 1, fi.size());
52 } finally {
53 b1.release();
54 }
52 }55 }
5356
54 @Test57 @Test
55 public void testIndexValidity() throws Exception {58 public void testIndexValidity() throws Exception {
56 final Buffer b1 = getABuffer();59 final Buffer b1 = getABuffer();
57 b1.init(Buffer.PAGE_TYPE_GARBAGE);60 try {
58 FastIndex fi = b1.getFastIndex();61 b1.init(Buffer.PAGE_TYPE_GARBAGE);
59 fi.invalidate();62 FastIndex fi = b1.getFastIndex();
60 assertEquals(false, fi.isValid());63 fi.invalidate();
61 fi.recompute();64 assertEquals(false, fi.isValid());
62 assertEquals(false, fi.isValid());65 fi.recompute();
63 b1.init(Buffer.PAGE_TYPE_DATA);66 assertEquals(false, fi.isValid());
64 fi.recompute();67 b1.init(Buffer.PAGE_TYPE_DATA);
65 assertEquals(true, fi.isValid());68 fi.recompute();
66 assertEquals(true, fi.verify());69 assertEquals(true, fi.isValid());
67 fi.invalidate();70 assertEquals(true, fi.verify());
68 assertEquals(false, fi.isValid());71 fi.invalidate();
69 assertEquals(false, fi.verify());72 assertEquals(false, fi.isValid());
70 b1.release();73 assertEquals(false, fi.verify());
74 } finally {
75 b1.release();
76 }
71 }77 }
7278
73 @Test79 @Test
74 public void testFastIndexRecompute() throws Exception {80 public void testFastIndexRecompute() throws Exception {
75 Buffer b1 = getABuffer();81 Buffer b1 = getABuffer();
76 b1.init(Buffer.PAGE_TYPE_DATA);82 try {
77 Key key = new Key(_persistit);83 b1.init(Buffer.PAGE_TYPE_DATA);
78 Value value = new Value(_persistit);84 Key key = new Key(_persistit);
79 RawValueWriter vwriter = new RawValueWriter();85 Value value = new Value(_persistit);
80 vwriter.init(value);86 RawValueWriter vwriter = new RawValueWriter();
81 fakeKey(key, "A");87 FastIndex fi = b1.getFastIndex();
82 b1.putValue(key, vwriter);88 fi.recompute();
83 fakeKey(key, "ABC");89 vwriter.init(value);
84 b1.putValue(key, vwriter);90 fakeKey(key, "A");
85 fakeKey(key, "ABK");91 b1.putValue(key, vwriter);
86 b1.putValue(key, vwriter);92 fakeKey(key, "ABC");
87 fakeKey(key, "ABZ");93 b1.putValue(key, vwriter);
88 b1.putValue(key, vwriter);94 fakeKey(key, "ABK");
89 fakeKey(key, "AC");95 b1.putValue(key, vwriter);
90 b1.putValue(key, vwriter);96 fakeKey(key, "ABZ");
91 fakeKey(key, "C");97 b1.putValue(key, vwriter);
92 b1.putValue(key, vwriter);98 fakeKey(key, "AC");
93 fakeKey(key, "B");99 b1.putValue(key, vwriter);
94 b1.putValue(key, vwriter);100 fakeKey(key, "C");
95 fakeKey(key, "E");101 b1.putValue(key, vwriter);
96 b1.putValue(key, vwriter);102 fakeKey(key, "B");
97 fakeKey(key, "D");103 b1.putValue(key, vwriter);
98 b1.putValue(key, vwriter);104 fakeKey(key, "E");
99 fakeKey(key, "DA");105 b1.putValue(key, vwriter);
100 b1.putValue(key, vwriter);106 fakeKey(key, "D");
101 fakeKey(key, "ABB");107 b1.putValue(key, vwriter);
102 b1.putValue(key, vwriter);108 fakeKey(key, "DA");
103 fakeKey(key, "ABA");109 b1.putValue(key, vwriter);
104 b1.putValue(key, vwriter);110 fakeKey(key, "ABB");
105 fakeKey(key, "ABJ");111 b1.putValue(key, vwriter);
106 b1.putValue(key, vwriter);112 fakeKey(key, "ABA");
113 b1.putValue(key, vwriter);
114 fakeKey(key, "ABJ");
115 b1.putValue(key, vwriter);
107116
108 FastIndex fi = b1.getFastIndex();117 String inserteds = fi.toString();
109 String inserteds = fi.toString();118 fi.recompute();
110 fi.recompute();119 String computeds = fi.toString();
111 String computeds = fi.toString();120 assertEquals(inserteds, computeds);
112 assertEquals(inserteds, computeds);121 } finally {
113 b1.release();122 b1.release();
123 }
114 }124 }
115125
116 private void fakeKey(final Key key, final String v) {126 private void fakeKey(final Key key, final String v) {
@@ -123,31 +133,30 @@
123 public void testRandomInsert() throws Exception {133 public void testRandomInsert() throws Exception {
124 Random random = new Random(3);134 Random random = new Random(3);
125 Buffer b1 = getABuffer();135 Buffer b1 = getABuffer();
126 FastIndex f1 = b1.getFastIndex();136 try {
127137 FastIndex f1 = b1.getFastIndex();
128 b1.init(Buffer.PAGE_TYPE_DATA);138
129 Key key = new Key(_persistit);139 b1.init(Buffer.PAGE_TYPE_DATA);
130 Value value = new Value(_persistit);140 Key key = new Key(_persistit);
131 for (int i = 0; i < 1000; i++) {141 Value value = new Value(_persistit);
132 int size = random.nextInt(10) + 2;142 for (int i = 0; i < 1000; i++) {
133 byte[] bytes = new byte[size];143 int size = random.nextInt(10) + 2;
134 random.nextBytes(bytes);144 byte[] bytes = new byte[size];
135 System.arraycopy(bytes, 0, key.getEncodedBytes(), 0, size);145 random.nextBytes(bytes);
136 key.setEncodedSize(size);146 System.arraycopy(bytes, 0, key.getEncodedBytes(), 0, size);
137 RawValueWriter vwriter = new RawValueWriter();147 key.setEncodedSize(size);
138 vwriter.init(value);148 RawValueWriter vwriter = new RawValueWriter();
139 b1.putValue(key, vwriter);149 vwriter.init(value);
140150 b1.putValue(key, vwriter);
141 String s1 = f1.toString();151
142 f1.recompute();152 String s1 = f1.toString();
143 String s2 = f1.toString();153 f1.recompute();
144 assertEquals(s1, s2);154 String s2 = f1.toString();
155 assertEquals(s1, s2);
156 }
157 } finally {
158 b1.release();
145 }159 }
146 b1.release();
147 }
148
149 @Override
150 public void runAllTests() {
151 }160 }
152161
153}162}
154163
=== modified file 'src/test/java/com/persistit/VolumeTest.java'
--- src/test/java/com/persistit/VolumeTest.java 2012-05-25 18:50:59 +0000
+++ src/test/java/com/persistit/VolumeTest.java 2012-06-25 15:43:24 +0000
@@ -35,6 +35,7 @@
35import org.junit.Test;35import org.junit.Test;
3636
37import com.persistit.exception.CorruptVolumeException;37import com.persistit.exception.CorruptVolumeException;
38import com.persistit.exception.InUseException;
38import com.persistit.exception.InvalidVolumeSpecificationException;39import com.persistit.exception.InvalidVolumeSpecificationException;
39import com.persistit.exception.VolumeFullException;40import com.persistit.exception.VolumeFullException;
40import com.persistit.unit.PersistitUnitTestCase;41import com.persistit.unit.PersistitUnitTestCase;
@@ -184,12 +185,12 @@
184 final Volume vol2 = _persistit.loadVolume(vs);185 final Volume vol2 = _persistit.loadVolume(vs);
185 final long statTimestamp = vol2.getStatistics().getLastGlobalTimestamp();186 final long statTimestamp = vol2.getStatistics().getLastGlobalTimestamp();
186 // Greater than is ok (other activity may have occurred)187 // Greater than is ok (other activity may have occurred)
187 if(statTimestamp < MARKER) {188 if (statTimestamp < MARKER) {
188 assertEquals("Saved and loaded timestamp", MARKER, statTimestamp);189 assertEquals("Saved and loaded timestamp", MARKER, statTimestamp);
189 }190 }
190 }191 }
191192
192 @Test(expected=CorruptVolumeException.class)193 @Test(expected = CorruptVolumeException.class)
193 public void volumeFromFutureIsRejected() throws Exception {194 public void volumeFromFutureIsRejected() throws Exception {
194 final int RECORDS = 100;195 final int RECORDS = 100;
195196
@@ -227,6 +228,25 @@
227 _persistit.initialize(properties);228 _persistit.initialize(properties);
228 }229 }
229230
231 @Test
232 public void timeoutWhenPageIsInUse() throws Exception {
233 final Exchange exchange = _persistit.getExchange(UnitTestProperties.VOLUME_NAME, "VolumeTest", true);
234 final Buffer buffer = exchange.getBufferPool().get(exchange.getVolume(), 1, false, true);
235 try {
236 final long start = System.currentTimeMillis();
237 try {
238 exchange.getVolume().close(5000);
239 fail("Expect an InUseException");
240 } catch (InUseException e) {
241 final long elapsed = System.currentTimeMillis() - start;
242 assertTrue("Expected InUseException to happen at 5000 ms but was " + elapsed, elapsed > 4000
243 && elapsed < 10000);
244 }
245 } finally {
246 buffer.release();
247 }
248 }
249
230 private VolumeSpecification validVolumeSpecification(final String specification) throws Exception {250 private VolumeSpecification validVolumeSpecification(final String specification) throws Exception {
231 try {251 try {
232 return _persistit.getConfiguration().volumeSpecification(specification);252 return _persistit.getConfiguration().volumeSpecification(specification);
@@ -245,10 +265,4 @@
245 }265 }
246 }266 }
247267
248 @Override
249 public void runAllTests() throws Exception {
250 // TODO Auto-generated method stub
251
252 }
253
254}268}

Subscribers

People subscribed via source and target branches