package com.yammer.breakerbox.jdbi;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.google.common.base.Optional;
import com.yammer.breakerbox.jdbi.args.DateTimeArgumentFactory;
import com.yammer.breakerbox.jdbi.args.DependencyIdArgumentFactory;
import com.yammer.breakerbox.jdbi.args.ServiceIdArgumentFactory;
import com.yammer.breakerbox.jdbi.args.TenacityConfigurationArgumentFactory;
import com.yammer.breakerbox.store.BreakerboxStore;
import com.yammer.breakerbox.store.DependencyId;
import com.yammer.breakerbox.store.ServiceId;
import com.yammer.breakerbox.store.model.DependencyModel;
import com.yammer.breakerbox.store.model.ServiceModel;
import io.dropwizard.jdbi.DBIFactory;
import io.dropwizard.migrations.CloseableLiquibase;
import io.dropwizard.migrations.CloseableLiquibaseWithClassPathMigrationsFile;
import io.dropwizard.setup.Environment;
import org.joda.time.DateTime;
import org.skife.jdbi.v2.DBI;
import org.skife.jdbi.v2.exceptions.DBIException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JdbiStore extends BreakerboxStore {
public static final String MIGRATIONS_FILENAME = "migrations.xml";
private static final Logger LOGGER = LoggerFactory.getLogger(JdbiStore.class);
protected final ServiceDB serviceDB;
protected final DependencyDB dependencyDB;
private final MetricRegistry metricRegistry;
protected JdbiConfiguration configuration;
public JdbiStore(JdbiConfiguration storeConfiguration,
Environment environment) throws Exception {
this(storeConfiguration, environment,
new DBIFactory().build(environment, storeConfiguration.getDataSourceFactory(), "breakerbox"));
}
public JdbiStore(JdbiConfiguration storeConfiguration,
Environment environment,
DBI database) {
super(storeConfiguration, environment);
database.registerArgumentFactory(new DependencyIdArgumentFactory());
database.registerArgumentFactory(new ServiceIdArgumentFactory());
database.registerArgumentFactory(new TenacityConfigurationArgumentFactory(environment.getObjectMapper()));
database.registerArgumentFactory(new DateTimeArgumentFactory());
dependencyDB = database.onDemand(DependencyDB.class);
serviceDB = database.onDemand(ServiceDB.class);
this.configuration = storeConfiguration;
metricRegistry = environment.metrics();
}
@Override
public boolean initialize() {
try (CloseableLiquibase liquibase = new CloseableLiquibaseWithClassPathMigrationsFile(configuration
.getDataSourceFactory()
.build(metricRegistry, "liquibase"), MIGRATIONS_FILENAME)) {
liquibase.update("");
return true;
} catch (Exception err) {
LOGGER.error("Failed to create liquibase", err);
throw new IllegalStateException(err);
}
}
@Override
public boolean store(DependencyModel dependencyModel) {
try {
final Optional<DependencyModel> storedModel = retrieve(dependencyModel.getDependencyId(), dependencyModel.getDateTime());
return (storedModel.isPresent() && storedModel.get().equals(dependencyModel)) || dependencyDB.insert(dependencyModel) == 1;
} catch (DBIException err) {
LOGGER.warn("Failed to store: {}", dependencyModel, err);
}
return false;
}
@Override
public boolean store(ServiceModel serviceModel) {
try {
return retrieve(serviceModel.getServiceId(), serviceModel.getDependencyId()).isPresent() ||
serviceDB.insert(serviceModel) == 1;
} catch (DBIException err) {
LOGGER.warn("Failed to store: {}", serviceModel, err);
}
return false;
}
@Override
public boolean delete(ServiceModel serviceModel) {
try {
return serviceDB.delete(serviceModel) >= 0;
} catch (DBIException err) {
LOGGER.warn("Failed to delete: {}", serviceModel, err);
return false;
}
}
@Override
public boolean delete(DependencyModel dependencyModel) {
try {
return dependencyDB.delete(dependencyModel.getDependencyId(), dependencyModel.getDateTime()) >= 0;
} catch (DBIException err) {
LOGGER.warn("Failed to delete: {}", dependencyModel, err);
return false;
}
}
@Override
public boolean delete(ServiceId serviceId, DependencyId dependencyId) {
return delete(new ServiceModel(serviceId, dependencyId));
}
@Override
public boolean delete(DependencyId dependencyId, DateTime dateTime) {
try {
return dependencyDB.delete(dependencyId, dateTime) >= 0;
} catch (DBIException err) {
LOGGER.warn("Failed to delete: {}, {}", dependencyId, dateTime, err);
return false;
}
}
@Override
public Optional<ServiceModel> retrieve(ServiceId serviceId, DependencyId dependencyId) {
try {
return Optional.fromNullable(serviceDB.find(new ServiceModel(serviceId, dependencyId)));
} catch (DBIException err) {
LOGGER.warn("Failed to retrieve {}:{}", serviceId, dependencyId, err);
return Optional.absent();
}
}
@Override
public Optional<DependencyModel> retrieve(DependencyId dependencyId, DateTime dateTime) {
try {
return Optional.fromNullable(dependencyDB.find(dependencyId, dateTime));
} catch (DBIException err) {
LOGGER.warn("Failed to retrieve {}, {}", dependencyId, dateTime.getMillis(), err);
return Optional.absent();
}
}
@Override
public Optional<DependencyModel> retrieveLatest(DependencyId dependencyId, ServiceId serviceId) {
try {
return Optional.fromNullable(dependencyDB.findLatest(dependencyId, serviceId));
} catch (DBIException err) {
LOGGER.warn("Failed to retrieve {}, {}", dependencyId, serviceId, err);
return Optional.absent();
}
}
@Override
public Iterable<ServiceModel> allServiceModels() {
try (Timer.Context timerContext = listServices.time()) {
return serviceDB.all();
}
}
@Override
public Iterable<ServiceModel> listDependenciesFor(ServiceId serviceId) {
try (Timer.Context timerContext = listService.time()) {
return serviceDB.all(serviceId);
}
}
@Override
public Iterable<DependencyModel> allDependenciesFor(DependencyId dependencyId, ServiceId serviceId) {
try (Timer.Context timerContext = dependencyConfigs.time()) {
return dependencyDB.all(dependencyId, serviceId);
}
}
}