Merge lp:~pbeaman/akiban-persistit/remove_fast_index_ratio into lp:akiban-persistit
- remove_fast_index_ratio
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Akiban Build User | Needs Fixing | ||
Nathan Williams | Approve | ||
Review via email: mp+110391@code.launchpad.net |
Commit message
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.
Akiban Build User (build-akiban) wrote : | # |
There were 2 failures during build/test:
* job persistit-build failed at build number 360: http://
* view must-pass failed: persistit-build is yellow
Peter Beaman (pbeaman) wrote : | # |
New constructor for FastIndex was off by one.
Nathan Williams (nwilliams) wrote : | # |
Was hung in teardown, again:
http://
Peter Beaman (pbeaman) wrote : | # |
Yup, there was an actual bug in the modified version of Buffer. As a consequence, FastIndexTest#
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.
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.
Akiban Build User (build-akiban) wrote : | # |
There were 3 failures during build/test:
* job persistit-build failed at build number 365: http://
* view must-pass failed: server-build is blue_anime
* view must-pass failed: persistit-build is yellow
Nathan Williams (nwilliams) wrote : | # |
Real failure, but in an unrelated area (TransactionTes
Nathan Williams (nwilliams) wrote : | # |
Well, this has failed twice now in the server histogramsIT test with output like:
com.akiban.
http://
http://
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.
> Persistit Data Layer error: Volume
> akiban_
> level=0 page=16 key=<{"
>
> http://
> http://
>
>
> --
>
> https:/
> You are the owner of lp:~pbeaman/akiban-persistit/remove_fast_index_ratio.
>
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#
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.
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?
Peter Beaman (pbeaman) wrote : | # |
Added unit test BufferTest2#
Nathan Williams (nwilliams) wrote : | # |
Thanks for the new test.
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.
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
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 | } |
Makes sense.