package water.persist;
import java.io.*;
import water.*;
import water.util.Log;
import water.util.Utils;
/**
* Persistence backend using local file system.
*/
public final class PersistFS extends Persist {
public final File _root;
public final File _dir;
PersistFS(File root) {
_root = root;
_dir = new File(root, "ice" + H2O.API_PORT);
// Make the directory as-needed
root.mkdirs();
if( !(root.isDirectory() && root.canRead() && root.canWrite()) ) {
Log.die("ice_root not a read/writable directory");
}
}
@Override public String getPath() {
return _dir.toString();
}
@Override public void clear() {
clear(_dir);
}
private void clear(File f) {
File[] cs = f.listFiles();
if( cs != null ) {
for( File c : cs ) {
if( c.isDirectory() ) clear(c);
c.delete();
}
}
}
@Override public void loadExisting() {
loadExisting(_dir);
}
private void loadExisting(File f) {
for( File c : f.listFiles() ) {
if( c.isDirectory() ) {
loadExisting(c); // Recursively keep loading K/V pairs
} else {
Key k = str2Key(c.getName());
Value ice = new Value(k, (int) c.length());
ice.setdsk();
H2O.putIfAbsent_raw(k, ice);
}
}
}
private File getFile(Value v) {
return new File(_dir, getIceName(v));
}
@Override public byte[] load(Value v) {
File f = getFile(v);
if( f.length() < v._max ) { // Should be fully on disk...
// or it's a racey delete of a spilled value
assert !v.isPersisted() : f.length() + " " + v._max + " " + v._key;
return null; // No value
}
try {
FileInputStream s = new FileInputStream(f);
try {
AutoBuffer ab = new AutoBuffer(s.getChannel(), true, Value.ICE);
byte[] b = ab.getA1(v._max);
ab.close();
return b;
} finally {
s.close();
}
} catch( IOException e ) { // Broken disk / short-file???
throw new RuntimeException(Log.err("File load failed: ", e));
}
}
// Store Value v to disk.
@Override public void store(Value v) {
assert !v.isPersisted();
new File(_dir, getIceDirectory(v._key)).mkdirs();
// Nuke any prior file.
FileOutputStream s = null;
try {
s = new FileOutputStream(getFile(v));
} catch( FileNotFoundException e ) {
String info = "Key: " + v._key.toString() + "\nEncoded: " + getFile(v);
throw new RuntimeException(Log.err("Encoding a key to a file failed!\n" + info, e));
}
try {
byte[] m = v.memOrLoad(); // we are not single threaded anymore
assert m != null && m.length == v._max : "Trying to save partial file: value key=" + v._key + ", length to save=" + m + ", value max size=" + v._max; // Assert not saving partial files
new AutoBuffer(s.getChannel(), false, Value.ICE).putA1(m, m.length).close();
v.setdsk(); // Set as write-complete to disk
} finally {
Utils.close(s);
}
}
@Override public void delete(Value v) {
assert !v.isPersisted(); // Upper layers already cleared out
File f = getFile(v);
f.delete();
}
@Override public long getUsableSpace() {
return _root.getUsableSpace();
}
@Override public long getTotalSpace() {
return _root.getTotalSpace();
}
}