package com.liveramp.hank.coordinator.zk;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.yaml.snakeyaml.Yaml;
import com.liveramp.hank.coordinator.AbstractDomain;
import com.liveramp.hank.coordinator.Domain;
import com.liveramp.hank.coordinator.DomainVersion;
import com.liveramp.hank.coordinator.DomainVersionProperties;
import com.liveramp.hank.coordinator.DomainVersionPropertiesSerialization;
import com.liveramp.hank.coordinator.Hosts;
import com.liveramp.hank.generated.DomainMetadata;
import com.liveramp.hank.partitioner.Partitioner;
import com.liveramp.hank.storage.StorageEngine;
import com.liveramp.hank.storage.StorageEngineFactory;
import com.liveramp.hank.storage.incremental.IncrementalDomainVersionProperties;
import com.liveramp.hank.zookeeper.WatchedMap;
import com.liveramp.hank.zookeeper.WatchedThriftNode;
import com.liveramp.hank.zookeeper.ZkPath;
import com.liveramp.hank.zookeeper.ZooKeeperPlus;
public class ZkDomain extends AbstractDomain implements Domain {
private static final Logger LOG = LoggerFactory.getLogger(ZkDomain.class);
protected static final String VERSIONS_PATH = "v";
private final String path;
private final WatchedThriftNode<DomainMetadata> metadata;
private final String name;
private final WatchedMap<ZkDomainVersion> versions;
private final ZooKeeperPlus zk;
private Partitioner partitioner;
public static ZkDomain create(ZooKeeperPlus zk,
String domainsRoot,
String domainName,
int numParts,
String storageEngineFactoryClassName,
String storageEngineOptions,
String partitionerClassName,
int id,
List<String> requiredHostFlags) throws KeeperException, InterruptedException, IOException {
String path = ZkPath.append(domainsRoot, domainName);
DomainMetadata initialValue = new DomainMetadata(id, numParts, storageEngineFactoryClassName,
storageEngineOptions, partitionerClassName, Hosts.joinHostFlags(requiredHostFlags), 0);
return new ZkDomain(zk, path, true, initialValue);
}
public ZkDomain(ZooKeeperPlus zk, String path) throws KeeperException, InterruptedException {
this(zk, path, false, null);
}
public ZkDomain(ZooKeeperPlus zk, String path, boolean create, DomainMetadata initialMetadata) throws KeeperException, InterruptedException {
this.zk = zk;
this.path = path;
this.name = ZkPath.getFilename(path);
metadata = new WatchedThriftNode<DomainMetadata>(zk, path, true, create ? CreateMode.PERSISTENT : null, initialMetadata, new DomainMetadata());
if (create) {
zk.ensureCreated(ZkPath.append(path, VERSIONS_PATH), null);
}
this.versions = new WatchedMap<ZkDomainVersion>(zk, ZkPath.append(path, VERSIONS_PATH),
new WatchedMap.ElementLoader<ZkDomainVersion>() {
@Override
public ZkDomainVersion load(ZooKeeperPlus zk, String basePath, String relPath) throws KeeperException, InterruptedException {
return new ZkDomainVersion(zk, ZkPath.append(basePath, relPath), getDomainVersionPropertiesSerialization());
}
}
);
}
public void update(final int id,
final int numParts,
final String storageEngineFactoryClassName,
final String storageEngineOptions,
final String partitionerClassName,
final List<String> requiredHostFlags) throws IOException, InterruptedException, KeeperException {
metadata.update(metadata.new Updater() {
@Override
public void updateCopy(DomainMetadata currentCopy) {
currentCopy.set_id(id);
currentCopy.set_num_partitions(numParts);
currentCopy.set_storage_engine_factory_class(storageEngineFactoryClassName);
currentCopy.set_storage_engine_options(storageEngineOptions);
currentCopy.set_partitioner_class(partitionerClassName);
currentCopy.set_required_host_flags(Hosts.joinHostFlags(requiredHostFlags));
}
});
}
@Override
public String getName() {
return name;
}
@Override
public int getId() {
return metadata.get().get_id();
}
@Override
public int getNumParts() {
return metadata.get().get_num_partitions();
}
@Override
public Class<? extends StorageEngineFactory> getStorageEngineFactoryClass() {
try {
return (Class<? extends StorageEngineFactory>)Class.forName(getStorageEngineFactoryClassName());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
@Override
public String getStorageEngineFactoryClassName() {
return metadata.get().get_storage_engine_factory_class();
}
@Override
public StorageEngine getStorageEngine() {
String storageEngineFactoryClassName = getStorageEngineFactoryClassName();
try {
StorageEngineFactory factory = (StorageEngineFactory)Class.forName(storageEngineFactoryClassName).newInstance();
return factory.getStorageEngine(getStorageEngineOptions(), this);
} catch (Exception e) {
LOG.error("Could not instantiate storage engine from factory " + storageEngineFactoryClassName
+ " with options " + getStorageEngineOptions(), e);
return null;
}
}
@Override
public Map<String, Object> getStorageEngineOptions() {
return (Map<String, Object>)new Yaml().load(metadata.get().get_storage_engine_options());
}
private DomainVersionPropertiesSerialization getDomainVersionPropertiesSerialization() {
return new IncrementalDomainVersionProperties.Serialization();
}
@Override
public Partitioner getPartitioner() {
if (partitioner == null) {
String partitionerClassName = getPartitionerClassName();
try {
partitioner = (Partitioner)((Class)Class.forName(getPartitionerClassName())).newInstance();
} catch (Exception e) {
throw new RuntimeException("Could not instantiate partitioner " + partitionerClassName, e);
}
}
return partitioner;
}
@Override
public String getPartitionerClassName() {
return metadata.get().get_partitioner_class();
}
@Override
public List<String> getRequiredHostFlags() {
String requiredHostFlagsStr = metadata.get().get_required_host_flags();
if (requiredHostFlagsStr == null) {
return Collections.emptyList();
} else {
return Hosts.splitHostFlags(requiredHostFlagsStr);
}
}
@Override
public SortedSet<DomainVersion> getVersions() throws IOException {
return new TreeSet<DomainVersion>(versions.values());
}
@Override
public DomainVersion openNewVersion(DomainVersionProperties domainVersionProperties) throws IOException {
// First, copy next version number
int versionNumber = metadata.get().get_next_version_number();
// Then, increment next version counter
try {
metadata.update(metadata.new Updater() {
@Override
public void updateCopy(DomainMetadata currentCopy) {
currentCopy.set_next_version_number(currentCopy.get_next_version_number() + 1);
}
});
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (KeeperException e) {
throw new RuntimeException(e);
}
try {
ZkDomainVersion newVersion = ZkDomainVersion.create(zk, path, versionNumber, domainVersionProperties, getDomainVersionPropertiesSerialization());
versions.put(ZkDomainVersion.getPathName(newVersion.getVersionNumber()), newVersion);
return newVersion;
} catch (Exception e) {
// pretty good chance that someone beat us to the punch.
LOG.warn("Got an exception when trying to open a version for domain " + path, e);
throw new IOException(e);
}
}
@Override
public DomainVersion getVersion(int versionNumber) throws IOException {
return findVersion(getVersions(), versionNumber);
}
@Override
public DomainVersion getVersionShallow(int versionNumber) throws IOException {
if (versions.isLoaded()) {
return findVersion(getVersions(), versionNumber);
} else {
try {
return new ZkDomainVersion(zk,
ZkPath.append(path, VERSIONS_PATH, ZkDomainVersion.getPathName(versionNumber)),
getDomainVersionPropertiesSerialization());
} catch (InterruptedException e) {
return null;
} catch (KeeperException e) {
return null;
}
}
}
@Override
public boolean deleteVersion(int versionNumber) throws IOException {
ZkDomainVersion domainVersion = versions.remove(ZkDomainVersion.getPathName(versionNumber));
if (domainVersion == null) {
return false;
} else {
domainVersion.delete();
return true;
}
}
public boolean delete() throws IOException {
try {
zk.deleteNodeRecursively(path);
return true;
} catch (Exception e) {
throw new IOException(e);
}
}
@Override
public String toString() {
return "ZkDomain [domainPath=" + path + ", id=" + getId() + ", name=" + name + ", numParts=" + getNumParts()
+ ", partitioner=" + getPartitionerClassName() + ", storageEngine=" + getStorageEngine()
+ ", storageEngineFactoryClassName=" + getStorageEngineFactoryClassName() + ", storageEngineOptions="
+ getStorageEngineOptions() + "]";
}
}