/* * 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.facebook.presto.raptor.storage.organization; import com.facebook.presto.raptor.storage.StorageManagerConfig; import io.airlift.concurrent.ThreadPoolExecutorMBean; import io.airlift.log.Logger; import io.airlift.stats.CounterStat; import org.weakref.jmx.Managed; import org.weakref.jmx.Nested; import javax.annotation.PreDestroy; import javax.inject.Inject; import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicBoolean; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.Sets.newConcurrentHashSet; import static io.airlift.concurrent.Threads.daemonThreadsNamed; import static java.util.Objects.requireNonNull; import static java.util.concurrent.CompletableFuture.runAsync; import static java.util.concurrent.Executors.newFixedThreadPool; public class ShardOrganizer { private static final Logger log = Logger.get(ShardOrganizer.class); private final ExecutorService executorService; private final ThreadPoolExecutorMBean executorMBean; private final AtomicBoolean shutdown = new AtomicBoolean(); // Tracks shards that are scheduled for compaction so that we do not schedule them more than once private final Set<UUID> shardsInProgress = newConcurrentHashSet(); private final JobFactory jobFactory; private final CounterStat successCount = new CounterStat(); private final CounterStat failureCount = new CounterStat(); @Inject public ShardOrganizer(JobFactory jobFactory, StorageManagerConfig config) { this(jobFactory, config.getOrganizationThreads()); } public ShardOrganizer(JobFactory jobFactory, int threads) { checkArgument(threads > 0, "threads must be > 0"); this.jobFactory = requireNonNull(jobFactory, "jobFactory is null"); this.executorService = newFixedThreadPool(threads, daemonThreadsNamed("shard-organizer-%s")); this.executorMBean = new ThreadPoolExecutorMBean((ThreadPoolExecutor) executorService); } @PreDestroy public void shutdown() { if (!shutdown.getAndSet(true)) { executorService.shutdownNow(); } } public CompletableFuture<?> enqueue(OrganizationSet organizationSet) { shardsInProgress.addAll(organizationSet.getShards()); return runAsync(jobFactory.create(organizationSet), executorService) .whenComplete((none, throwable) -> { shardsInProgress.removeAll(organizationSet.getShards()); if (throwable == null) { successCount.update(1); } else { log.warn(throwable, "Error running organization job"); failureCount.update(1); } }); } public boolean inProgress(UUID shardUuid) { return shardsInProgress.contains(shardUuid); } @Managed @Nested public ThreadPoolExecutorMBean getExecutor() { return executorMBean; } @Managed public int getShardsInProgress() { return shardsInProgress.size(); } @Managed @Nested public CounterStat getSuccessCount() { return successCount; } @Managed @Nested public CounterStat getFailureCount() { return failureCount; } }