/* * 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.db; import org.apache.cassandra.index.Index; import org.apache.cassandra.utils.concurrent.OpOrder; public class ReadOrderGroup implements AutoCloseable { // For every reads private final OpOrder.Group baseOp; // For index reads private final OpOrder.Group indexOp; private final OpOrder.Group writeOp; private ReadOrderGroup(OpOrder.Group baseOp, OpOrder.Group indexOp, OpOrder.Group writeOp) { this.baseOp = baseOp; this.indexOp = indexOp; this.writeOp = writeOp; } public OpOrder.Group baseReadOpOrderGroup() { return baseOp; } public OpOrder.Group indexReadOpOrderGroup() { return indexOp; } public OpOrder.Group writeOpOrderGroup() { return writeOp; } public static ReadOrderGroup emptyGroup() { return new ReadOrderGroup(null, null, null); } @SuppressWarnings("resource") // ops closed during group close public static ReadOrderGroup forCommand(ReadCommand command) { ColumnFamilyStore baseCfs = Keyspace.openAndGetStore(command.metadata()); ColumnFamilyStore indexCfs = maybeGetIndexCfs(baseCfs, command); if (indexCfs == null) { return new ReadOrderGroup(baseCfs.readOrdering.start(), null, null); } else { OpOrder.Group baseOp = null, indexOp = null, writeOp = null; // OpOrder.start() shouldn't fail, but better safe than sorry. try { baseOp = baseCfs.readOrdering.start(); indexOp = indexCfs.readOrdering.start(); // TODO: this should perhaps not open and maintain a writeOp for the full duration, but instead only *try* to delete stale entries, without blocking if there's no room // as it stands, we open a writeOp and keep it open for the duration to ensure that should this CF get flushed to make room we don't block the reclamation of any room being made writeOp = Keyspace.writeOrder.start(); return new ReadOrderGroup(baseOp, indexOp, writeOp); } catch (RuntimeException e) { // Note that must have writeOp == null since ReadOrderGroup ctor can't fail assert writeOp == null; try { if (baseOp != null) baseOp.close(); } finally { if (indexOp != null) indexOp.close(); } throw e; } } } private static ColumnFamilyStore maybeGetIndexCfs(ColumnFamilyStore baseCfs, ReadCommand command) { Index index = command.getIndex(baseCfs); return index == null ? null : index.getBackingTable().orElse(null); } public void close() { try { if (baseOp != null) baseOp.close(); } finally { if (indexOp != null) { try { indexOp.close(); } finally { writeOp.close(); } } } } }