package com.yammer.breakerbox.azure; import com.codahale.metrics.Timer; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Ordering; import com.microsoft.windowsazure.services.table.client.TableConstants; import com.microsoft.windowsazure.services.table.client.TableQuery; import com.yammer.breakerbox.azure.core.TableId; import com.yammer.breakerbox.azure.core.TableType; import com.yammer.breakerbox.azure.healthchecks.TableClientHealthcheck; import com.yammer.breakerbox.azure.model.DependencyEntity; import com.yammer.breakerbox.azure.model.DependencyModelByTimestamp; import com.yammer.breakerbox.azure.model.Entities; import com.yammer.breakerbox.azure.model.ServiceEntity; 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.setup.Environment; import org.joda.time.DateTime; public class AzureStore extends BreakerboxStore { protected final TableClient tableClient; public AzureStore(AzureTableConfiguration azureTableConfiguration, Environment environment) { super(azureTableConfiguration, environment); this.tableClient = new TableClientFactory(azureTableConfiguration).create(); environment.healthChecks().register("azure", new TableClientHealthcheck(tableClient)); } private <T extends TableType> boolean delete(Optional<T> tableType) { return !tableType.isPresent() || tableClient.remove(tableType.get()); } @Override public boolean initialize() { for (TableId tableId : TableId.values()) { tableClient.create(tableId); } return true; } @Override public boolean store(DependencyModel dependencyModel) { return tableClient.insertOrReplace(Entities.from(dependencyModel)); } @Override public boolean store(ServiceModel serviceModel) { return tableClient.insertOrReplace(Entities.from(serviceModel)); } @Override public boolean delete(ServiceModel serviceModel) { return delete(tableClient.<ServiceEntity>retrieve(Entities.from(serviceModel))); } @Override public boolean delete(DependencyModel dependencyModel) { return delete(tableClient.<DependencyEntity>retrieve(Entities.from(dependencyModel))); } @Override public boolean delete(ServiceId serviceId, DependencyId dependencyId) { return delete(tableClient.<ServiceEntity>retrieve(ServiceEntity.build(serviceId, dependencyId))); } @Override public boolean delete(DependencyId dependencyId, DateTime dateTime) { return delete(fetchByTimestamp(dependencyId, dateTime.getMillis())); } @Override public Optional<ServiceModel> retrieve(ServiceId serviceId, DependencyId dependencyId) { return Entities.toServiceModel(tableClient.<ServiceEntity>retrieve(ServiceEntity.build(serviceId, dependencyId))); } @Override public Optional<DependencyModel> retrieve(DependencyId dependencyId, DateTime dateTime) { return Entities.toDependencyModel(fetchByTimestamp(dependencyId, dateTime.getMillis())); } @Override public Optional<DependencyModel> retrieveLatest(DependencyId dependencyId, ServiceId serviceId) { return fetchLatest(allDependenciesFor(dependencyId, serviceId)); } @Override public Iterable<ServiceModel> allServiceModels() { return Entities.toServiceModelList(allServiceEntities()); } @Override public Iterable<ServiceModel> listDependenciesFor(final ServiceId serviceId) { return Entities.toServiceModelList(allServiceEntities(serviceId)); } @Override public Iterable<DependencyModel> allDependenciesFor(DependencyId dependencyId, ServiceId serviceId) { try (Timer.Context timerContext = dependencyConfigs.time()) { return Entities.toDependencyModelList(tableClient.search(TableQuery .from(TableId.DEPENDENCY.toString(), DependencyEntity.class) .where(TableQuery.combineFilters( partitionEquals(dependencyId), TableQuery.Operators.AND, serviceIdEquals(serviceId))))); } } private static Optional<DependencyModel> fetchLatest(Iterable<DependencyModel> dependencyModels) { if (Iterables.isEmpty(dependencyModels)) { return Optional.absent(); } else { return Optional.of(Ordering .from(new DependencyModelByTimestamp()) .reverse() .immutableSortedCopy(dependencyModels) .get(0)); } } private Optional<DependencyEntity> fetchByTimestamp(DependencyId dependencyId, long timestamp) { final ImmutableList<DependencyEntity> dependencyEntities = getConfiguration(dependencyId, timestamp); if (dependencyEntities.isEmpty()) { return Optional.absent(); } else { return Optional.of(dependencyEntities.get(0)); } } private ImmutableList<ServiceEntity> allServiceEntities(ServiceId serviceId) { try (Timer.Context timerContext = listService.time()) { return tableClient.search(TableQuery .from(TableId.SERVICE.toString(), ServiceEntity.class) .where(partitionKeyEquals(serviceId))); } } private static String partitionKeyEquals(ServiceId serviceId) { return TableQuery .generateFilterCondition( TableConstants.PARTITION_KEY, TableQuery.QueryComparisons.EQUAL, serviceId.getId()); } private ImmutableList<ServiceEntity> allServiceEntities() { try (Timer.Context timerContext = listService.time()) { return tableClient.search(TableQuery .from(TableId.SERVICE.toString(), ServiceEntity.class)); } } private ImmutableList<DependencyEntity> getConfiguration(DependencyId dependencyId, long targetTimeStamp) { try (Timer.Context timerContext = dependencyConfigs.time()) { return tableClient.search(TableQuery .from(TableId.DEPENDENCY.toString(), DependencyEntity.class) .where(TableQuery.combineFilters( partitionEquals(dependencyId), TableQuery.Operators.AND, timestampEquals(targetTimeStamp)))); } } private static String serviceIdEquals(ServiceId serviceId) { return TableQuery.generateFilterCondition( "ServiceName", TableQuery.QueryComparisons.EQUAL, serviceId.getId()); } private static String timestampEquals(long timestamp) { return TableQuery.generateFilterCondition( TableConstants.ROW_KEY, TableQuery.QueryComparisons.EQUAL, String.valueOf(timestamp) ); } private static String partitionEquals(DependencyId dependencyId) { return TableQuery.generateFilterCondition( TableConstants.PARTITION_KEY, TableQuery.QueryComparisons.EQUAL, dependencyId.getId()); } }