
/*
 * Copyright (C) 2014 Archie L. Cobbs. All rights reserved.
 *
 * $Id$
 */

package org.jsimpledb.kv;

import java.util.Iterator;

/**
 * General API into a key/value store where the keys are sorted lexicographically as unsigned bytes.
 *
 * <p>
 * Implementations are not required to support accessing keys that start with {@code 0xff}.
 * </p>
 *
 * <p><b>Lock-free Counters</b></p>
 *
 * <p>
 * Implementations are encouraged to include support for encoding a 64 bit counter in a key/value pair such that the counter
 * can be efficiently {@linkplain #adjustCounter adjusted} by concurrent transactions without conflict.
 * In practice this means no locking is required to increment or decrement the counter by some amount, as long as
 * it's not necessary to actually directly read or write the counter value in the same transaction.
 * Whether counter adjustments are actually lock-free is implementation dependent, however, the counter methods
 * {@link #encodeCounter encodeCounter()}, {@link #decodeCounter decodeCounter()}, and {@link #adjustCounter adjustCounter()}
 * must function correctly as specified in all cases.
 * </p>
 *
 * <p>
 * How counters are encoded is specific to the implementation. Clients needing to read or write counter values directly
 * should use {@link #decodeCounter decodeCounter()} and {@link #encodeCounter encodeCounter()}, respectively.
 * Counters are removed using the normal methods (i.e., {@link #remove remove()} and {@link #removeRange removeRange()}).
 * </p>
 */
public interface KVStore {

    /**
     * Get the value associated with the given key, if any.
     *
     * @param key key
     * @return value associated with key, or null if not found
     * @throws IllegalArgumentException if {@code key} starts with {@code 0xff} and such keys are not supported
     * @throws StaleTransactionException if an underlying transaction is no longer usable
     * @throws RetryTransactionException if an underlying transaction must be retried and is no longer usable
     * @throws NullPointerException if {@code key} is null
     */
    byte[] get(byte[] key);

    /**
     * Get the key/value pair having the smallest key greater than or equal to the given minimum, if any.
     *
     * <p>
     * If keys starting with {@code 0xff} are not supported by this instance, and {@code minKey} starts with {@code 0xff},
     * then this method returns null.
     * </p>
     *
     * @param minKey minimum key (inclusive), or null for no minimum (get the smallest key)
     * @return smallest key/value pair with {@code key >= minKey}, or null if none exists
     * @throws StaleTransactionException if an underlying transaction is no longer usable
     * @throws RetryTransactionException if an underlying transaction must be retried and is no longer usable
     */
    KVPair getAtLeast(byte[] minKey);

    /**
     * Get the key/value pair having the largest key strictly less than the given maximum, if any.
     *
     * <p>
     * If keys starting with {@code 0xff} are not supported by this instance, and {@code maxKey} starts with {@code 0xff},
     * then this method behaves as if {@code maxKey} were null.
     * </p>
     *
     * @param maxKey maximum key (exclusive), or null for no maximum (get the largest key)
     * @return largest key/value pair with {@code key < maxKey}, or null if none exists
     * @throws StaleTransactionException if an underlying transaction is no longer usable
     * @throws RetryTransactionException if an underlying transaction must be retried and is no longer usable
     */
    KVPair getAtMost(byte[] maxKey);

    /**
     * Iterate the key/value pairs in the specified range. The returned {@link Iterator}'s {@link Iterator#remove remove()}
     * must be supported and should have the same effect as invoking {@link #remove remove()} on the corresponding key.
     *
     * <p>
     * If keys starting with {@code 0xff} are not supported by this instance, and {@code minKey} starts with {@code 0xff},
     * then this method returns an empty iteration.
     * </p>
     *
     * <p>
     * If keys starting with {@code 0xff} are not supported by this instance, and {@code maxKey} starts with {@code 0xff},
     * then this method behaves as if {@code maxKey} were null.
     * </p>
     *
     * <p>
     * The returned {@link Iterator} must not throw {@link java.util.ConcurrentModificationException};
     * however, whether or not a "live" {@link Iterator} reflects any modifications made after its creation is
     * implementation dependent. Implementations that do make post-creation updates visible in the {@link Iterator},
     * even if the update occurs after some delay, must preserve the order in which the modifications actually occurred.
     * </p>
     *
     * <p>
     * The returned {@link Iterator} is not guaranteed to be thread safe.
     * </p>
     *
     * @param minKey minimum key (inclusive), or null for no minimum (start at the smallest key)
     * @param maxKey maximum key (exclusive), or null for no maximum (end at the largest key)
     * @param reverse true to return key/value pairs in reverse order (i.e., keys descending)
     * @return iteration of key/value pairs in the range {@code minKey} (inclusive) to {@code maxKey} (exclusive)
     * @throws IllegalArgumentException if {@code minKey > maxKey}
     * @throws StaleTransactionException if an underlying transaction is no longer usable
     * @throws RetryTransactionException if an underlying transaction must be retried and is no longer usable
     */
    Iterator<KVPair> getRange(byte[] minKey, byte[] maxKey, boolean reverse);

    /**
     * Set the value associated with the given key.
     *
     * @param key key
     * @param value value
     * @throws IllegalArgumentException if {@code key} starts with {@code 0xff} and such keys are not supported
     * @throws StaleTransactionException if an underlying transaction is no longer usable
     * @throws RetryTransactionException if an underlying transaction must be retried and is no longer usable
     * @throws NullPointerException if {@code key} or {@code value} is null
     */
    void put(byte[] key, byte[] value);

    /**
     * Remove the key/value pair with the given key, if it exists.
     *
     * @param key key
     * @throws IllegalArgumentException if {@code key} starts with {@code 0xff} and such keys are not supported
     * @throws StaleTransactionException if an underlying transaction is no longer usable
     * @throws RetryTransactionException if an underlying transaction must be retried and is no longer usable
     * @throws NullPointerException if {@code key} is null
     */
    void remove(byte[] key);

    /**
     * Remove all key/value pairs whose keys are in a given range.
     *
     * <p>
     * The {@code minKey} must be less than or equal to {@code maxKey}; if they equal (and not null)
     * then nothing happens; if they are both null then all entries are deleted.
     * </p>
     *
     * <p>
     * If keys starting with {@code 0xff} are not supported by this instance, then:
     * <ul>
     *  <li>If {@code minKey} starts with {@code 0xff}, then no change occurs</li>
     *  <li>If {@code maxKey} starts with {@code 0xff}, then this method behaves as if {@code maxKey} were null</li>
     * </ul>
     *
     * @param minKey minimum key (inclusive), or null for no minimum
     * @param maxKey maximum key (exclusive), or null for no maximum
     * @throws IllegalArgumentException if {@code minKey > maxKey}
     * @throws StaleTransactionException if an underlying transaction is no longer usable
     * @throws RetryTransactionException if an underlying transaction must be retried and is no longer usable
     */
    void removeRange(byte[] minKey, byte[] maxKey);

    /**
     * Encode a counter value into a {@code byte[]} value suitable for use with {@link #decodeCounter decodeCounter()}
     * and/or {@link #adjustCounter adjustCounter()}.
     *
     * @param value desired counter value
     * @return encoded counter value
     * @throws StaleTransactionException if an underlying transaction is no longer usable
     * @throws RetryTransactionException if an underlying transaction must be retried and is no longer usable
     */
    byte[] encodeCounter(long value);

    /**
     * Decode a counter value previously encoded by {@link #encodeCounter encodeCounter()}.
     *
     * @param value encoded counter value
     * @return decoded counter value
     * @throws IllegalArgumentException if {@code value} is not a valid counter value
     * @throws StaleTransactionException if an underlying transaction is no longer usable
     * @throws RetryTransactionException if an underlying transaction must be retried and is no longer usable
     * @throws NullPointerException if {@code value} is null
     */
    long decodeCounter(byte[] value);

    /**
     * Adjust the counter at the given key by the given amount.
     *
     * <p>
     * Ideally this operation should behave in a lock-free manner, so that concurrent transactions can invoke it without
     * conflict. However, when lock-free behavior occurs (if at all) depends on the implementation.
     * </p>
     *
     * <p>
     * If there is no value associated with {@code key}, or {@code key}'s value is not a valid counter encoding as
     * would be acceptable to {@link #decodeCounter decodeCounter()}, then how this operation affects {@code key}'s
     * value is undefined.
     * </p>
     *
     * @param key key
     * @param amount amount to adjust counter value by
     * @throws StaleTransactionException if an underlying transaction is no longer usable
     * @throws RetryTransactionException if an underlying transaction must be retried and is no longer usable
     * @throws NullPointerException if {@code key} is null
     */
    void adjustCounter(byte[] key, long amount);
}

