/* * Copyright 2014, Tuplejump 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.tuplejump.stargate; import com.tuplejump.stargate.lucene.BasicIndexer; import com.tuplejump.stargate.lucene.Indexer; import com.tuplejump.stargate.lucene.SearcherCallback; import org.apache.cassandra.db.DecoratedKey; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.MultiReader; import org.apache.lucene.search.IndexSearcher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * User: satya */ public class PerVNodeIndexContainer implements IndexContainer { protected static final Logger logger = LoggerFactory.getLogger(RowIndex.class); static ExecutorService executorService = Executors.newFixedThreadPool(10); Map<Range<Token>, Indexer> indexers = new HashMap<>(); private ReadWriteLock indexLock = new ReentrantReadWriteLock(); private final Lock writeLock = indexLock.writeLock(); Analyzer analyzer; String keyspace; String cf; String indexName; public PerVNodeIndexContainer(Analyzer analyzer, String keyspace, String cf, String indexName) { indexers = new HashMap<>(); this.analyzer = analyzer; this.keyspace = keyspace; this.cf = cf; this.indexName = indexName; } @Override public void updateIndexers(Collection<Range<Token>> ranges) { writeLock.lock(); Boolean isInfoLoggingEnabled = logger.isInfoEnabled(); try { if (indexers.isEmpty()) { if (isInfoLoggingEnabled) { logger.info("Adding VNode indexers"); } for (Range<Token> range : ranges) { String rangeStr = range.left.toString(); AtomicLong records = Stargate.getInstance().getAtomicLong(INDEX_RECORDS + "-" + indexName + "-" + rangeStr); Indexer indexer = new BasicIndexer(records, analyzer, keyspace, cf, indexName, rangeStr); indexers.put(range, indexer); if (isInfoLoggingEnabled) { logger.info("Added VNode indexers for range {}", range); } } } else { if (isInfoLoggingEnabled) { logger.info("Change in VNode indexers"); } HashMap<Range<Token>, Indexer> indexersToRemove = new HashMap<>(indexers); for (Range<Token> range : ranges) { indexersToRemove.remove(range); } for (Map.Entry<Range<Token>, Indexer> entry : indexersToRemove.entrySet()) { if (isInfoLoggingEnabled) { logger.info("Removing indexer for range {}", entry.getKey()); } Indexer indexer = indexers.remove(entry.getKey()); indexer.removeIndex(); if (isInfoLoggingEnabled) { logger.info("Removed indexer for range {}", entry.getKey()); } } } } finally { writeLock.unlock(); } } @Override public <T> T search(SearcherCallback<T> searcherCallback) { List<IndexReader> indexReaders = new ArrayList<>(); Map<Indexer, IndexSearcher> indexSearchers = new HashMap<>(); for (Map.Entry<Range<Token>, Indexer> entry : indexers.entrySet()) { Range<Token> range = entry.getKey(); boolean intersects = intersects(searcherCallback.filterRange(), searcherCallback.isSingleToken(), searcherCallback.isFullRange(), range); if (intersects) { Indexer indexer = entry.getValue(); IndexSearcher searcher = indexer.acquire(); indexSearchers.put(indexer, searcher); indexReaders.add(searcher.getIndexReader()); } } IndexReader[] indexReadersArr = new IndexReader[indexReaders.size()]; indexReaders.toArray(indexReadersArr); MultiReader multiReader = null; try { multiReader = new MultiReader(indexReadersArr, false); IndexSearcher allSearcher = new IndexSearcher(multiReader, executorService); return searcherCallback.doWithSearcher(allSearcher); } catch (Exception e) { throw new RuntimeException(e); } finally { try { if (multiReader != null) multiReader.close(); } catch (IOException e) { logger.error("Could not close reader", e); } for (Map.Entry<Indexer, IndexSearcher> entry : indexSearchers.entrySet()) { entry.getKey().release(entry.getValue()); } } } private boolean intersects(Range<Token> filterRange, boolean isSingleToken, boolean isFullRange, Range<Token> range) { boolean intersects; if (isFullRange) intersects = true; else if (isSingleToken) intersects = range.contains(filterRange.left); else { intersects = range.intersects(filterRange); } return intersects; } @Override public Indexer indexer(DecoratedKey decoratedKey) { for (Map.Entry<Range<Token>, Indexer> entry : indexers.entrySet()) { if (entry.getKey().contains(decoratedKey.getToken())) return entry.getValue(); } throw new IllegalStateException("No VNodeIndexer found for indexing key [" + decoratedKey + "]"); } @Override public void commit() { for (Indexer indexer : indexers.values()) { indexer.commit(); } } @Override public void close() { for (Indexer indexer : indexers.values()) { indexer.close(); } } @Override public long size() { long size = 0; for (Indexer indexer : indexers.values()) { size += (indexer == null) ? 0 : indexer.size(); } return size; } @Override public long liveSize() { long size = 0; for (Indexer indexer : indexers.values()) { size += (indexer == null) ? 0 : indexer.liveSize(); } return size; } @Override public long rowCount() { long size = 0; for (Indexer indexer : indexers.values()) { size += (indexer == null) ? 0 : indexer.approxRowCount(); } return size; } @Override public void remove() { for (Indexer indexer : indexers.values()) { if (indexer != null) { indexer.removeIndex(); } } } @Override public void truncate(long l) { for (Indexer indexer : indexers.values()) { if (indexer != null) { indexer.truncate(l); if (logger.isInfoEnabled()) { logger.info(" Truncated index {}.", indexName); } } } } @Override public String indexName() { return indexName; } }