/* * Copyright © 2014 Cask Data, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package co.cask.cdap.data.stream; import co.cask.cdap.common.conf.Constants; import co.cask.cdap.common.conf.PropertyStore; import co.cask.cdap.common.io.Codec; import co.cask.cdap.common.zookeeper.coordination.ResourceCoordinator; import co.cask.cdap.common.zookeeper.coordination.ResourceCoordinatorClient; import co.cask.cdap.common.zookeeper.coordination.ResourceModifier; import co.cask.cdap.common.zookeeper.coordination.ResourceRequirement; import co.cask.cdap.common.zookeeper.store.ZKPropertyStore; import co.cask.cdap.proto.Id; import com.google.common.collect.ImmutableSet; import com.google.inject.Inject; import com.google.inject.Singleton; import org.apache.twill.internal.zookeeper.ReentrantDistributedLock; import org.apache.twill.zookeeper.ZKClient; import org.apache.twill.zookeeper.ZKClients; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Set; import java.util.concurrent.locks.Lock; import javax.annotation.Nullable; /** * A {@link StreamCoordinatorClient} uses ZooKeeper to implementation coordination needed for stream. It also uses a * {@link ResourceCoordinator} to elect each handler as the leader of a set of streams. */ @Singleton public final class DistributedStreamCoordinatorClient extends AbstractStreamCoordinatorClient { private static final Logger LOG = LoggerFactory.getLogger(DistributedStreamCoordinatorClient.class); private final ResourceCoordinatorClient resourceCoordinatorClient; private final ZKClient zkClient; @Inject public DistributedStreamCoordinatorClient(ZKClient zkClient) { super(); this.zkClient = zkClient; this.resourceCoordinatorClient = new ResourceCoordinatorClient(getCoordinatorZKClient()); } @Override protected void doStartUp() throws Exception { resourceCoordinatorClient.startAndWait(); } @Override protected void doShutDown() throws Exception { if (resourceCoordinatorClient != null) { resourceCoordinatorClient.stopAndWait(); } } @Override protected <T> PropertyStore<T> createPropertyStore(Codec<T> codec) { return ZKPropertyStore.create(zkClient, "/" + Constants.Service.STREAMS + "/properties", codec); } @Override protected Lock getLock(Id.Stream streamId) { // It's ok to create new locks every time as it's backed by ZK for distributed lock ZKClient lockZKClient = ZKClients.namespace(zkClient, "/" + Constants.Service.STREAMS + "/locks"); return new ReentrantDistributedLock(lockZKClient, streamId.toString()); } @Override protected void streamCreated(final Id.Stream streamId) { resourceCoordinatorClient.modifyRequirement( Constants.Service.STREAMS, new ResourceModifier() { @Nullable @Override public ResourceRequirement apply(@Nullable ResourceRequirement existingRequirement) { LOG.debug("Modifying requirement to add stream {} as a resource", streamId); Set<ResourceRequirement.Partition> partitions; if (existingRequirement != null) { partitions = existingRequirement.getPartitions(); } else { partitions = ImmutableSet.of(); } ResourceRequirement.Partition newPartition = new ResourceRequirement.Partition(streamId.toString(), 1); if (partitions.contains(newPartition)) { return null; } ResourceRequirement.Builder builder = ResourceRequirement.builder(Constants.Service.STREAMS); builder.addPartition(newPartition); for (ResourceRequirement.Partition partition : partitions) { builder.addPartition(partition); } return builder.build(); } }); } @Override protected void streamDeleted(final Id.Stream streamId) { resourceCoordinatorClient.modifyRequirement(Constants.Service.STREAMS, new ResourceModifier() { @Nullable @Override public ResourceRequirement apply(@Nullable ResourceRequirement existingRequirement) { LOG.debug("Modifying requirement to remove stream {}", streamId); if (existingRequirement == null) { return null; } Set<ResourceRequirement.Partition> partitions = existingRequirement.getPartitions(); ResourceRequirement.Builder builder = ResourceRequirement.builder(Constants.Service.STREAMS); for (ResourceRequirement.Partition partition : partitions) { if (!partition.getName().equals(streamId.toString())) { builder.addPartition(partition); } } return builder.build(); } }); } private ZKClient getCoordinatorZKClient() { return ZKClients.namespace(zkClient, Constants.Stream.STREAM_ZK_COORDINATION_NAMESPACE); } }