com.bazaarvoice.emodb.databus.core.CanaryManager Maven / Gradle / Ivy
package com.bazaarvoice.emodb.databus.core;
import com.bazaarvoice.emodb.common.dropwizard.lifecycle.LifeCycleRegistry;
import com.bazaarvoice.emodb.common.dropwizard.log.RateLimitedLogFactory;
import com.bazaarvoice.emodb.databus.ChannelNames;
import com.bazaarvoice.emodb.databus.SystemIdentity;
import com.bazaarvoice.emodb.databus.api.Databus;
import com.bazaarvoice.emodb.event.owner.OstrichOwnerFactory;
import com.bazaarvoice.emodb.event.owner.OstrichOwnerGroupFactory;
import com.bazaarvoice.emodb.event.owner.OwnerGroup;
import com.bazaarvoice.emodb.sor.api.Intrinsic;
import com.bazaarvoice.emodb.sor.condition.Condition;
import com.bazaarvoice.emodb.sor.condition.Conditions;
import com.bazaarvoice.emodb.table.db.ClusterInfo;
import com.bazaarvoice.emodb.table.db.Placements;
import com.bazaarvoice.emodb.table.db.consistency.DatabusClusterInfo;
import com.bazaarvoice.ostrich.PartitionContext;
import com.bazaarvoice.ostrich.PartitionContextBuilder;
import com.codahale.metrics.MetricRegistry;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Ordering;
import com.google.common.util.concurrent.Service;
import com.google.inject.Inject;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
/** Starts the Databus canary threads, one for each SoR Cassandra cluster, subject to ZooKeeper leader election. */
public class CanaryManager {
@Inject
public CanaryManager(final LifeCycleRegistry lifeCycle,
@DatabusClusterInfo Collection clusterInfo,
Placements placements,
final DatabusFactory databusFactory,
final @SystemIdentity String systemId,
final RateLimitedLogFactory logFactory,
OstrichOwnerGroupFactory ownerGroupFactory,
final MetricRegistry metricRegistry) {
// Index the Cassandra cluster info objects by their name.
final Map clusterInfoMap = Maps.newLinkedHashMap();
for (ClusterInfo cluster : clusterInfo) {
clusterInfoMap.put(cluster.getCluster(), cluster);
}
// Figure out which placement strings belong to which cluster.
Multimap clusterToPlacements = ArrayListMultimap.create();
for (String placement : placements.getValidPlacements()) {
Optional cluster = placements.getLocalCluster(placement);
if (cluster.isPresent()) {
clusterToPlacements.put(cluster.get(), placement);
}
}
// Build a Databus Condition object that selects updates based on the underlying Cassandra cluster.
final Map clusterToConditionMap = Maps.newHashMap();
for (Map.Entry> entry : clusterToPlacements.asMap().entrySet()) {
String cluster = entry.getKey();
List clusterPlacements = Ordering.natural().immutableSortedCopy(entry.getValue());
checkState(clusterInfoMap.containsKey(cluster),
"Placement(s) map to unknown cluster '%s': %s", cluster, clusterPlacements);
clusterToConditionMap.put(cluster,
Conditions.intrinsic(Intrinsic.PLACEMENT, Conditions.in(clusterPlacements)));
}
// Since the canary reads from the databus it must either (a) execute on the same server that owns
// and manages the databus canary subscription or (b) go through the Ostrich client that forwards
// requests to the right server. This code implements the first option by using the same consistent
// hash calculation used by Ostrich to determine which server owns the canary subscription. As Emo
// servers join and leave the pool, the OwnerGroup will track which server owns the canary subscription
// at a given point in time and start and stop the canary service appropriately.
OwnerGroup ownerGroup = ownerGroupFactory.create("Canary", new OstrichOwnerFactory() {
@Override
public PartitionContext getContext(String cluster) {
return PartitionContextBuilder.of(ChannelNames.getMasterCanarySubscription(cluster));
}
@Override
public Service create(String clusterName) {
ClusterInfo cluster = requireNonNull(clusterInfoMap.get(clusterName), clusterName);
Condition condition = requireNonNull(clusterToConditionMap.get(clusterName), clusterName);
Databus databus = databusFactory.forOwner(systemId);
return new Canary(cluster, condition, databus, logFactory, metricRegistry);
}
}, null);
lifeCycle.manage(ownerGroup);
// Start one canary for each Cassandra data cluster.
for (String cluster : clusterToConditionMap.keySet()) {
ownerGroup.startIfOwner(cluster, Duration.ZERO);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy