Files
scylladb/db/consistency_level.hh
2015-01-29 19:40:07 +01:00

393 lines
14 KiB
C++

/*
* 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.
*/
/*
* Copyright 2015 Cloudius Systems
*
* Modified by Cloudius Systems
*/
#pragma once
#include "exceptions/exceptions.hh"
#include "core/sstring.hh"
#include "database.hh"
namespace db {
#if 0
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Iterables;
import net.nicoulaj.compilecommand.annotations.Inline;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.config.ReadRepairDecision;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.exceptions.UnavailableException;
import org.apache.cassandra.locator.AbstractReplicationStrategy;
import org.apache.cassandra.locator.NetworkTopologyStrategy;
import org.apache.cassandra.transport.ProtocolException;
#endif
enum class consistency_level {
ANY,
ONE,
TWO,
THREE,
QUORUM,
ALL,
LOCAL_QUORUM,
EACH_QUORUM,
SERIAL,
LOCAL_SERIAL,
LOCAL_ONE
};
#if 0
private static final Logger logger = LoggerFactory.getLogger(ConsistencyLevel.class);
// Used by the binary protocol
public final int code;
private final boolean isDCLocal;
private static final ConsistencyLevel[] codeIdx;
static
{
int maxCode = -1;
for (ConsistencyLevel cl : ConsistencyLevel.values())
maxCode = Math.max(maxCode, cl.code);
codeIdx = new ConsistencyLevel[maxCode + 1];
for (ConsistencyLevel cl : ConsistencyLevel.values())
{
if (codeIdx[cl.code] != null)
throw new IllegalStateException("Duplicate code");
codeIdx[cl.code] = cl;
}
}
private ConsistencyLevel(int code)
{
this(code, false);
}
private ConsistencyLevel(int code, boolean isDCLocal)
{
this.code = code;
this.isDCLocal = isDCLocal;
}
public static ConsistencyLevel fromCode(int code)
{
if (code < 0 || code >= codeIdx.length)
throw new ProtocolException(String.format("Unknown code %d for a consistency level", code));
return codeIdx[code];
}
private int quorumFor(Keyspace keyspace)
{
return (keyspace.getReplicationStrategy().getReplicationFactor() / 2) + 1;
}
private int localQuorumFor(Keyspace keyspace, String dc)
{
return (keyspace.getReplicationStrategy() instanceof NetworkTopologyStrategy)
? (((NetworkTopologyStrategy) keyspace.getReplicationStrategy()).getReplicationFactor(dc) / 2) + 1
: quorumFor(keyspace);
}
public int blockFor(Keyspace keyspace)
{
switch (this)
{
case ONE:
case LOCAL_ONE:
return 1;
case ANY:
return 1;
case TWO:
return 2;
case THREE:
return 3;
case QUORUM:
case SERIAL:
return quorumFor(keyspace);
case ALL:
return keyspace.getReplicationStrategy().getReplicationFactor();
case LOCAL_QUORUM:
case LOCAL_SERIAL:
return localQuorumFor(keyspace, DatabaseDescriptor.getLocalDataCenter());
case EACH_QUORUM:
if (keyspace.getReplicationStrategy() instanceof NetworkTopologyStrategy)
{
NetworkTopologyStrategy strategy = (NetworkTopologyStrategy) keyspace.getReplicationStrategy();
int n = 0;
for (String dc : strategy.getDatacenters())
n += localQuorumFor(keyspace, dc);
return n;
}
else
{
return quorumFor(keyspace);
}
default:
throw new UnsupportedOperationException("Invalid consistency level: " + toString());
}
}
#endif
static inline
bool is_datacenter_local(consistency_level l)
{
return l == consistency_level::LOCAL_ONE || l == consistency_level::LOCAL_QUORUM;
}
#if 0
public boolean isLocal(InetAddress endpoint)
{
return DatabaseDescriptor.getLocalDataCenter().equals(DatabaseDescriptor.getEndpointSnitch().getDatacenter(endpoint));
}
public int countLocalEndpoints(Iterable<InetAddress> liveEndpoints)
{
int count = 0;
for (InetAddress endpoint : liveEndpoints)
if (isLocal(endpoint))
count++;
return count;
}
private Map<String, Integer> countPerDCEndpoints(Keyspace keyspace, Iterable<InetAddress> liveEndpoints)
{
NetworkTopologyStrategy strategy = (NetworkTopologyStrategy) keyspace.getReplicationStrategy();
Map<String, Integer> dcEndpoints = new HashMap<String, Integer>();
for (String dc: strategy.getDatacenters())
dcEndpoints.put(dc, 0);
for (InetAddress endpoint : liveEndpoints)
{
String dc = DatabaseDescriptor.getEndpointSnitch().getDatacenter(endpoint);
dcEndpoints.put(dc, dcEndpoints.get(dc) + 1);
}
return dcEndpoints;
}
public List<InetAddress> filterForQuery(Keyspace keyspace, List<InetAddress> liveEndpoints)
{
return filterForQuery(keyspace, liveEndpoints, ReadRepairDecision.NONE);
}
public List<InetAddress> filterForQuery(Keyspace keyspace, List<InetAddress> liveEndpoints, ReadRepairDecision readRepair)
{
/*
* Endpoints are expected to be restricted to live replicas, sorted by snitch preference.
* For LOCAL_QUORUM, move local-DC replicas in front first as we need them there whether
* we do read repair (since the first replica gets the data read) or not (since we'll take
* the blockFor first ones).
*/
if (isDCLocal)
Collections.sort(liveEndpoints, DatabaseDescriptor.getLocalComparator());
switch (readRepair)
{
case NONE:
return liveEndpoints.subList(0, Math.min(liveEndpoints.size(), blockFor(keyspace)));
case GLOBAL:
return liveEndpoints;
case DC_LOCAL:
List<InetAddress> local = new ArrayList<InetAddress>();
List<InetAddress> other = new ArrayList<InetAddress>();
for (InetAddress add : liveEndpoints)
{
if (isLocal(add))
local.add(add);
else
other.add(add);
}
// check if blockfor more than we have localep's
int blockFor = blockFor(keyspace);
if (local.size() < blockFor)
local.addAll(other.subList(0, Math.min(blockFor - local.size(), other.size())));
return local;
default:
throw new AssertionError();
}
}
public boolean isSufficientLiveNodes(Keyspace keyspace, Iterable<InetAddress> liveEndpoints)
{
switch (this)
{
case ANY:
// local hint is acceptable, and local node is always live
return true;
case LOCAL_ONE:
return countLocalEndpoints(liveEndpoints) >= 1;
case LOCAL_QUORUM:
return countLocalEndpoints(liveEndpoints) >= blockFor(keyspace);
case EACH_QUORUM:
if (keyspace.getReplicationStrategy() instanceof NetworkTopologyStrategy)
{
for (Map.Entry<String, Integer> entry : countPerDCEndpoints(keyspace, liveEndpoints).entrySet())
{
if (entry.getValue() < localQuorumFor(keyspace, entry.getKey()))
return false;
}
return true;
}
// Fallthough on purpose for SimpleStrategy
default:
return Iterables.size(liveEndpoints) >= blockFor(keyspace);
}
}
public void assureSufficientLiveNodes(Keyspace keyspace, Iterable<InetAddress> liveEndpoints) throws UnavailableException
{
int blockFor = blockFor(keyspace);
switch (this)
{
case ANY:
// local hint is acceptable, and local node is always live
break;
case LOCAL_ONE:
if (countLocalEndpoints(liveEndpoints) == 0)
throw new UnavailableException(this, 1, 0);
break;
case LOCAL_QUORUM:
int localLive = countLocalEndpoints(liveEndpoints);
if (localLive < blockFor)
{
if (logger.isDebugEnabled())
{
StringBuilder builder = new StringBuilder("Local replicas [");
for (InetAddress endpoint : liveEndpoints)
{
if (isLocal(endpoint))
builder.append(endpoint).append(",");
}
builder.append("] are insufficient to satisfy LOCAL_QUORUM requirement of ").append(blockFor).append(" live nodes in '").append(DatabaseDescriptor.getLocalDataCenter()).append("'");
logger.debug(builder.toString());
}
throw new UnavailableException(this, blockFor, localLive);
}
break;
case EACH_QUORUM:
if (keyspace.getReplicationStrategy() instanceof NetworkTopologyStrategy)
{
for (Map.Entry<String, Integer> entry : countPerDCEndpoints(keyspace, liveEndpoints).entrySet())
{
int dcBlockFor = localQuorumFor(keyspace, entry.getKey());
int dcLive = entry.getValue();
if (dcLive < dcBlockFor)
throw new UnavailableException(this, dcBlockFor, dcLive);
}
break;
}
// Fallthough on purpose for SimpleStrategy
default:
int live = Iterables.size(liveEndpoints);
if (live < blockFor)
{
logger.debug("Live nodes {} do not satisfy ConsistencyLevel ({} required)", Iterables.toString(liveEndpoints), blockFor);
throw new UnavailableException(this, blockFor, live);
}
break;
}
}
#endif
static inline
void validate_for_read(const sstring keyspace_name, consistency_level cl) {
switch (cl) {
case consistency_level::ANY:
throw exceptions::invalid_request_exception("ANY ConsistencyLevel is only supported for writes");
case consistency_level::EACH_QUORUM:
throw exceptions::invalid_request_exception("EACH_QUORUM ConsistencyLevel is only supported for writes");
default:
break;
}
}
static inline
void validate_for_write(const sstring keyspace_name, consistency_level cl) {
switch (cl) {
case consistency_level::SERIAL:
case consistency_level::LOCAL_SERIAL:
throw exceptions::invalid_request_exception("You must use conditional updates for serializable writes");
default:
break;
}
}
#if 0
// This is the same than validateForWrite really, but we include a slightly different error message for SERIAL/LOCAL_SERIAL
public void validateForCasCommit(String keyspaceName) throws InvalidRequestException
{
switch (this)
{
case EACH_QUORUM:
requireNetworkTopologyStrategy(keyspaceName);
break;
case SERIAL:
case LOCAL_SERIAL:
throw new InvalidRequestException(this + " is not supported as conditional update commit consistency. Use ANY if you mean \"make sure it is accepted but I don't care how many replicas commit it for non-SERIAL reads\"");
}
}
public void validateForCas() throws InvalidRequestException
{
if (!isSerialConsistency())
throw new InvalidRequestException("Invalid consistency for conditional update. Must be one of SERIAL or LOCAL_SERIAL");
}
#endif
static inline
bool is_serial_consistency(consistency_level cl) {
return cl == consistency_level::SERIAL || cl == consistency_level::LOCAL_SERIAL;
}
static inline
void validate_counter_for_write(schema_ptr s, consistency_level cl) {
if (cl == consistency_level::ANY) {
throw exceptions::invalid_request_exception(sprint("Consistency level ANY is not yet supported for counter table %s", s->cf_name));
}
if (is_serial_consistency(cl)) {
throw exceptions::invalid_request_exception("Counter operations are inherently non-serializable");
}
}
#if 0
private void requireNetworkTopologyStrategy(String keyspaceName) throws InvalidRequestException
{
AbstractReplicationStrategy strategy = Keyspace.open(keyspaceName).getReplicationStrategy();
if (!(strategy instanceof NetworkTopologyStrategy))
throw new InvalidRequestException(String.format("consistency level %s not compatible with replication strategy (%s)", this, strategy.getClass().getName()));
}
#endif
}