@ThreadSafe public class MutableView extends AbstractKVStore implements ReadTracking, Cloneable
KVStore
.
Instances intercept all operations to the underlying KVStore
, recording mutations in a Writes
instance
instead of applying them to the KVStore
. Instances then provide a view of the mutated KVStore
based those
mutations. Mutations that overwrite previous mutations are consolidated.
Unlike writes, reads are passed through to the underlying KVStore
, except where they intersect a previous write.
In all cases, the underlying KVStore
is never modified.
Instances ensure that counter adjustment mutations never overlap put or remove mutations.
Read Tracking
During construction, instances may be configured to record all keys read into a Reads
object (this is typically
used for MVCC conflict detection). When reads are being tracked, tracking may temporarily be paused and resumed via
getReadTrackingControl()
. Read tracking may be permanently disabled (and any recorded reads discarded) via
disableReadTracking()
.
Instances are thread safe; however, directly accessing the associated Reads
or Writes
is not thread safe
without first locking the containing instance.
Constructor and Description |
---|
MutableView(KVStore kv)
Constructor.
|
MutableView(KVStore kv,
Reads reads,
Writes writes)
|
Modifier and Type | Method and Description |
---|---|
void |
adjustCounter(byte[] key,
long amount)
Adjust the counter at the given key by the given amount.
|
void |
apply(Mutations mutations)
Apply all the given
Mutations to this instance. |
MutableView |
clone()
Clone this instance.
|
long |
decodeCounter(byte[] bytes)
Decode a counter value previously encoded by
encodeCounter() . |
void |
disableReadTracking()
Permanently disable read tracking and discard the
Reads associated with this instance. |
byte[] |
encodeCounter(long value)
Encode a counter value into a
byte[] value suitable for use with decodeCounter()
and/or adjustCounter() . |
byte[] |
get(byte[] key)
Get the value associated with the given key, if any.
|
KVStore |
getKVStore()
Get the underlying
KVStore associated with this instance. |
CloseableIterator<KVPair> |
getRange(byte[] minKey,
byte[] maxKey,
boolean reverse)
Iterate the key/value pairs in the specified range.
|
Reads |
getReads()
Get the
Reads associated with this instance. |
AtomicBoolean |
getReadTrackingControl()
Get an
AtomicBoolean that can be used to temporarily pause/un-pause read tracking. |
Writes |
getWrites()
Get the
Writes associated with this instance. |
void |
put(byte[] key,
byte[] value)
Set the value associated with the given key.
|
void |
remove(byte[] key)
Remove the key/value pair with the given key, if it exists.
|
void |
removeRange(byte[] minKey,
byte[] maxKey)
Remove all key/value pairs whose keys are in a given range.
|
void |
setKVStore(KVStore kv)
Swap out the underlying
KVStore associated with this instance. |
void |
setReadOnly()
Configure this instance as read-only.
|
String |
toString() |
getAtLeast, getAtMost
equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
getAtLeast, getAtMost, getRange, getRange, removeRange
public MutableView(KVStore kv)
The instance will use a new, empty Reads
instance for read tracking.
kv
- underlying KVStore
IllegalArgumentException
- if kv
is nullpublic MutableView(KVStore kv, Reads reads, Writes writes)
kv
- underlying KVStore
reads
- recorded reads, or null for nonewrites
- recorded writesIllegalArgumentException
- if kv
is nullIllegalArgumentException
- if writes
is nullpublic KVStore getKVStore()
KVStore
associated with this instance.KVStore
public void setKVStore(KVStore kv)
KVStore
associated with this instance.
Note: the new KVStore
should have a consistent encoding of counter values as the previous KVStore
,
otherwise a concurrent thread may read previously written counter values back incorrectly.
kv
- new underlying KVStore
IllegalArgumentException
- if kv
is nullpublic Reads getReads()
Reads
associated with this instance.
This includes all keys explicitly or implicitly read by calls to
get()
, getAtLeast()
, getAtMost()
, and getRange()
.
The returned object is "live" and should only be accessed while synchronized on this instance.
disableReadTracking()
public Writes getWrites()
Writes
associated with this instance.
The returned object should only be accessed while synchronized on this instance.
public void disableReadTracking()
Reads
associated with this instance.
Can be used to save some memory when read tracking information is no longer needed.
public AtomicBoolean getReadTrackingControl()
ReadTracking
AtomicBoolean
that can be used to temporarily pause/un-pause read tracking.
By default the returned control is true. While set to false, read tracking is disabled; setting back to true re-enables read tracking.
For re-entrance safety, this should be done as follows:
final boolean previous = kv.getReadTrackingControl().getAndSet(false);
try {
// do something without tracking reads...
} finally {
kv.getReadTrackingControl().set(previous);
}
getReadTrackingControl
in interface ReadTracking
public void setReadOnly()
Any subsequent invocations of put()
, remove()
, removeRange()
,
or adjustCounter()
will result in an IllegalStateException
.
public byte[] get(byte[] key)
KVStore
Modifications to the returned byte[]
array do not affect this instance.
get
in interface KVStore
get
in class AbstractKVStore
key
- keypublic CloseableIterator<KVPair> getRange(byte[] minKey, byte[] maxKey, boolean reverse)
KVStore
Iterator
's
remove()
method must be supported and should have the same effect as
invoking remove()
on the corresponding key.
If keys starting with 0xff
are not supported by this instance, and minKey
starts with 0xff
,
then this method returns an empty iteration.
If keys starting with 0xff
are not supported by this instance, and maxKey
starts with 0xff
,
then this method behaves as if maxKey
were null.
The returned Iterator
must not throw ConcurrentModificationException
;
however, whether or not a "live" Iterator
reflects any modifications made after its creation is
implementation dependent. Implementations that do make post-creation updates visible in the Iterator
,
even if the update occurs after some delay, must preserve the order in which the modifications actually occurred.
The returned Iterator
itself is not guaranteed to be thread safe.
Invokers of this method are encouraged to close()
the returned iterators,
though this is not required for correct behavior.
Modifications to the returned KVPair
key and value byte[]
arrays do not affect this instance.
getRange
in interface KVStore
minKey
- minimum key (inclusive), or null for no minimum (start at the smallest key)maxKey
- maximum key (exclusive), or null for no maximum (end at the largest key)reverse
- true to return key/value pairs in reverse order (i.e., keys descending)minKey
(inclusive) to maxKey
(exclusive)public void put(byte[] key, byte[] value)
KVStore
put
in interface KVStore
put
in class AbstractKVStore
key
- keyvalue
- valuepublic void remove(byte[] key)
KVStore
remove
in interface KVStore
remove
in class AbstractKVStore
key
- keypublic void removeRange(byte[] minKey, byte[] maxKey)
KVStore
The minKey
must be less than or equal to maxKey
; if they equal (and not null)
then nothing happens; if they are both null then all entries are deleted.
If keys starting with 0xff
are not supported by this instance, then:
minKey
starts with 0xff
, then no change occursmaxKey
starts with 0xff
, then this method behaves as if maxKey
were nullremoveRange
in interface KVStore
removeRange
in class AbstractKVStore
minKey
- minimum key (inclusive), or null for no minimummaxKey
- maximum key (exclusive), or null for no maximumpublic byte[] encodeCounter(long value)
KVStore
byte[]
value suitable for use with decodeCounter()
and/or adjustCounter()
.encodeCounter
in interface KVStore
encodeCounter
in class AbstractKVStore
value
- desired counter valuepublic long decodeCounter(byte[] bytes)
KVStore
encodeCounter()
.decodeCounter
in interface KVStore
decodeCounter
in class AbstractKVStore
bytes
- encoded counter valuepublic void adjustCounter(byte[] key, long amount)
KVStore
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.
If there is no value associated with key
, or key
's value is not a valid counter encoding as
would be acceptable to decodeCounter()
, then how this operation affects key
's
value is undefined.
adjustCounter
in interface KVStore
adjustCounter
in class AbstractKVStore
key
- keyamount
- amount to adjust counter value bypublic void apply(Mutations mutations)
KVStore
Mutations
to this instance.
Mutations are always to be applied in this order: removes, puts, counter adjustments.
The implementation in KVStore
simply iterates over the individual changes and applies them
via remove()
(for removals of a single key), removeRange()
,
put()
, and/or adjustCounter()
. Implementations that can process
batch updates more efficiently are encouraged to override this method.
public MutableView clone()
Copyright © 2022. All rights reserved.