blob: d9bfc148038285dbbc4b4dea938508786281ca08 [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.impala.catalog;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
/**
* Thread safe cache for storing CatalogObjects. Enforces that updates to existing
* entries only get applied if the new/updated object has a larger catalog version.
* add() and remove() functions also update the entries of the global instance of
* CatalogObjectVersionSet which keeps track of the catalog objects versions.
*/
public class CatalogObjectCache<T extends CatalogObject> implements Iterable<T> {
private final boolean caseInsensitiveKeys_;
/**
* Creates a new instance of the CatalogObjectCache that compares keys as
* insensitive.
*/
public CatalogObjectCache() {
this(true);
}
/**
* Creates a new instance of the CatalogObjectCache that compares keys as case
* insensitive/sensitive based on whether 'caseInsensitiveKeys' is true/false.
*/
public CatalogObjectCache(boolean caseInsensitiveKeys) {
caseInsensitiveKeys_ = caseInsensitiveKeys;
}
// Map of lower-case object name to CatalogObject. New entries are added
// by calling add(). Updates of the cache must be synchronized because adding
// new entries may require two cache accesses that must be performed atomically.
// TODO: For simplicity, consider using a (non-concurrent) HashMap and marking
// all methods as synchronized.
private final Map<String, T> metadataCache_ = new ConcurrentHashMap<String, T>();
/**
* Adds a new catalogObject to the cache. If a catalogObject with the same name already
* exists in the cache, the new item will only be added if it has a larger catalog
* version.
* Synchronized because add() may require two cache accesses that must be performed
* atomically.
* Returns true if this item was added or false if the existing value was preserved.
*/
public synchronized boolean add(T catalogObject) {
Preconditions.checkNotNull(catalogObject);
String key = catalogObject.getName();
if (caseInsensitiveKeys_) key = key.toLowerCase();
T existingItem = metadataCache_.putIfAbsent(key, catalogObject);
if (existingItem == null) {
CatalogObjectVersionSet.INSTANCE.addVersion(
catalogObject.getCatalogVersion());
return true;
}
if (existingItem.getCatalogVersion() < catalogObject.getCatalogVersion()) {
// When existingItem != null it indicates there was already an existing entry
// associated with the key. Add the updated object iff it has a catalog
// version greater than the existing entry.
metadataCache_.put(key, catalogObject);
CatalogObjectVersionSet.INSTANCE.updateVersions(
existingItem.getCatalogVersion(), catalogObject.getCatalogVersion());
return true;
}
return false;
}
/**
* Removes an item from the metadata cache and returns the removed item, or null
* if no item was removed.
*/
public synchronized T remove(String name) {
if (caseInsensitiveKeys_) name = name.toLowerCase();
T removedObject = metadataCache_.remove(name);
if (removedObject != null) {
CatalogObjectVersionSet.INSTANCE.removeVersion(
removedObject.getCatalogVersion());
}
return removedObject;
}
/**
* Clears all items in the cache.
*/
public synchronized void clear() {
metadataCache_.clear();
}
/**
* Returns the set of all known object names. The returned set is backed by
* the cache, so updates to the cache will be visible in the returned set
* and vice-versa. However, updates to the cache should not be done via the
* returned set, use add()/remove() instead.
*/
public Set<String> keySet() {
return metadataCache_.keySet();
}
/**
* Returns all the known object values.
*/
public List<T> getValues() {
return Lists.newArrayList(metadataCache_.values());
}
/**
* Returns true if the metadataCache_ contains a key with the given name.
*/
public boolean contains(String name) {
if (caseInsensitiveKeys_) name = name.toLowerCase();
return metadataCache_.containsKey(name);
}
/**
* Returns the catalog object corresponding to the supplied name if it exists in the
* cache, or null if there is no entry in metadataCache_ associated with this
* key.
*/
public T get(String name) {
if (caseInsensitiveKeys_) name = name.toLowerCase();
return metadataCache_.get(name);
}
/**
* Returns an iterator for the values in the cache. There are no guarantees
* about the order in which elements are returned. All items at the time of
* iterator creation will be visible and new items may or may not be visible.
* Thread safe (will never throw a ConcurrentModificationException).
*/
@Override
public Iterator<T> iterator() {
return metadataCache_.values().iterator();
}
}