Merge lp:~pbeaman/akiban-persistit/fix-1126868-lock-table-pruning into lp:akiban-persistit
- fix-1126868-lock-table-pruning
- Merge into trunk
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Peter Beaman | ||||||||
Approved revision: | 435 | ||||||||
Merged at revision: | 424 | ||||||||
Proposed branch: | lp:~pbeaman/akiban-persistit/fix-1126868-lock-table-pruning | ||||||||
Merge into: | lp:akiban-persistit | ||||||||
Diff against target: |
1090 lines (+312/-112) 24 files modified
src/main/java/com/persistit/Buffer.java (+19/-12) src/main/java/com/persistit/BufferPool.java (+2/-2) src/main/java/com/persistit/CleanupManager.java (+14/-7) src/main/java/com/persistit/Exchange.java (+14/-8) src/main/java/com/persistit/IntegrityCheck.java (+1/-1) src/main/java/com/persistit/Management.java (+8/-3) src/main/java/com/persistit/ManagementImpl.java (+2/-0) src/main/java/com/persistit/Persistit.java (+21/-11) src/main/java/com/persistit/Transaction.java (+39/-0) src/main/java/com/persistit/TransactionIndex.java (+12/-0) src/main/java/com/persistit/Tree.java (+2/-2) src/main/java/com/persistit/Volume.java (+20/-9) src/main/java/com/persistit/VolumeStorageL2.java (+89/-0) src/main/java/com/persistit/VolumeStorageT2.java (+15/-14) src/main/java/com/persistit/VolumeStructure.java (+6/-4) src/main/java/com/persistit/ui/AdminUISummaryPanel.java (+2/-2) src/main/java/com/persistit/ui/AdminUITreePanel.java (+1/-1) src/main/java/com/persistit/ui/VTComboBoxModel.java (+1/-1) src/main/resources/com/persistit/ui/AdminUI.properties (+2/-2) src/test/java/com/persistit/Bug1041293Test.java (+3/-2) src/test/java/com/persistit/CleanupManagerTest.java (+3/-1) src/test/java/com/persistit/ExchangeLockTest.java (+29/-24) src/test/java/com/persistit/MVCCPruneBufferTest.java (+5/-5) src/test/java/com/persistit/TestShim.java (+2/-1) |
||||||||
To merge this branch: | bzr merge lp:~pbeaman/akiban-persistit/fix-1126868-lock-table-pruning | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Akiban Build User | Needs Fixing | ||
Nathan Williams | Approve | ||
Review via email: mp+152063@code.launchpad.net |
Commit message
Description of the change
Fixes bug1126868. The bug is that in a typical data loading scenario, or in any process where update transactions are performing a large volume of locks, the lock volume can be overwhelmed with new entries in such a way that obsolete entries are never pruned. When that happens the lock volume grows rapidly, and in one server test exceeds the 2GB sanity limit for temporary volumes.
A new test in ExchangeLockTest demonstrates the phenomenon by simply creating a large number of locks (10,000) in each of 500 transactions. Prior to the fix this test causes runaway growth of the lock volume.
This proposal fixes this phenomenon by adding a new pruning mode in which all locks are synchronously pruned upon when a transaction that has used any locks either commits or aborts.
The majority of the changes provide plumbing so that a call to Buffer#
This branch also fixes a couple of related issues discovered while diagnosing and fixing the problem.
(1) Temporary volumes are not designed to be threadsafe. So there is now a new VolumeStorageL2 class that combines behavior of VolumeStorageT2 and VolumeStorageV2. In particular, VolumeStorageL2 has a head page that is used for synchronization. Also, since there is at most a single lock volume in the system, it now has a distinguished name, and it appears on the visible volumes list in the UI.
(2) The UI was using the volume's path as its identifier. This has been wrong, but unnoticed, for some time. The UI now uses the name as the unique identifier, and it now possible to examine trees in the lock volume using the UI. One minor change in the AdminUI.properties file swaps the name and path columns of the UI's main page.
Peter Beaman (pbeaman) wrote : | # |
Yes, removing the call to updateActiveTra
Good point about L2. It is now a subclass of T2 with almost no code and corrected Javadoc.
Nathan Williams (nwilliams) wrote : | # |
Making transactions that perform writes slower by doing the cache update synchronously seems like a bad conscious choice to make. Bucket locking, which recomputing the cache does, is exclusive right now. I have a feeling this will really hurt concurrency.
Do we have any perf tests for this or am I being too pessimistic?
Peter Beaman (pbeaman) wrote : | # |
I don't like it either, but I couldn't find another way to solve the
problem. I guess the question we need to ask is how many transactions will
actually lock something. Certainly no read transactions. Can you suggest
a good multi-thread concurrent server benchmark?
On Mon, Mar 11, 2013 at 4:08 PM, Nathan Williams <email address hidden>wrote:
> Making transactions that perform writes slower by doing the cache update
> synchronously seems like a bad conscious choice to make. Bucket locking,
> which recomputing the cache does, is exclusive right now. I have a feeling
> this will really hurt concurrency.
>
> Do we have any perf tests for this or am I being too pessimistic?
> --
>
> https:/
> You are the owner of
> lp:~pbeaman/akiban-persistit/fix-1126868-lock-table-pruning.
>
Thomas Jones-Low (tjoneslo) wrote : | # |
The Multi-Thread Test (MTT) in the akiban-
Nathan Williams (nwilliams) wrote : | # |
Peter and I discussed this offline. Merging this now makes sense and we can double check the data-loading-test works as desired.
We can confirm concurrency impact in parallel/after and if there is a problem, there is a bucket level change that probably makes sense.
Akiban Build User (build-akiban) wrote : | # |
There were 2 failures during build/test:
* job server-build failed at build number 3869: http://
* view must-pass failed: server-build is yellow
Peter Beaman (pbeaman) wrote : | # |
Approving this again. The failure is strange, and the line numbers displayed in the stack traces don't match the code. I will watch this build.
Akiban Build User (build-akiban) wrote : | # |
There were 2 failures during build/test:
* job server-build failed at build number 3879: http://
* view must-pass failed: server-build is yellow
Peter Beaman (pbeaman) wrote : | # |
Server tests run fine on local machine, but fail on build machine. Adding better logging and Approving in attempt to diagnose the problem.
Akiban Build User (build-akiban) wrote : | # |
There were 2 failures during build/test:
* job server-build failed at build number 3881: http://
* view must-pass failed: server-build is yellow
- 434. By Peter Beaman
-
Correct BufferSizeUnava
ilableException cases - 435. By Peter Beaman
-
Get buffer pool from volume structure instead of lookup by page size
Peter Beaman (pbeaman) wrote : | # |
Trying yet again with some additional changes.
Preview Diff
1 | === modified file 'src/main/java/com/persistit/Buffer.java' |
2 | --- src/main/java/com/persistit/Buffer.java 2013-01-19 17:52:20 +0000 |
3 | +++ src/main/java/com/persistit/Buffer.java 2013-03-13 17:42:20 +0000 |
4 | @@ -26,6 +26,7 @@ |
5 | import java.util.List; |
6 | import java.util.Set; |
7 | |
8 | +import com.persistit.CleanupManager.CleanupAction; |
9 | import com.persistit.CleanupManager.CleanupAntiValue; |
10 | import com.persistit.Exchange.Sequence; |
11 | import com.persistit.JournalRecord.IV; |
12 | @@ -522,7 +523,7 @@ |
13 | final Volume volume = getVolume(); |
14 | if (volume != null) { |
15 | if (prune) { |
16 | - pruneMvvValues(null, false); |
17 | + pruneMvvValues(null, false, null); |
18 | } |
19 | clearSlack(); |
20 | save(); |
21 | @@ -3599,7 +3600,8 @@ |
22 | * buffer |
23 | * @throws PersistitException |
24 | */ |
25 | - boolean pruneMvvValues(final Tree tree, final boolean pruneLongMVVs) throws PersistitException { |
26 | + boolean pruneMvvValues(final Tree tree, final boolean pruneLongMVVs, final List<CleanupAction> cleanupActions) |
27 | + throws PersistitException { |
28 | boolean changed = false; |
29 | try { |
30 | boolean hasLongMvvRecords = false; |
31 | @@ -3611,7 +3613,7 @@ |
32 | final long timestamp = _persistit.getTimestampAllocator().updateTimestamp(); |
33 | _mvvCount = 0; |
34 | writePageOnCheckpoint(timestamp); |
35 | - final int flags = pruneMvvValuesHelper(tree); |
36 | + final int flags = pruneMvvValuesHelper(tree, cleanupActions); |
37 | changed = (flags & PRUNE_MVV_HELPER_CHANGED) != 0; |
38 | hasLongMvvRecords = (flags & PRUNE_MVV_HELPER_HAS_LONG) != 0; |
39 | |
40 | @@ -3627,7 +3629,7 @@ |
41 | |
42 | final Buffer copy = new Buffer(this); |
43 | final boolean copyChanged = copy.pruneLongMvvValues(tree, prunedVersions, deferredExceptions, |
44 | - oldChainsToDeallocate); |
45 | + oldChainsToDeallocate, cleanupActions); |
46 | if (copyChanged) { |
47 | changed = true; |
48 | final long copyTimestamp = _persistit.getTimestampAllocator().updateTimestamp(); |
49 | @@ -3662,7 +3664,8 @@ |
50 | return changed; |
51 | } |
52 | |
53 | - private int pruneMvvValuesHelper(final Tree tree) throws PersistitException { |
54 | + private int pruneMvvValuesHelper(final Tree tree, final List<CleanupAction> cleanupActions) |
55 | + throws PersistitException { |
56 | boolean changed = false; |
57 | boolean hasLongMvvRecords = false; |
58 | final List<PrunedVersion> prunedVersions = new ArrayList<PrunedVersion>(); |
59 | @@ -3708,7 +3711,7 @@ |
60 | incCountIfMvv(_bytes, offset, newSize); |
61 | } |
62 | |
63 | - if (pruneAntiValue(valueByte, p, tree)) { |
64 | + if (pruneAntiValue(valueByte, p, tree, cleanupActions)) { |
65 | changed = true; |
66 | p -= KEYBLOCK_LENGTH; |
67 | } |
68 | @@ -3726,7 +3729,8 @@ |
69 | * successful completion of this process. |
70 | */ |
71 | private boolean pruneLongMvvValues(final Tree tree, final List<PrunedVersion> prunedVersions, |
72 | - final List<PersistitException> deferredExceptions, final List<Long> toDeallocate) { |
73 | + final List<PersistitException> deferredExceptions, final List<Long> toDeallocate, |
74 | + final List<CleanupAction> cleanupActions) { |
75 | |
76 | boolean changed = false; |
77 | for (int p = KEY_BLOCK_START; p < _keyBlockEnd; p += KEYBLOCK_LENGTH) { |
78 | @@ -3768,7 +3772,7 @@ |
79 | value.changeLongRecordMode(false); |
80 | } |
81 | } |
82 | - if (pruneAntiValue(valueByte, p, tree)) { |
83 | + if (pruneAntiValue(valueByte, p, tree, cleanupActions)) { |
84 | changed = true; |
85 | p -= KEYBLOCK_LENGTH; |
86 | } |
87 | @@ -3781,13 +3785,16 @@ |
88 | return changed; |
89 | } |
90 | |
91 | - private boolean pruneAntiValue(final int valueByte, final int p, final Tree tree) { |
92 | + private boolean pruneAntiValue(final int valueByte, final int p, final Tree tree, |
93 | + final List<CleanupAction> cleanupActions) { |
94 | if (valueByte == MVV.TYPE_ANTIVALUE) { |
95 | if (p == KEY_BLOCK_START) { |
96 | if (tree != null) { |
97 | - if (!_enqueuedForAntiValuePruning) { |
98 | - final int treeHandle = tree.getHandle(); |
99 | - assert treeHandle != 0 : "MVV found in a temporary tree " + tree; |
100 | + final int treeHandle = tree.getHandle(); |
101 | + assert treeHandle != 0 : "MVV found in a temporary tree " + tree; |
102 | + if (cleanupActions != null) { |
103 | + cleanupActions.add(new CleanupAntiValue(treeHandle, getPageAddress())); |
104 | + } else if (!_enqueuedForAntiValuePruning) { |
105 | if (_persistit.getCleanupManager().offer(new CleanupAntiValue(treeHandle, getPageAddress()))) { |
106 | _enqueuedForAntiValuePruning = true; |
107 | } |
108 | |
109 | === modified file 'src/main/java/com/persistit/BufferPool.java' |
110 | --- src/main/java/com/persistit/BufferPool.java 2013-01-28 10:42:29 +0000 |
111 | +++ src/main/java/com/persistit/BufferPool.java 2013-03-13 17:42:20 +0000 |
112 | @@ -789,7 +789,7 @@ |
113 | // page will find it. |
114 | // |
115 | buffer.setValid(); |
116 | - if (vol.isTemporary()) { |
117 | + if (vol.isTemporary() || vol.isLockVolume()) { |
118 | buffer.setTemporary(); |
119 | } else { |
120 | buffer.clearTemporary(); |
121 | @@ -1468,7 +1468,7 @@ |
122 | } |
123 | Util.spinSleep(); |
124 | } |
125 | - if (volume1 != null && !volume1.isTemporary()) { |
126 | + if (volume1 != null && !volume1.isTemporary() && !volume1.isLockVolume()) { |
127 | value.clear().setStreamMode(true); |
128 | value.put(volume1.getHandle()); |
129 | value.put(page1); |
130 | |
131 | === modified file 'src/main/java/com/persistit/CleanupManager.java' |
132 | --- src/main/java/com/persistit/CleanupManager.java 2012-11-26 17:19:45 +0000 |
133 | +++ src/main/java/com/persistit/CleanupManager.java 2013-03-13 17:42:20 +0000 |
134 | @@ -33,7 +33,7 @@ |
135 | |
136 | interface CleanupAction extends Comparable<CleanupAction> { |
137 | |
138 | - void performAction(Persistit persistit) throws PersistitException; |
139 | + void performAction(Persistit persistit, List<CleanupAction> consequentActions) throws PersistitException; |
140 | } |
141 | |
142 | final static long DEFAULT_CLEANUP_INTERVAL_MS = 1000; |
143 | @@ -177,7 +177,7 @@ |
144 | |
145 | for (final CleanupAction action : workList) { |
146 | try { |
147 | - action.performAction(_persistit); |
148 | + action.performAction(_persistit, null); |
149 | _performed.incrementAndGet(); |
150 | } catch (final PersistitException e) { |
151 | lastException(e); |
152 | @@ -227,7 +227,11 @@ |
153 | } else { |
154 | return false; |
155 | } |
156 | + } |
157 | |
158 | + @Override |
159 | + public int hashCode() { |
160 | + return (int) (_treeHandle ^ _page); |
161 | } |
162 | |
163 | @Override |
164 | @@ -275,10 +279,11 @@ |
165 | } |
166 | |
167 | @Override |
168 | - public void performAction(final Persistit persistit) throws PersistitException { |
169 | + public void performAction(final Persistit persistit, final List<CleanupAction> consequentActions) |
170 | + throws PersistitException { |
171 | final Exchange exchange = getExchange(persistit); |
172 | if (exchange != null) { |
173 | - exchange.pruneLeftEdgeValue(_page); |
174 | + exchange.pruneLeftEdgeValue(_page, consequentActions); |
175 | } |
176 | } |
177 | } |
178 | @@ -290,10 +295,11 @@ |
179 | } |
180 | |
181 | @Override |
182 | - public void performAction(final Persistit persistit) throws PersistitException { |
183 | + public void performAction(final Persistit persistit, final List<CleanupAction> consequentActions) |
184 | + throws PersistitException { |
185 | final Exchange exchange = getExchange(persistit); |
186 | if (exchange != null) { |
187 | - exchange.prune(_page); |
188 | + exchange.prune(_page, consequentActions); |
189 | } |
190 | } |
191 | } |
192 | @@ -307,7 +313,8 @@ |
193 | } |
194 | |
195 | @Override |
196 | - public void performAction(final Persistit persistit) throws PersistitException { |
197 | + public void performAction(final Persistit persistit, final List<CleanupAction> consequentActions) |
198 | + throws PersistitException { |
199 | final Exchange exchange = getExchange(persistit); |
200 | if (exchange != null) { |
201 | exchange.fixIndexHole(_page, _level); |
202 | |
203 | === modified file 'src/main/java/com/persistit/Exchange.java' |
204 | --- src/main/java/com/persistit/Exchange.java 2013-01-24 18:18:40 +0000 |
205 | +++ src/main/java/com/persistit/Exchange.java 2013-03-13 17:42:20 +0000 |
206 | @@ -40,11 +40,13 @@ |
207 | import java.util.ArrayList; |
208 | import java.util.List; |
209 | |
210 | +import com.persistit.CleanupManager.CleanupAction; |
211 | import com.persistit.Key.Direction; |
212 | import com.persistit.MVV.PrunedVersion; |
213 | import com.persistit.ValueHelper.MVVValueWriter; |
214 | import com.persistit.ValueHelper.RawValueWriter; |
215 | import com.persistit.VolumeStructure.Chain; |
216 | +import com.persistit.exception.BufferSizeUnavailableException; |
217 | import com.persistit.exception.CorruptVolumeException; |
218 | import com.persistit.exception.InUseException; |
219 | import com.persistit.exception.PersistitException; |
220 | @@ -400,6 +402,7 @@ |
221 | * |
222 | * @param tree |
223 | * The <code>Tree</code> to access. |
224 | + * @throws BufferSizeUnavailableException |
225 | */ |
226 | public Exchange(final Tree tree) { |
227 | this(tree._persistit); |
228 | @@ -427,7 +430,8 @@ |
229 | final Volume volume = tree.getVolume(); |
230 | _ignoreTransactions = volume.isTemporary(); |
231 | _ignoreMVCCFetch = false; |
232 | - _pool = _persistit.getBufferPool(volume.getPageSize()); |
233 | + _pool = volume.getStructure().getPool(); |
234 | + |
235 | _transaction = _persistit.getTransaction(); |
236 | _key.clear(); |
237 | _value.clear(); |
238 | @@ -1640,7 +1644,7 @@ |
239 | if (splitRequired && !treeClaimAcquired) { |
240 | if (!didPrune && buffer.isDataPage()) { |
241 | didPrune = true; |
242 | - if (buffer.pruneMvvValues(_tree, false)) { |
243 | + if (buffer.pruneMvvValues(_tree, false, null)) { |
244 | continue; |
245 | } |
246 | } |
247 | @@ -1856,7 +1860,6 @@ |
248 | Debug.$assert0.t((buffer.getStatus() & SharedResource.WRITER_MASK) != 0 |
249 | && (buffer.getStatus() & SharedResource.CLAIMED_MASK) != 0); |
250 | final Sequence sequence = lc.sequence(foundAt); |
251 | - |
252 | long timestamp = timestamp(); |
253 | buffer.writePageOnCheckpoint(timestamp); |
254 | |
255 | @@ -2853,6 +2856,9 @@ |
256 | lockExchange.getValue().clear().putAntiValueMVV(); |
257 | final int options = StoreOptions.WAIT | StoreOptions.DONT_JOURNAL | StoreOptions.MVCC; |
258 | lockExchange.storeInternal(lockExchange.getKey(), lockExchange.getValue(), 0, options); |
259 | + final long page = lockExchange._levelCache[0]._page; |
260 | + _transaction.addLockPage(page, lockExchange.getTree().getHandle()); |
261 | + _persistit.releaseExchange(lockExchange); |
262 | } |
263 | |
264 | /** |
265 | @@ -3993,7 +3999,7 @@ |
266 | search(key, true); |
267 | buffer = _levelCache[0]._buffer; |
268 | if (buffer != null) { |
269 | - return buffer.pruneMvvValues(_tree, true); |
270 | + return buffer.pruneMvvValues(_tree, true, null); |
271 | } else { |
272 | return false; |
273 | } |
274 | @@ -4015,7 +4021,7 @@ |
275 | |
276 | while (buffer != null) { |
277 | checkPageType(buffer, Buffer.PAGE_TYPE_DATA, false); |
278 | - pruned |= buffer.pruneMvvValues(_tree, true); |
279 | + pruned |= buffer.pruneMvvValues(_tree, true, null); |
280 | final int foundAt = buffer.findKey(key2); |
281 | if (!buffer.isAfterRightEdge(foundAt)) { |
282 | break; |
283 | @@ -4037,11 +4043,11 @@ |
284 | return pruned; |
285 | } |
286 | |
287 | - boolean prune(final long page) throws PersistitException { |
288 | + boolean prune(final long page, final List<CleanupAction> consequentActions) throws PersistitException { |
289 | Buffer buffer = null; |
290 | try { |
291 | buffer = _pool.get(_volume, page, true, true); |
292 | - return buffer.pruneMvvValues(_tree, true); |
293 | + return buffer.pruneMvvValues(_tree, true, consequentActions); |
294 | } finally { |
295 | if (buffer != null) { |
296 | buffer.release(); |
297 | @@ -4049,7 +4055,7 @@ |
298 | } |
299 | } |
300 | |
301 | - boolean pruneLeftEdgeValue(final long page) throws PersistitException { |
302 | + boolean pruneLeftEdgeValue(final long page, final List<CleanupAction> consequentActions) throws PersistitException { |
303 | _ignoreTransactions = true; |
304 | Buffer buffer = null; |
305 | try { |
306 | |
307 | === modified file 'src/main/java/com/persistit/IntegrityCheck.java' |
308 | --- src/main/java/com/persistit/IntegrityCheck.java 2012-09-12 22:02:22 +0000 |
309 | +++ src/main/java/com/persistit/IntegrityCheck.java 2013-03-13 17:42:20 +0000 |
310 | @@ -1170,7 +1170,7 @@ |
311 | _counters._mvvPageCount++; |
312 | if (_prune && !_currentVolume.isReadOnly() && _counters._pruningErrorCount < MAX_PRUNING_ERRORS) { |
313 | try { |
314 | - buffer.pruneMvvValues(tree, true); |
315 | + buffer.pruneMvvValues(tree, true, null); |
316 | _counters._prunedPageCount++; |
317 | } catch (final PersistitException e) { |
318 | _counters._pruningErrorCount++; |
319 | |
320 | === modified file 'src/main/java/com/persistit/Management.java' |
321 | --- src/main/java/com/persistit/Management.java 2012-08-31 14:06:47 +0000 |
322 | +++ src/main/java/com/persistit/Management.java 2013-03-13 17:42:20 +0000 |
323 | @@ -464,7 +464,7 @@ |
324 | * occurred while attempting to acquire the Class. |
325 | * @throws RemoteException |
326 | */ |
327 | - public Class getRemoteClass(String className) throws RemoteException; |
328 | + public Class<?> getRemoteClass(String className) throws RemoteException; |
329 | |
330 | /** |
331 | * Return the <code>VolumeInfo</code> for the volume specified by the |
332 | @@ -1450,7 +1450,7 @@ |
333 | * returns information about all open Volumes in an array of |
334 | * <code>VolumeInfo</code> elements. |
335 | */ |
336 | - public static class VolumeInfo extends AcquisitionTimeBase implements Serializable { |
337 | + public static class VolumeInfo extends AcquisitionTimeBase implements Serializable, Comparable<VolumeInfo> { |
338 | public final static long serialVersionUID = -1231320633942497896L; |
339 | |
340 | int pageSize; |
341 | @@ -1785,7 +1785,12 @@ |
342 | @Override |
343 | public boolean equals(final Object object) { |
344 | return object instanceof VolumeInfo && ((VolumeInfo) object).getId() == id |
345 | - && ((VolumeInfo) object).getPath().equals(path); |
346 | + && ((VolumeInfo) object).getName().equals(name); |
347 | + } |
348 | + |
349 | + @Override |
350 | + public int compareTo(final VolumeInfo volumeInfo) { |
351 | + return name.compareTo(volumeInfo.name); |
352 | } |
353 | } |
354 | |
355 | |
356 | === modified file 'src/main/java/com/persistit/ManagementImpl.java' |
357 | --- src/main/java/com/persistit/ManagementImpl.java 2012-08-24 13:57:19 +0000 |
358 | +++ src/main/java/com/persistit/ManagementImpl.java 2013-03-13 17:42:20 +0000 |
359 | @@ -22,6 +22,7 @@ |
360 | import java.rmi.registry.LocateRegistry; |
361 | import java.rmi.server.UnicastRemoteObject; |
362 | import java.util.ArrayList; |
363 | +import java.util.Arrays; |
364 | import java.util.HashMap; |
365 | import java.util.Iterator; |
366 | import java.util.List; |
367 | @@ -798,6 +799,7 @@ |
368 | for (int index = 0; index < volumes.size(); index++) { |
369 | result[index] = new VolumeInfo(volumes.get(index)); |
370 | } |
371 | + Arrays.sort(result); |
372 | return result; |
373 | } |
374 | |
375 | |
376 | === modified file 'src/main/java/com/persistit/Persistit.java' |
377 | --- src/main/java/com/persistit/Persistit.java 2013-02-26 17:23:35 +0000 |
378 | +++ src/main/java/com/persistit/Persistit.java 2013-03-13 17:42:20 +0000 |
379 | @@ -1079,15 +1079,7 @@ |
380 | * @throws PersistitException |
381 | */ |
382 | public Volume createTemporaryVolume() throws PersistitException { |
383 | - int pageSize = _configuration.getTmpVolPageSize(); |
384 | - if (pageSize == 0) { |
385 | - for (final int size : _bufferPoolTable.keySet()) { |
386 | - if (size > pageSize) { |
387 | - pageSize = size; |
388 | - } |
389 | - } |
390 | - } |
391 | - return createTemporaryVolume(pageSize); |
392 | + return createTemporaryVolume(temporaryVolumePageSize()); |
393 | } |
394 | |
395 | /** |
396 | @@ -1115,6 +1107,18 @@ |
397 | return Volume.createTemporaryVolume(this, pageSize, directory); |
398 | } |
399 | |
400 | + private int temporaryVolumePageSize() { |
401 | + int pageSize = _configuration.getTmpVolPageSize(); |
402 | + if (pageSize == 0) { |
403 | + for (final int size : _bufferPoolTable.keySet()) { |
404 | + if (size > pageSize) { |
405 | + pageSize = size; |
406 | + } |
407 | + } |
408 | + } |
409 | + return pageSize; |
410 | + } |
411 | + |
412 | /** |
413 | * Delete a volume currently loaded volume and remove it from the list |
414 | * returned by {@link #getVolumes()}. |
415 | @@ -1296,8 +1300,14 @@ |
416 | checkInitialized(); |
417 | checkClosed(); |
418 | if (_lockVolume == null) { |
419 | - _lockVolume = createTemporaryVolume(); |
420 | - _lockVolume.setHandle(Volume.LOCK_VOLUME_HANDLE); |
421 | + final int pageSize = temporaryVolumePageSize(); |
422 | + if (!Volume.isValidPageSize(pageSize)) { |
423 | + throw new IllegalArgumentException("Invalid page size " + pageSize); |
424 | + } |
425 | + final String directoryName = getConfiguration().getTmpVolDir(); |
426 | + final File directory = directoryName == null ? null : new File(directoryName); |
427 | + _lockVolume = Volume.createLockVolume(this, pageSize, directory); |
428 | + _volumes.add(_lockVolume); |
429 | } |
430 | return _lockVolume; |
431 | } |
432 | |
433 | === modified file 'src/main/java/com/persistit/Transaction.java' |
434 | --- src/main/java/com/persistit/Transaction.java 2013-02-20 16:52:45 +0000 |
435 | +++ src/main/java/com/persistit/Transaction.java 2013-03-13 17:42:20 +0000 |
436 | @@ -21,8 +21,14 @@ |
437 | import static com.persistit.util.ThreadSequencer.sequence; |
438 | |
439 | import java.nio.ByteBuffer; |
440 | +import java.util.ArrayList; |
441 | +import java.util.HashSet; |
442 | +import java.util.List; |
443 | +import java.util.Set; |
444 | |
445 | import com.persistit.Accumulator.Delta; |
446 | +import com.persistit.CleanupManager.CleanupAction; |
447 | +import com.persistit.CleanupManager.CleanupPruneAction; |
448 | import com.persistit.JournalRecord.D0; |
449 | import com.persistit.JournalRecord.D1; |
450 | import com.persistit.JournalRecord.DR; |
451 | @@ -434,6 +440,8 @@ |
452 | |
453 | private String _threadName; |
454 | |
455 | + private final Set<CleanupAction> _lockCleanupActions = new HashSet<CleanupAction>(); |
456 | + |
457 | public static enum CommitPolicy { |
458 | /** |
459 | * The {@link Transaction#commit} method returns before all updates have |
460 | @@ -675,6 +683,11 @@ |
461 | _commitCount++; |
462 | _rollbacksSinceLastCommit = 0; |
463 | } |
464 | + try { |
465 | + pruneLockPages(); |
466 | + } catch (Exception e) { |
467 | + _persistit.getLogBase().pruneException.log(e, "locks"); |
468 | + } |
469 | _transactionStatus = null; |
470 | _rollbackPending = false; |
471 | _threadName = null; |
472 | @@ -1317,6 +1330,32 @@ |
473 | return treeHandle; |
474 | } |
475 | |
476 | + void addLockPage(final Long page, final int treeHandle) { |
477 | + _lockCleanupActions.add(new CleanupPruneAction(treeHandle, page)); |
478 | + } |
479 | + |
480 | + void pruneLockPages() { |
481 | + if (_lockCleanupActions.isEmpty()) { |
482 | + return; |
483 | + } |
484 | + |
485 | + _persistit.getTransactionIndex().updateActiveTransactionCache(_commitTimestamp); |
486 | + List<CleanupAction> actions = new ArrayList<CleanupAction>(_lockCleanupActions); |
487 | + _lockCleanupActions.clear(); |
488 | + |
489 | + while (!actions.isEmpty()) { |
490 | + final List<CleanupAction> consequentActions = new ArrayList<CleanupAction>(); |
491 | + for (final CleanupAction cleanupAction : actions) { |
492 | + try { |
493 | + cleanupAction.performAction(_persistit, consequentActions); |
494 | + } catch (final PersistitException pe) { |
495 | + _persistit.getLogBase().pruneException.log(pe, this); |
496 | + } |
497 | + actions = consequentActions; |
498 | + } |
499 | + } |
500 | + } |
501 | + |
502 | /** |
503 | * For unit tests only |
504 | * |
505 | |
506 | === modified file 'src/main/java/com/persistit/TransactionIndex.java' |
507 | --- src/main/java/com/persistit/TransactionIndex.java 2012-10-26 19:44:17 +0000 |
508 | +++ src/main/java/com/persistit/TransactionIndex.java 2013-03-13 17:42:20 +0000 |
509 | @@ -969,6 +969,18 @@ |
510 | } |
511 | } |
512 | |
513 | + /** |
514 | + * Update the floor only if it is smaller than the supplied timestamp. |
515 | + * |
516 | + * @param ts |
517 | + * The timestamp |
518 | + */ |
519 | + public void updateActiveTransactionCache(final long ts) { |
520 | + if (_atCache._floor < ts) { |
521 | + updateActiveTransactionCache(); |
522 | + } |
523 | + } |
524 | + |
525 | /* |
526 | * (non-Javadoc) |
527 | * |
528 | |
529 | === modified file 'src/main/java/com/persistit/Tree.java' |
530 | --- src/main/java/com/persistit/Tree.java 2013-01-19 17:42:22 +0000 |
531 | +++ src/main/java/com/persistit/Tree.java 2013-03-13 17:42:20 +0000 |
532 | @@ -244,8 +244,8 @@ |
533 | */ |
534 | @Override |
535 | public String toString() { |
536 | - return "<Tree " + _name + " rootPageAddr=" + _rootPageAddr + " depth=" + _depth + " status=" |
537 | - + getStatusDisplayString() + ">"; |
538 | + return "<Tree " + _name + " in volume " + _volume.getName() + " rootPageAddr=" + _rootPageAddr + " depth=" |
539 | + + _depth + " status=" + getStatusDisplayString() + ">"; |
540 | } |
541 | |
542 | /** |
543 | |
544 | === modified file 'src/main/java/com/persistit/Volume.java' |
545 | --- src/main/java/com/persistit/Volume.java 2013-01-19 17:42:22 +0000 |
546 | +++ src/main/java/com/persistit/Volume.java 2013-03-13 17:42:20 +0000 |
547 | @@ -20,6 +20,7 @@ |
548 | import java.util.concurrent.atomic.AtomicInteger; |
549 | import java.util.concurrent.atomic.AtomicReference; |
550 | |
551 | +import com.persistit.exception.BufferSizeUnavailableException; |
552 | import com.persistit.exception.InUseException; |
553 | import com.persistit.exception.PersistitException; |
554 | import com.persistit.exception.ReadOnlyVolumeException; |
555 | @@ -49,6 +50,7 @@ |
556 | public class Volume { |
557 | |
558 | final static int LOCK_VOLUME_HANDLE = Integer.MAX_VALUE; |
559 | + final static String LOCK_VOLUME_NAME = "_lock"; |
560 | |
561 | private final String _name; |
562 | private long _id; |
563 | @@ -86,7 +88,21 @@ |
564 | final Volume volume = new Volume( |
565 | Thread.currentThread().getName() + TEMP_VOLUME_NAME_SUFFIX_FOR_FIXUP_DETECTION, |
566 | TEMP_VOLUME_ID_FOR_FIXUP_DETECTION); |
567 | - volume.openTemporary(persistit, pageSize, tempDirectory); |
568 | + volume.openInternal(persistit, pageSize); |
569 | + |
570 | + volume._storage = new VolumeStorageT2(persistit, volume, tempDirectory); |
571 | + volume._storage.create(); |
572 | + |
573 | + return volume; |
574 | + } |
575 | + |
576 | + static Volume createLockVolume(final Persistit persistit, final int pageSize, final File tempDirectory) |
577 | + throws PersistitException { |
578 | + final Volume volume = new Volume(LOCK_VOLUME_NAME, 0); |
579 | + volume.setHandle(Volume.LOCK_VOLUME_HANDLE); |
580 | + volume.openInternal(persistit, pageSize); |
581 | + volume._storage = new VolumeStorageL2(persistit, volume, tempDirectory); |
582 | + volume._storage.create(); |
583 | return volume; |
584 | } |
585 | |
586 | @@ -452,7 +468,7 @@ |
587 | throw new UnderSpecifiedVolumeException(getName()); |
588 | } |
589 | if (persistit.getBufferPool(_specification.getPageSize()) == null) { |
590 | - throw new IllegalStateException(getName()); |
591 | + throw new BufferSizeUnavailableException(getName()); |
592 | } |
593 | final boolean exists = VolumeHeader.verifyVolumeHeader(_specification, persistit.getCurrentTimestamp()); |
594 | |
595 | @@ -485,21 +501,16 @@ |
596 | persistit.addVolume(this); |
597 | } |
598 | |
599 | - void openTemporary(final Persistit persistit, final int pageSize, final File tempDirectory) |
600 | - throws PersistitException { |
601 | + private void openInternal(final Persistit persistit, final int pageSize) throws PersistitException { |
602 | checkClosing(); |
603 | if (_storage != null) { |
604 | throw new IllegalStateException("This volume has already been opened"); |
605 | } |
606 | if (persistit.getBufferPool(pageSize) == null) { |
607 | - throw new IllegalStateException("There is no buffer pool for pages of size " + pageSize); |
608 | + throw new BufferSizeUnavailableException("There is no buffer pool for pages of size " + pageSize); |
609 | } |
610 | - |
611 | _structure = new VolumeStructure(persistit, this, pageSize); |
612 | - _storage = new VolumeStorageT2(persistit, this, tempDirectory); |
613 | _statistics = new VolumeStatistics(); |
614 | - |
615 | - _storage.create(); |
616 | } |
617 | |
618 | public String getName() { |
619 | |
620 | === added file 'src/main/java/com/persistit/VolumeStorageL2.java' |
621 | --- src/main/java/com/persistit/VolumeStorageL2.java 1970-01-01 00:00:00 +0000 |
622 | +++ src/main/java/com/persistit/VolumeStorageL2.java 2013-03-13 17:42:20 +0000 |
623 | @@ -0,0 +1,89 @@ |
624 | +/** |
625 | + * Copyright © 2011-2012 Akiban Technologies, Inc. All rights reserved. |
626 | + * |
627 | + * This program and the accompanying materials are made available |
628 | + * under the terms of the Eclipse Public License v1.0 which |
629 | + * accompanies this distribution, and is available at |
630 | + * http://www.eclipse.org/legal/epl-v10.html |
631 | + * |
632 | + * This program may also be available under different license terms. |
633 | + * For more information, see www.akiban.com or contact licensing@akiban.com. |
634 | + * |
635 | + * Contributors: |
636 | + * Akiban Technologies, Inc. |
637 | + */ |
638 | + |
639 | +package com.persistit; |
640 | + |
641 | +import java.io.File; |
642 | + |
643 | +import com.persistit.exception.InUseException; |
644 | +import com.persistit.exception.PersistitException; |
645 | + |
646 | +/** |
647 | + * Manage all details of file I/O for a special <code>Volume</code> that backs |
648 | + * the {@link Exchange#lock()} method. The lock volume differs from a temporary |
649 | + * volume in the following ways: |
650 | + * <ul> |
651 | + * <li>It has a defined name and is visible in the |
652 | + * {@link Persistit#getVolumes()} array.</li> |
653 | + * <li>It is threadsafe.</li> |
654 | + * </ul> |
655 | + * Like a temporary volume, it is intended that pages of the lock volume should |
656 | + * seldom, if ever, need to be written to disk. And also like a temporary |
657 | + * volume, the contents of the lock volume are not recovered during startup. |
658 | + * |
659 | + * @author peter |
660 | + */ |
661 | +class VolumeStorageL2 extends VolumeStorageT2 { |
662 | + |
663 | + final static String LOCK_VOLUME_FILE_PREFIX = "persistit_lockvol_"; |
664 | + private Buffer _headBuffer; |
665 | + |
666 | + VolumeStorageL2(final Persistit persistit, final Volume volume, final File tempDirectory) { |
667 | + super(persistit, volume, tempDirectory); |
668 | + } |
669 | + |
670 | + /** |
671 | + * Indicate whether this is a temporary volume. The Lock volume is not |
672 | + * temporary, although it inherits most of the characteristics of a |
673 | + * temporary volume. |
674 | + * |
675 | + * @return <code>true</code> if this volume is temporary |
676 | + */ |
677 | + @Override |
678 | + boolean isTemp() { |
679 | + return false; |
680 | + } |
681 | + |
682 | + @Override |
683 | + void truncate() throws PersistitException { |
684 | + if (!claim(true, 0)) { |
685 | + throw new InUseException("Unable to acquire claim on " + this); |
686 | + } |
687 | + try { |
688 | + _headBuffer = _volume.getStructure().getPool().get(_volume, 0, true, false); |
689 | + _headBuffer.init(Buffer.PAGE_TYPE_HEAD); |
690 | + _headBuffer.setFixed(); |
691 | + |
692 | + truncateInternal(); |
693 | + releaseHeadBuffer(); |
694 | + |
695 | + } finally { |
696 | + release(); |
697 | + } |
698 | + } |
699 | + |
700 | + @Override |
701 | + void claimHeadBuffer() throws PersistitException { |
702 | + if (!_headBuffer.claim(true)) { |
703 | + throw new InUseException("Unable to acquire claim on " + _headBuffer); |
704 | + } |
705 | + } |
706 | + |
707 | + @Override |
708 | + void releaseHeadBuffer() { |
709 | + _headBuffer.release(); |
710 | + } |
711 | + |
712 | +} |
713 | |
714 | === modified file 'src/main/java/com/persistit/VolumeStorageT2.java' |
715 | --- src/main/java/com/persistit/VolumeStorageT2.java 2013-01-04 00:12:33 +0000 |
716 | +++ src/main/java/com/persistit/VolumeStorageT2.java 2013-03-13 17:42:20 +0000 |
717 | @@ -44,7 +44,6 @@ |
718 | class VolumeStorageT2 extends VolumeStorage { |
719 | |
720 | final static String TEMP_FILE_PREFIX = "persistit_tempvol_"; |
721 | - private final static String TEMP_FILE_UNCREATED_NAME = "temp_volume_file_not_created_yet"; |
722 | private final File _tempDirectory; |
723 | private long _maxPages; |
724 | private volatile String _path; |
725 | @@ -117,7 +116,7 @@ |
726 | void create() throws PersistitException { |
727 | final long maxSize = _persistit.getConfiguration().getTmpVolMaxSize(); |
728 | _maxPages = maxSize / _volume.getStructure().getPageSize(); |
729 | - _path = TEMP_FILE_UNCREATED_NAME; |
730 | + _path = ""; |
731 | _channel = null; |
732 | truncate(); |
733 | _opened = true; |
734 | @@ -220,21 +219,23 @@ |
735 | throw new InUseException("Unable to acquire claim on " + this); |
736 | } |
737 | try { |
738 | - final VolumeStatistics stat = _volume.getStatistics(); |
739 | - final VolumeStructure struc = _volume.getStructure(); |
740 | - |
741 | - final long now = System.currentTimeMillis(); |
742 | - stat.setCreateTime(now); |
743 | - stat.setOpenTime(now); |
744 | - |
745 | - _nextAvailablePage = 1; |
746 | - |
747 | - struc.init(0, 0); |
748 | + truncateInternal(); |
749 | } finally { |
750 | release(); |
751 | } |
752 | - flushMetaData(); |
753 | - |
754 | + } |
755 | + |
756 | + protected void truncateInternal() throws PersistitException { |
757 | + final VolumeStatistics stat = _volume.getStatistics(); |
758 | + final VolumeStructure struc = _volume.getStructure(); |
759 | + |
760 | + final long now = System.currentTimeMillis(); |
761 | + stat.setCreateTime(now); |
762 | + stat.setOpenTime(now); |
763 | + |
764 | + _nextAvailablePage = 1; |
765 | + |
766 | + struc.init(0, 0); |
767 | } |
768 | |
769 | @Override |
770 | |
771 | === modified file 'src/main/java/com/persistit/VolumeStructure.java' |
772 | --- src/main/java/com/persistit/VolumeStructure.java 2012-11-28 17:13:21 +0000 |
773 | +++ src/main/java/com/persistit/VolumeStructure.java 2013-03-13 17:42:20 +0000 |
774 | @@ -26,6 +26,7 @@ |
775 | |
776 | import com.persistit.AlertMonitor.AlertLevel; |
777 | import com.persistit.AlertMonitor.Event; |
778 | +import com.persistit.exception.BufferSizeUnavailableException; |
779 | import com.persistit.exception.CorruptVolumeException; |
780 | import com.persistit.exception.InUseException; |
781 | import com.persistit.exception.PersistitException; |
782 | @@ -128,13 +129,13 @@ |
783 | _persistit.getJournalManager().truncate(_volume, timestamp); |
784 | } |
785 | |
786 | - Exchange directoryExchange() { |
787 | + Exchange directoryExchange() throws BufferSizeUnavailableException { |
788 | final Exchange ex = new Exchange(_directoryTree); |
789 | ex.ignoreTransactions(); |
790 | return ex; |
791 | } |
792 | |
793 | - Exchange accumulatorExchange() { |
794 | + Exchange accumulatorExchange() throws BufferSizeUnavailableException { |
795 | return new Exchange(_directoryTree); |
796 | } |
797 | |
798 | @@ -502,7 +503,9 @@ |
799 | return buffer; |
800 | } finally { |
801 | garbageBuffer = releaseBuffer(garbageBuffer); |
802 | - deallocateGarbageChain(chains); |
803 | + if (!chains.isEmpty()) { |
804 | + deallocateGarbageChain(chains); |
805 | + } |
806 | } |
807 | } |
808 | } finally { |
809 | @@ -517,7 +520,6 @@ |
810 | buffer.init(Buffer.PAGE_TYPE_UNALLOCATED); |
811 | Debug.$assert0.t(buffer.getPageAddress() != 0); |
812 | return buffer; |
813 | - |
814 | } |
815 | |
816 | void deallocateGarbageChain(final long left, final long right) throws PersistitException { |
817 | |
818 | === modified file 'src/main/java/com/persistit/ui/AdminUISummaryPanel.java' |
819 | --- src/main/java/com/persistit/ui/AdminUISummaryPanel.java 2012-08-24 13:57:19 +0000 |
820 | +++ src/main/java/com/persistit/ui/AdminUISummaryPanel.java 2013-03-13 17:42:20 +0000 |
821 | @@ -250,7 +250,7 @@ |
822 | final Management.VolumeInfo[] array = (Management.VolumeInfo[]) _volumeInfoArrayModel |
823 | .getInfoArray(); |
824 | if (array != null && index < array.length) { |
825 | - _selectedVolumeName = array[index].getPath(); |
826 | + _selectedVolumeName = array[index].getName(); |
827 | } else { |
828 | _selectedVolumeName = null; |
829 | } |
830 | @@ -423,7 +423,7 @@ |
831 | final Management.VolumeInfo[] via = management.getVolumeInfoArray(); |
832 | |
833 | if (_selectedVolumeName == null && via.length > 0) { |
834 | - _selectedVolumeName = via[0].getPath(); |
835 | + _selectedVolumeName = via[0].getName(); |
836 | } |
837 | |
838 | for (int index = 0; index < via.length; index++) { |
839 | |
840 | === modified file 'src/main/java/com/persistit/ui/AdminUITreePanel.java' |
841 | --- src/main/java/com/persistit/ui/AdminUITreePanel.java 2012-08-24 13:57:19 +0000 |
842 | +++ src/main/java/com/persistit/ui/AdminUITreePanel.java 2013-03-13 17:42:20 +0000 |
843 | @@ -263,7 +263,7 @@ |
844 | } |
845 | |
846 | private void selectVolume(final Management.VolumeInfo volumeInfo) { |
847 | - final String newName = volumeInfo == null ? null : volumeInfo.getPath(); |
848 | + final String newName = volumeInfo == null ? null : volumeInfo.getName(); |
849 | if (!equals(newName, _selectedVolumeName)) { |
850 | _logicalRecordArrayModel.setInfoArray(null); |
851 | _selectedTreeName = null; |
852 | |
853 | === modified file 'src/main/java/com/persistit/ui/VTComboBoxModel.java' |
854 | --- src/main/java/com/persistit/ui/VTComboBoxModel.java 2012-08-24 13:57:19 +0000 |
855 | +++ src/main/java/com/persistit/ui/VTComboBoxModel.java 2013-03-13 17:42:20 +0000 |
856 | @@ -74,7 +74,7 @@ |
857 | if (_parent == null) { |
858 | final VolumeInfo[] volumes = _management.getVolumeInfoArray(); |
859 | for (int index = 0; index < volumes.length; index++) { |
860 | - _cachedList.add(volumes[index].getPath()); |
861 | + _cachedList.add(volumes[index].getName()); |
862 | } |
863 | } else { |
864 | final String volumeName = (String) _parent.getSelectedItem(); |
865 | |
866 | === modified file 'src/main/resources/com/persistit/ui/AdminUI.properties' |
867 | --- src/main/resources/com/persistit/ui/AdminUI.properties 2012-11-08 15:04:52 +0000 |
868 | +++ src/main/resources/com/persistit/ui/AdminUI.properties 2013-03-13 17:42:20 +0000 |
869 | @@ -37,8 +37,8 @@ |
870 | lnf = com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel |
871 | |
872 | VolumeInfo.flags = A:All,S:Size,P:Performance,T:Time |
873 | -VolumeInfo.column.0 = getPath:180:A:Path |
874 | -VolumeInfo.column.1 = getName:100:A:Name |
875 | +VolumeInfo.column.0 = getName:100:A:Name |
876 | +VolumeInfo.column.1 = getPath:180:A:Path |
877 | VolumeInfo.column.2 = getPageSize:100:A:Page Size |
878 | VolumeInfo.column.3 = getCurrentSize:100:A:Current Size |
879 | VolumeInfo.column.4 = getMaximumSize:100:A:Max Size |
880 | |
881 | === modified file 'src/test/java/com/persistit/Bug1041293Test.java' |
882 | --- src/test/java/com/persistit/Bug1041293Test.java 2012-10-06 02:44:20 +0000 |
883 | +++ src/test/java/com/persistit/Bug1041293Test.java 2013-03-13 17:42:20 +0000 |
884 | @@ -17,6 +17,7 @@ |
885 | |
886 | import org.junit.Test; |
887 | |
888 | +import com.persistit.exception.BufferSizeUnavailableException; |
889 | import com.persistit.exception.UnderSpecifiedVolumeException; |
890 | |
891 | /** |
892 | @@ -39,7 +40,7 @@ |
893 | volume.open(_persistit); |
894 | } |
895 | |
896 | - @Test(expected = IllegalStateException.class) |
897 | + @Test(expected = BufferSizeUnavailableException.class) |
898 | public void mismatchedVolumeSpecificationNPE() throws Exception { |
899 | final Configuration config = _persistit.getConfiguration(); |
900 | final VolumeSpecification vspec = config.volumeSpecification("${datapath}/test,pageSize:2048,create," |
901 | @@ -48,7 +49,7 @@ |
902 | volume.open(_persistit); |
903 | } |
904 | |
905 | - @Test(expected = IllegalStateException.class) |
906 | + @Test(expected = BufferSizeUnavailableException.class) |
907 | public void mismatchedTemporaryVolumePageSize() throws Exception { |
908 | _persistit.createTemporaryVolume(2048); |
909 | } |
910 | |
911 | === modified file 'src/test/java/com/persistit/CleanupManagerTest.java' |
912 | --- src/test/java/com/persistit/CleanupManagerTest.java 2012-08-24 13:57:19 +0000 |
913 | +++ src/test/java/com/persistit/CleanupManagerTest.java 2013-03-13 17:42:20 +0000 |
914 | @@ -19,6 +19,7 @@ |
915 | import static org.junit.Assert.assertTrue; |
916 | |
917 | import java.lang.ref.WeakReference; |
918 | +import java.util.List; |
919 | |
920 | import org.junit.Test; |
921 | |
922 | @@ -48,7 +49,8 @@ |
923 | } |
924 | |
925 | @Override |
926 | - public void performAction(final Persistit persistit) throws PersistitException { |
927 | + public void performAction(final Persistit persistit, final List<CleanupAction> consequentActions) |
928 | + throws PersistitException { |
929 | assertEquals(_last + 1, _sequence); |
930 | _last = _sequence; |
931 | _counter++; |
932 | |
933 | === modified file 'src/test/java/com/persistit/ExchangeLockTest.java' |
934 | --- src/test/java/com/persistit/ExchangeLockTest.java 2013-02-15 15:39:42 +0000 |
935 | +++ src/test/java/com/persistit/ExchangeLockTest.java 2013-03-13 17:42:20 +0000 |
936 | @@ -19,6 +19,8 @@ |
937 | import static org.junit.Assert.assertTrue; |
938 | import static org.junit.Assert.fail; |
939 | |
940 | +import java.util.Properties; |
941 | +import java.util.Random; |
942 | import java.util.concurrent.Semaphore; |
943 | |
944 | import org.junit.Test; |
945 | @@ -26,11 +28,17 @@ |
946 | import com.persistit.exception.InUseException; |
947 | import com.persistit.exception.InvalidKeyException; |
948 | import com.persistit.exception.PersistitException; |
949 | +import com.persistit.unit.UnitTestProperties; |
950 | |
951 | public class ExchangeLockTest extends PersistitUnitTestCase { |
952 | private final static long DMILLIS = SharedResource.DEFAULT_MAX_WAIT_TIME; |
953 | private final Semaphore _coordinator = new Semaphore(0); |
954 | |
955 | + @Override |
956 | + public Properties getProperties(final boolean cleanup) { |
957 | + return UnitTestProperties.getBiggerProperties(cleanup); |
958 | + } |
959 | + |
960 | @Test |
961 | public void singleThreadedLock() throws Exception { |
962 | final Exchange ex = _persistit.getExchange("persistit", "ExchangeLockTest", true); |
963 | @@ -231,37 +239,34 @@ |
964 | assertEquals(1, succeeded); |
965 | } |
966 | |
967 | + /** |
968 | + * This test is intended to exercise lock management for transactions |
969 | + * executed sequentially, each of which performs lots of locks (similar to |
970 | + * DataLoadingTest.) |
971 | + * |
972 | + * @throws Exception |
973 | + */ |
974 | @Test |
975 | public void lockTablePruning() throws Exception { |
976 | final Exchange ex = _persistit.getExchange("persistit", "ExchangeLockTest", true); |
977 | + final Random random = new Random(); |
978 | final Transaction txn = ex.getTransaction(); |
979 | - txn.begin(); |
980 | - for (int i = 0; i < 10000; i++) { |
981 | - ex.clear().append(i).append(RED_FOX).lock(); |
982 | + for (int j = 0; j < 100; j++) { |
983 | + txn.begin(); |
984 | + for (int i = 0; i < 10000; i++) { |
985 | + final int k = random.nextInt(100000); |
986 | + ex.clear().append(k + (j * 100000)).append(RED_FOX).lock(); |
987 | + } |
988 | + txn.commit(); |
989 | + txn.end(); |
990 | } |
991 | - txn.commit(); |
992 | - txn.end(); |
993 | + assertTrue("Too many lock volume pages uses", |
994 | + _persistit.getLockVolume().getStorage().getNextAvailablePage() < 100); |
995 | |
996 | final Exchange lockExchange = new Exchange(_persistit.getLockVolume().getTree("ExchangeLockTest", false)); |
997 | - lockExchange.ignoreMVCCFetch(true); |
998 | - |
999 | - final int count1 = keyCount(lockExchange); |
1000 | - assertTrue(count1 > 0); |
1001 | - |
1002 | - _persistit.getTransactionIndex().updateActiveTransactionCache(); |
1003 | - |
1004 | - for (int i = 0; i < 10000; i++) { |
1005 | - lockExchange.clear().append(i).append(RED_FOX).prune(); |
1006 | - } |
1007 | - |
1008 | - final int count2 = keyCount(lockExchange); |
1009 | - assertTrue(count2 < count1); |
1010 | - |
1011 | - _persistit.getCleanupManager().poll(); |
1012 | - |
1013 | - final int count3 = keyCount(lockExchange); |
1014 | - assertEquals(0, count3); |
1015 | - |
1016 | + final int count = keyCount(lockExchange); |
1017 | + |
1018 | + assertEquals("Unpruned lock records", 0, count); |
1019 | } |
1020 | |
1021 | private int keyCount(final Exchange ex) throws PersistitException { |
1022 | |
1023 | === modified file 'src/test/java/com/persistit/MVCCPruneBufferTest.java' |
1024 | --- src/test/java/com/persistit/MVCCPruneBufferTest.java 2012-09-26 13:31:13 +0000 |
1025 | +++ src/test/java/com/persistit/MVCCPruneBufferTest.java 2013-03-13 17:42:20 +0000 |
1026 | @@ -43,7 +43,7 @@ |
1027 | final int available = buffer1.getAvailableSize(); |
1028 | final int keys = buffer1.getKeyCount(); |
1029 | buffer1.claim(true); |
1030 | - buffer1.pruneMvvValues(null, true); |
1031 | + buffer1.pruneMvvValues(null, true, null); |
1032 | assertEquals(keys, buffer1.getKeyCount()); |
1033 | assertTrue(buffer1.getMvvCount() > 0); |
1034 | assertEquals(available, buffer1.getAvailableSize()); |
1035 | @@ -53,7 +53,7 @@ |
1036 | |
1037 | _persistit.getTransactionIndex().updateActiveTransactionCache(); |
1038 | |
1039 | - buffer1.pruneMvvValues(null, true); |
1040 | + buffer1.pruneMvvValues(null, true, null); |
1041 | assertEquals(0, _persistit.getCleanupManager().getAcceptedCount()); |
1042 | assertTrue("Pruning should have removed primordial Anti-values", keys > buffer1.getKeyCount()); |
1043 | // mvvCount is 1 because there is still a leading primordial AntiValue |
1044 | @@ -78,7 +78,7 @@ |
1045 | final int available = buffer1.getAvailableSize(); |
1046 | final int keys = buffer1.getKeyCount(); |
1047 | buffer1.claim(true); |
1048 | - buffer1.pruneMvvValues(null, true); |
1049 | + buffer1.pruneMvvValues(null, true, null); |
1050 | buffer1.release(); |
1051 | assertEquals(keys, buffer1.getKeyCount()); |
1052 | assertTrue(buffer1.getMvvCount() > 0); |
1053 | @@ -102,7 +102,7 @@ |
1054 | for (long page = 1; page < pageCount; page++) { |
1055 | final Buffer buffer = ex1.getBufferPool().get(ex1.getVolume(), page, true, true); |
1056 | try { |
1057 | - buffer.pruneMvvValues(null, true); |
1058 | + buffer.pruneMvvValues(null, true, null); |
1059 | } finally { |
1060 | buffer.release(); |
1061 | } |
1062 | @@ -123,7 +123,7 @@ |
1063 | for (long page = 1; page < pageCount; page++) { |
1064 | final Buffer buffer = ex1.getBufferPool().get(ex1.getVolume(), page, true, true); |
1065 | try { |
1066 | - buffer.pruneMvvValues(ex1.getTree(), true); |
1067 | + buffer.pruneMvvValues(ex1.getTree(), true, null); |
1068 | } finally { |
1069 | buffer.release(); |
1070 | } |
1071 | |
1072 | === modified file 'src/test/java/com/persistit/TestShim.java' |
1073 | --- src/test/java/com/persistit/TestShim.java 2012-08-24 13:57:19 +0000 |
1074 | +++ src/test/java/com/persistit/TestShim.java 2013-03-13 17:42:20 +0000 |
1075 | @@ -18,6 +18,7 @@ |
1076 | import java.nio.ByteBuffer; |
1077 | import java.nio.channels.FileChannel; |
1078 | |
1079 | +import com.persistit.exception.BufferSizeUnavailableException; |
1080 | import com.persistit.exception.InvalidKeyException; |
1081 | import com.persistit.exception.PersistitException; |
1082 | import com.persistit.exception.PersistitInterruptedException; |
1083 | @@ -104,7 +105,7 @@ |
1084 | jman.copyBack(); |
1085 | } |
1086 | |
1087 | - public static Exchange directoryExchange(final Volume volume) { |
1088 | + public static Exchange directoryExchange(final Volume volume) throws BufferSizeUnavailableException { |
1089 | return volume.getStructure().directoryExchange(); |
1090 | } |
1091 |
Does pruneLockPages() really need to call updateActiveTra nsactionCache( )? It already happens every 10ms and doing it synchronously seems unnecessary.
VolumeStorageT2 and L2 are 99% identical. Can we pull some of that out into a base class, particularly the non-trivial stuff like read and write page? The javadoc also wasn't changed for L2.
Otherwise looks fine.