Interface KeyValueTable
-
- All Superinterfaces:
java.lang.AutoCloseable
@Beta public interface KeyValueTable extends java.lang.AutoCloseable
Defines all operations that are supported on a Key-Value Table.A Key-Value Table is a distributed Key-Value Store that indexes Entries by Keys. It uses Table Segments (non-distributed Key-Value Store backed by a single Pravega Segment) as the fundamental storage primitive and provides a unified view of all Table Segments involved. Each
TableKey
is hashed to a Table Partition which may be represented by one or more Table Segments (depending on the Key-Value Table configuration chosen when it was created). Such partitioning enables the Key-Value Table to be distributed across the Pravega cluster but also introduces some constraints for certain operations (such as multi-key/entry atomic updates). See below for details.TableKey
s are made up of two components: Primary Key and Secondary Key.- The Primary Key is mandatory and is used to group all
TableKey
s with the same Primary Key together in the same Table Partition; this allows multiple keys/entries sharing the same Primary Key to be updated/removed atomically. The Primary Keys must all have the same length throughout the Key-Value Table. This needs to be configured when the Key-Value Table is created (SeeKeyValueTableConfiguration.getPrimaryKeyLength()
and must be greater than 0. - The Secondary Key is optional. The Secondary Keys must all have the same length throughout the Key-Value Table.
This needs to be configured when the Key-Value Table is created (See
KeyValueTableConfiguration.getSecondaryKeyLength()
and must be at least 0. For Key-Value Tables with no Secondary Keys, set this value to 0. It is important to note that if specifying a non-zero value, then allTableKey
s must have both the Primary Key and Secondary Key set; so we may not haveTableKey
s without Secondary Keys in this case. The Secondary Key is optional at the Table level, not at the Key level. - Two
TableKey
instances are considered equal if they have the same Primary Key and same Secondary Key. The same Secondary Key may be "associated" with different Primary Keys, in which case the resultingTableKey
s will be distinct. For example, if we consider Primary Keys PK1 != PK2 and Secondary Key SK1, then key PK1.SK1 is different from key PK2.SK1. - Multiple
TableKey
/TableEntry
instances with the same Primary Key can be updated or removed atomically (either all changes will be applied or none will). However, it is important to note that it is not possible to mix updates with removals in the same atomic operation. -
TableKey
s that do not share the same Primary Key may be hashed to different Key-Value Table Partitions and cannot be used for multi-key/entry atomic updates or removals. -
TableKey
s sharing the same Primary Key are grouped into the same Table Segment; as such, the choice of Table Key can have performance implications. As Primary Keys are hashed to Table Segments, a large number of Primary Keys (larger than the number of Table Segments) and a uniform distribution of operations across keys leads to an even distribution of load across Table Segments. Many realistic workloads are skewed, however, and can lead to an uneven load distribution across Table Segments. The use of Secondary Keys also has implications to the load balance. All keys containing a given Primary Key map to the same Table Segment independent of Secondary Key. In the presence of Secondary Keys, the total load for a given Primary Key depends on the load aggregate across all of its Secondary Keys. When the load distribution is highly skewed, one option to consider is eliminating Secondary Keys and only using Primary Keys. The application must be able to encode the key into a single one rather than split into two parts. To illustrate, if we have two Table Keys "A.B" and "A.C", "." representing the separation between Primary and Secondary Keys, then they map to the same Table Segment because they have the same Primary Key "A". If instead, we use "AB" and "AC" as keys, eliminating the Secondary Keys but retaining the necessary information in the Primary Key, then we should be able to map those keys to different Table Segments depending on the hashing.
Types of Updates:
- Unconditional Updates will update a value associated with a
TableKey
, regardless of whether thatTableKey
previously existed or not, or what what itsVersion
is. Note that Unconditional Updates will either update an existing value or insert a newTableEntry
into the table if the specifiedTableKey
does not already exist. Unconditional Updates can be performed usingupdate(io.pravega.client.tables.TableModification)
by passingPut
instances with a null (Put.getVersion()
. - Conditional Updates will only overwrite an existing value if the specified
Put.getVersion()
matches the one that is currently present on the server. Conditional updates can performed usingupdate(io.pravega.client.tables.TableModification)
by passingPut
instances with a non-nullPut.getVersion()
. Conditional inserts can be performed using the same methods by passing anInsert
instance and will only succeed if the associatedTableKey
does not already exist. - Unconditional Removals will remove a
TableKey
if it exists. Such removals can be performed usingupdate(io.pravega.client.tables.TableModification)
by passing aRemove
havingRemove.getVersion()
be null. The operation will also succeed (albeit with no effect) if theTableModification.getKey()
does not exist. - Conditional Removals will remove a
TableKey
only if the specifiedRemove.getVersion()
matches the one that is currently present on the server. Such removals can be performed usingupdate(io.pravega.client.tables.TableModification)
by passing aRemove
havingRemove.getVersion()
non-null. - Multi-key updates allow any number of conditions to be specified (including no conditions) in the same atomic
batch. All the conditions that are present in the update batch must be satisfied in order for the update batch to be
accepted - the condition checks and updates are performed atomically.
Insert
s may be mixed withPut
havingPut.getVersion()
either null or non-null.Remove
s may not be mixed with anything else (i.e.,Insert
orRemove
). Useupdate(Iterable)
for such an update.
Conditional Update Responses:
- Success: the update or removal has been atomically validated and performed; all updates or removals in the request have been accepted.
- Failure: the update or removal has been rejected due to version mismatch; no update or removal has been performed.
-
NoSuchKeyException
: the update or removal has been conditioned on a specific version (different fromVersion.NOT_EXISTS
orVersion.NO_VERSION
) but the Key does not exist in theKeyValueTable
. -
BadKeyVersionException
: the update or removal has been conditioned on a specific version (different fromVersion.NO_VERSION
but the Key exists in theKeyValueTable
with a different version.
-
-
Field Summary
Fields Modifier and Type Field Description static int
MAXIMUM_VALUE_LENGTH
The maximum length of a Table Segment Value.
-
Method Summary
All Methods Instance Methods Abstract Methods Modifier and Type Method Description void
close()
Closes theKeyValueTable
.java.util.concurrent.CompletableFuture<java.lang.Boolean>
exists(@NonNull TableKey key)
Determines if the givenTableKey
exists or not.java.util.concurrent.CompletableFuture<TableEntry>
get(@NonNull TableKey key)
Gets the latest value for aTableKey
.java.util.concurrent.CompletableFuture<java.util.List<TableEntry>>
getAll(@NonNull java.lang.Iterable<TableKey> keys)
Gets the latest values for a set ofTableKey
s.KeyValueTableIterator.Builder
iterator()
Creates a newKeyValueTableIterator.Builder
that can be used to construct and execute an Iterator over theTableKey
/TableEntry
instances in thisKeyValueTable
.java.util.concurrent.CompletableFuture<Version>
update(@NonNull TableModification update)
Performs a specificTableModification
to theKeyValueTable
, as described below: Ifupdate
is aInsert
, this will be interpreted as a Conditional Insert, so theTableEntryUpdate.getValue()
will only be inserted (and associated withTableModification.getKey()
if there doesn't already exist aTableKey
in theKeyValueTable
that matchesTableModification.getKey()
.java.util.concurrent.CompletableFuture<java.util.List<Version>>
update(@NonNull java.lang.Iterable<TableModification> updates)
Performs a batch ofTableModification
s to theKeyValueTable
forTableKey
s that have the sameTableKey.getPrimaryKey()
.
-
-
-
Field Detail
-
MAXIMUM_VALUE_LENGTH
static final int MAXIMUM_VALUE_LENGTH
The maximum length of a Table Segment Value.- See Also:
- Constant Field Values
-
-
Method Detail
-
update
java.util.concurrent.CompletableFuture<Version> update(@NonNull @NonNull TableModification update)
Performs a specificTableModification
to theKeyValueTable
, as described below:- If
update
is aInsert
, this will be interpreted as a Conditional Insert, so theTableEntryUpdate.getValue()
will only be inserted (and associated withTableModification.getKey()
if there doesn't already exist aTableKey
in theKeyValueTable
that matchesTableModification.getKey()
. - If
update
is aPut
, this will be an update. IfPut.getVersion()
is null, this will be interpreted as an Unconditional Update, and theTableEntryUpdate.getValue()
will be associated withTableModification.getKey()
in theKeyValueTable
regardless of whether the Key existed before or not. IfPut.getVersion()
is non-null, this will be interpreted as a Conditional Update, and theTableEntryUpdate.getValue()
will only be associated withTableModification.getKey()
if there exists aTableKey
in theKeyValueTable
whoseVersion
matchesPut.getVersion()
. NOTE: ifPut.getVersion()
equalsVersion.NOT_EXISTS
, this is equivalent toInsert
(i.e., it will be a Conditional Insert). - If
update
is aRemove
, this will remove theTableModification.getKey()
from theKeyValueTable
. IfRemove.getVersion()
is null, this will be interpreted as an Unconditional Removal, so the Key will be removed regardless of itsVersion
(and the operation will also be successful if theTableModification.getKey()
does not exist). IfRemove.getVersion()
is non-null, this will be interpreted as a Conditional Removal, and the Key will only be removed if it exists and itsVersion
matchesRemove.getVersion()
.
- Parameters:
update
- TheTableModification
to apply to theKeyValueTable
.- Returns:
- A CompletableFuture that, when completed, will contain the
Version
associated with any newly updated or inserted entry (forInsert
orPut
) ornull
forRemove
.
- If
-
update
java.util.concurrent.CompletableFuture<java.util.List<Version>> update(@NonNull @NonNull java.lang.Iterable<TableModification> updates)
Performs a batch ofTableModification
s to theKeyValueTable
forTableKey
s that have the sameTableKey.getPrimaryKey()
. All changes are performed atomically (either all or none will be accepted).See
update(TableModification)
for details on Conditional/Unconditional Updates. Conditional and unconditional updates may be mixed together in this call. Each individualTableModification
will be individually assessed (from a conditional point of view) and will prevent the entire batch from committing if it fails to meet the condition.- Parameters:
updates
- AnIterable
ofTableModification
s to apply to theKeyValueTable
. Note thatRemove
instances may not be mixed together withInsert
orPut
instances, butInsert
s andPut
s may be mixed together.- Returns:
- A CompletableFuture that, when completed, will contain a List of
Version
instances which represent the versions for the updated or inserted keys. The size of this list will be the same as the number of items inupdates
and the versions will be in the same order as theupdates
. This list will be empty ifupdates
contains onlyRemove
instances. - Throws:
java.lang.IllegalArgumentException
- If there exist two or moreTableModification
s inupdates
that refer toTableKey
s with differentTableKey.getPrimaryKey()
.java.lang.IllegalArgumentException
- Ifupdates
contains at least oneRemove
instance and at least oneTableModification
that is notRemove
.
-
exists
java.util.concurrent.CompletableFuture<java.lang.Boolean> exists(@NonNull @NonNull TableKey key)
Determines if the givenTableKey
exists or not.- Parameters:
key
- TheTableKey
to check.- Returns:
- A CompletableFuture that, when completed, will contain a
Boolean
representing the result.
-
get
java.util.concurrent.CompletableFuture<TableEntry> get(@NonNull @NonNull TableKey key)
Gets the latest value for aTableKey
.
-
getAll
java.util.concurrent.CompletableFuture<java.util.List<TableEntry>> getAll(@NonNull @NonNull java.lang.Iterable<TableKey> keys)
Gets the latest values for a set ofTableKey
s.- Parameters:
keys
- AnIterable
of @link TableKey} to get values for.- Returns:
- A CompletableFuture that, when completed, will contain a List of
TableEntry
instances for the requested keys. The size of the list will be the same askeys.size()
and the results will be in the same order as the requested keys. Any keys which do not have a value will have a null entry at their index.
-
iterator
KeyValueTableIterator.Builder iterator()
Creates a newKeyValueTableIterator.Builder
that can be used to construct and execute an Iterator over theTableKey
/TableEntry
instances in thisKeyValueTable
.- Returns:
- A new instance of a
KeyValueTableIterator.Builder
.
-
close
void close()
Closes theKeyValueTable
. No more updates, removals, retrievals or iterators may be performed using it.- Specified by:
close
in interfacejava.lang.AutoCloseable
- See Also:
AutoCloseable.close()
-
-