/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.accumulo.tserver.tablet; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.accumulo.core.client.sample.SamplerConfiguration; import org.apache.accumulo.core.data.Column; import org.apache.accumulo.core.data.Key; import org.apache.accumulo.core.data.Value; import org.apache.accumulo.core.data.thrift.IterInfo; import org.apache.accumulo.core.iterators.IterationInterruptedException; import org.apache.accumulo.core.iterators.IteratorEnvironment; import org.apache.accumulo.core.iterators.IteratorUtil; import org.apache.accumulo.core.iterators.IteratorUtil.IteratorScope; import org.apache.accumulo.core.iterators.SortedKeyValueIterator; import org.apache.accumulo.core.iterators.system.InterruptibleIterator; import org.apache.accumulo.core.iterators.system.MultiIterator; import org.apache.accumulo.core.iterators.system.SourceSwitchingIterator.DataSource; import org.apache.accumulo.core.iterators.system.StatsIterator; import org.apache.accumulo.core.metadata.schema.DataFileValue; import org.apache.accumulo.core.sample.impl.SamplerConfigurationImpl; import org.apache.accumulo.core.security.Authorizations; import org.apache.accumulo.core.util.Pair; import org.apache.accumulo.server.fs.FileRef; import org.apache.accumulo.tserver.FileManager.ScanFileManager; import org.apache.accumulo.tserver.InMemoryMap.MemoryIterator; import org.apache.accumulo.tserver.TabletIteratorEnvironment; import org.apache.accumulo.tserver.TabletServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Iterables; class ScanDataSource implements DataSource { private static final Logger log = LoggerFactory.getLogger(ScanDataSource.class); // data source state private final Tablet tablet; private ScanFileManager fileManager; private SortedKeyValueIterator<Key,Value> iter; private long expectedDeletionCount; private List<MemoryIterator> memIters = null; private long fileReservationId; private AtomicBoolean interruptFlag; private StatsIterator statsIterator; private final ScanOptions options; private final boolean loadIters; private static final Set<Column> EMPTY_COLS = Collections.emptySet(); ScanDataSource(Tablet tablet, Authorizations authorizations, byte[] defaultLabels, HashSet<Column> columnSet, List<IterInfo> ssiList, Map<String,Map<String,String>> ssio, AtomicBoolean interruptFlag, SamplerConfiguration samplerConfig, long batchTimeOut, String context) { this(tablet, tablet.getDataSourceDeletions(), new ScanOptions(-1, authorizations, defaultLabels, columnSet, ssiList, ssio, interruptFlag, false, samplerConfig, batchTimeOut, context), interruptFlag, true); } ScanDataSource(Tablet tablet, ScanOptions options) { this(tablet, tablet.getDataSourceDeletions(), options, options.getInterruptFlag(), true); } ScanDataSource(Tablet tablet, Authorizations authorizations, byte[] defaultLabels, AtomicBoolean iFlag) { this(tablet, tablet.getDataSourceDeletions(), new ScanOptions(-1, authorizations, defaultLabels, EMPTY_COLS, null, null, iFlag, false, null, -1, null), iFlag, false); } ScanDataSource(Tablet tablet, long expectedDeletionCount, ScanOptions options, AtomicBoolean interruptFlag, boolean loadIters) { this.tablet = tablet; this.expectedDeletionCount = expectedDeletionCount; this.options = options; this.interruptFlag = interruptFlag; this.loadIters = loadIters; log.trace("new scan data source, tablet: {}, options: {}, interruptFlag: {}, loadIterators: {}", this.tablet, this.options, this.interruptFlag, this.loadIters); } @Override public DataSource getNewDataSource() { if (!isCurrent()) { // log.debug("Switching data sources during a scan"); if (memIters != null) { tablet.getTabletMemory().returnIterators(memIters); memIters = null; tablet.getDatafileManager().returnFilesForScan(fileReservationId); fileReservationId = -1; } if (fileManager != null) fileManager.releaseOpenFiles(false); expectedDeletionCount = tablet.getDataSourceDeletions(); iter = null; return this; } else return this; } @Override public boolean isCurrent() { return expectedDeletionCount == tablet.getDataSourceDeletions(); } @Override public SortedKeyValueIterator<Key,Value> iterator() throws IOException { if (iter == null) iter = createIterator(); return iter; } private SortedKeyValueIterator<Key,Value> createIterator() throws IOException { Map<FileRef,DataFileValue> files; SamplerConfigurationImpl samplerConfig = options.getSamplerConfigurationImpl(); synchronized (tablet) { if (memIters != null) throw new IllegalStateException("Tried to create new scan iterator w/o releasing memory"); if (tablet.isClosed()) throw new TabletClosedException(); if (interruptFlag.get()) throw new IterationInterruptedException(tablet.getExtent().toString() + " " + interruptFlag.hashCode()); // only acquire the file manager when we know the tablet is open if (fileManager == null) { fileManager = tablet.getTabletResources().newScanFileManager(); tablet.addActiveScans(this); } if (fileManager.getNumOpenFiles() != 0) throw new IllegalStateException("Tried to create new scan iterator w/o releasing files"); // set this before trying to get iterators in case // getIterators() throws an exception expectedDeletionCount = tablet.getDataSourceDeletions(); memIters = tablet.getTabletMemory().getIterators(samplerConfig); Pair<Long,Map<FileRef,DataFileValue>> reservation = tablet.getDatafileManager().reserveFilesForScan(); fileReservationId = reservation.getFirst(); files = reservation.getSecond(); } Collection<InterruptibleIterator> mapfiles = fileManager.openFiles(files, options.isIsolated(), samplerConfig); for (SortedKeyValueIterator<Key,Value> skvi : Iterables.concat(mapfiles, memIters)) ((InterruptibleIterator) skvi).setInterruptFlag(interruptFlag); List<SortedKeyValueIterator<Key,Value>> iters = new ArrayList<>(mapfiles.size() + memIters.size()); iters.addAll(mapfiles); iters.addAll(memIters); MultiIterator multiIter = new MultiIterator(iters, tablet.getExtent()); TabletIteratorEnvironment iterEnv = new TabletIteratorEnvironment(IteratorScope.scan, tablet.getTableConfiguration(), fileManager, files, options.getAuthorizations(), samplerConfig); statsIterator = new StatsIterator(multiIter, TabletServer.seekCount, tablet.getScannedCounter()); SortedKeyValueIterator<Key,Value> visFilter = IteratorUtil.setupSystemScanIterators(statsIterator, options.getColumnSet(), options.getAuthorizations(), options.getDefaultLabels()); if (!loadIters) { return visFilter; } else if (null == options.getClassLoaderContext()) { log.trace("Loading iterators for scan"); return iterEnv.getTopLevelIterator(IteratorUtil.loadIterators(IteratorScope.scan, visFilter, tablet.getExtent(), tablet.getTableConfiguration(), options.getSsiList(), options.getSsio(), iterEnv)); } else { log.trace("Loading iterators for scan with scan context: {}", options.getClassLoaderContext()); return iterEnv.getTopLevelIterator(IteratorUtil.loadIterators(IteratorScope.scan, visFilter, tablet.getExtent(), tablet.getTableConfiguration(), options.getSsiList(), options.getSsio(), iterEnv, true, options.getClassLoaderContext())); } } void close(boolean sawErrors) { if (memIters != null) { tablet.getTabletMemory().returnIterators(memIters); memIters = null; tablet.getDatafileManager().returnFilesForScan(fileReservationId); fileReservationId = -1; } synchronized (tablet) { if (tablet.removeScan(this) == 0) tablet.notifyAll(); } if (fileManager != null) { fileManager.releaseOpenFiles(sawErrors); fileManager = null; } if (statsIterator != null) { statsIterator.report(); } } public void interrupt() { interruptFlag.set(true); } @Override public DataSource getDeepCopyDataSource(IteratorEnvironment env) { throw new UnsupportedOperationException(); } public void reattachFileManager() throws IOException { if (fileManager != null) fileManager.reattach(options.getSamplerConfigurationImpl()); } public void detachFileManager() { if (fileManager != null) fileManager.detach(); } @Override public void setInterruptFlag(AtomicBoolean flag) { throw new UnsupportedOperationException(); } }