package voldemort.store.rocksdb; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.log4j.Logger; import org.rocksdb.ColumnFamilyDescriptor; import org.rocksdb.ColumnFamilyHandle; import org.rocksdb.ColumnFamilyOptions; import org.rocksdb.DBOptions; import org.rocksdb.RocksDB; import voldemort.routing.RoutingStrategy; import voldemort.server.VoldemortConfig; import voldemort.store.StorageConfiguration; import voldemort.store.StorageEngine; import voldemort.store.StorageInitializationException; import voldemort.store.StoreBinaryFormat; import voldemort.store.StoreDefinition; import voldemort.utils.ByteArray; public class RocksDbStorageConfiguration implements StorageConfiguration { static { RocksDB.loadLibrary(); } private final int lockStripes; public static final String TYPE_NAME = "rocksdb"; private static Logger logger = Logger.getLogger(RocksDbStorageConfiguration.class); private final VoldemortConfig voldemortconfig; private Map<String, RocksDbStorageEngine> stores = new HashMap<String, RocksDbStorageEngine>(); public RocksDbStorageConfiguration(VoldemortConfig config) { /** * - TODO 1. number of default locks need to debated. This default is * same as that of Krati's. 2. Later add the property to VoldemortConfig */ this.voldemortconfig = config; this.lockStripes = config.getAllProps().getInt("rocksdb.lock.stripes", 50); } @Override public StorageEngine<ByteArray, byte[], byte[]> getStore(StoreDefinition storeDef, RoutingStrategy strategy) { String storeName = storeDef.getName(); if(!stores.containsKey(storeName)) { String dataDir = this.voldemortconfig.getRdbDataDirectory() + "/" + storeName; new File(dataDir).mkdirs(); Properties dbProperties = parseProperties(VoldemortConfig.ROCKSDB_DB_OPTIONS); DBOptions dbOptions = (dbProperties.size() > 0) ? DBOptions.getDBOptionsFromProps(dbProperties) : new DBOptions(); if (dbOptions == null) { throw new StorageInitializationException("Unable to parse Data Base Options."); } dbOptions.setCreateIfMissing(true); dbOptions.setCreateMissingColumnFamilies(true); dbOptions.createStatistics(); Properties cfProperties = parseProperties(VoldemortConfig.ROCKSDB_CF_OPTIONS); if(this.voldemortconfig.getRocksdbPrefixKeysWithPartitionId()) { cfProperties.setProperty("prefix_extractor", "fixed:" + StoreBinaryFormat.PARTITIONID_PREFIX_SIZE); } ColumnFamilyOptions cfOptions = (cfProperties.size() > 0) ? ColumnFamilyOptions.getColumnFamilyOptionsFromProps(cfProperties) : new ColumnFamilyOptions(); if (cfOptions == null) { throw new StorageInitializationException("Unable to parse Column Family Options."); } // Create a non default Column Family tp hold the store data. List<ColumnFamilyDescriptor> descriptors = new ArrayList<ColumnFamilyDescriptor>(); descriptors.add(new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, cfOptions)); descriptors.add(new ColumnFamilyDescriptor(storeName.getBytes(), cfOptions)); List<ColumnFamilyHandle> handles = new ArrayList<ColumnFamilyHandle>(); try { RocksDB rdbStore = RocksDB.open(dbOptions, dataDir, descriptors, handles); // Dispose of the default Column Family immediately. We don't use it and if it has not been disposed // by the time the DB is closed then the RocksDB code can terminate abnormally (if the RocksDB code is // built with assertions enabled). The handle will go out of scope on its own and the Java finalizer // will (eventually) do this for us, but, that is not fast enough for the unit tests. handles.get(0).dispose(); ColumnFamilyHandle storeHandle = handles.get(1); RocksDbStorageEngine rdbStorageEngine; if(this.voldemortconfig.getRocksdbPrefixKeysWithPartitionId()) { rdbStorageEngine = new PartitionPrefixedRocksDbStorageEngine(storeName, rdbStore, storeHandle, cfOptions, lockStripes, strategy, voldemortconfig.isRocksdbEnableReadLocks()); } else { rdbStorageEngine = new RocksDbStorageEngine(storeName, rdbStore, storeHandle, cfOptions, lockStripes, voldemortconfig.isRocksdbEnableReadLocks()); } stores.put(storeName, rdbStorageEngine); } catch(Exception e) { throw new StorageInitializationException(e); } } return stores.get(storeName); } private Properties parseProperties(String prefix) { Properties properties = new Properties(); for (Map.Entry<String, String> entry : voldemortconfig.getAllProps().entrySet()) { String key = entry.getKey(); if (key.startsWith(prefix)) { properties.put(key. substring(prefix.length()), entry.getValue()); } } return properties; } @Override public String getType() { return TYPE_NAME; } @Override public void update(StoreDefinition storeDef) { throw new UnsupportedOperationException(); } @Override public void close() { for(RocksDbStorageEngine rdbStorageEngine: stores.values()) { rdbStorageEngine.close(); } stores.clear(); } @Override public void removeStorageEngine(StorageEngine<ByteArray, byte[], byte[]> engine) { RocksDbStorageEngine rdbStorageEngine = (RocksDbStorageEngine) engine; rdbStorageEngine.close(); stores.remove(rdbStorageEngine.getName()); } }