Merge lp:~pbeaman/akiban-persistit/fix_1018526_temp_trees_in_journal into lp:akiban-persistit
- fix_1018526_temp_trees_in_journal
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Peter Beaman |
Approved revision: | 335 |
Merged at revision: | 330 |
Proposed branch: | lp:~pbeaman/akiban-persistit/fix_1018526_temp_trees_in_journal |
Merge into: | lp:akiban-persistit |
Diff against target: |
447 lines (+201/-29) 12 files modified
src/main/java/com/persistit/Buffer.java (+3/-1) src/main/java/com/persistit/Exchange.java (+3/-1) src/main/java/com/persistit/IntegrityCheck.java (+5/-4) src/main/java/com/persistit/JournalManager.java (+13/-1) src/main/java/com/persistit/Persistit.java (+2/-4) src/main/java/com/persistit/RecoveryManager.java (+18/-6) src/main/java/com/persistit/Transaction.java (+10/-7) src/main/java/com/persistit/Tree.java (+11/-0) src/main/java/com/persistit/Volume.java (+26/-1) src/main/java/com/persistit/VolumeStructure.java (+6/-1) src/test/java/com/persistit/Bug1018526Test.java (+104/-0) src/test/java/com/persistit/Bug932097Test.java (+0/-3) |
To merge this branch: | bzr merge lp:~pbeaman/akiban-persistit/fix_1018526_temp_trees_in_journal |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Nathan Williams | Needs Fixing | ||
Peter Beaman | Needs Resubmitting | ||
Akiban Build User | Needs Fixing | ||
Review via email: mp+112454@code.launchpad.net |
Commit message
Description of the change
Fix bug1018526 in which every tree created in a temporary volume has an Identify Tree (IT) record in the journal.
Removes proactive call to JournalManager#
Adds a test to verify that no records are written to the journal within a transaction if only temporary volumes are modified.
Nathan Williams (nwilliams) wrote : | # |
What is the intended lifetime of the tree handle now? It looks like it will get created on remove key, remove tree, store key, and accumulator delta inside of a transaction. It looks like _ignoreTransactions hould always mirror isTemporary but it is up to the caller. Maybe an assert inside of JournalManager#
A couple of the checks for handle != 0 are also slightly confusing. We shouldn't have MVVs in a temporary tree (txns being ignored) so if we are pruning, we should always have a handle. Similarly, how could we get an index hole if they can't be shared across threads and aren't recovered?
The new test with the "upper bound" to avoid a checkpoint race. We already have a couple intermittent test failures, it would be nice to structure this so it is always deterministic.
Peter Beaman (pbeaman) wrote : | # |
Added code to detect IT records that refer to temporary volumes during recovery. Such trees are not restored into the handleToTreeMap and treeToHandleMap collections, so that when the journal next rolls over, the recorded tree map will contain only permanent trees and volumes. This logic will fix up existing systems.
Once we have had this version in the field on all sites we can then remove the cleanup code.
Note that from the information in the journal, the detection of a temporary volume is somewhat ad hoc. It is known by the conjunction of two things:
id value is 12345 (for no very good reason temporary volumes have all been marked with that ID number)
name ends with "_temporary_volume"
We feel it is unlikely there are any non-temporary volumes that have these two conditions at this time. However, eventually the detection code should be removed since it is less than perfectly precise.
Peter Beaman (pbeaman) wrote : | # |
Responses to Nathan:
What is the intended lifetime of the tree handle now? It looks like it will get created on remove key, remove tree, store key, and accumulator delta inside of a transaction. -- Correct
It looks like _ignoreTransactions hould always mirror isTemporary but it is up to the caller. - Not quite: currently the directory tree exchange operates with _ignoreTransactions on non-temporary volumes.
Maybe an assert inside of JournalManager#
A couple of the checks for handle != 0 are also slightly confusing. We shouldn't have MVVs in a temporary tree (txns being ignored) so if we are pruning, we should always have a handle. - Correct. Changed to asserts.
Similarly, how could we get an index hole if they can't be shared across threads and aren't recovered? - There's an obscure, gnarly re-balance case in raw_removeKeyRa
The new test with the "upper bound" to avoid a checkpoint race. We already have a couple intermittent test failures, it would be nice to structure this so it is always deterministic. - Yes. I suggest we should do that comprehensively in a different branch.
Nathan Williams (nwilliams) wrote : | # |
Thanks for the changes, looks good.
Akiban Build User (build-akiban) wrote : | # |
There were 2 failures during build/test:
* job persistit-build failed at build number 374: http://
* view must-pass failed: persistit-build is yellow
- 334. By Peter Beaman
-
Always allocate tree handles for permanent trees
Peter Beaman (pbeaman) wrote : | # |
Modified the protocol yet again. Tree handles are now assigned on non-temporary trees as soon as the tree object is created. This permits the Exchange#
Nathan Williams (nwilliams) wrote : | # |
A static final end for the two magic values (12345 and _temporary_volume) would be nice. Coming across that in the future could be confusing otherwise.
Now that tree handles are always loaded proactively, it might be nice to make JournalManage#
I don't mean to harp, but since we are trying to fix a bug about the handle lifetime it seems preferable to check all of our assumptions related to it.
- 335. By Peter Beaman
-
Changes per Nathan's review
Peter Beaman (pbeaman) wrote : | # |
Modified as requested.
Nathan Williams (nwilliams) wrote : | # |
I don't mean to nitpick, but the other source of the constants is in Persistit#
Feel free to self-approve after that tiny change.
- 336. By Peter Beaman
-
Use constants when creating temp volume
Peter Beaman (pbeaman) wrote : | # |
Good suggestion. Delegated createTemporary
Preview Diff
1 | === modified file 'src/main/java/com/persistit/Buffer.java' |
2 | --- src/main/java/com/persistit/Buffer.java 2012-06-29 20:54:00 +0000 |
3 | +++ src/main/java/com/persistit/Buffer.java 2012-06-30 15:14:20 +0000 |
4 | @@ -3712,8 +3712,10 @@ |
5 | if (p == KEY_BLOCK_START) { |
6 | if (tree != null) { |
7 | if (!_enqueuedForAntiValuePruning) { |
8 | + final int treeHandle = tree.getHandle(); |
9 | + assert treeHandle != 0 : "MVV found in a temporary tree " + tree; |
10 | if (_persistit.getCleanupManager().offer( |
11 | - new CleanupAntiValue(tree.getHandle(), getPageAddress()))) { |
12 | + new CleanupAntiValue(treeHandle, getPageAddress()))) { |
13 | _enqueuedForAntiValuePruning = true; |
14 | } |
15 | } |
16 | |
17 | === modified file 'src/main/java/com/persistit/Exchange.java' |
18 | --- src/main/java/com/persistit/Exchange.java 2012-06-29 20:54:00 +0000 |
19 | +++ src/main/java/com/persistit/Exchange.java 2012-06-30 15:14:20 +0000 |
20 | @@ -2777,7 +2777,9 @@ |
21 | fetchFixupForLongRecords(value, Integer.MAX_VALUE); |
22 | if (MVV.isArrayMVV(value.getEncodedBytes(), 0, value.getEncodedSize())) { |
23 | if (bufferForPruning != null) { |
24 | - bufferForPruning.enqueuePruningAction(_tree.getHandle()); |
25 | + final int treeHandle = _tree.getHandle(); |
26 | + assert treeHandle != 0 : "MVV found in a temporary tree " + _tree; |
27 | + bufferForPruning.enqueuePruningAction(treeHandle); |
28 | } |
29 | visible = mvccFetch(value, minimumBytes); |
30 | fetchFixupForLongRecords(value, minimumBytes); |
31 | |
32 | === modified file 'src/main/java/com/persistit/IntegrityCheck.java' |
33 | --- src/main/java/com/persistit/IntegrityCheck.java 2012-06-28 20:08:30 +0000 |
34 | +++ src/main/java/com/persistit/IntegrityCheck.java 2012-06-30 15:14:20 +0000 |
35 | @@ -833,9 +833,9 @@ |
36 | } |
37 | _currentTree = null; |
38 | } |
39 | - if (_holes.size() > 0) { |
40 | - postMessage(" Tree " + resourceName(tree) + " has " + plural(_holes.size(), "unindexed page"), |
41 | - LOG_NORMAL); |
42 | + if (_counters._indexHoleCount > 0) { |
43 | + postMessage(" Tree " + resourceName(tree) + " has " |
44 | + + plural((int) _counters._indexHoleCount, "unindexed page"), LOG_NORMAL); |
45 | if (_fixHoles) { |
46 | int offered = 0; |
47 | for (final CleanupIndexHole hole : _holes) { |
48 | @@ -1082,7 +1082,8 @@ |
49 | } |
50 | |
51 | _counters._indexHoleCount++; |
52 | - if (_holes.size() < MAX_HOLES_TO_FIX) { |
53 | + final int treeHandle = _currentTree.getHandle(); |
54 | + if (treeHandle != 0 && _holes.size() < MAX_HOLES_TO_FIX) { |
55 | _holes.add(new CleanupIndexHole(_currentTree.getHandle(), page, level)); |
56 | } |
57 | |
58 | |
59 | === modified file 'src/main/java/com/persistit/JournalManager.java' |
60 | --- src/main/java/com/persistit/JournalManager.java 2012-06-22 19:07:13 +0000 |
61 | +++ src/main/java/com/persistit/JournalManager.java 2012-06-30 15:14:20 +0000 |
62 | @@ -213,6 +213,8 @@ |
63 | |
64 | private volatile long _earliestAbortedTimestamp = Long.MAX_VALUE; |
65 | |
66 | + private boolean _allowHandlesForTempVolumesAndTrees; |
67 | + |
68 | /** |
69 | * <p> |
70 | * Initialize the new journal. This method takes its information from the |
71 | @@ -540,7 +542,10 @@ |
72 | return Math.min(urgency, URGENT); |
73 | } |
74 | |
75 | - public int handleForVolume(final Volume volume) throws PersistitException { |
76 | + int handleForVolume(final Volume volume) throws PersistitException { |
77 | + if (!_allowHandlesForTempVolumesAndTrees && volume.isTemporary()) { |
78 | + throw new IllegalStateException("Creating handle for temporary volume " + volume); |
79 | + } |
80 | if (volume.getHandle() != 0) { |
81 | return volume.getHandle(); |
82 | } |
83 | @@ -580,6 +585,9 @@ |
84 | } |
85 | |
86 | int handleForTree(final Tree tree) throws PersistitException { |
87 | + if (!_allowHandlesForTempVolumesAndTrees && tree.getVolume().isTemporary()) { |
88 | + throw new IllegalStateException("Creating handle for temporary tree " + tree); |
89 | + } |
90 | if (tree.getHandle() != 0) { |
91 | return tree.getHandle(); |
92 | } |
93 | @@ -2835,6 +2843,10 @@ |
94 | return files; |
95 | } |
96 | |
97 | + void unitTestAllowHandlesForTemporaryVolumesAndTrees() { |
98 | + _allowHandlesForTempVolumesAndTrees = true; |
99 | + } |
100 | + |
101 | public PageNode queryPageNode(final int volumeHandle, final long pageAddress) { |
102 | PageNode pn = _pageMap.get(new PageNode(volumeHandle, pageAddress, -1, -1)); |
103 | if (pn != null) { |
104 | |
105 | === modified file 'src/main/java/com/persistit/Persistit.java' |
106 | --- src/main/java/com/persistit/Persistit.java 2012-06-22 19:35:13 +0000 |
107 | +++ src/main/java/com/persistit/Persistit.java 2012-06-30 15:14:20 +0000 |
108 | @@ -1108,7 +1108,7 @@ |
109 | * <p /> |
110 | * The backing store file for a temporary volume is created in the directory |
111 | * specified by the configuration property <code>tmpvoldir</code>, or if |
112 | - * unspecified, the system temporary directory.. |
113 | + * unspecified, the system temporary directory. |
114 | * |
115 | * @param pageSize |
116 | * The page size for the volume. Must be one of 1024, 2048, 4096, |
117 | @@ -1121,9 +1121,7 @@ |
118 | if (!Volume.isValidPageSize(pageSize)) { |
119 | throw new IllegalArgumentException("Invalid page size " + pageSize); |
120 | } |
121 | - Volume volume = new Volume(Thread.currentThread().getName() + "_temporary_volume", 12345); |
122 | - volume.openTemporary(this, pageSize); |
123 | - return volume; |
124 | + return Volume.createTemporaryVolume(this, pageSize); |
125 | } |
126 | |
127 | /** |
128 | |
129 | === modified file 'src/main/java/com/persistit/RecoveryManager.java' |
130 | --- src/main/java/com/persistit/RecoveryManager.java 2012-06-14 20:15:48 +0000 |
131 | +++ src/main/java/com/persistit/RecoveryManager.java 2012-06-30 15:14:20 +0000 |
132 | @@ -595,8 +595,13 @@ |
133 | |
134 | void collectRecoveredVolumeMaps(final Map<Integer, Volume> handleToVolumeMap, |
135 | final Map<Volume, Integer> volumeToHandleMap) { |
136 | - volumeToHandleMap.putAll(_volumeToHandleMap); |
137 | - handleToVolumeMap.putAll(_handleToVolumeMap); |
138 | + for (final Map.Entry<Integer, Volume> entry : _handleToVolumeMap.entrySet()) { |
139 | + final Volume volume = entry.getValue(); |
140 | + if (!volume.isTemporary()) { |
141 | + volumeToHandleMap.put(volume, entry.getKey()); |
142 | + handleToVolumeMap.put(entry.getKey(), volume); |
143 | + } |
144 | + } |
145 | } |
146 | |
147 | void collectRecoveredTreeMaps(final Map<Integer, TreeDescriptor> handleToTreeMap, |
148 | @@ -994,10 +999,17 @@ |
149 | final Integer handle = Integer.valueOf(IT.getHandle(_readBuffer)); |
150 | final String treeName = IT.getTreeName(_readBuffer); |
151 | final Integer volumeHandle = Integer.valueOf(IT.getVolumeHandle(_readBuffer)); |
152 | - final TreeDescriptor td = new TreeDescriptor(volumeHandle, treeName); |
153 | - _handleToTreeMap.put(handle, td); |
154 | - _treeToHandleMap.put(td, handle); |
155 | - _persistit.getLogBase().recoveryRecord.log("IT", addressToString(address, timestamp), treeName, timestamp); |
156 | + final Volume volume = _handleToVolumeMap.get(volumeHandle); |
157 | + if (volume == null) { |
158 | + throw new CorruptJournalException("IT JournalRecord refers to unidentified volume handle " + volumeHandle |
159 | + + " at position " + addressToString(address, timestamp)); |
160 | + } |
161 | + if (!volume.isTemporary()) { |
162 | + final TreeDescriptor td = new TreeDescriptor(volumeHandle, treeName); |
163 | + _handleToTreeMap.put(handle, td); |
164 | + _treeToHandleMap.put(td, handle); |
165 | + _persistit.getLogBase().recoveryRecord.log("IT", addressToString(address, timestamp), treeName, timestamp); |
166 | + } |
167 | } |
168 | |
169 | /** |
170 | |
171 | === modified file 'src/main/java/com/persistit/Transaction.java' |
172 | --- src/main/java/com/persistit/Transaction.java 2012-05-25 18:50:59 +0000 |
173 | +++ src/main/java/com/persistit/Transaction.java 2012-06-30 15:14:20 +0000 |
174 | @@ -1097,8 +1097,7 @@ |
175 | void store(Exchange exchange, Key key, Value value) throws PersistitException { |
176 | if (_nestedDepth > 0) { |
177 | checkPendingRollback(); |
178 | - final int treeHandle = _persistit.getJournalManager().handleForTree(exchange.getTree()); |
179 | - writeStoreRecordToJournal(treeHandle, key, value); |
180 | + writeStoreRecordToJournal(treeHandle(exchange.getTree()), key, value); |
181 | } |
182 | } |
183 | |
184 | @@ -1113,8 +1112,7 @@ |
185 | void remove(Exchange exchange, Key key1, Key key2) throws PersistitException { |
186 | if (_nestedDepth > 0) { |
187 | checkPendingRollback(); |
188 | - final int treeHandle = _persistit.getJournalManager().handleForTree(exchange.getTree()); |
189 | - writeDeleteRecordToJournal(treeHandle, key1, key2); |
190 | + writeDeleteRecordToJournal(treeHandle(exchange.getTree()), key1, key2); |
191 | } |
192 | } |
193 | |
194 | @@ -1127,8 +1125,7 @@ |
195 | void removeTree(Exchange exchange) throws PersistitException { |
196 | if (_nestedDepth > 0) { |
197 | checkPendingRollback(); |
198 | - final int treeHandle = _persistit.getJournalManager().handleForTree(exchange.getTree()); |
199 | - writeDeleteTreeToJournal(treeHandle); |
200 | + writeDeleteTreeToJournal(treeHandle(exchange.getTree())); |
201 | } |
202 | } |
203 | |
204 | @@ -1203,7 +1200,7 @@ |
205 | } |
206 | |
207 | synchronized void writeDeltaToJournal(final Delta delta) throws PersistitException { |
208 | - final int treeHandle = _persistit.getJournalManager().handleForTree(delta.getAccumulator().getTree()); |
209 | + final int treeHandle = treeHandle(delta.getAccumulator().getTree()); |
210 | if (delta.getValue() == 1) { |
211 | prepare(D0.OVERHEAD); |
212 | JournalRecord.putLength(_buffer, D0.OVERHEAD); |
213 | @@ -1292,6 +1289,12 @@ |
214 | + MAXIMUM_STEP); |
215 | } |
216 | } |
217 | + |
218 | + private int treeHandle(final Tree tree) { |
219 | + final int treeHandle = tree.getHandle(); |
220 | + assert treeHandle != 0 : "Undefined tree handle in " + tree; |
221 | + return treeHandle; |
222 | + } |
223 | |
224 | /** |
225 | * For unit tests only |
226 | |
227 | === modified file 'src/main/java/com/persistit/Tree.java' |
228 | --- src/main/java/com/persistit/Tree.java 2012-05-25 18:50:59 +0000 |
229 | +++ src/main/java/com/persistit/Tree.java 2012-06-30 15:14:20 +0000 |
230 | @@ -279,6 +279,17 @@ |
231 | } |
232 | |
233 | /** |
234 | + * Assign are set the tree handle. The tree must may not be a member of a |
235 | + * temporary volume. |
236 | + * |
237 | + * @throws PersistitException |
238 | + */ |
239 | + void loadHandle() throws PersistitException { |
240 | + assert !_volume.isTemporary() : "Handle allocation for temporary tree " + this; |
241 | + _persistit.getJournalManager().handleForTree(this); |
242 | + } |
243 | + |
244 | + /** |
245 | * Return an <code>Accumulator</code> for this Tree. The caller provides the |
246 | * type (SUM, MAX, MIN or SEQ) of accumulator, and an index value between 0 |
247 | * and 63, inclusive. If the <code>Tree</code> does not yet have an |
248 | |
249 | === modified file 'src/main/java/com/persistit/Volume.java' |
250 | --- src/main/java/com/persistit/Volume.java 2012-06-15 14:26:11 +0000 |
251 | +++ src/main/java/com/persistit/Volume.java 2012-06-30 15:14:20 +0000 |
252 | @@ -62,6 +62,16 @@ |
253 | private volatile VolumeStatistics _statistics; |
254 | private volatile VolumeStructure _structure; |
255 | |
256 | + /* |
257 | + * These two constants are used to identify temporary volumes that may have |
258 | + * been identified in existing journal files due to bug 1018526. They are |
259 | + * used in code to detect and remove these records. Once all existing |
260 | + * Persistit volumes sites have been cleaned up, we can remove these |
261 | + * constants and the logic that depends on them. |
262 | + */ |
263 | + private final static long TEMP_VOLUME_ID_FOR_FIXUP_DETECTION = 12345; |
264 | + private final static String TEMP_VOLUME_NAME_SUFFIX_FOR_FIXUP_DETECTION = "_temporary_volume"; |
265 | + |
266 | public static boolean isValidPageSize(final int pageSize) { |
267 | for (int b = 1024; b <= 16384; b *= 2) { |
268 | if (b == pageSize) { |
269 | @@ -71,6 +81,13 @@ |
270 | return false; |
271 | } |
272 | |
273 | + static Volume createTemporaryVolume(final Persistit persistit, final int pageSize) throws PersistitException { |
274 | + Volume volume = new Volume(Thread.currentThread().getName() + TEMP_VOLUME_NAME_SUFFIX_FOR_FIXUP_DETECTION, |
275 | + TEMP_VOLUME_ID_FOR_FIXUP_DETECTION); |
276 | + volume.openTemporary(persistit, pageSize); |
277 | + return volume; |
278 | + } |
279 | + |
280 | /** |
281 | * Construct a hollow Volume - used by JournalManager |
282 | * |
283 | @@ -289,7 +306,15 @@ |
284 | return getStructure().getDirectoryTree(); |
285 | } |
286 | |
287 | - boolean isTemporary() { // TODO |
288 | + boolean isTemporary() { |
289 | + if (_storage == null) { |
290 | + /* |
291 | + * TODO - Temporary code to detect temporary volumes on existing |
292 | + * systems to resolve side-effects of bug 1018526. |
293 | + */ |
294 | + return _id == TEMP_VOLUME_ID_FOR_FIXUP_DETECTION |
295 | + && _name.endsWith(TEMP_VOLUME_NAME_SUFFIX_FOR_FIXUP_DETECTION); |
296 | + } |
297 | return getStorage().isTemp(); |
298 | } |
299 | |
300 | |
301 | === modified file 'src/main/java/com/persistit/VolumeStructure.java' |
302 | --- src/main/java/com/persistit/VolumeStructure.java 2012-06-22 19:07:13 +0000 |
303 | +++ src/main/java/com/persistit/VolumeStructure.java 2012-06-30 15:14:20 +0000 |
304 | @@ -79,6 +79,9 @@ |
305 | _directoryTree.setRootPageAddress(rootPageAddr); |
306 | updateDirectoryTree(_directoryTree); |
307 | } |
308 | + if (!_volume.isTemporary()) { |
309 | + _directoryTree.loadHandle(); |
310 | + } |
311 | _directoryTree.setValid(); |
312 | } |
313 | |
314 | @@ -188,7 +191,9 @@ |
315 | } else { |
316 | return null; |
317 | } |
318 | - _persistit.getJournalManager().handleForTree(tree); |
319 | + if (!_volume.isTemporary()) { |
320 | + tree.loadHandle(); |
321 | + } |
322 | _treeNameHashMap.put(name, new WeakReference<Tree>(tree)); |
323 | return tree; |
324 | } |
325 | |
326 | === added file 'src/test/java/com/persistit/Bug1018526Test.java' |
327 | --- src/test/java/com/persistit/Bug1018526Test.java 1970-01-01 00:00:00 +0000 |
328 | +++ src/test/java/com/persistit/Bug1018526Test.java 2012-06-30 15:14:20 +0000 |
329 | @@ -0,0 +1,104 @@ |
330 | +/** |
331 | + * Copyright © 2011-2012 Akiban Technologies, Inc. All rights reserved. |
332 | + * |
333 | + * This program is free software: you can redistribute it and/or modify |
334 | + * it under the terms of the GNU Affero General Public License as |
335 | + * published by the Free Software Foundation, version 3 (only) of the |
336 | + * License. |
337 | + * |
338 | + * This program is distributed in the hope that it will be useful, |
339 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
340 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
341 | + * GNU Affero General Public License for more details. |
342 | + * |
343 | + * You should have received a copy of the GNU Affero General Public License |
344 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
345 | + * |
346 | + * This program may also be available under different license terms. For more |
347 | + * information, see www.akiban.com or contact licensing@akiban.com. |
348 | + */ |
349 | + |
350 | +package com.persistit; |
351 | + |
352 | +import static org.junit.Assert.*; |
353 | +import static org.junit.Assert.assertEquals; |
354 | +import static org.junit.Assert.assertTrue; |
355 | +import static org.junit.Assert.fail; |
356 | + |
357 | +import java.util.HashSet; |
358 | +import java.util.Map; |
359 | +import java.util.Set; |
360 | + |
361 | +import org.junit.Test; |
362 | + |
363 | +import com.persistit.JournalManager.TreeDescriptor; |
364 | +import com.persistit.unit.PersistitUnitTestCase; |
365 | + |
366 | +public class Bug1018526Test extends PersistitUnitTestCase { |
367 | + |
368 | + @Test |
369 | + public void tempVolumesAndTreesDoNotGetHandles() throws Exception { |
370 | + final Volume volume = _persistit.createTemporaryVolume(); |
371 | + final Exchange exchange = _persistit.getExchange(volume, "a_temp_tree", true); |
372 | + assertEquals("Handle should be 0", 0, volume.getHandle()); |
373 | + assertEquals("Handle should be 0", 0, exchange.getTree().getHandle()); |
374 | + } |
375 | + |
376 | + @Test |
377 | + public void txnOnTempVolumeDoesNotWriteToJournal() throws Exception { |
378 | + final Transaction txn = _persistit.getTransaction(); |
379 | + final JournalManager jman = _persistit.getJournalManager(); |
380 | + int failed = 0; |
381 | + for (int i = 0; i < 10; i++) { |
382 | + final long startingAddress = jman.getCurrentAddress(); |
383 | + txn.begin(); |
384 | + try { |
385 | + final Volume volume = _persistit.createTemporaryVolume(); |
386 | + final Exchange exchange = _persistit.getExchange(volume, "a_temp_tree", true); |
387 | + exchange.clear().append("abc"); |
388 | + exchange.getValue().put(RED_FOX); |
389 | + exchange.store(); |
390 | + txn.commit(); |
391 | + if (jman.getCurrentAddress() != startingAddress) { |
392 | + failed++; |
393 | + } |
394 | + } finally { |
395 | + txn.end(); |
396 | + } |
397 | + } |
398 | + /* |
399 | + * Don't require 0 because a checkpoint could write to the journal in a race |
400 | + */ |
401 | + assertTrue("Transaction on temporary volume should not have written to journal", failed < 3); |
402 | + } |
403 | + |
404 | + @Test |
405 | + public void temporaryVolumesAndTreesNotReloaded() throws Exception { |
406 | + final Set<Integer> permTreeHandleSet = new HashSet<Integer>(); |
407 | + final Volume permVolume = _persistit.getVolume("persistit"); |
408 | + _persistit.getJournalManager().unitTestAllowHandlesForTemporaryVolumesAndTrees(); |
409 | + for (int i = 0; i < 20; i++) { |
410 | + final Volume tempVolume = _persistit.createTemporaryVolume(); |
411 | + for (int j = 0; j < 10; j++) { |
412 | + final Tree tempTree = tempVolume.getTree("temp_tree_" + i + "_" + j, true); |
413 | + _persistit.getJournalManager().handleForTree(tempTree); |
414 | + final Tree permTree= permVolume.getTree("perm_tree_" + i + "_" + j, true); |
415 | + _persistit.getJournalManager().handleForTree(permTree); |
416 | + if (!permTreeHandleSet.add(permTree.getHandle())) { |
417 | + fail("Duplicate tree handle " + permTree.getHandle() + " for " + permTree); |
418 | + } |
419 | + } |
420 | + } |
421 | + final Configuration cfg = _persistit.getConfiguration(); |
422 | + _persistit.close(); |
423 | + _persistit = new Persistit(); |
424 | + _persistit.initialize(cfg); |
425 | + Map<Integer, TreeDescriptor> map = _persistit.getJournalManager().queryTreeMap(); |
426 | + for (Integer handle : permTreeHandleSet) { |
427 | + TreeDescriptor td = map.remove(handle); |
428 | + assertNotNull("Permanent Tree should be un the tree map", td); |
429 | + } |
430 | + // expect 1: the directory tree |
431 | + assertEquals("Recovered tree map should contain only permanent trees", 1, map.size()); |
432 | + } |
433 | +} |
434 | |
435 | === modified file 'src/test/java/com/persistit/Bug932097Test.java' |
436 | --- src/test/java/com/persistit/Bug932097Test.java 2012-05-25 18:50:59 +0000 |
437 | +++ src/test/java/com/persistit/Bug932097Test.java 2012-06-30 15:14:20 +0000 |
438 | @@ -24,9 +24,6 @@ |
439 | |
440 | import org.junit.Test; |
441 | |
442 | -import com.persistit.Exchange; |
443 | -import com.persistit.Persistit; |
444 | -import com.persistit.Transaction; |
445 | import com.persistit.unit.PersistitUnitTestCase; |
446 | |
447 | public class Bug932097Test extends PersistitUnitTestCase { |
After reconsideration I'm going to add code to remove temporary trees from the _handleToTreeMap on recovery. This will clean up any mess at customer sites related to this bug when we install this update and restart the server.