Skip to content

Commit

Permalink
Changed ConfigurationStore to save temp keys inside .temp directory.
Browse files Browse the repository at this point in the history
  • Loading branch information
bbansal committed Nov 21, 2009
1 parent 4269e6b commit 5ea0bc3
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 121 deletions.
173 changes: 62 additions & 111 deletions src/java/voldemort/store/configuration/ConfigurationStorageEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,10 @@
import org.apache.log4j.Logger;

import voldemort.VoldemortException;
import voldemort.store.NoSuchCapabilityException;
import voldemort.store.StorageEngine;
import voldemort.store.StoreCapabilityType;
import voldemort.store.StoreUtils;
import voldemort.utils.ByteArray;
import voldemort.store.metadata.MetadataStore;
import voldemort.utils.ClosableIterator;
import voldemort.utils.Pair;
import voldemort.versioning.ObsoleteVersionException;
Expand All @@ -42,16 +41,7 @@

/**
* A FileSystem based Storage Engine to persist configuration metadata.<br>
* Creates/updates a File of filename 'key' for each key and write value as UTF
* strings in it, saves version of latest entry in '.version' directory.<br>
* Keeps a backup copy of key and old version in '.bak' directory<br>
* This store is limited as for persisting metadata, hence some simplifications
* are made.
* <ul>
* <li>Delete Operation is not permitted.</li>
* <li>Iteration over entries is not permitted.</li>
* <li>Store keeps a backup file and can be rolled back by copying the file to
* master location.</li>
* <imp>Used only by {@link MetadataStore}</imp><br>
*
* @author bbansal
*
Expand All @@ -60,9 +50,6 @@ public class ConfigurationStorageEngine implements StorageEngine<String, String>

private final String name;
private final File directory;
private final File versionDirectory;
private final File backupDirectory;
private final File backupVersionDirectory;

private static final Logger logger = Logger.getLogger(ConfigurationStorageEngine.class);

Expand All @@ -72,14 +59,6 @@ public ConfigurationStorageEngine(String name, String directory) {
if(!this.directory.exists() && this.directory.canRead())
throw new IllegalArgumentException("Directory " + this.directory.getAbsolutePath()
+ " does not exist or can not be read.");
this.versionDirectory = new File(this.directory, ".version");
this.backupDirectory = new File(this.directory, ".bak");
this.backupVersionDirectory = new File(this.backupDirectory, ".version");

// create version and backup directory if not exist.
this.versionDirectory.mkdirs();
this.backupDirectory.mkdirs();
this.backupVersionDirectory.mkdirs();
}

public ClosableIterator<Pair<String, Versioned<String>>> entries() {
Expand All @@ -96,25 +75,7 @@ public synchronized boolean delete(String key, Version version) throws Voldemort

public synchronized List<Versioned<String>> get(String key) throws VoldemortException {
StoreUtils.assertValidKey(key);
return get(key, this.directory.listFiles());
}

private List<Versioned<String>> get(String key, File[] files) {
try {
List<Versioned<String>> found = new ArrayList<Versioned<String>>();
for(File file: files) {
if(file.getName().equals(key)) {
VectorClock clock = readVersion(key);
if(null != clock) {
found.add(new Versioned<String>(FileUtils.readFileToString(file, "UTF-8"),
clock));
}
}
}
return found;
} catch(IOException e) {
throw new VoldemortException(e);
}
return get(key, getDirectory(key).listFiles());
}

public List<Version> getVersions(String key) {
Expand All @@ -131,7 +92,7 @@ public synchronized Map<String, List<Versioned<String>>> getAll(Iterable<String>
StoreUtils.assertValidKeys(keys);
Map<String, List<Versioned<String>>> result = StoreUtils.newEmptyHashMap(keys);
for(String key: keys) {
List<Versioned<String>> values = get(key, this.directory.listFiles());
List<Versioned<String>> values = get(key, getDirectory(key).listFiles());
if(!values.isEmpty())
result.put(key, values);
}
Expand All @@ -149,105 +110,71 @@ public synchronized void put(String key, Versioned<String> value) throws Voldemo
throw new VoldemortException("metadata cannot be null !!");
}
// Check for obsolete version
File[] files = this.directory.listFiles();
File[] files = getDirectory(key).listFiles();
for(File file: files) {
if(file.getName().equals(key)) {
VectorClock clock = readVersion(key);
if(value.getVersion().compare(clock) == Occured.AFTER)
updateBackup(key);
else if(value.getVersion().compare(clock) == Occured.BEFORE) {
if(value.getVersion().compare(clock) == Occured.AFTER) {
// continue
} else if(value.getVersion().compare(clock) == Occured.BEFORE) {
throw new ObsoleteVersionException("A successor version to this exists.");
} else if(value.getVersion().compare(clock) == Occured.CONCURRENTLY) {
throw new ObsoleteVersionException("Concurrent Operation not allowed on Metadata.");
}
}
}

VectorClock clock = (VectorClock) value.getVersion();
File keyFile = new File(this.directory, key);
File keyFile = new File(getDirectory(key), key);
VectorClock newClock = (VectorClock) value.getVersion();
if(!keyFile.exists() || keyFile.delete()) {
try {
FileUtils.writeStringToFile(keyFile, value.getValue(), "UTF-8");
writeVersion(key, clock);
writeVersion(key, newClock);
} catch(IOException e) {
try {
rollbackFromBackup(key);
} catch(Exception rollbackError) {
logger.error("Failed to rollback with exception ", rollbackError);
}
throw new VoldemortException(e);
}
}
}

/**
* rollback from backup directory
*
* @param key
*/
public boolean rollbackFromBackup(String key) {
File backupKeyFile = new File(this.backupDirectory, key);
File backupVersionFile = new File(this.backupVersionDirectory, key);

if(backupKeyFile.exists() && backupVersionFile.exists()) {
File keyFile = new File(this.directory, key);
File versionFile = new File(this.versionDirectory, key);

if(keyFile.delete() && versionFile.delete())
return backupKeyFile.renameTo(keyFile) && backupVersionFile.renameTo(versionFile);
private File getDirectory(String key) {
if(MetadataStore.OPTIONAL_KEYS.contains(key))
return getTempDirectory();
else
return this.directory;
}

} else {
logger.warn("Rollback attempted but no backup File found:" + backupKeyFile);
private List<Versioned<String>> get(String key, File[] files) {
try {
List<Versioned<String>> found = new ArrayList<Versioned<String>>();
for(File file: files) {
if(file.getName().equals(key)) {
VectorClock clock = readVersion(key);
if(null != clock) {
found.add(new Versioned<String>(FileUtils.readFileToString(file, "UTF-8"),
clock));
}
}
}
return found;
} catch(IOException e) {
throw new VoldemortException(e);
}

throw new VoldemortException("Failed to rollBack for key:" + key);
}

/**
* Saves the key/version file in backup directory
* If key is a temp state value write it inside tempDirectory to avoid
* clutter.
*
* @param key
* @return
* @param clock
*/
private boolean updateBackup(String key) {
File keyFile = new File(this.directory, key);
File versionFile = new File(this.versionDirectory, key);
if(!versionFile.exists()) {
writeVersion(key, new VectorClock());
versionFile = new File(this.versionDirectory, key);
}

if(keyFile.exists() && versionFile.exists()) {
File backupKeyFile = new File(this.backupDirectory, key);
File backupVersionFile = new File(this.backupVersionDirectory, key);

try {
// delete old backup
backupKeyFile.delete();
backupVersionFile.delete();

return keyFile.renameTo(backupKeyFile) && versionFile.renameTo(backupVersionFile);
} catch(Exception e) {
logger.error("Failed to backup with exception ", e);
}
}
private void writeValue(String key, Versioned<String> value) {

throw new VoldemortException("Failed to take backup for key:" + key);
}

public Object getCapability(StoreCapabilityType capability) {
switch(capability) {
case ROLLBACK_FROM_BACKUP:
return this;
default:
throw new NoSuchCapabilityException(capability, getName());

}
}

private VectorClock readVersion(String key) {
try {
File versionFile = new File(this.versionDirectory, key);
File versionFile = new File(getVersionDirectory(), key);
if(!versionFile.exists()) {
// bootstrap file save default clock as version.
VectorClock clock = new VectorClock();
Expand All @@ -265,7 +192,7 @@ private VectorClock readVersion(String key) {

private void writeVersion(String key, VectorClock version) {
try {
File versionFile = new File(this.versionDirectory, key);
File versionFile = new File(getVersionDirectory(), key);
if(!versionFile.exists() || versionFile.delete()) {
// write the version file.
String hexCode = new String(Hex.encodeHex(version.toBytes()));
Expand All @@ -275,4 +202,28 @@ private void writeVersion(String key, VectorClock version) {
throw new VoldemortException("Failed to write Version for Key:" + key, e);
}
}

private File getVersionDirectory() {
File versionDir = new File(this.directory, ".version");
if(!versionDir.exists() || !versionDir.isDirectory()) {
versionDir.delete();
versionDir.mkdirs();
}

return versionDir;
}

private File getTempDirectory() {
File tempDir = new File(this.directory, ".temp");
if(!tempDir.exists() || !tempDir.isDirectory()) {
tempDir.delete();
tempDir.mkdirs();
}

return tempDir;
}

public Object getCapability(StoreCapabilityType capability) {
throw new VoldemortException("No extra capability.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,14 +128,4 @@ public void testEmacsTempFile() throws IOException {
assertEquals("Only one file of name key should be present.", 1, map.get(keyName).size());
assertEquals("Value should match.", "testValue1", map.get(keyName).get(0).getValue());
}

public void testRollBackFeature() {
VectorClock clock1 = new VectorClock();
getStore().put("test.key", new Versioned<String>("version 1", clock1));
getStore().put("test.key", new Versioned<String>("version 2", clock1.incremented(0, 1)));

// rollback key.
((ConfigurationStorageEngine) getStore()).rollbackFromBackup("test.key");
assertEquals("version 1", getStore().get("test.key").get(0).getValue());
}
}

0 comments on commit 5ea0bc3

Please sign in to comment.