Java tutorial
/** * Copyright (C) 2014-2016 LinkedIn Corp. (pinot-core@linkedin.com) * * 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.linkedin.pinot.controller.helix.core; import java.io.File; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import org.apache.commons.io.FileUtils; import org.apache.helix.AccessOption; import org.apache.helix.HelixAdmin; import org.apache.helix.ZNRecord; import org.apache.helix.store.zk.ZkHelixPropertyStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.linkedin.pinot.common.config.TableNameBuilder; import com.linkedin.pinot.common.metadata.ZKMetadataProvider; /** * */ public class SegmentDeletionManager { private static final Logger LOGGER = LoggerFactory.getLogger(SegmentDeletionManager.class); private static final long MAX_DELETION_DELAY_SECONDS = 3600L; private static final long DEFAULT_DELETION_DELAY_SECONDS = 2L; private final ScheduledExecutorService _executorService; private final String _localDiskDir; private final String _helixClusterName; private final HelixAdmin _helixAdmin; private final ZkHelixPropertyStore<ZNRecord> _propertyStore; SegmentDeletionManager(String localDiskDir, HelixAdmin helixAdmin, String helixClusterName, ZkHelixPropertyStore<ZNRecord> propertyStore) { _localDiskDir = localDiskDir; _helixAdmin = helixAdmin; _helixClusterName = helixClusterName; _propertyStore = propertyStore; _executorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { @Override public Thread newThread(Runnable runnable) { Thread thread = new Thread(runnable); thread.setName("PinotHelixResourceManagerExecutorService"); return thread; } }); } public void stop() { _executorService.shutdownNow(); } public void deleteSegment(final String tableName, final String segmentId) { deleteSegmentWithDelay(tableName, segmentId, DEFAULT_DELETION_DELAY_SECONDS); } private void deleteSegmentWithDelay(final String tableName, final String segmentId, final long deletionDelaySeconds) { _executorService.schedule(new Runnable() { @Override public void run() { deleteSegmentFromPropertyStoreAndLocal(tableName, segmentId, deletionDelaySeconds); } }, deletionDelaySeconds, TimeUnit.SECONDS); } /** * Check if segment got deleted from IdealStates and ExternalView. * If segment got removed, then delete this segment from PropertyStore and local disk. * * @param tableName * @param segmentId */ private synchronized void deleteSegmentFromPropertyStoreAndLocal(String tableName, String segmentId, long deletionDelay) { // Check if segment got removed from ExternalView and IdealStates if (_helixAdmin.getResourceExternalView(_helixClusterName, tableName) == null || _helixAdmin.getResourceIdealState(_helixClusterName, tableName) == null) { LOGGER.warn("Resource: {} is not set up in idealState or ExternalView, won't do anything", tableName); return; } boolean isSegmentReadyToDelete = false; try { Map<String, String> segmentToInstancesMapFromExternalView = _helixAdmin .getResourceExternalView(_helixClusterName, tableName).getStateMap(segmentId); Map<String, String> segmentToInstancesMapFromIdealStates = _helixAdmin .getResourceIdealState(_helixClusterName, tableName).getInstanceStateMap(segmentId); if ((segmentToInstancesMapFromExternalView == null || segmentToInstancesMapFromExternalView.isEmpty()) && (segmentToInstancesMapFromIdealStates == null || segmentToInstancesMapFromIdealStates.isEmpty())) { isSegmentReadyToDelete = true; } else { long effectiveDeletionDelay = Math.min(deletionDelay * 2, MAX_DELETION_DELAY_SECONDS); LOGGER.info( "Segment: {} is still in IdealStates: {} or ExternalView: {}, will retry in {} seconds.", segmentId, segmentToInstancesMapFromIdealStates, segmentToInstancesMapFromExternalView, effectiveDeletionDelay); deleteSegmentWithDelay(tableName, segmentId, effectiveDeletionDelay); return; } } catch (Exception e) { LOGGER.warn("Caught exception while processing segment " + segmentId, e); isSegmentReadyToDelete = true; } if (isSegmentReadyToDelete) { String segmentPropertyStorePath = ZKMetadataProvider.constructPropertyStorePathForSegment(tableName, segmentId); LOGGER.info("Trying to delete segment : {} from Property store.", segmentId); boolean deletionFromPropertyStoreSuccessful = true; if (_propertyStore.exists(segmentPropertyStorePath, AccessOption.PERSISTENT)) { deletionFromPropertyStoreSuccessful = _propertyStore.remove(segmentPropertyStorePath, AccessOption.PERSISTENT); } if (!deletionFromPropertyStoreSuccessful) { long effectiveDeletionDelay = Math.min(deletionDelay * 2, MAX_DELETION_DELAY_SECONDS); LOGGER.warn("Failed to delete segment {} from property store, will retry in {} seconds.", segmentId, effectiveDeletionDelay); deleteSegmentWithDelay(tableName, segmentId, effectiveDeletionDelay); return; } switch (TableNameBuilder.getTableTypeFromTableName(tableName)) { case OFFLINE: if (_localDiskDir != null) { File fileToDelete = new File(new File(_localDiskDir, tableName), segmentId); if (fileToDelete.exists()) { FileUtils.deleteQuietly(fileToDelete); LOGGER.info("Delete segment : " + segmentId + " from local directory : " + fileToDelete.getAbsolutePath()); } else { LOGGER.warn("Not found local segment file for segment : " + segmentId); } } else { LOGGER.info("localDiskDir is not configured, won't delete anything from disk"); } break; case REALTIME: LOGGER.info("No local segment file for RealtimeSegment in Controller"); break; default: throw new UnsupportedOperationException("Not support ResourceType for semgnet - " + segmentId); } } else { long effectiveDeletionDelay = Math.min(deletionDelay * 2, MAX_DELETION_DELAY_SECONDS); LOGGER.info("Segment: {} is still in IdealStates or ExternalView, will retry in {} seconds.", segmentId, effectiveDeletionDelay); deleteSegmentWithDelay(tableName, segmentId, effectiveDeletionDelay); } } }