/*
* Copyright © 2014-2015 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.internal.app.runtime.flow;
import co.cask.cdap.api.flow.flowlet.FlowletContext;
import co.cask.cdap.api.flow.flowlet.FlowletSpecification;
import co.cask.cdap.api.metrics.Metrics;
import co.cask.cdap.api.metrics.MetricsCollectionService;
import co.cask.cdap.api.metrics.MetricsContext;
import co.cask.cdap.api.plugin.Plugin;
import co.cask.cdap.app.metrics.ProgramUserMetrics;
import co.cask.cdap.app.program.Program;
import co.cask.cdap.app.runtime.Arguments;
import co.cask.cdap.common.conf.Constants;
import co.cask.cdap.common.logging.LoggingContext;
import co.cask.cdap.common.utils.ImmutablePair;
import co.cask.cdap.data2.dataset2.DatasetFramework;
import co.cask.cdap.internal.app.runtime.AbstractContext;
import co.cask.cdap.logging.context.FlowletLoggingContext;
import co.cask.tephra.TransactionSystemClient;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import org.apache.twill.api.RunId;
import org.apache.twill.discovery.DiscoveryServiceClient;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Internal implementation of {@link FlowletContext}.
*/
final class BasicFlowletContext extends AbstractContext implements FlowletContext {
private final String flowId;
private final String flowletId;
private final long groupId;
private final int instanceId;
private final FlowletSpecification flowletSpec;
private volatile int instanceCount;
private final Metrics userMetrics;
private final LoadingCache<String, MetricsContext> queueMetrics;
private final LoadingCache<ImmutablePair<String, String>, MetricsContext> producerMetrics;
BasicFlowletContext(Program program, final String flowletId,
int instanceId, RunId runId,
int instanceCount, Set<String> datasets,
Arguments runtimeArguments, FlowletSpecification flowletSpec,
MetricsCollectionService metricsCollectionService,
DiscoveryServiceClient discoveryServiceClient,
TransactionSystemClient txClient,
DatasetFramework dsFramework) {
super(program, runId, runtimeArguments, datasets,
getMetricCollector(metricsCollectionService, program, flowletId, runId.getId(), instanceId),
dsFramework, txClient, discoveryServiceClient, false);
this.flowId = program.getName();
this.flowletId = flowletId;
this.groupId = FlowUtils.generateConsumerGroupId(program, flowletId);
this.instanceId = instanceId;
this.instanceCount = instanceCount;
this.flowletSpec = flowletSpec;
this.userMetrics = new ProgramUserMetrics(getMetricCollector(metricsCollectionService, program,
flowletId, runId.getId(), instanceId));
// TODO - does this have to cache the metric collectors? Metrics framework itself has a cache [CDAP-2334]
//noinspection NullableProblems
this.queueMetrics = CacheBuilder.newBuilder()
.expireAfterAccess(1, TimeUnit.HOURS)
.build(new CacheLoader<String, MetricsContext>() {
@Override
public MetricsContext load(String key) throws Exception {
return getProgramMetrics().childContext(Constants.Metrics.Tag.FLOWLET_QUEUE, key);
}
});
//noinspection NullableProblems
this.producerMetrics = CacheBuilder.newBuilder()
.expireAfterAccess(1, TimeUnit.HOURS)
.build(new CacheLoader<ImmutablePair<String, String>, MetricsContext>() {
@Override
public MetricsContext load(ImmutablePair<String, String> key) throws Exception {
return getProgramMetrics()
.childContext(ImmutableMap.of(Constants.Metrics.Tag.PRODUCER, key.getFirst(),
Constants.Metrics.Tag.FLOWLET_QUEUE, key.getSecond(),
Constants.Metrics.Tag.CONSUMER, BasicFlowletContext.this.flowletId));
}
});
}
@Override
public String toString() {
return String.format("flowlet=%s, instance=%d, groupsize=%s, %s",
getFlowletId(), getInstanceId(), getInstanceCount(), super.toString());
}
@Override
public Map<String, Plugin> getPlugins() {
return null;
}
@Override
public int getInstanceCount() {
return instanceCount;
}
@Override
public String getName() {
return getFlowletId();
}
@Override
public FlowletSpecification getSpecification() {
return flowletSpec;
}
public void setInstanceCount(int count) {
instanceCount = count;
}
public String getFlowId() {
return flowId;
}
public String getFlowletId() {
return flowletId;
}
@Override
public int getInstanceId() {
return instanceId;
}
public LoggingContext getLoggingContext() {
return new FlowletLoggingContext(getNamespaceId(), getApplicationId(), getFlowId(), getFlowletId(),
getRunId().getId(), String.valueOf(getInstanceId()));
}
@Override
public Metrics getMetrics() {
return userMetrics;
}
public MetricsContext getQueueMetrics(String flowletQueueName) {
return queueMetrics.getUnchecked(flowletQueueName);
}
public MetricsContext getProducerMetrics(ImmutablePair<String, String> producerAndQueue) {
return producerMetrics.getUnchecked(producerAndQueue);
}
public long getGroupId() {
return groupId;
}
private static MetricsContext getMetricCollector(MetricsCollectionService service, Program program,
String flowletName, String runId, int instanceId) {
if (service == null) {
return null;
}
Map<String, String> tags = Maps.newHashMap(getMetricsContext(program, runId));
tags.put(Constants.Metrics.Tag.FLOWLET, flowletName);
tags.put(Constants.Metrics.Tag.INSTANCE_ID, String.valueOf(instanceId));
return service.getContext(tags);
}
}