Java tutorial
/* * Copyright 2013 Twitter, 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 com.twitter.aurora.scheduler.periodic; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.util.Set; import java.util.UUID; import java.util.logging.Logger; import javax.inject.Inject; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; import com.google.common.collect.Maps; import com.google.inject.BindingAnnotation; import com.google.protobuf.ByteString; import org.apache.mesos.Protos.ExecutorID; import org.apache.mesos.Protos.ExecutorInfo; import org.apache.mesos.Protos.Offer; import org.apache.mesos.Protos.OfferID; import org.apache.mesos.Protos.TaskID; import org.apache.mesos.Protos.TaskInfo; import org.apache.mesos.Protos.TaskStatus; import com.twitter.aurora.Protobufs; import com.twitter.aurora.codec.ThriftBinaryCodec; import com.twitter.aurora.codec.ThriftBinaryCodec.CodingException; import com.twitter.aurora.gen.comm.AdjustRetainedTasks; import com.twitter.aurora.scheduler.PulseMonitor; import com.twitter.aurora.scheduler.TaskLauncher; import com.twitter.aurora.scheduler.base.CommandUtil; import com.twitter.aurora.scheduler.base.Query; import com.twitter.aurora.scheduler.base.Tasks; import com.twitter.aurora.scheduler.configuration.Resources; import com.twitter.aurora.scheduler.storage.Storage; import com.twitter.aurora.scheduler.storage.entities.IScheduledTask; import com.twitter.common.quantity.Amount; import com.twitter.common.quantity.Data; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; import static com.google.common.base.Preconditions.checkNotNull; /** * A task launcher that periodically initiates garbage collection on a host, re-using a single * garbage collection executor */ public class GcExecutorLauncher implements TaskLauncher { private static final Logger LOG = Logger.getLogger(GcExecutorLauncher.class.getName()); /** * Binding annotation for gc executor-related fields.. */ @BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME) public @interface GcExecutor { } @VisibleForTesting static final Resources TOTAL_GC_EXECUTOR_RESOURCES = new Resources(0.2, Amount.of(128L, Data.MB), Amount.of(16L, Data.MB), 0); // An epsilon is used because we are required to supply executor and task resources. @VisibleForTesting static final Resources EPSILON = new Resources(0.01, Amount.of(1L, Data.MB), Amount.of(1L, Data.MB), 0); private static final Resources GC_EXECUTOR_RESOURCES = Resources.subtract(TOTAL_GC_EXECUTOR_RESOURCES, EPSILON); private static final String SYSTEM_TASK_PREFIX = "system-gc-"; private static final String EXECUTOR_NAME = "aurora.gc"; private final PulseMonitor<String> pulseMonitor; private final Optional<String> gcExecutorPath; private final Storage storage; @Inject GcExecutorLauncher(@GcExecutor PulseMonitor<String> pulseMonitor, @GcExecutor Optional<String> gcExecutorPath, Storage storage) { this.pulseMonitor = checkNotNull(pulseMonitor); this.gcExecutorPath = checkNotNull(gcExecutorPath); this.storage = checkNotNull(storage); } @Override public Optional<TaskInfo> createTask(Offer offer) { if (!gcExecutorPath.isPresent() || !Resources.from(offer).greaterThanOrEqual(TOTAL_GC_EXECUTOR_RESOURCES) || pulseMonitor.isAlive(offer.getHostname())) { return Optional.absent(); } Set<IScheduledTask> tasksOnHost = Storage.Util.weaklyConsistentFetchTasks(storage, Query.slaveScoped(offer.getHostname())); AdjustRetainedTasks message = new AdjustRetainedTasks() .setRetainedTasks(Maps.transformValues(Tasks.mapById(tasksOnHost), Tasks.GET_STATUS)); byte[] data; try { data = ThriftBinaryCodec.encode(message); } catch (CodingException e) { LOG.severe("Failed to encode retained tasks message: " + message); return Optional.absent(); } pulseMonitor.pulse(offer.getHostname()); ExecutorInfo.Builder executor = ExecutorInfo.newBuilder() .setExecutorId(ExecutorID.newBuilder().setValue(EXECUTOR_NAME)).setName(EXECUTOR_NAME) .setSource(offer.getHostname()).addAllResources(GC_EXECUTOR_RESOURCES.toResourceList()) .setCommand(CommandUtil.create(gcExecutorPath.get())); return Optional.of(TaskInfo.newBuilder().setName("system-gc") .setTaskId(TaskID.newBuilder().setValue(SYSTEM_TASK_PREFIX + UUID.randomUUID().toString())) .setSlaveId(offer.getSlaveId()).setData(ByteString.copyFrom(data)).setExecutor(executor) .addAllResources(EPSILON.toResourceList()).build()); } @Override public boolean statusUpdate(TaskStatus status) { if (status.getTaskId().getValue().startsWith(SYSTEM_TASK_PREFIX)) { LOG.info("Received status update for GC task: " + Protobufs.toString(status)); return true; } else { return false; } } @Override public void cancelOffer(OfferID offer) { // No-op. } }