package water.persist; import java.io.*; import java.nio.channels.FileChannel; import water.*; // Persistence backend for network file system. // Just for loading or storing files. // // @author cliffc public final class PersistNFS extends Persist { static final String KEY_PREFIX = "nfs:"; public static final int KEY_PREFIX_LENGTH = KEY_PREFIX.length(); // file implementation ------------------------------------------------------- public static Key decodeFile(File f) { String kname = KEY_PREFIX + File.separator + f.toString(); assert (kname.length() <= 512); // all NFS keys are NFS-kind keys return Key.make(kname.getBytes()); } // Returns the file for given key. private static File getFileForKey(Key k) { final int len = KEY_PREFIX_LENGTH+1; // Strip key prefix & leading slash final int off = k._kb[0]==Key.DVEC ? water.fvec.Vec.KEY_PREFIX_LEN : 0; String s = new String(k._kb,len+off,k._kb.length-(len+off)); return new File(s); } public static InputStream openStream(Key k) throws IOException { return new FileInputStream(getFileForKey(k)); } // Read up to 'len' bytes of Value. Value should already be persisted to // disk. A racing delete can trigger a failure where we get a null return, // but no crash (although one could argue that a racing load&delete is a bug // no matter what). @Override public byte[] load(Value v) { long skip = 0; Key k = v._key; // Convert a chunk into a long-offset from the base file. if( k._kb[0] == Key.DVEC ) skip = water.fvec.NFSFileVec.chunkOffset(k); // The offset try { FileInputStream s = null; try { s = new FileInputStream(getFileForKey(k)); FileChannel fc = s.getChannel(); fc.position(skip); AutoBuffer ab = new AutoBuffer(fc, true, Value.NFS); byte[] b = ab.getA1(v._max); ab.close(); assert v.isPersisted(); return b; } finally { if( s != null ) s.close(); } } catch( IOException e ) { // Broken disk / short-file??? H2O.ignore(e); return null; } } // Store Value v to disk. @Override public void store(Value v) { // Only the home node does persistence on NFS if( !v._key.home() ) return; // A perhaps useless cutout: the upper layers should test this first. if( v.isPersisted() ) return; try { File f = getFileForKey(v._key); f.mkdirs(); FileOutputStream s = new FileOutputStream(f); try { byte[] m = v.memOrLoad(); assert (m == null || m.length == v._max); // Assert not saving partial files if( m != null ) new AutoBuffer(s.getChannel(), false, Value.NFS).putA1(m, m.length).close(); v.setdsk(); // Set as write-complete to disk } finally { s.close(); } } catch( IOException e ) { H2O.ignore(e); } } // TODO needed if storing ice to S3 @Override public String getPath() { throw new UnsupportedOperationException(); } @Override public void clear() { throw new UnsupportedOperationException(); } @Override public void loadExisting() { throw new UnsupportedOperationException(); } @Override public void delete(Value v) { throw new UnsupportedOperationException(); } }