Merge lp:~nfernandez/akiban-persistit/persistit-warmup-capability into lp:akiban-persistit

Proposed by Nohemi Fernandez
Status: Merged
Approved by: Peter Beaman
Approved revision: 363
Merged at revision: 352
Proposed branch: lp:~nfernandez/akiban-persistit/persistit-warmup-capability
Merge into: lp:akiban-persistit
Diff against target: 438 lines (+259/-11)
6 files modified
src/main/java/com/persistit/BufferPool.java (+114/-9)
src/main/java/com/persistit/Configuration.java (+66/-1)
src/main/java/com/persistit/Persistit.java (+10/-0)
src/test/java/com/persistit/WarmupTest.java (+66/-0)
src/test/java/com/persistit/unit/PersistitUnitTestCase.java (+2/-1)
src/test/java/com/persistit/unit/UnitTestProperties.java (+1/-0)
To merge this branch: bzr merge lp:~nfernandez/akiban-persistit/persistit-warmup-capability
Reviewer Review Type Date Requested Status
Peter Beaman Approve
Nohemi Fernandez (community) Needs Resubmitting
Review via email: mp+117780@code.launchpad.net

Description of the change

Optimization: Creates a file of sample page data to insert into the next 'cold' start of Persistit.

To post a comment you must log in.
Revision history for this message
Nohemi Fernandez (nfernandez) wrote :

Some optimizations to consider for this branch:

1. Add checks for corrupt files
2. Store temporary back-up file of data in case of a system crash
3. Define names for multiple buffer pools-- this will be implemented if we switch from the current file I/O implementation to one using Exchange objects.

356. By Nohemi Fernandez

revert changes to irrelevant files

357. By Nohemi Fernandez

merge from trunk

358. By Nohemi Fernandez

clean-up diff

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

I've been thinking some more about the general problem and have some suggestions.

This version represents good progress. It passes all tests and makes the warmup process optional.

Here are some additional issues I'd like to address, perhaps over time:

Writing the buffer pool inventory should be optional. I suggest using the same configuration property, isBufferWarmupEnabled(), to control the polling action. I also think we might be able to put the inventory-writing activity on the CHECKPOINT_MANAGER thread since it does work only about once every two minutes. The Javadoc for setBufferWarmupEnabled(boolean) says the property is settable from the Management class. We would want to actually make it so.

When we load a large buffer pool, say 1M pages of 16Kb each (which we already have on largish machines) it will be important to optimize the order in which these are read to reduce physical I/O delays. About the best we can do is to read pages in file offset order which is approximately linear with page address order - "approximately" because some pages may be read from the journal instead of the volume file. We probably want to sort the list of pages being read by physical file address order before reading anything.

We may benefit from repopulating the BufferPool in "clock" order. This will generally not be the same order in which we load the pages. The idea would be to read the pages in physical file order but put them in the _buffers array in an order that approximates their distance from the "clock" pointer at the time the inventory was taken. To do this we'll need a method other than BufferPool#get(...) to read and install the pages. I'm giving this some thought and will probably propose something later. We can do without this for now.

I wonder if there is any benefit in reloading only pages that have been in the BufferPool for awhile. For example, if we inventory the BufferPool during a data load operating we'll record lots of pages in the warmup file that when reloaded will never be used again. One possibility is to snapshot the inventory somewhat more frequently, say once per minute, and only include pages that have been in place for two or three cycles.

Small detail - should have brought this up earlier. Each Volume in the BufferPool has a small integer handle available through Volume#getHandle(). This is a good alternative to recording the volume name.

Finally, we can't put this in trunk without resolving where the warmup file goes; clearly we can't keep it in a fixed location in the /tmp directory. I think if we can resolve the file location, make the writing of the file optional through configuration, fix the Javadoc for setBufferWarmupEnabled not to refer to a method that currently does not exist in Management, we could probably merge this and at least try it in some of our bigger tests.

359. By Nohemi Fernandez

change configuration property for buffer pools

360. By Nohemi Fernandez

add configuration setting for warm-up polling time

Revision history for this message
Nohemi Fernandez (nfernandez) wrote :

I added configuration properties for the buffer pool warm-up as follows:
1. "bufferinventory" --> path name where buffer pool information will be stored, if null then pool does not get warmed up
2. "bufferpollinginterval" -> number of seconds for polling

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

Does the PAGE_CACHER thread try to call populateWarmuFile using a path of null when? I didn't see logic to disable the thread when the bufferinventoryfile property is missing.

review: Needs Information
Revision history for this message
Nohemi Fernandez (nfernandez) wrote :

316 + if (_configuration.getBufferInventoryPathName() != null) {
317 + warmupBufferPools();
318 + }

The page cacher thread is not started unless the above method is called, so the path in BufferPool is unnecessary (null) if there is no path name in the configuration.

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

Suggestions:

- Merge from trunk
- Range-check the buffer poll interval - buffer inventory poll interval. Decide and document in javadoc whether it is specified in ms, seconds or minutes.
- Move property from PersistitUnitTestCase to UnitTestProperties

361. By Nohemi Fernandez

range check buffer inventory poll interval

362. By Nohemi Fernandez

merge from trunk

363. By Nohemi Fernandez

update WarmupTest header

Revision history for this message
Nohemi Fernandez (nfernandez) wrote :

Suggestions followed.

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

Looks okay to me. Thanks for the changes.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/main/java/com/persistit/BufferPool.java'
2--- src/main/java/com/persistit/BufferPool.java 2012-08-04 19:19:44 +0000
3+++ src/main/java/com/persistit/BufferPool.java 2012-08-07 21:14:18 +0000
4@@ -16,7 +16,6 @@
5 package com.persistit;
6
7 import java.io.DataOutputStream;
8-import java.io.IOException;
9 import java.nio.ByteBuffer;
10 import java.util.Arrays;
11 import java.util.HashSet;
12@@ -37,6 +36,7 @@
13 import com.persistit.exception.VolumeClosedException;
14 import com.persistit.util.Debug;
15 import com.persistit.util.Util;
16+import java.io.*;
17
18 /**
19 * A pool of {@link Buffer} objects, maintained on various lists that permit
20@@ -52,7 +52,7 @@
21 private final static long DEFAULT_WRITER_POLL_INTERVAL = 5000;
22
23 private final static int PAGE_WRITER_TRANCHE_SIZE = 5000;
24-
25+
26 /**
27 * Sleep time when buffers are exhausted
28 */
29@@ -196,11 +196,24 @@
30 private volatile long _writerPollInterval = DEFAULT_WRITER_POLL_INTERVAL;
31
32 private volatile int _pageWriterTrancheSize = PAGE_WRITER_TRANCHE_SIZE;
33+
34+ /**
35+ * Polling interval for PageCacher
36+ */
37+ private volatile long _cacherPollInterval;
38+
39 /**
40 * The PAGE_WRITER IOTaskRunnable
41 */
42 private PageWriter _writer;
43-
44+
45+ /**
46+ * The PAGE_CACHER IOTaskRunnable
47+ */
48+ private PageCacher _cacher;
49+
50+ private String _defaultLogPath;
51+
52 /**
53 * Construct a BufferPool with the specified count of <code>Buffer</code>s
54 * of the specified size.
55@@ -237,7 +250,7 @@
56 _hashTable = new Buffer[_bufferCount * HASH_MULTIPLE];
57 _hashLocks = new ReentrantLock[HASH_LOCKS];
58 _maxKeys = (_bufferSize - Buffer.HEADER_SIZE) / Buffer.MAX_KEY_RATIO;
59-
60+
61 for (int index = 0; index < HASH_LOCKS; index++) {
62 _hashLocks[index] = new ReentrantLock();
63 }
64@@ -271,25 +284,59 @@
65 throw e;
66 }
67 _writer = new PageWriter();
68-
69- }
70-
71- void startThreads() {
72+ _cacher = new PageCacher();
73+ }
74+
75+ void warmupBufferPool(String pathName, String fname) throws PersistitException {
76+ File file = new File(pathName, fname + ".log");
77+ _defaultLogPath = file.getAbsolutePath();
78+
79+ try {
80+ if (!file.exists()) {
81+ file.createNewFile();
82+ }
83+
84+ BufferedReader reader = new BufferedReader(new FileReader(file));
85+ String currLine;
86+ while ((currLine = reader.readLine()) != null) {
87+ String[] info = currLine.split(" ");
88+ if (info.length == 2) {
89+ Volume vol = _persistit.getVolume(info[1]);
90+ if (vol != null) {
91+ long page = Long.parseLong(info[0]);
92+ Buffer buff = get(vol, page, false, true);
93+ buff.release();
94+ }
95+ }
96+ }
97+ reader.close();
98+ _cacherPollInterval = _persistit.getConfiguration().getBufferInventoryPollingInterval();
99+ _cacher.start();
100+ }
101+ catch (IOException e) {
102+ throw new PersistitException(e);
103+ }
104+ }
105+
106+ void startThreads() throws PersistitException {
107 _writer.start();
108 }
109
110 void close() {
111 _closed.set(true);
112 _persistit.waitForIOTaskStop(_writer);
113+ _persistit.waitForIOTaskStop(_cacher);
114 _writer = null;
115+ _cacher = null;
116 }
117
118 /**
119- * Abruptly stop (using {@link Thread#stop()}) the writer and collector
120+ * Abruptly stop (using {@link Thread#stop()}) the writer, cacher, and collector
121 * threads. This method should be used only by tests.
122 */
123 void crash() {
124 IOTaskRunnable.crash(_writer);
125+ IOTaskRunnable.crash(_cacher);
126 }
127
128 void flush(final long timestamp) throws PersistitInterruptedException {
129@@ -380,6 +427,35 @@
130 buffer.populateInfo(array[index]);
131 }
132 }
133+
134+ private void populateWarmupFile() throws PersistitException {
135+ File file = new File(_defaultLogPath);
136+
137+ try {
138+ BufferedWriter writer = new BufferedWriter(new FileWriter(file));
139+ for (int i = 0; i < _buffers.length; ++i) {
140+ Buffer b = _buffers[i];
141+ if (b != null && b.isValid() && !b.isDirty()) {
142+ long page = b.getPageAddress();
143+ Volume volume = b.getVolume();
144+ long page2 = b.getPageAddress();
145+ Volume volume2 = b.getVolume();
146+
147+ // Check if buffer has changed while reading
148+ if (page == page2 && volume == volume2 && volume != null) {
149+ String addr = Long.toString(page);
150+ String vol = volume.getName();
151+ writer.append(addr + " " + vol);
152+ writer.newLine();
153+ writer.flush();
154+ }
155+ }
156+ }
157+ writer.close();
158+ } catch (IOException e) {
159+ throw new PersistitException(e);
160+ }
161+ }
162
163 private boolean selected(Buffer buffer, int includeMask, int excludeMask) {
164 return ((includeMask == 0) || (buffer.getStatus() & includeMask) != 0)
165@@ -1315,6 +1391,35 @@
166 return isFlushing() ? 0 : _writerPollInterval;
167 }
168 }
169+
170+ /**
171+ * Implementation of PAGE_CACHER thread
172+ */
173+ class PageCacher extends IOTaskRunnable {
174+
175+ PageCacher() {
176+ super(BufferPool.this._persistit);
177+ }
178+
179+ void start() {
180+ start("PAGE_CACHER:" + _bufferSize, _cacherPollInterval);
181+ }
182+
183+ @Override
184+ public void runTask() throws Exception {
185+ populateWarmupFile();
186+ }
187+
188+ @Override
189+ protected boolean shouldStop() {
190+ return _closed.get() && !isFlushing();
191+ }
192+
193+ @Override
194+ protected long pollInterval() {
195+ return isFlushing() ? 0 : _cacherPollInterval;
196+ }
197+ }
198
199 @Override
200 public String toString() {
201
202=== modified file 'src/main/java/com/persistit/Configuration.java'
203--- src/main/java/com/persistit/Configuration.java 2012-08-02 14:19:26 +0000
204+++ src/main/java/com/persistit/Configuration.java 2012-08-07 21:14:18 +0000
205@@ -264,15 +264,23 @@
206 * Property name for the "append only" property.
207 */
208 public final static String APPEND_ONLY_PROPERTY = "appendonly";
209-
210+
211 /**
212 * Property name for the "ignore missing volumes" property.
213 */
214 public final static String IGNORE_MISSING_VOLUMES_PROPERTY = "ignoremissingvolumes";
215+
216 /**
217 * Property name to specify the default {@link SplitPolicy}.
218 */
219 public final static String SPLIT_POLICY_PROPERTY_NAME = "splitpolicy";
220+
221+ /**
222+ * Property name to specify the"buffer inventory" property name.
223+ */
224+ public final static String BUFFER_INVENTORY_PROPERTY_NAME = "bufferinventory";
225+
226+ public final static String BUFFER_POLLING_INTERVAL_PROPERTY = "bufferpollinginterval";
227
228 /**
229 * Property name to specify the default {@link JoinPolicy}.
230@@ -624,6 +632,8 @@
231 private int rmiServerPort;
232 private boolean jmx = true;
233 private boolean appendOnly;
234+ private String bufferInventoryPathName;
235+ private long bufferInventoryPollInterval = 3000000; // default five minute polling
236 private boolean ignoreMissingVolumes;
237 private String tmpVolDir;
238 private int tmpVolPageSize;
239@@ -702,6 +712,8 @@
240 }
241
242 void loadProperties() throws InvalidVolumeSpecificationException {
243+ setBufferInventoryPathName(getProperty(BUFFER_INVENTORY_PROPERTY_NAME));
244+ setBufferInventoryPollingInterval(getLongProperty(BUFFER_POLLING_INTERVAL_PROPERTY, bufferInventoryPollInterval));
245 setAppendOnly(getBooleanProperty(APPEND_ONLY_PROPERTY, false));
246 setCommitPolicy(getProperty(COMMIT_POLICY_PROPERTY_NAME));
247 setConstructorOverride(getBooleanProperty(CONSTRUCTOR_OVERRIDE_PROPERTY_NAME, false));
248@@ -1802,6 +1814,59 @@
249 }
250
251 /**
252+ * Return the path name defined by {@link #getBufferInventoryPathName}
253+ * @return the path where file to warm-up Persistit with sample buffer data is stored
254+ */
255+ public String getBufferInventoryPathName() {
256+ return bufferInventoryPathName;
257+ }
258+
259+ /**
260+ * <p>
261+ * Control where Persistit stores its buffer inventory. In this mode
262+ * Persistit restarts with information from the last run. This method initializes
263+ * the warm-up file at the specified location, if none is specified the buffer
264+ * pool is not warmed up on start-up.
265+ * </p>
266+ * <p>
267+ * Default value is <code>null</code><br />
268+ * Property name is {@value #BUFFER_INVENTORY_PROPERTY_NAME}
269+ * </p>
270+ *
271+ * @param pathName
272+ * the name of the path to the warm-up file
273+ */
274+ public void setBufferInventoryPathName(String pathName) {
275+ bufferInventoryPathName = pathName;
276+
277+ }
278+
279+ /**
280+ * Return polling interval defined by {@link #getBufferInventoryPollingInterval}
281+ * @return the number of seconds wait between warm-up polls
282+ */
283+ public long getBufferInventoryPollingInterval() {
284+ return bufferInventoryPollInterval;
285+ }
286+
287+ /**
288+ * <p>
289+ * Control the number of seconds between each poll for the
290+ * cache warm-up option in Persistit.
291+ * </p>
292+ * <p>
293+ * Default value is <code>3000</code><br />
294+ * Property name is {@value #BUFFER_POLLING_INTERVAL_PROPERTY}
295+ * </p>
296+ *
297+ * @param seconds
298+ * the number of seconds between polls
299+ */
300+ public void setBufferInventoryPollingInterval(long seconds) {
301+ bufferInventoryPollInterval = Util.rangeCheck(seconds, 60L, Long.MAX_VALUE) * 1000L;
302+ }
303+
304+ /**
305 * Return the value defined by {@link #setIgnoreMissingVolumes(boolean)}
306 *
307 * @return the whether to start Persistit in ignore-missing-volumes mode
308
309=== modified file 'src/main/java/com/persistit/Persistit.java'
310--- src/main/java/com/persistit/Persistit.java 2012-08-02 14:19:26 +0000
311+++ src/main/java/com/persistit/Persistit.java 2012-08-07 21:14:18 +0000
312@@ -589,6 +589,9 @@
313 initializeVolumes();
314 startJournal();
315 startBufferPools();
316+ if (_configuration.getBufferInventoryPathName() != null) {
317+ warmupBufferPools();
318+ }
319 finishRecovery();
320 startCheckpointManager();
321 startTransactionIndexPollTask();
322@@ -716,6 +719,13 @@
323 pool.startThreads();
324 }
325 }
326+
327+ void warmupBufferPools() throws PersistitException {
328+ String pathName = _configuration.getBufferInventoryPathName();
329+ for (final BufferPool pool : _bufferPoolTable.values()) {
330+ pool.warmupBufferPool(pathName, pool.toString());
331+ }
332+ }
333
334 void startJournal() throws PersistitException {
335 _journalManager.startJournal();
336
337=== added file 'src/test/java/com/persistit/WarmupTest.java'
338--- src/test/java/com/persistit/WarmupTest.java 1970-01-01 00:00:00 +0000
339+++ src/test/java/com/persistit/WarmupTest.java 2012-08-07 21:14:18 +0000
340@@ -0,0 +1,66 @@
341+/**
342+ * Copyright © 2012 Akiban Technologies, Inc. All rights reserved.
343+ *
344+ * This program and the accompanying materials are made available
345+ * under the terms of the Eclipse Public License v1.0 which
346+ * accompanies this distribution, and is available at
347+ * http://www.eclipse.org/legal/epl-v10.html
348+ *
349+ * This program may also be available under different license terms.
350+ * For more information, see www.akiban.com or contact licensing@akiban.com.
351+ *
352+ * Contributors:
353+ * Akiban Technologies, Inc.
354+ */
355+
356+package com.persistit;
357+
358+import static org.junit.Assert.*;
359+import java.util.Properties;
360+
361+import org.junit.Test;
362+
363+import com.persistit.unit.PersistitUnitTestCase;
364+
365+public class WarmupTest extends PersistitUnitTestCase {
366+
367+ @Test
368+ public void testWarmup() throws Exception {
369+ Exchange ex = _persistit.getExchange("persistit", "WarmupTest", true);
370+ for (int i = 1; i <= 1000; i++) {
371+ ex.getValue().put(RED_FOX);
372+ ex.clear().append(i).store();
373+ }
374+
375+ // Assumption: only one buffer pool is created
376+ int poolCount = 0;
377+ String pathName = "";
378+ Buffer[] buff = new Buffer[100];
379+ for (BufferPool p: _persistit.getBufferPoolHashMap().values()) {
380+ poolCount = p.getBufferCount();
381+ pathName = p.toString();
382+ for (int i = 0; i < poolCount; ++i) {
383+ buff[i] = p.getBufferCopy(i);
384+ }
385+ }
386+
387+ Properties properties = _persistit.getProperties();
388+ ex = null;
389+ _persistit.close();
390+
391+ _persistit = new Persistit();
392+ _persistit.initialize(properties);
393+
394+ int poolCount1 = 0;
395+ for (BufferPool p: _persistit.getBufferPoolHashMap().values()) {
396+ poolCount1 = p.getBufferCount();
397+ for (int i = 0; i < poolCount1; ++i) {
398+ Buffer bufferCopy = p.getBufferCopy(i);
399+ assertEquals(bufferCopy.getPageAddress(), buff[i].getPageAddress());
400+ assertEquals(bufferCopy.getPageType(), buff[i].getPageType());
401+ assertEquals(bufferCopy.getBufferSize(), buff[i].getBufferSize());
402+ }
403+ }
404+ assertEquals(poolCount, poolCount1);
405+ }
406+}
407
408=== modified file 'src/test/java/com/persistit/unit/PersistitUnitTestCase.java'
409--- src/test/java/com/persistit/unit/PersistitUnitTestCase.java 2012-08-02 04:45:28 +0000
410+++ src/test/java/com/persistit/unit/PersistitUnitTestCase.java 2012-08-07 21:14:18 +0000
411@@ -53,6 +53,7 @@
412 @Before
413 public void setUp() throws Exception {
414 checkNoPersistitThreads();
415+
416 _persistit.initialize(getProperties(true));
417 }
418
419@@ -83,7 +84,7 @@
420 } catch (final Throwable t) {
421 t.printStackTrace();
422 } finally {
423- tearDown();
424+ tearDown();
425 }
426 }
427
428
429=== modified file 'src/test/java/com/persistit/unit/UnitTestProperties.java'
430--- src/test/java/com/persistit/unit/UnitTestProperties.java 2012-08-02 04:45:28 +0000
431+++ src/test/java/com/persistit/unit/UnitTestProperties.java 2012-08-07 21:14:18 +0000
432@@ -43,6 +43,7 @@
433 p.setProperty("tmpvoldir", "${datapath}");
434 p.setProperty("rmiport", System.getProperty("rmiport", "8081"));
435 p.setProperty("jmx", "true");
436+ p.setProperty("bufferinventory", "/tmp/persistit_test_data");
437 return p;
438 }
439

Subscribers

People subscribed via source and target branches