package water.persist;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import com.google.common.io.ByteStreams;
import tachyon.client.ReadType;
import tachyon.client.TachyonFS;
import water.*;
import water.Job.ProgressMonitor;
import water.fvec.FileVec;
import water.fvec.Vec;
import water.util.*;
public class PersistTachyon extends Persist<TachyonFS> {
public static final String PREFIX = "tachyon://";
public static final String DEFAULT_CLIENT_URI = "tachyon://localhost:19998";
private final String _defaultUri;
PersistTachyon() { this(DEFAULT_CLIENT_URI); }
PersistTachyon(String uri) { _defaultUri = uri; }
public static InputStream openStream(Key k, ProgressMonitor pmon) {
return new H2OTachyonInputStream(k, pmon);
}
@Override public byte[] load(Value v) {
Key k = v._key; // key for value
if (k._kb[0] != Key.DVEC) throw H2O.unimpl(); // Load only from values stored in vector
long skip = FileVec.chunkOffset(k); // Compute skip for this value
long start_io_ms = System.currentTimeMillis();
final byte[] b = MemoryManager.malloc1(v._max);
String[] keyComp = decodeKey(k);
String clientUri = keyComp[0];
String fpath = keyComp[1];
TachyonFS tfs = null;
InputStream is = null;
try {
tfs = (TachyonFS) (Persist.I[Value.TACHYON].createClient(clientUri));
long start_ns = System.nanoTime(); // Blocking i/o call timing - without counting repeats
is = tfs.getFile(fpath).getInStream(ReadType.NO_CACHE);
ByteStreams.skipFully(is, skip);
ByteStreams.readFully(is, b);
TimeLine.record_IOclose(start_ns, start_io_ms, 1/* read */, v._max, Value.TACHYON);
return b;
} catch (IOException e) {
throw new RuntimeException(Log.err("File load failed: ", e));
} finally {
if (is!=null) Utils.close(is);
}
}
public static final class H2OTachyonInputStream extends RIStream {
final private Key key;
final private String clientURI;
final private String fpath;
protected H2OTachyonInputStream(Key k, long from, ProgressMonitor pmon) {
super(from, pmon);
key = k;
String[] c = decodeKey(k);
clientURI = c[0];
fpath = c[1];
}
public H2OTachyonInputStream(Key k, ProgressMonitor pmon) {
this(k, 0, pmon);
}
@Override protected InputStream open(long offset) throws IOException {
TachyonFS tfs = (TachyonFS) (Persist.I[Value.TACHYON].createClient(clientURI));
InputStream is = tfs.getFile(fpath).getInStream(ReadType.NO_CACHE);
is.skip(offset);
return is;
}
}
/** Split key name composed of tachyon://<client-uri>/filename into two parts:
* - client-uri without tachyon:// prefix
* - filename
* And returns both components.
*/
private static String[] decodeKey(Key k) {
String s = new String((k.isChunkKey()) ? Arrays.copyOfRange(k._kb, Vec.KEY_PREFIX_LEN, k._kb.length):k._kb);
return decode(s);
}
public static String[] decode(String s) {
assert s.startsWith(PREFIX) : "Unsupported key name for tachyon: " + s;
s = s.substring(PREFIX.length());
int nextSlash = s.indexOf('/');
String clientUri, filename;
if (nextSlash!=-1) {
clientUri = s.substring(0, nextSlash);
filename = s.substring(nextSlash);
} else {
clientUri = s;
filename = "/";
}
return new String[] { clientUri, filename };
}
//
// Un-implemented methods
//
@Override public String getPath() {
throw new UnsupportedOperationException();
}
@Override public void clear() {
throw new UnsupportedOperationException();
}
@Override public void loadExisting() {
throw new UnsupportedOperationException();
}
@Override public void store(Value v) {
throw new UnsupportedOperationException();
}
@Override public void delete(Value v) {
throw new UnsupportedOperationException();
}
//
// Methods providing low-level client implementation
//
@Override public String getDefaultURI() {
return _defaultUri;
}
@Override public TachyonFS createClient(String uri) throws IOException {
if (!uri.startsWith(PREFIX)) uri = PREFIX + uri;
return TachyonFS.get(uri);
}
}