blob: 1c82f5520af7102b17b7b7a9b62692a970e9e9a2 [file] [log] [blame]
/*
* 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.cache;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.cassandra.exceptions.ConfigurationException;
import static org.apache.cassandra.utils.FBUtilities.fromJsonMap;
/*
CQL: { 'keys' : 'ALL|NONE', 'rows_per_partition': '200|NONE|ALL' }
*/
public class CachingOptions
{
public static final CachingOptions KEYS_ONLY = new CachingOptions(new KeyCache(KeyCache.Type.ALL), new RowCache(RowCache.Type.NONE));
public static final CachingOptions ALL = new CachingOptions(new KeyCache(KeyCache.Type.ALL), new RowCache(RowCache.Type.ALL));
public static final CachingOptions ROWS_ONLY = new CachingOptions(new KeyCache(KeyCache.Type.NONE), new RowCache(RowCache.Type.ALL));
public static final CachingOptions NONE = new CachingOptions(new KeyCache(KeyCache.Type.NONE), new RowCache(RowCache.Type.NONE));
public final KeyCache keyCache;
public final RowCache rowCache;
private static final Set<String> legacyOptions = new HashSet<>(Arrays.asList("ALL", "NONE", "KEYS_ONLY", "ROWS_ONLY"));
public CachingOptions(KeyCache kc, RowCache rc)
{
this.keyCache = kc;
this.rowCache = rc;
}
public static CachingOptions fromString(String cache) throws ConfigurationException
{
if (legacyOptions.contains(cache.toUpperCase()))
return fromLegacyOption(cache.toUpperCase());
return fromMap(fromJsonMap(cache));
}
public static CachingOptions fromMap(Map<String, String> cacheConfig) throws ConfigurationException
{
validateCacheConfig(cacheConfig);
if (!cacheConfig.containsKey("keys") && !cacheConfig.containsKey("rows_per_partition"))
return CachingOptions.NONE;
if (!cacheConfig.containsKey("keys"))
return new CachingOptions(new KeyCache(KeyCache.Type.NONE), RowCache.fromString(cacheConfig.get("rows_per_partition")));
if (!cacheConfig.containsKey("rows_per_partition"))
return CachingOptions.KEYS_ONLY;
return new CachingOptions(KeyCache.fromString(cacheConfig.get("keys")), RowCache.fromString(cacheConfig.get("rows_per_partition")));
}
private static void validateCacheConfig(Map<String, String> cacheConfig) throws ConfigurationException
{
for (Map.Entry<String, String> entry : cacheConfig.entrySet())
{
String value = entry.getValue().toUpperCase();
if (entry.getKey().equals("keys"))
{
if (!(value.equals("ALL") || value.equals("NONE")))
{
throw new ConfigurationException("'keys' can only have values 'ALL' or 'NONE', but was '" + value + "'");
}
}
else if (entry.getKey().equals("rows_per_partition"))
{
if (!(value.equals("ALL") || value.equals("NONE") || StringUtils.isNumeric(value)))
{
throw new ConfigurationException("'rows_per_partition' can only have values 'ALL', 'NONE' or be numeric, but was '" + value + "'.");
}
}
else
throw new ConfigurationException("Only supported CachingOptions parameters are 'keys' and 'rows_per_partition', but was '" + entry.getKey() + "'");
}
}
@Override
public String toString()
{
return String.format("{\"keys\":\"%s\", \"rows_per_partition\":\"%s\"}", keyCache.toString(), rowCache.toString());
}
private static CachingOptions fromLegacyOption(String cache)
{
if (cache.equals("ALL"))
return ALL;
if (cache.equals("KEYS_ONLY"))
return KEYS_ONLY;
if (cache.equals("ROWS_ONLY"))
return ROWS_ONLY;
return NONE;
}
@Override
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CachingOptions o2 = (CachingOptions) o;
if (!keyCache.equals(o2.keyCache)) return false;
if (!rowCache.equals(o2.rowCache)) return false;
return true;
}
@Override
public int hashCode()
{
int result = keyCache.hashCode();
result = 31 * result + rowCache.hashCode();
return result;
}
// FIXME: move to ThriftConversion
public static CachingOptions fromThrift(String caching, String cellsPerRow) throws ConfigurationException
{
RowCache rc = new RowCache(RowCache.Type.NONE);
KeyCache kc = new KeyCache(KeyCache.Type.ALL);
// if we get a caching string from thrift it is legacy, "ALL", "KEYS_ONLY" etc, fromString handles those
if (caching != null)
{
CachingOptions givenOptions = CachingOptions.fromString(caching);
rc = givenOptions.rowCache;
kc = givenOptions.keyCache;
}
// if we get cells_per_row from thrift, it is either "ALL" or "<number of cells to cache>".
if (cellsPerRow != null && rc.isEnabled())
rc = RowCache.fromString(cellsPerRow);
return new CachingOptions(kc, rc);
}
// FIXME: move to ThriftConversion
public String toThriftCaching()
{
if (rowCache.isEnabled() && keyCache.isEnabled())
return "ALL";
if (rowCache.isEnabled())
return "ROWS_ONLY";
if (keyCache.isEnabled())
return "KEYS_ONLY";
return "NONE";
}
// FIXME: move to ThriftConversion
public String toThriftCellsPerRow()
{
if (rowCache.cacheFullPartitions())
return "ALL";
return String.valueOf(rowCache.rowsToCache);
}
public static class KeyCache
{
public final Type type;
public KeyCache(Type type)
{
this.type = type;
}
public enum Type
{
ALL, NONE
}
public static KeyCache fromString(String keyCache)
{
return new KeyCache(Type.valueOf(keyCache.toUpperCase()));
}
public boolean isEnabled()
{
return type == Type.ALL;
}
@Override
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
KeyCache keyCache = (KeyCache) o;
if (type != keyCache.type) return false;
return true;
}
@Override
public int hashCode()
{
return type.hashCode();
}
@Override
public String toString()
{
return type.toString();
}
}
public static class RowCache
{
public final Type type;
public final int rowsToCache;
public RowCache(Type type)
{
this(type, (type == Type.ALL) ? Integer.MAX_VALUE : 0);
}
public RowCache(Type type, int rowsToCache)
{
this.type = type;
this.rowsToCache = rowsToCache;
}
public enum Type
{
ALL, NONE, HEAD
}
public static RowCache fromString(String rowCache)
{
if (rowCache == null || rowCache.equalsIgnoreCase("none"))
return new RowCache(Type.NONE, 0);
else if (rowCache.equalsIgnoreCase("all"))
return new RowCache(Type.ALL, Integer.MAX_VALUE);
return new RowCache(Type.HEAD, Integer.parseInt(rowCache));
}
public boolean isEnabled()
{
return (type == Type.ALL) || (type == Type.HEAD);
}
public boolean cacheFullPartitions()
{
return type == Type.ALL;
}
@Override
public String toString()
{
if (type == Type.ALL) return "ALL";
if (type == Type.NONE) return "NONE";
return String.valueOf(rowsToCache);
}
@Override
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RowCache rowCache = (RowCache) o;
if (rowsToCache != rowCache.rowsToCache) return false;
if (type != rowCache.type) return false;
return true;
}
@Override
public int hashCode()
{
int result = type.hashCode();
result = 31 * result + rowsToCache;
return result;
}
}
}