
com.tectonica.kvs.KeyValueStore Maven / Gradle / Ivy
Show all versions of tectonica-kvs Show documentation
/*
* Copyright (C) 2014 Zach Melamed
*
* Latest version available online at https://github.com/zach-m/tectonica-kvs
*
* Licensed 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 com.tectonica.kvs;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import com.tectonica.kvs.Index.IndexMapper;
import com.tectonica.kvs.KeyValueStore.KeyValue;
/**
* Simple, yet powerful, framework (and approach) for handling a key-value store. It allows for multiple concurrent readers but a single
* concurrent writer on each entry. It also provides a read-before-update mechanism which makes life simpler and keeps data consistent.
* Indexing is also supported as part of the framework. The interface is intuitive and straightforward. This class itself is abstract and
* subclasses are included for several backend persistence engines, including in-memory (which is great for development).
*
* The usage of this framework would probably yield the most benefit when used to prototype a data model, where changes are frequent and not
* backwards-compatible. However, in more than a few scenarios, this framework can be used in production. The heavy-lifting is done by the
* backend database and cache either way.
*
* @author Zach Melamed
*/
public interface KeyValueStore extends Iterable>
{
public static interface KeyValue
{
K getKey();
V getValue();
}
public static interface KeyMapper
{
public K getKeyOf(V value);
}
public static interface ValueGenerator
{
V generate(K key);
}
public static enum EventType
{
PreUpdate, PrePersistUpdate, PreAdd, PrePut;
}
public interface EventHandler
{
public void handle(EventType type, K key, V value);
}
// GETTERS
V get(K key);
V get(K key, boolean cacheResult);
/**
* Executes {@link #iteratorFor(Collection, boolean)} with {@code postponeCaching} set to false
*/
Iterator> iteratorFor(Collection keys);
/**
* Returns an iterator for a list of entries whose keys were passed to the method
*
* IMPORTANT: Unlike simple 'SELECT WHERE KEY IN (..)', the list returned here is guaranteed to be at the exact same order of the
* keys passed. Moreover, if a certain key is passed more than once, so will its corresponding entry. Clearly, if a key doesn't exist in
* the store, it will be skipped (i.e. the returning list will never contain nulls).
*
* @param keys
* ordered collection of keys to lookup (this is not necessarily a set, as keys can appear more than once in the collection)
* @param postponeCaching
* indicates whether cache entries individually as each is read from the backend storage, or cache them all in a single call
* at the end of the iteration (use only if you'll certainly iterate through the entire result)
*/
Iterator> iteratorFor(Collection keys, boolean postponeCaching);
Iterator keyIterator();
Iterator valueIterator();
Iterator valueIteratorFor(Collection keys);
Iterator valueIteratorFor(Collection keys, boolean postponeCaching);
Set keySet();
List values();
List valuesFor(Collection keys);
boolean containsKey(K key);
// SETTERS
/**
* inserts a new entry, whose key doesn't already exist in storage. it's a faster and more resource-efficient way to insert entries
* compared to {@link #put(Object, Object)} as it doesn't use any locking. do not use if you're not completely sure whether the key
* already exists. the behavior of the store in such case is undetermined and implementation-dependent.
*/
void add(K key, V value);
/**
* inserts or updates an entry. if you're sure that the entry is new (i.e. its key doesn't already exist), use the more efficient
* {@link #add(Object, Object)} instead
*/
void put(K key, V value);
/**
* insert and entry only if the key is not yet taken
*
* @return
* the newly added value, or null if nothing was inserted
*/
V putIfAbsent(K key, ValueGenerator generator);
V update(K key, Updater updater);
int update(Collection keys, Updater updater);
/**
* convenience method to update all entries
*/
int updateAll(Updater updater);
/**
* convenience method applicable when {@code keyMapper} is provided
*
* @see {@link #update(Object, Updater)}
*/
V updateValue(V value, Updater updater);
/**
* convenience method applicable when {@code keyMapper} is provided
*
* @see {@link #add(Object, Object)}
*/
void addValue(V value);
/**
* convenience method applicable when {@code keyMapper} is provided
*
* @see {@link #put(Object, Object)}
*/
void putValue(V value);
// DELETERS
boolean delete(K key);
int deleteAll();
// ETC
/**
* returns an object representing an {@link Index} on the stored entries. The index allows quick search of keys by a certain expression,
* calculated whenever an entry is stored / modified.
*
* NOTE: When the underlying storage engine is SQL-based, use {@link #createTypedIndex(String, Class, IndexMapper)} instead.
*/
Index createIndex(String indexName, IndexMapper mapFunc);
/**
* returns an object representing an {@link Index} on the stored entries. The index allows quick search of keys by a certain expression,
* calculated whenever an entry is stored / modified.
*
* The {@code indexClz} parameter can be null, when the underlying storage engine is not typed (true for most NoSQL engines)
*/
Index createTypedIndex(String indexName, Class indexClz, IndexMapper mapFunc);
void clearCache();
void addListener(EventType type, EventHandler handler);
}