/* * 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.cassandra.index.sasi; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.apache.cassandra.db.DecoratedKey; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.index.sasi.conf.ColumnIndex; import org.apache.cassandra.index.sasi.disk.OnDiskIndex; import org.apache.cassandra.index.sasi.disk.OnDiskIndexBuilder; import org.apache.cassandra.index.sasi.disk.Token; import org.apache.cassandra.index.sasi.plan.Expression; import org.apache.cassandra.index.sasi.utils.RangeIterator; import org.apache.cassandra.io.FSReadError; import org.apache.cassandra.io.sstable.format.SSTableReader; import org.apache.cassandra.io.util.FileUtils; import org.apache.cassandra.utils.concurrent.Ref; import org.apache.commons.lang3.builder.HashCodeBuilder; import com.google.common.base.Function; public class SSTableIndex { private final ColumnIndex columnIndex; private final Ref<SSTableReader> sstableRef; private final SSTableReader sstable; private final OnDiskIndex index; private final AtomicInteger references = new AtomicInteger(1); private final AtomicBoolean obsolete = new AtomicBoolean(false); public SSTableIndex(ColumnIndex index, File indexFile, SSTableReader referent) { this.columnIndex = index; this.sstableRef = referent.tryRef(); this.sstable = sstableRef.get(); if (sstable == null) throw new IllegalStateException("Couldn't acquire reference to the sstable: " + referent); AbstractType<?> validator = columnIndex.getValidator(); assert validator != null; assert indexFile.exists() : String.format("SSTable %s should have index %s.", sstable.getFilename(), columnIndex.getIndexName()); this.index = new OnDiskIndex(indexFile, validator, new DecoratedKeyFetcher(sstable)); } public OnDiskIndexBuilder.Mode mode() { return index.mode(); } public boolean hasMarkedPartials() { return index.hasMarkedPartials(); } public ByteBuffer minTerm() { return index.minTerm(); } public ByteBuffer maxTerm() { return index.maxTerm(); } public ByteBuffer minKey() { return index.minKey(); } public ByteBuffer maxKey() { return index.maxKey(); } public RangeIterator<Long, Token> search(Expression expression) { return index.search(expression); } public SSTableReader getSSTable() { return sstable; } public String getPath() { return index.getIndexPath(); } public boolean reference() { while (true) { int n = references.get(); if (n <= 0) return false; if (references.compareAndSet(n, n + 1)) return true; } } public void release() { int n = references.decrementAndGet(); if (n == 0) { FileUtils.closeQuietly(index); sstableRef.release(); if (obsolete.get() || sstableRef.globalCount() == 0) FileUtils.delete(index.getIndexPath()); } } public void markObsolete() { obsolete.getAndSet(true); release(); } public boolean isObsolete() { return obsolete.get(); } public boolean equals(Object o) { return o instanceof SSTableIndex && index.getIndexPath().equals(((SSTableIndex) o).index.getIndexPath()); } public int hashCode() { return new HashCodeBuilder().append(index.getIndexPath()).build(); } public String toString() { return String.format("SSTableIndex(column: %s, SSTable: %s)", columnIndex.getColumnName(), sstable.descriptor); } private static class DecoratedKeyFetcher implements Function<Long, DecoratedKey> { private final SSTableReader sstable; DecoratedKeyFetcher(SSTableReader reader) { sstable = reader; } public DecoratedKey apply(Long offset) { try { return sstable.keyAt(offset); } catch (IOException e) { throw new FSReadError(new IOException("Failed to read key from " + sstable.descriptor, e), sstable.getFilename()); } } public int hashCode() { return sstable.descriptor.hashCode(); } public boolean equals(Object other) { return other instanceof DecoratedKeyFetcher && sstable.descriptor.equals(((DecoratedKeyFetcher) other).sstable.descriptor); } } }