package org.apache.blur.console.util;
/**
* 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.
*/
import org.apache.blur.thirdparty.thrift_0_9_0.TException;
import org.apache.blur.thrift.BlurClient;
import org.apache.blur.thrift.generated.Blur.Iface;
import org.apache.blur.thrift.generated.BlurException;
import org.apache.blur.thrift.generated.BlurQueryStatus;
import org.apache.blur.thrift.generated.ColumnDefinition;
import org.apache.blur.thrift.generated.Schema;
import org.apache.blur.thrift.generated.TableDescriptor;
import org.apache.blur.thrift.generated.TableStats;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.*;
public class CachingBlurClient {
private final int timeout;
private final Log log = LogFactory.getLog(CachingBlurClient.class);
private Map<String, Item> clusterListCache = new HashMap<String, Item>(1);
private Map<String, Item> tableListCache = new HashMap<String, Item>();
private Map<String, Item> queryListCache = new HashMap<String, Item>();
private Map<String, Item> queryStatusCache = new HashMap<String, Item>();
private Map<String, Item> tableDescriptionCache = new HashMap<String, Item>();
private Map<String, Item> tableStatsCache = new HashMap<String, Item>();
private Map<String, Item> schemaCache = new HashMap<String, Item>();
private Map<String, Item> controllerListCache = new HashMap<String, Item>();
private Map<String, Item> shardListCache = new HashMap<String, Item>();
private long cacheHits;
private long cacheMisses;
public CachingBlurClient(int timeout) {
this.timeout = timeout;
if(this.timeout > 0) {
startCleanup();
}
}
public List<String> shardClusterList() throws TException {
return getFromCache(null, clusterListCache, new Retriever<List<String>>() {
@Override
public List<String> retrieve() throws TException {
return getClient().shardClusterList();
}
});
}
private void startCleanup() {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
boolean run = true;
while (run) {
try {
cleanup(clusterListCache);
cleanup(tableListCache);
cleanup(queryListCache);
cleanup(queryStatusCache);
cleanup(tableDescriptionCache);
cleanup(tableStatsCache);
cleanup(schemaCache);
cleanup(controllerListCache);
cleanup(shardListCache);
log.info("Cache: " + cacheHits + " hits, " + cacheMisses + " misses");
} catch (Exception e) {
log.error("Error cleaning up all caches", e);
}
try {
Thread.sleep(timeout * 2);
} catch (InterruptedException e) {
run = false;
}
}
}
});
t.setDaemon(true);
t.start();
}
private void cleanup(Map<String, Item> cache) {
if (cache != null) {
try {
synchronized (cache) {
Iterator<Map.Entry<String, Item>> iterator = cache.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Item> entry = iterator.next();
if (entry.getValue().expired(timeout)) {
iterator.remove();
}
}
}
} catch (Exception e) {
log.error("Error cleaning up cache", e);
}
}
}
private Iface getClient() {
return BlurClient.getClient(Config.getBlurConfig());
}
@SuppressWarnings("unchecked")
private <T extends Object> T getFromCache(String key, Map<String, Item> cache, Retriever<T> retriever) throws TException {
synchronized (cache) {
if(this.timeout <= 0) {
return retriever.retrieve();
}
Item item = cache.get(key);
if (item == null || item.expired(timeout)) {
cacheMisses++;
item = new Item(retriever.retrieve());
cache.put(key, item);
} else {
cacheHits++;
}
return (T) item.getValue();
}
}
public void cancelQuery(String table, String uuid) throws TException {
getClient().cancelQuery(table, uuid);
invalidateQuery(table, uuid);
}
public void createTable(TableDescriptor td) throws TException {
getClient().createTable(td);
cleanup(tableListCache);
}
public void addColumnDefinition(String table, ColumnDefinition def) throws BlurException, TException {
getClient().addColumnDefinition(table, def);
}
private void invalidateQuery(String table, String uuid) {
synchronized (queryStatusCache) {
queryStatusCache.remove(queryKey(table,uuid));
}
}
private void invalidateTable(String table) {
synchronized (tableListCache) {
tableListCache.clear();
}
}
private String queryKey(String table, String uuid) {
return table + ":" + uuid;
}
public List<String> enabledTables() throws TException {
List<String> tables = tableList();
List<String> enabledTables = new ArrayList<String>(tables.size());
for (String table : tables) {
try {
TableDescriptor descriptor = describe(table);
if (descriptor != null && descriptor.isEnabled()) {
enabledTables.add(table);
}
} catch(TException e) {
log.warn("error getting descriptor for " + table, e);
}
}
return enabledTables;
}
public List<String> tableList() throws TException {
return getFromCache(null, tableListCache, new Retriever<List<String>>() {
@Override
public List<String> retrieve() throws TException {
return getClient().tableList();
}
});
}
public List<String> controllerList() throws TException {
return getFromCache(null, controllerListCache, new Retriever<List<String>>() {
@Override
public List<String> retrieve() throws TException {
return getClient().controllerServerList();
}
});
}
public List<String> shardList(final String cluster) throws TException {
return getFromCache(cluster, shardListCache, new Retriever<List<String>>() {
@Override
public List<String> retrieve() throws TException {
return getClient().shardServerList(cluster);
}
});
}
public List<String> queryStatusIdList(final String table) throws TException {
return getFromCache(table, queryListCache, new Retriever<List<String>>() {
@Override
public List<String> retrieve() throws TException {
return getClient().queryStatusIdList(table);
}
});
}
public BlurQueryStatus queryStatusById(final String table, final String id) throws TException {
return getFromCache(queryKey(table,id), queryStatusCache, new Retriever<BlurQueryStatus>() {
@Override
public BlurQueryStatus retrieve() throws TException {
return getClient().queryStatusById(table, id);
}
});
}
public List<String> tableListByCluster(final String cluster) throws TException {
return getFromCache(cluster, tableListCache, new Retriever<List<String>>() {
@Override
public List<String> retrieve() throws TException {
return getClient().tableListByCluster(cluster);
}
});
}
public TableDescriptor describe(final String table) throws TException {
return getFromCache(table, tableDescriptionCache, new Retriever<TableDescriptor>() {
@Override
public TableDescriptor retrieve() throws TException {
return getClient().describe(table);
}
});
}
public TableStats tableStats(final String table) throws TException {
return getFromCache(table, tableStatsCache, new Retriever<TableStats>() {
@Override
public TableStats retrieve() throws TException {
return getClient().tableStats(table);
}
});
}
public Schema schema(final String table) throws TException {
return getFromCache(table, schemaCache, new Retriever<Schema>() {
@Override
public Schema retrieve() throws TException {
return getClient().schema(table);
}
});
}
public List<String> terms(String table, String family, String column, String startWith, short i) throws TException {
TableDescriptor descriptor = describe(table);
if (descriptor != null && descriptor.isEnabled()) {
return getClient().terms(table, family, column, startWith, i);
}
return new ArrayList<String>(0);
}
public void disableTable(String table) throws TException {
getClient().disableTable(table);
invalidateTable(table);
}
public void enableTable(String table) throws TException {
getClient().enableTable(table);
invalidateTable(table);
}
public void removeTable(String table, boolean includeFiles) throws TException {
getClient().removeTable(table, includeFiles);
invalidateTable(table);
}
private static class Item {
private Object value;
private long insertionTime;
public Item(Object value) {
this.value = value;
insertionTime = System.currentTimeMillis();
}
public boolean expired(long timeout) {
return insertionTime < System.currentTimeMillis() - timeout;
}
public Object getValue() {
return value;
}
}
private abstract static class Retriever<T> {
public abstract T retrieve() throws TException;
}
}