Commit 9b9b7286 authored by Donald H. (Donnie) Pinkston, III's avatar Donald H. (Donnie) Pinkston, III
Browse files

Buffer Manager now listens to property changes

The Buffer Manager wasn't tied into the property-registry mechanism,
so it wouldn't notice when the page-cache size was changed.  This is
now hooked together with a property-observer interface.
parent eb289261
No related merge requests found
Showing with 163 additions and 138 deletions
+163 -138
......@@ -72,69 +72,6 @@ public class NanoDBServer implements ServerProperties {
private ReentrantReadWriteLock schemaLock;
/**
* Initialize all of the properties that the database server knows about.
* Some of these properties are read-write, and others are read-only and
* are initialized right at database startup, before any other steps
* occur.
*/
private void initProperties() {
propertyRegistry.addProperty(PROP_BASE_DIRECTORY,
new StringValueValidator(), DEFAULT_BASE_DIRECTORY,
/* readonly */ true);
propertyRegistry.addProperty(PROP_PAGECACHE_SIZE,
new IntegerValueValidator(MIN_PAGECACHE_SIZE, MAX_PAGECACHE_SIZE),
DEFAULT_PAGECACHE_SIZE);
propertyRegistry.addProperty(PROP_PAGECACHE_POLICY,
new StringEnumValidator(PAGECACHE_POLICY_VALUES),
DEFAULT_PAGECACHE_POLICY);
propertyRegistry.addProperty(PROP_PAGE_SIZE,
new IntegerValueValidator(DBFile::isValidPageSize,
"Specified page-size %d is invalid."), DEFAULT_PAGE_SIZE);
propertyRegistry.addProperty(PROP_ENABLE_TRANSACTIONS,
new BooleanFlagValidator(), false, /* readonly */ true);
propertyRegistry.addProperty(PROP_ENFORCE_KEY_CONSTRAINTS,
new BooleanFlagValidator(), true);
propertyRegistry.addProperty(PROP_ENABLE_INDEXES,
new BooleanFlagValidator(), false, /* reaadonly */ true);
propertyRegistry.addProperty(PROP_CREATE_INDEXES_ON_KEYS,
new BooleanFlagValidator(), false);
propertyRegistry.addProperty(PROP_PLANNER_CLASS,
new PlannerClassValidator(), DEFAULT_PLANNER_CLASS);
propertyRegistry.addProperty(PROP_FLUSH_AFTER_CMD,
new BooleanFlagValidator(), true);
}
/**
* This helper function sets the NanoDB server properties based on the
* contents of a Java {@code Properties} object. This allows us to set
* NanoDB properties from system properties and/or other sources of
* properties.
*
* @param properties the properties to apply to NanoDB's configuration.
*/
private void setProperties(Properties properties) {
if (properties == null)
throw new IllegalArgumentException("properties cannot be null");
for (String name : properties.stringPropertyNames()) {
if (propertyRegistry.hasProperty(name))
propertyRegistry.setPropertyValue(name,
properties.getProperty(name));
}
}
/**
* This static method encapsulates all of the operations necessary for
* cleanly starting the NanoDB server. Database server properties are
......@@ -166,13 +103,12 @@ public class NanoDBServer implements ServerProperties {
// Everything needs configuration.
propertyRegistry = new PropertyRegistry();
initProperties();
// Apply system properties first (populated from e.g. command line),
// then override with any arguments to this function.
setProperties(System.getProperties());
propertyRegistry.setProperties(System.getProperties());
if (initialProperties != null)
setProperties(initialProperties);
propertyRegistry.setProperties(initialProperties);
propertyRegistry.setupCompleted();
......
package edu.caltech.nanodb.server.properties;
/**
* An interface that components can implement to be notified of changes to
* property-values during system operation.
*/
public interface PropertyObserver {
/**
* This method is called on all property observers when a given property
* is changed to a new value.
*
* @param propertyName the name of the property that was changed
*
* @param newValue the new value of the property
*/
void propertyChanged(String propertyName, Object newValue);
}
package edu.caltech.nanodb.server.properties;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import edu.caltech.nanodb.storage.DBFile;
/**
* This is the central location where all properties exposed by the database
......@@ -12,7 +16,7 @@ import java.util.concurrent.ConcurrentHashMap;
* configurable properties with the registry, and then they can be accessed
* and/or set from the SQL prompt.
*/
public class PropertyRegistry {
public class PropertyRegistry implements ServerProperties {
private class PropertyDescriptor {
/** The name of the property. */
......@@ -69,6 +73,13 @@ public class PropertyRegistry {
new ConcurrentHashMap<>();
/**
* A collection of property observers to be informed when property values
* change.
*/
private ArrayList<PropertyObserver> observers = new ArrayList<>();
/**
* This flag indicates whether the database is still in "start-up mode"
* or not. During start-up mode, all read-only properties may also be
......@@ -77,6 +88,75 @@ public class PropertyRegistry {
private boolean setup = true;
public PropertyRegistry() {
initProperties();
}
/**
* Initialize all of the properties that the database server knows about.
* Some of these properties are read-write, and others are read-only and
* are initialized right at database startup, before any other steps
* occur.
*
* @review (donnie) It's not great that this configuration is in here.
* Someday, should probably migrate it back into
* {@code NanoDBServer}.
*/
private void initProperties() {
addProperty(PROP_BASE_DIRECTORY,
new StringValueValidator(), DEFAULT_BASE_DIRECTORY,
/* readonly */ true);
addProperty(PROP_PAGECACHE_SIZE,
new IntegerValueValidator(MIN_PAGECACHE_SIZE, MAX_PAGECACHE_SIZE),
DEFAULT_PAGECACHE_SIZE);
addProperty(PROP_PAGECACHE_POLICY,
new StringEnumValidator(PAGECACHE_POLICY_VALUES),
DEFAULT_PAGECACHE_POLICY, /* readonly */ true);
addProperty(PROP_PAGE_SIZE,
new IntegerValueValidator(DBFile::isValidPageSize,
"Specified page-size %d is invalid."), DEFAULT_PAGE_SIZE);
addProperty(PROP_ENABLE_TRANSACTIONS,
new BooleanFlagValidator(), false, /* readonly */ true);
addProperty(PROP_ENFORCE_KEY_CONSTRAINTS,
new BooleanFlagValidator(), true);
addProperty(PROP_ENABLE_INDEXES,
new BooleanFlagValidator(), false, /* reaadonly */ true);
addProperty(PROP_CREATE_INDEXES_ON_KEYS,
new BooleanFlagValidator(), false);
addProperty(PROP_PLANNER_CLASS,
new PlannerClassValidator(), DEFAULT_PLANNER_CLASS);
addProperty(PROP_FLUSH_AFTER_CMD, new BooleanFlagValidator(), true);
}
/**
* This helper function sets the server properties based on the contents
* of a Java {@code Properties} object. This allows us to set NanoDB
* properties from system properties and/or other sources of properties.
*
* @param properties the properties to apply to NanoDB's configuration.
*/
public void setProperties(Properties properties) {
if (properties == null)
throw new IllegalArgumentException("properties cannot be null");
for (String name : properties.stringPropertyNames()) {
if (hasProperty(name))
setPropertyValue(name, properties.getProperty(name));
}
}
/**
* Records that setup has been completed, and read-only properties
* should no longer be allowed to change.
......@@ -86,6 +166,19 @@ public class PropertyRegistry {
}
/**
* Records a property-change observer on the property registry.
*
* @param observer the observer to receive property-change notifications
*/
public void addObserver(PropertyObserver observer) {
if (observer == null)
throw new IllegalArgumentException("observer cannot be null");
observers.add(observer);
}
/**
* Add a read-only or read-write property to the registry, along with a
* type and an initial value.
......@@ -168,6 +261,9 @@ public class PropertyRegistry {
}
properties.get(name).setValue(value);
for (PropertyObserver obs : observers)
obs.propertyChanged(name, value);
}
......
......@@ -14,6 +14,9 @@ import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import edu.caltech.nanodb.server.SessionState;
import edu.caltech.nanodb.server.properties.PropertyObserver;
import edu.caltech.nanodb.server.properties.PropertyRegistry;
import edu.caltech.nanodb.server.properties.ServerProperties;
/**
......@@ -29,35 +32,15 @@ import edu.caltech.nanodb.server.SessionState;
*/
public class BufferManager {
/** The default page-cache size is defined to be 20MiB. */
public static final int DEFAULT_PAGECACHE_SIZE = 20 * 1024 * 1024;
/**
* The minimum page-cache size is defined to be 32KiB, which is really
* only sufficient to run smaller queries when page-pinning is working
* properly.
*/
public static final int MIN_PAGECACHE_SIZE = 32 * 1024;
/**
* The system property that can be used to specify the page replacement
* policy in the buffer manager.
*/
public static final String PROP_PAGECACHE_POLICY = "nanodb.pagecache.policy";
/** The default page-cache policy is LRU. */
public static final String DEFAULT_PAGECACHE_POLICY = "lru";
/**
* This helper class keeps track of a data page that is currently cached.
*/
private static class CachedPageInfo {
public DBFile dbFile;
DBFile dbFile;
public int pageNo;
int pageNo;
public CachedPageInfo(DBFile dbFile, int pageNo) {
CachedPageInfo(DBFile dbFile, int pageNo) {
if (dbFile == null)
throw new IllegalArgumentException("dbFile cannot be null");
......@@ -154,34 +137,39 @@ public class BufferManager {
/**
* A string indicating the buffer manager's page replacement policy.
* Currently it can be "lru" or "fifo".
* Currently it can be "LRU" or "FIFO".
*/
private String replacementPolicy;
public BufferManager(FileManager fileManager) {
this.fileManager = fileManager;
private class BufferPropertyObserver
implements PropertyObserver, ServerProperties {
public void propertyChanged(String propertyName, Object newValue) {
// We only care about the pagecache-size value.
if (PROP_PAGECACHE_SIZE.equals(propertyName)) {
setMaxCacheSize((Integer) newValue);
}
}
}
maxCacheSize = DEFAULT_PAGECACHE_SIZE;
/* TODO: Factor out the replacement policy implementation so that
* it's easier to replace/configure in the future.
*/
replacementPolicy = configureReplacementPolicy();
cachedPages = new LinkedHashMap<>(16, 0.75f, "lru".equals(replacementPolicy));
totalBytesCached = 0;
public BufferManager(FileManager fileManager,
PropertyRegistry propertyRegistry) {
this.fileManager = fileManager;
propertyRegistry.addObserver(new BufferPropertyObserver());
/* TODO: Get rid of this. Something outside of this class should
* configure it when it is initialized.
maxCacheSize = propertyRegistry.getIntProperty(
ServerProperties.PROP_PAGECACHE_SIZE);
if (server != null) {
// Register properties that the Buffer Manager exposes.
server.getPropertyRegistry().registerProperties(
new BufferManagerPropertyHandler(),
PROP_PAGECACHE_POLICY, PROP_PAGECACHE_SIZE);
}
*/
// TODO: Factor out the replacement policy implementation so that
// it's easier to replace/configure in the future.
replacementPolicy = propertyRegistry.getStringProperty(
ServerProperties.PROP_PAGECACHE_POLICY);
cachedPages =
new LinkedHashMap<>(16, 0.75f, "LRU".equals(replacementPolicy));
totalBytesCached = 0;
}
......@@ -193,9 +181,10 @@ public class BufferManager {
* @param maxCacheSize the maximum size for the buffer cache.
*/
public void setMaxCacheSize(int maxCacheSize) {
if (maxCacheSize < MIN_PAGECACHE_SIZE) {
if (maxCacheSize < ServerProperties.MIN_PAGECACHE_SIZE) {
throw new IllegalArgumentException(
"maxCacheSize must be at least " + MIN_PAGECACHE_SIZE);
"maxCacheSize must be at least " +
ServerProperties.MIN_PAGECACHE_SIZE);
}
synchronized (guard) {
......@@ -223,21 +212,6 @@ public class BufferManager {
}
private String configureReplacementPolicy() {
String str = DEFAULT_PAGECACHE_POLICY;
str = str.trim().toLowerCase();
if (!("lru".equals(str) || "fifo".equals(str))) {
logger.error(String.format(
"Unrecognized value \"%s\" for page-cache replacement " +
"policy; using default value of LRU.",
System.getProperty(PROP_PAGECACHE_POLICY)));
}
return str;
}
/**
* Add another observer to the buffer manager.
*
......
......@@ -13,7 +13,6 @@ import edu.caltech.nanodb.indexes.IndexManager;
import edu.caltech.nanodb.indexes.IndexUpdater;
import edu.caltech.nanodb.relations.DatabaseConstraintEnforcer;
import edu.caltech.nanodb.server.EventDispatcher;
import edu.caltech.nanodb.server.NanoDBException;
import edu.caltech.nanodb.server.NanoDBServer;
import edu.caltech.nanodb.server.properties.PropertyRegistry;
import edu.caltech.nanodb.server.properties.ServerProperties;
......@@ -22,7 +21,8 @@ import edu.caltech.nanodb.transactions.TransactionManager;
/**
*
* The Storage Manager provides facilities for managing files of tuples,
* including in-memory buffering of data pages and support for transactions.
*
* @todo This class requires synchronization, once we support multiple clients.
*/
......@@ -32,11 +32,6 @@ public class StorageManager {
private static Logger logger = LogManager.getLogger(StorageManager.class);
/*========================================================================
* STATIC FIELDS AND METHODS
*/
/*========================================================================
* NON-STATIC FIELDS AND METHODS
*/
......@@ -139,7 +134,7 @@ public class StorageManager {
logger.info("Using base directory " + baseDir);
fileManager = new FileManagerImpl(baseDir);
bufferManager = new BufferManager(fileManager);
bufferManager = new BufferManager(fileManager, serverProps);
tupleFileManagers.put(DBFileType.HEAP_TUPLE_FILE,
new HeapTupleFileManager(this));
......
......@@ -9,6 +9,7 @@ import org.apache.commons.io.FileUtils;
import static org.mockito.Mockito.*;
import edu.caltech.nanodb.server.properties.PropertyRegistry;
import edu.caltech.test.nanodb.framework.Concurrent;
import org.testng.annotations.Test;
......@@ -34,7 +35,8 @@ public class TestBufferManager extends StorageTestCase {
FileUtils.cleanDirectory(testBaseDir);
FileManager fileMgr = spy(new FileManagerImpl(testBaseDir));
BufferManager bufMgr = new BufferManager(fileMgr);
BufferManager bufMgr =
new BufferManager(fileMgr, new PropertyRegistry());
DBFile file = fileMgr.createDBFile("TestBufferManager_testBuffering",
DBFileType.TEST_FILE, 4096);
......@@ -72,7 +74,8 @@ public class TestBufferManager extends StorageTestCase {
FileUtils.cleanDirectory(testBaseDir);
FileManager fileMgr = spy(new FileManagerImpl(testBaseDir));
BufferManager bufMgr = new BufferManager(fileMgr);
BufferManager bufMgr =
new BufferManager(fileMgr, new PropertyRegistry());
// Set the Buffer Manager's pool size to 4MiB. This will force a
// significant number of pool evictions during the test.
......
package edu.caltech.test.nanodb.storage;
import edu.caltech.nanodb.server.properties.PropertyRegistry;
import org.testng.annotations.Test;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.AfterClass;
......@@ -48,7 +49,8 @@ public class TestDBPage extends StorageTestCase {
public void beforeClass() {
fileMgr = new FileManagerImpl(testBaseDir);
BufferManager bufMgr = new BufferManager(fileMgr);
BufferManager bufMgr =
new BufferManager(fileMgr, new PropertyRegistry());
// Get DBFile
DBFileType type = DBFileType.HEAP_TUPLE_FILE;
......
......@@ -4,6 +4,7 @@ package edu.caltech.test.nanodb.storage;
import java.io.File;
import java.io.IOException;
import edu.caltech.nanodb.server.properties.PropertyRegistry;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
......@@ -29,7 +30,7 @@ public class TestFileManager extends StorageTestCase {
@BeforeClass
public void beforeClass() {
fileMgr = new FileManagerImpl(testBaseDir);
bufMgr = new BufferManager(fileMgr);
bufMgr = new BufferManager(fileMgr, new PropertyRegistry());
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment