This is an automated email from the ASF dual-hosted git repository.
tkalkirill pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 2526e8dab7 IGNITE-23283 Throw CompactedException for single get
metastorage methods (#4524)
2526e8dab7 is described below
commit 2526e8dab7aedd3a78d8f45e51ea0ba6d94a5cc8
Author: Kirill Tkalenko <[email protected]>
AuthorDate: Wed Oct 9 11:14:20 2024 +0300
IGNITE-23283 Throw CompactedException for single get metastorage methods
(#4524)
---
.../internal/metastorage/MetaStorageManager.java | 114 +++++++++++++++++-
.../metastorage/exceptions/CompactedException.java | 20 +++-
.../internal/metastorage/impl/EntryImpl.java | 10 ++
.../metastorage/impl/MetaStorageManagerImpl.java | 30 +----
.../metastorage/server/KeyValueStorage.java | 51 +++++++-
.../metastorage/server/KeyValueStorageUtils.java | 38 +++++-
.../server/persistence/RocksDbKeyValueStorage.java | 77 +++++++-----
.../AbstractCompactionKeyValueStorageTest.java | 130 ++++++++++++++++++++-
.../server/KeyValueStorageUtilsTest.java | 50 ++++++--
.../server/SimpleInMemoryKeyValueStorage.java | 49 +++++---
10 files changed, 474 insertions(+), 95 deletions(-)
diff --git
a/modules/metastorage-api/src/main/java/org/apache/ignite/internal/metastorage/MetaStorageManager.java
b/modules/metastorage-api/src/main/java/org/apache/ignite/internal/metastorage/MetaStorageManager.java
index 053bbeb61d..c83eeadc8d 100644
---
a/modules/metastorage-api/src/main/java/org/apache/ignite/internal/metastorage/MetaStorageManager.java
+++
b/modules/metastorage-api/src/main/java/org/apache/ignite/internal/metastorage/MetaStorageManager.java
@@ -55,12 +55,72 @@ public interface MetaStorageManager extends IgniteComponent
{
long appliedRevision();
/**
- * Retrieves an entry for the given key.
+ * Returns a future of getting the latest version of an entry by key from
the metastore leader.
+ *
+ * <p>Never completes with a {@link CompactedException}.</p>
+ *
+ * <p>Future may complete with {@link NodeStoppingException} if the node
is in the process of stopping.</p>
+ *
+ * @param key The key.
*/
CompletableFuture<Entry> get(ByteArray key);
/**
- * Retrieves an entry for the given key and the revision upper bound.
+ * Returns a future of getting an entry for the given key and the revision
upper bound from the metastore leader.
+ *
+ * <p>Future may complete with exceptions:</p>
+ * <ul>
+ * <li>{@link NodeStoppingException} - if the node is in the process
of stopping.</li>
+ * <li>{@link CompactedException} - If the requested entry was not
found and the {@code revUpperBound} is less than or equal to the
+ * last compacted one.</li>
+ * </ul>
+ *
+ * <p>Let's consider examples of the work of the method and compaction of
the metastore. Let's assume that we have keys with revisions
+ * "foo" [1, 2] and "bar" [1, 2 (tombstone)], and the key "some" has never
been in the metastore.</p>
+ * <ul>
+ * <li>Compaction revision is {@code 1}.
+ * <ul>
+ * <li>get("foo", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("foo", 2) - will return a single value with revision
2.</li>
+ * <li>get("foo", 3) - will return a single value with revision
2.</li>
+ * <li>get("bar", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("bar", 2) - will return a single value with revision
2.</li>
+ * <li>get("bar", 3) - will return a single value with revision
2.</li>
+ * <li>get("some", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("some", 2) - will return an empty value.</li>
+ * <li>get("some", 3) - will return an empty value.</li>
+ * </ul>
+ * </li>
+ * <li>Compaction revision is {@code 2}.
+ * <ul>
+ * <li>get("foo", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("foo", 2) - will return a single value with revision
2.</li>
+ * <li>get("foo", 3) - will return a single value with revision
2.</li>
+ * <li>get("bar", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("bar", 2) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("bar", 3) - will return a single value with revision
2.</li>
+ * <li>get("some", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("some", 2) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("some", 3) - will return an empty value.</li>
+ * </ul>
+ * </li>
+ * <li>Compaction revision is {@code 3}.
+ * <ul>
+ * <li>get("foo", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("foo", 2) - will return a single value with revision
2.</li>
+ * <li>get("foo", 3) - will return a single value with revision
2.</li>
+ * <li>get("bar", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("bar", 2) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("bar", 3) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("some", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("some", 2) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("some", 3) - a {@link CompactedException} will be
thrown.</li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
+ * @param key The key.
+ * @param revUpperBound The upper bound of revision.
*/
CompletableFuture<Entry> get(ByteArray key, long revUpperBound);
@@ -82,15 +142,61 @@ public interface MetaStorageManager extends
IgniteComponent {
List<Entry> getLocally(byte[] key, long revLowerBound, long revUpperBound);
/**
- * Returns an entry by the given key and bounded by the given revision.
The entry is obtained
- * from the local storage.
+ * Returns an entry for the given key and the revision upper bound locally.
*
* <p>This method doesn't wait for the storage's revision to become
greater or equal to the revUpperBound parameter, so it is
* up to user to wait for the appropriate time to call this method.
*
+ * <p>Let's consider examples of the work of the method and compaction of
the metastore. Let's assume that we have keys with revisions
+ * "foo" [1, 2] and "bar" [1, 2 (tombstone)], and the key "some" has never
been in the metastore.</p>
+ * <ul>
+ * <li>Compaction revision is {@code 1}.
+ * <ul>
+ * <li>get("foo", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("foo", 2) - will return a single value with revision
2.</li>
+ * <li>get("foo", 3) - will return a single value with revision
2.</li>
+ * <li>get("bar", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("bar", 2) - will return a single value with revision
2.</li>
+ * <li>get("bar", 3) - will return a single value with revision
2.</li>
+ * <li>get("some", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("some", 2) - will return an empty value.</li>
+ * <li>get("some", 3) - will return an empty value.</li>
+ * </ul>
+ * </li>
+ * <li>Compaction revision is {@code 2}.
+ * <ul>
+ * <li>get("foo", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("foo", 2) - will return a single value with revision
2.</li>
+ * <li>get("foo", 3) - will return a single value with revision
2.</li>
+ * <li>get("bar", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("bar", 2) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("bar", 3) - will return a single value with revision
2.</li>
+ * <li>get("some", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("some", 2) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("some", 3) - will return an empty value.</li>
+ * </ul>
+ * </li>
+ * <li>Compaction revision is {@code 3}.
+ * <ul>
+ * <li>get("foo", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("foo", 2) - will return a single value with revision
2.</li>
+ * <li>get("foo", 3) - will return a single value with revision
2.</li>
+ * <li>get("bar", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("bar", 2) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("bar", 3) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("some", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("some", 2) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("some", 3) - a {@link CompactedException} will be
thrown.</li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
* @param key The key.
* @param revUpperBound The upper bound of revision.
* @return Value corresponding to the given key.
+ * @throws IgniteInternalException with cause {@link
NodeStoppingException} if the node is in the process of stopping.
+ * @throws CompactedException If the requested entry was not found and the
{@code revUpperBound} is less than or equal to the last
+ * compacted one.
*/
Entry getLocally(ByteArray key, long revUpperBound);
diff --git
a/modules/metastorage-api/src/main/java/org/apache/ignite/internal/metastorage/exceptions/CompactedException.java
b/modules/metastorage-api/src/main/java/org/apache/ignite/internal/metastorage/exceptions/CompactedException.java
index 3f11f48f65..7c1cbaaba1 100644
---
a/modules/metastorage-api/src/main/java/org/apache/ignite/internal/metastorage/exceptions/CompactedException.java
+++
b/modules/metastorage-api/src/main/java/org/apache/ignite/internal/metastorage/exceptions/CompactedException.java
@@ -36,10 +36,17 @@ public class CompactedException extends
MetaStorageException {
/**
* Constructs an exception with a given message.
*
- * @param revision Requested revision.
+ * @param requestedRevision Requested revision.
+ * @param latestCompactedRevision Latest compacted revision.
*/
- public CompactedException(long revision) {
- super(COMPACTED_ERR, "Requested revision has already been compacted: "
+ revision);
+ public CompactedException(long requestedRevision, long
latestCompactedRevision) {
+ super(
+ COMPACTED_ERR,
+ String.format(
+ "Requested revision has already been compacted:
[requested=%s, lastCompacted=%s]",
+ requestedRevision, latestCompactedRevision
+ )
+ );
}
/**
@@ -69,4 +76,11 @@ public class CompactedException extends MetaStorageException
{
public CompactedException(Throwable cause) {
super(COMPACTED_ERR, cause);
}
+
+ /** Throws {@link CompactedException} if the requested revision is less
than or equal to the last compacted one. */
+ public static void throwIfRequestedRevisionLessThanOrEqualToCompacted(long
requestedRevision, long compactedRevision) {
+ if (requestedRevision <= compactedRevision) {
+ throw new CompactedException(requestedRevision, compactedRevision);
+ }
+ }
}
diff --git
a/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/impl/EntryImpl.java
b/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/impl/EntryImpl.java
index 8646bdbc60..015217e628 100644
---
a/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/impl/EntryImpl.java
+++
b/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/impl/EntryImpl.java
@@ -23,6 +23,7 @@ import java.util.Arrays;
import java.util.Objects;
import org.apache.ignite.internal.hlc.HybridTimestamp;
import org.apache.ignite.internal.metastorage.Entry;
+import org.apache.ignite.internal.metastorage.server.Value;
import org.jetbrains.annotations.Nullable;
/** Implementation of the {@link Entry}. */
@@ -134,4 +135,13 @@ public final class EntryImpl implements Entry {
+ ", timestamp=" + timestamp
+ '}';
}
+
+ /** Converts to {@link EntryImpl}. */
+ public static Entry toEntry(byte[] key, long revision, Value value) {
+ if (value.tombstone()) {
+ return tombstone(key, revision, value.operationTimestamp());
+ }
+
+ return new EntryImpl(key, value.bytes(), revision,
value.operationTimestamp());
+ }
}
diff --git
a/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/impl/MetaStorageManagerImpl.java
b/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/impl/MetaStorageManagerImpl.java
index b07ce360c7..560f5ece69 100644
---
a/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/impl/MetaStorageManagerImpl.java
+++
b/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/impl/MetaStorageManagerImpl.java
@@ -686,28 +686,12 @@ public class MetaStorageManagerImpl implements
MetaStorageManager, MetastorageGr
@Override
public CompletableFuture<Entry> get(ByteArray key) {
- if (!busyLock.enterBusy()) {
- return failedFuture(new NodeStoppingException());
- }
-
- try {
- return metaStorageSvcFut.thenCompose(svc -> svc.get(key));
- } finally {
- busyLock.leaveBusy();
- }
+ return inBusyLockAsync(busyLock, () ->
metaStorageSvcFut.thenCompose(svc -> svc.get(key)));
}
@Override
public CompletableFuture<Entry> get(ByteArray key, long revUpperBound) {
- if (!busyLock.enterBusy()) {
- return failedFuture(new NodeStoppingException());
- }
-
- try {
- return metaStorageSvcFut.thenCompose(svc -> svc.get(key,
revUpperBound));
- } finally {
- busyLock.leaveBusy();
- }
+ return inBusyLockAsync(busyLock, () ->
metaStorageSvcFut.thenCompose(svc -> svc.get(key, revUpperBound)));
}
@Override
@@ -725,15 +709,7 @@ public class MetaStorageManagerImpl implements
MetaStorageManager, MetastorageGr
@Override
public Entry getLocally(ByteArray key, long revUpperBound) {
- if (!busyLock.enterBusy()) {
- throw new IgniteException(new NodeStoppingException());
- }
-
- try {
- return storage.get(key.bytes(), revUpperBound);
- } finally {
- busyLock.leaveBusy();
- }
+ return inBusyLock(busyLock, () -> storage.get(key.bytes(),
revUpperBound));
}
@Override
diff --git
a/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/server/KeyValueStorage.java
b/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/server/KeyValueStorage.java
index 37481cd00c..ad40c44db3 100644
---
a/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/server/KeyValueStorage.java
+++
b/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/server/KeyValueStorage.java
@@ -56,7 +56,9 @@ public interface KeyValueStorage extends ManuallyCloseable {
long revision();
/**
- * Returns an entry by the given key.
+ * Returns the latest version of an entry by key.
+ *
+ * <p>Never throws {@link CompactedException}.</p>
*
* @param key The key.
* @return Value corresponding to the given key.
@@ -66,9 +68,54 @@ public interface KeyValueStorage extends ManuallyCloseable {
/**
* Returns an entry by the given key and bounded by the given revision.
*
+ * <p>Let's consider examples of the work of the method and compaction of
the metastore. Let's assume that we have keys with revisions
+ * "foo" [1, 2] and "bar" [1, 2 (tombstone)], and the key "some" has never
been in the metastore.</p>
+ * <ul>
+ * <li>Compaction revision is {@code 1}.
+ * <ul>
+ * <li>get("foo", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("foo", 2) - will return a single value with revision
2.</li>
+ * <li>get("foo", 3) - will return a single value with revision
2.</li>
+ * <li>get("bar", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("bar", 2) - will return a single value with revision
2.</li>
+ * <li>get("bar", 3) - will return a single value with revision
2.</li>
+ * <li>get("some", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("some", 2) - will return an empty value.</li>
+ * <li>get("some", 3) - will return an empty value.</li>
+ * </ul>
+ * </li>
+ * <li>Compaction revision is {@code 2}.
+ * <ul>
+ * <li>get("foo", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("foo", 2) - will return a single value with revision
2.</li>
+ * <li>get("foo", 3) - will return a single value with revision
2.</li>
+ * <li>get("bar", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("bar", 2) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("bar", 3) - will return a single value with revision
2.</li>
+ * <li>get("some", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("some", 2) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("some", 3) - will return an empty value.</li>
+ * </ul>
+ * </li>
+ * <li>Compaction revision is {@code 3}.
+ * <ul>
+ * <li>get("foo", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("foo", 2) - will return a single value with revision
2.</li>
+ * <li>get("foo", 3) - will return a single value with revision
2.</li>
+ * <li>get("bar", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("bar", 2) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("bar", 3) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("some", 1) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("some", 2) - a {@link CompactedException} will be
thrown.</li>
+ * <li>get("some", 3) - a {@link CompactedException} will be
thrown.</li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
* @param key The key.
* @param revUpperBound The upper bound of revision.
- * @return Value corresponding to the given key.
+ * @throws CompactedException If the requested entry was not found and the
{@code revUpperBound} is less than or equal to the last
+ * {@link #setCompactionRevision compacted} one.
*/
Entry get(byte[] key, long revUpperBound);
diff --git
a/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/server/KeyValueStorageUtils.java
b/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/server/KeyValueStorageUtils.java
index e5b97cbb4d..5657008a34 100644
---
a/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/server/KeyValueStorageUtils.java
+++
b/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/server/KeyValueStorageUtils.java
@@ -24,11 +24,11 @@ import java.util.function.LongPredicate;
/** Helper class with useful methods and constants for {@link KeyValueStorage}
implementations. */
public class KeyValueStorageUtils {
- /** Special value indicating that there are no key revisions that need to
be compacted. */
- public static final int NOTHING_TO_COMPACT_INDEX = -1;
+ /** Constant meaning something could not be found. */
+ public static final int NOT_FOUND = -1;
/**
- * Calculates the revision index in key revisions up to which compaction
is needed or {@link #NOTHING_TO_COMPACT_INDEX} if nothing
+ * Calculates the revision index in key revisions up to which compaction
is needed or {@link #NOT_FOUND} if nothing
* needs to be compacted.
*
* <p>If the returned index points to the last revision and if the last
revision is <b>not</b> a tombstone, then the returned index is
@@ -43,19 +43,47 @@ public class KeyValueStorageUtils {
if (i < 0) {
if (i == -1) {
- return NOTHING_TO_COMPACT_INDEX;
+ return NOT_FOUND;
}
i = -(i + 2);
}
if (i == keyRevisions.length - 1 &&
!isTombstone.test(keyRevisions[i])) {
- i = i == 0 ? NOTHING_TO_COMPACT_INDEX : i - 1;
+ i = i == 0 ? NOT_FOUND : i - 1;
}
return i;
}
+ /**
+ * Returns index of maximum revision which must be less or equal to {@code
upperBoundRevision}. If there is no such revision then
+ * {@link #NOT_FOUND} will be returned.
+ *
+ * @param keyRevisions Metastorage key revisions in ascending order.
+ * @param upperBoundRevision Revision upper bound.
+ */
+ public static int maxRevisionIndex(long[] keyRevisions, long
upperBoundRevision) {
+ int i = binarySearch(keyRevisions, upperBoundRevision);
+
+ if (i < 0) {
+ if (i == -1) {
+ return NOT_FOUND;
+ }
+
+ i = -(i + 2);
+ }
+
+ return i;
+ }
+
+ /** Returns {@link true} if the requested index is the last index of the
array. */
+ public static boolean isLastIndex(long[] arr, int index) {
+ assert index >= 0 && index < arr.length : "index=" + index + ",
arr.length=" + arr.length;
+
+ return arr.length - 1 == index;
+ }
+
/**
* Converts bytes to UTF-8 string.
*
diff --git
a/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/server/persistence/RocksDbKeyValueStorage.java
b/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/server/persistence/RocksDbKeyValueStorage.java
index 773168b72d..a0769a46e3 100644
---
a/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/server/persistence/RocksDbKeyValueStorage.java
+++
b/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/server/persistence/RocksDbKeyValueStorage.java
@@ -19,10 +19,11 @@ package
org.apache.ignite.internal.metastorage.server.persistence;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.ignite.internal.hlc.HybridTimestamp.hybridTimestamp;
-import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.NOTHING_TO_COMPACT_INDEX;
+import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.NOT_FOUND;
import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.assertCompactionRevisionLessThanCurrent;
import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.assertRequestedRevisionLessThanOrEqualToCurrent;
import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.indexToCompact;
+import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.isLastIndex;
import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.toUtf8String;
import static org.apache.ignite.internal.metastorage.server.Value.TOMBSTONE;
import static
org.apache.ignite.internal.metastorage.server.persistence.RocksStorageUtils.appendLong;
@@ -93,6 +94,7 @@ import
org.apache.ignite.internal.metastorage.impl.MetaStorageManagerImpl;
import org.apache.ignite.internal.metastorage.server.Condition;
import org.apache.ignite.internal.metastorage.server.If;
import org.apache.ignite.internal.metastorage.server.KeyValueStorage;
+import org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils;
import org.apache.ignite.internal.metastorage.server.OnRevisionAppliedCallback;
import org.apache.ignite.internal.metastorage.server.Statement;
import org.apache.ignite.internal.metastorage.server.Value;
@@ -957,7 +959,7 @@ public class RocksDbKeyValueStorage implements
KeyValueStorage {
@Override
public void compact(long revision) {
- assert revision >= 0;
+ assert revision >= 0 : revision;
try {
compactKeys(revision);
@@ -1018,7 +1020,7 @@ public class RocksDbKeyValueStorage implements
KeyValueStorage {
try {
int indexToCompact = indexToCompact(revs, compactionRevision,
revision -> isTombstoneForCompaction(key, revision));
- if (NOTHING_TO_COMPACT_INDEX == indexToCompact) {
+ if (NOT_FOUND == indexToCompact) {
return;
}
@@ -1065,35 +1067,27 @@ public class RocksDbKeyValueStorage implements
KeyValueStorage {
return res;
}
- /**
- * Gets the value by key and revision.
- *
- * @param key Target key.
- * @param revUpperBound Target upper bound of revision.
- * @return Value.
- */
private Entry doGet(byte[] key, long revUpperBound) {
- assert revUpperBound >= 0 : "Invalid arguments: [revUpperBound=" +
revUpperBound + ']';
+ assert revUpperBound >= 0 : revUpperBound;
- long[] revs;
- try {
- revs = getRevisions(key);
- } catch (RocksDBException e) {
- throw new MetaStorageException(OP_EXECUTION_ERR, e);
- }
+ long[] keyRevisions = getRevisionsForOperation(key);
+ int maxRevisionIndex =
KeyValueStorageUtils.maxRevisionIndex(keyRevisions, revUpperBound);
+
+ if (maxRevisionIndex == NOT_FOUND) {
+
CompactedException.throwIfRequestedRevisionLessThanOrEqualToCompacted(revUpperBound,
compactionRevision);
- if (revs.length == 0) {
return EntryImpl.empty(key);
}
- long lastRev = maxRevision(revs, revUpperBound);
+ long revision = keyRevisions[maxRevisionIndex];
- // lastRev can be -1 if maxRevision return -1.
- if (lastRev == -1) {
- return EntryImpl.empty(key);
+ Value value = getValueForOperation(key, revision);
+
+ if (revUpperBound <= compactionRevision && (!isLastIndex(keyRevisions,
maxRevisionIndex) || value.tombstone())) {
+ throw new CompactedException(revUpperBound, compactionRevision);
}
- return doGetValue(key, lastRev);
+ return EntryImpl.toEntry(key, revision, value);
}
/**
@@ -1142,10 +1136,9 @@ public class RocksDbKeyValueStorage implements
KeyValueStorage {
}
/**
- * Get a list of the revisions of the entry corresponding to the key.
+ * Returns array of revisions of the entry corresponding to the key.
*
* @param key Key.
- * @return Array of revisions.
* @throws RocksDBException If failed to perform {@link
RocksDB#get(ColumnFamilyHandle, byte[])}.
*/
private long[] getRevisions(byte[] key) throws RocksDBException {
@@ -1158,6 +1151,20 @@ public class RocksDbKeyValueStorage implements
KeyValueStorage {
return getAsLongs(revisions);
}
+ /**
+ * Returns array of revisions of the entry corresponding to the key.
+ *
+ * @param key Key.
+ * @throws MetaStorageException If there was an error while getting the
revisions for the key.
+ */
+ private long[] getRevisionsForOperation(byte[] key) {
+ try {
+ return getRevisions(key);
+ } catch (RocksDBException e) {
+ throw new MetaStorageException(OP_EXECUTION_ERR, "Failed to get
revisions for the key: " + toUtf8String(key), e);
+ }
+ }
+
/**
* Returns maximum revision which must be less or equal to {@code
upperBoundRev}. If there is no such revision then {@code -1} will be
* returned.
@@ -1563,7 +1570,7 @@ public class RocksDbKeyValueStorage implements
KeyValueStorage {
@Override
public void saveCompactionRevision(long revision) {
- assert revision >= 0;
+ assert revision >= 0 : revision;
rwLock.writeLock().lock();
@@ -1582,7 +1589,7 @@ public class RocksDbKeyValueStorage implements
KeyValueStorage {
@Override
public void setCompactionRevision(long revision) {
- assert revision >= 0;
+ assert revision >= 0 : revision;
rwLock.writeLock().lock();
@@ -1696,4 +1703,20 @@ public class RocksDbKeyValueStorage implements
KeyValueStorage {
);
}
}
+
+ private Value getValueForOperation(byte[] key, long revision) {
+ try {
+ byte[] valueBytes = data.get(keyToRocksKey(revision, key));
+
+ assert valueBytes != null && valueBytes.length != 0 : "key=" +
toUtf8String(key) + ", revision=" + revision;
+
+ return bytesToValue(valueBytes);
+ } catch (RocksDBException e) {
+ throw new MetaStorageException(
+ OP_EXECUTION_ERR,
+ String.format("Failed to get value: [key=%s,
revision=%s]", toUtf8String(key), revision),
+ e
+ );
+ }
+ }
}
diff --git
a/modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/AbstractCompactionKeyValueStorageTest.java
b/modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/AbstractCompactionKeyValueStorageTest.java
index 097f534eb9..6bee5c9472 100644
---
a/modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/AbstractCompactionKeyValueStorageTest.java
+++
b/modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/AbstractCompactionKeyValueStorageTest.java
@@ -22,6 +22,7 @@ import static
org.apache.ignite.internal.metastorage.dsl.Operations.noop;
import static org.apache.ignite.internal.metastorage.dsl.Operations.ops;
import static org.apache.ignite.internal.metastorage.dsl.Operations.put;
import static org.apache.ignite.internal.metastorage.dsl.Operations.remove;
+import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.toUtf8String;
import static
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
@@ -58,6 +59,8 @@ public abstract class AbstractCompactionKeyValueStorageTest
extends AbstractKeyV
private static final byte[] SOME_VALUE = fromString("someValue");
+ private static final byte[] NOT_EXISTS_KEY = fromString("notExistsKey");
+
@WorkDirectory
Path workDir;
@@ -68,14 +71,19 @@ public abstract class AbstractCompactionKeyValueStorageTest
extends AbstractKeyV
void setUp() {
super.setUp();
+ // Revision = 1.
storage.putAll(List.of(FOO_KEY, BAR_KEY), List.of(SOME_VALUE,
SOME_VALUE), clock.now());
+ // Revision = 2.
storage.put(BAR_KEY, SOME_VALUE, clock.now());
+ // Revision = 3.
storage.put(FOO_KEY, SOME_VALUE, clock.now());
+ // Revision = 4.
storage.put(SOME_KEY, SOME_VALUE, clock.now());
var fooKey = new ByteArray(FOO_KEY);
var barKey = new ByteArray(BAR_KEY);
+ // Revision = 5.
var iif = new If(
new AndCondition(new ExistenceCondition(Type.EXISTS, FOO_KEY),
new ExistenceCondition(Type.EXISTS, BAR_KEY)),
new Statement(ops(put(fooKey, SOME_VALUE),
remove(barKey)).yield()),
@@ -84,14 +92,17 @@ public abstract class AbstractCompactionKeyValueStorageTest
extends AbstractKeyV
storage.invoke(iif, clock.now(), new
CommandIdGenerator(UUID::randomUUID).newId());
+ // Revision = 6.
storage.remove(SOME_KEY, clock.now());
+ // Revision = 7.
// Special revision update to prevent tests from failing.
storage.put(fromString("fake"), SOME_VALUE, clock.now());
+ assertEquals(7, storage.revision());
assertEquals(List.of(1, 3, 5), collectRevisions(FOO_KEY));
- assertEquals(List.of(1, 2, 5), collectRevisions(BAR_KEY));
- assertEquals(List.of(4, 6), collectRevisions(SOME_KEY));
+ assertEquals(List.of(1, 2, 5/* Tombstone */),
collectRevisions(BAR_KEY));
+ assertEquals(List.of(4, 6/* Tombstone */), collectRevisions(SOME_KEY));
}
@Test
@@ -326,6 +337,98 @@ public abstract class
AbstractCompactionKeyValueStorageTest extends AbstractKeyV
assertThrows(CompactedException.class, () ->
storage.revisionByTimestamp(timestamp3.subtractPhysicalTime(1)));
}
+ @Test
+ void testGetSingleEntryLatestAndCompaction() {
+ storage.setCompactionRevision(6);
+
+ assertDoesNotThrow(() -> storage.get(FOO_KEY));
+ assertDoesNotThrow(() -> storage.get(BAR_KEY));
+ assertDoesNotThrow(() -> storage.get(NOT_EXISTS_KEY));
+ }
+
+ @Test
+ void testGetSingleEntryAndCompactionForFooKey() {
+ // FOO_KEY has revisions: [1, 3, 5].
+ storage.setCompactionRevision(1);
+ assertThrowsCompactedExceptionForGetSingleValue(FOO_KEY, 1);
+ assertDoesNotThrowCompactedExceptionForGetSingleValue(FOO_KEY, 2);
+
+ storage.setCompactionRevision(2);
+ assertThrowsCompactedExceptionForGetSingleValue(FOO_KEY, 2);
+ assertDoesNotThrowCompactedExceptionForGetSingleValue(FOO_KEY, 3);
+
+ storage.setCompactionRevision(3);
+ assertThrowsCompactedExceptionForGetSingleValue(FOO_KEY, 3);
+ assertDoesNotThrowCompactedExceptionForGetSingleValue(FOO_KEY, 4);
+
+ storage.setCompactionRevision(4);
+ assertThrowsCompactedExceptionForGetSingleValue(FOO_KEY, 4);
+ assertDoesNotThrowCompactedExceptionForGetSingleValue(FOO_KEY, 5);
+
+ storage.setCompactionRevision(5);
+ assertThrowsCompactedExceptionForGetSingleValue(FOO_KEY, 4);
+ assertDoesNotThrowCompactedExceptionForGetSingleValue(FOO_KEY, 5);
+
+ storage.setCompactionRevision(6);
+ assertThrowsCompactedExceptionForGetSingleValue(FOO_KEY, 4);
+ assertDoesNotThrowCompactedExceptionForGetSingleValue(FOO_KEY, 5);
+ }
+
+ @Test
+ void testGetSingleEntryAndCompactionForBarKey() {
+ // BAR_KEY has revisions: [1, 2, 5 (tombstone)].
+ storage.setCompactionRevision(1);
+ assertThrowsCompactedExceptionForGetSingleValue(BAR_KEY, 1);
+ assertDoesNotThrowCompactedExceptionForGetSingleValue(BAR_KEY, 2);
+
+ storage.setCompactionRevision(2);
+ assertThrowsCompactedExceptionForGetSingleValue(BAR_KEY, 2);
+ assertDoesNotThrowCompactedExceptionForGetSingleValue(BAR_KEY, 3);
+
+ storage.setCompactionRevision(3);
+ assertThrowsCompactedExceptionForGetSingleValue(BAR_KEY, 3);
+ assertDoesNotThrowCompactedExceptionForGetSingleValue(BAR_KEY, 4);
+
+ storage.setCompactionRevision(4);
+ assertThrowsCompactedExceptionForGetSingleValue(BAR_KEY, 4);
+ assertDoesNotThrowCompactedExceptionForGetSingleValue(BAR_KEY, 5);
+
+ storage.setCompactionRevision(5);
+ assertThrowsCompactedExceptionForGetSingleValue(BAR_KEY, 5);
+ assertDoesNotThrowCompactedExceptionForGetSingleValue(BAR_KEY, 6);
+
+ storage.setCompactionRevision(6);
+ assertThrowsCompactedExceptionForGetSingleValue(BAR_KEY, 6);
+ assertDoesNotThrowCompactedExceptionForGetSingleValue(BAR_KEY, 7);
+ }
+
+ @Test
+ void testGetSingleEntryAndCompactionForNotExistsKey() {
+ storage.setCompactionRevision(1);
+ assertThrowsCompactedExceptionForGetSingleValue(NOT_EXISTS_KEY, 1);
+ assertDoesNotThrowCompactedExceptionForGetSingleValue(NOT_EXISTS_KEY,
2);
+
+ storage.setCompactionRevision(2);
+ assertThrowsCompactedExceptionForGetSingleValue(NOT_EXISTS_KEY, 2);
+ assertDoesNotThrowCompactedExceptionForGetSingleValue(NOT_EXISTS_KEY,
3);
+
+ storage.setCompactionRevision(3);
+ assertThrowsCompactedExceptionForGetSingleValue(NOT_EXISTS_KEY, 3);
+ assertDoesNotThrowCompactedExceptionForGetSingleValue(NOT_EXISTS_KEY,
4);
+
+ storage.setCompactionRevision(4);
+ assertThrowsCompactedExceptionForGetSingleValue(NOT_EXISTS_KEY, 4);
+ assertDoesNotThrowCompactedExceptionForGetSingleValue(NOT_EXISTS_KEY,
5);
+
+ storage.setCompactionRevision(5);
+ assertThrowsCompactedExceptionForGetSingleValue(NOT_EXISTS_KEY, 5);
+ assertDoesNotThrowCompactedExceptionForGetSingleValue(NOT_EXISTS_KEY,
6);
+
+ storage.setCompactionRevision(6);
+ assertThrowsCompactedExceptionForGetSingleValue(NOT_EXISTS_KEY, 6);
+ assertDoesNotThrowCompactedExceptionForGetSingleValue(NOT_EXISTS_KEY,
7);
+ }
+
private List<Integer> collectRevisions(byte[] key) {
var revisions = new ArrayList<Integer>();
@@ -343,4 +446,27 @@ public abstract class
AbstractCompactionKeyValueStorageTest extends AbstractKeyV
private static byte[] fromString(String s) {
return s.getBytes(UTF_8);
}
+
+ private void assertThrowsCompactedExceptionForGetSingleValue(byte[] key,
long endRevisionInclusive) {
+ for (long i = 0; i <= endRevisionInclusive; i++) {
+ long revisionUpperBound = i;
+
+ assertThrows(
+ CompactedException.class,
+ () -> storage.get(key, revisionUpperBound),
+ () -> String.format("key=%s, revision=%s",
toUtf8String(key), revisionUpperBound)
+ );
+ }
+ }
+
+ private void assertDoesNotThrowCompactedExceptionForGetSingleValue(byte[]
key, long startRevisionInclusive) {
+ for (long i = startRevisionInclusive; i <= storage.revision(); i++) {
+ long revisionUpperBound = i;
+
+ assertDoesNotThrow(
+ () -> storage.get(key, revisionUpperBound),
+ () -> String.format("key=%s, revision=%s",
toUtf8String(key), revisionUpperBound)
+ );
+ }
+ }
}
diff --git
a/modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/KeyValueStorageUtilsTest.java
b/modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/KeyValueStorageUtilsTest.java
index 241aeb9fbd..4211790552 100644
---
a/modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/KeyValueStorageUtilsTest.java
+++
b/modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/KeyValueStorageUtilsTest.java
@@ -18,11 +18,15 @@
package org.apache.ignite.internal.metastorage.server;
import static java.nio.charset.StandardCharsets.UTF_8;
-import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.NOTHING_TO_COMPACT_INDEX;
+import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.NOT_FOUND;
import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.indexToCompact;
+import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.isLastIndex;
+import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.maxRevisionIndex;
import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.toUtf8String;
import static org.apache.ignite.internal.util.ArrayUtils.LONG_EMPTY_ARRAY;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
@@ -30,30 +34,30 @@ import org.junit.jupiter.api.Test;
public class KeyValueStorageUtilsTest {
@Test
void testIndexToCompactNoRevisions() {
- assertEquals(NOTHING_TO_COMPACT_INDEX,
indexToCompact(LONG_EMPTY_ARRAY, 0, revision -> false));
- assertEquals(NOTHING_TO_COMPACT_INDEX,
indexToCompact(LONG_EMPTY_ARRAY, 0, revision -> true));
+ assertEquals(NOT_FOUND, indexToCompact(LONG_EMPTY_ARRAY, 0, revision
-> false));
+ assertEquals(NOT_FOUND, indexToCompact(LONG_EMPTY_ARRAY, 0, revision
-> true));
}
@Test
void testIndexToCompactSingleRevision() {
long[] keyRevisions = {2};
- assertEquals(NOTHING_TO_COMPACT_INDEX, indexToCompact(keyRevisions, 1,
revision -> false));
- assertEquals(NOTHING_TO_COMPACT_INDEX, indexToCompact(keyRevisions, 1,
revision -> true));
+ assertEquals(NOT_FOUND, indexToCompact(keyRevisions, 1, revision ->
false));
+ assertEquals(NOT_FOUND, indexToCompact(keyRevisions, 1, revision ->
true));
assertEquals(0, indexToCompact(keyRevisions, 2, revision -> true));
- assertEquals(NOTHING_TO_COMPACT_INDEX, indexToCompact(keyRevisions, 2,
revision -> false));
+ assertEquals(NOT_FOUND, indexToCompact(keyRevisions, 2, revision ->
false));
assertEquals(0, indexToCompact(keyRevisions, 3, revision -> true));
- assertEquals(NOTHING_TO_COMPACT_INDEX, indexToCompact(keyRevisions, 3,
revision -> false));
+ assertEquals(NOT_FOUND, indexToCompact(keyRevisions, 3, revision ->
false));
}
@Test
void testIndexToCompactMultipleRevisions() {
long[] keyRevisions = {2, 4, 5};
- assertEquals(NOTHING_TO_COMPACT_INDEX, indexToCompact(keyRevisions, 1,
revision -> true));
- assertEquals(NOTHING_TO_COMPACT_INDEX, indexToCompact(keyRevisions, 1,
revision -> false));
+ assertEquals(NOT_FOUND, indexToCompact(keyRevisions, 1, revision ->
true));
+ assertEquals(NOT_FOUND, indexToCompact(keyRevisions, 1, revision ->
false));
assertEquals(0, indexToCompact(keyRevisions, 2, revision -> true));
assertEquals(0, indexToCompact(keyRevisions, 2, revision -> false));
@@ -75,4 +79,32 @@ public class KeyValueStorageUtilsTest {
void testToUtf8String() {
assertEquals("foo", toUtf8String("foo".getBytes(UTF_8)));
}
+
+ @Test
+ void testMaxRevisionIndex() {
+ long[] keyRevisions = {3, 5, 7};
+
+ assertEquals(NOT_FOUND, maxRevisionIndex(keyRevisions, 1));
+ assertEquals(NOT_FOUND, maxRevisionIndex(keyRevisions, 2));
+
+ assertEquals(0, maxRevisionIndex(keyRevisions, 3));
+ assertEquals(0, maxRevisionIndex(keyRevisions, 4));
+
+ assertEquals(1, maxRevisionIndex(keyRevisions, 5));
+ assertEquals(1, maxRevisionIndex(keyRevisions, 6));
+
+ assertEquals(2, maxRevisionIndex(keyRevisions, 7));
+ assertEquals(2, maxRevisionIndex(keyRevisions, 8));
+ assertEquals(2, maxRevisionIndex(keyRevisions, 9));
+ }
+
+ @Test
+ void testIsLastIndex() {
+ long[] array = {3, 5, 7};
+
+ assertFalse(isLastIndex(array, 0));
+ assertFalse(isLastIndex(array, 1));
+
+ assertTrue(isLastIndex(array, 2));
+ }
}
diff --git
a/modules/metastorage/src/testFixtures/java/org/apache/ignite/internal/metastorage/server/SimpleInMemoryKeyValueStorage.java
b/modules/metastorage/src/testFixtures/java/org/apache/ignite/internal/metastorage/server/SimpleInMemoryKeyValueStorage.java
index ec35d7b12c..b92d7a611a 100644
---
a/modules/metastorage/src/testFixtures/java/org/apache/ignite/internal/metastorage/server/SimpleInMemoryKeyValueStorage.java
+++
b/modules/metastorage/src/testFixtures/java/org/apache/ignite/internal/metastorage/server/SimpleInMemoryKeyValueStorage.java
@@ -22,10 +22,11 @@ import static
java.util.concurrent.CompletableFuture.failedFuture;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
-import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.NOTHING_TO_COMPACT_INDEX;
+import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.NOT_FOUND;
import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.assertCompactionRevisionLessThanCurrent;
import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.assertRequestedRevisionLessThanOrEqualToCurrent;
import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.indexToCompact;
+import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.isLastIndex;
import static
org.apache.ignite.internal.metastorage.server.KeyValueStorageUtils.toUtf8String;
import static org.apache.ignite.internal.metastorage.server.Value.TOMBSTONE;
import static
org.apache.ignite.internal.metastorage.server.raft.MetaStorageWriteHandler.IDEMPOTENT_COMMAND_PREFIX;
@@ -417,7 +418,7 @@ public class SimpleInMemoryKeyValueStorage implements
KeyValueStorage {
@Override
public HybridTimestamp timestampByRevision(long revision) {
- assert revision >= 0;
+ assert revision >= 0 : revision;
synchronized (mux) {
assertRequestedRevisionLessThanOrEqualToCurrent(revision, rev);
@@ -542,7 +543,7 @@ public class SimpleInMemoryKeyValueStorage implements
KeyValueStorage {
@Override
public void compact(long revision) {
- assert revision >= 0;
+ assert revision >= 0 : revision;
for (Map.Entry<byte[], List<Long>> entry : keysIdx.entrySet()) {
synchronized (mux) {
@@ -681,7 +682,7 @@ public class SimpleInMemoryKeyValueStorage implements
KeyValueStorage {
private void compactForKey(byte[] key, long[] revs, long
compactionRevision) {
int indexToCompact = indexToCompact(revs, compactionRevision, revision
-> isTombstoneForCompaction(key, revision));
- if (indexToCompact == NOTHING_TO_COMPACT_INDEX) {
+ if (indexToCompact == NOT_FOUND) {
return;
}
@@ -725,22 +726,26 @@ public class SimpleInMemoryKeyValueStorage implements
KeyValueStorage {
}
private Entry doGet(byte[] key, long revUpperBound) {
- assert revUpperBound >= 0 : "Invalid arguments: [revUpperBound=" +
revUpperBound + ']';
+ assert revUpperBound >= 0 : revUpperBound;
- List<Long> revs = keysIdx.get(key);
+ long[] keyRevisions = toLongArray(keysIdx.get(key));
+ int maxRevisionIndex =
KeyValueStorageUtils.maxRevisionIndex(keyRevisions, revUpperBound);
+
+ if (maxRevisionIndex == NOT_FOUND) {
+
CompactedException.throwIfRequestedRevisionLessThanOrEqualToCompacted(revUpperBound,
compactionRevision);
- if (revs == null || revs.isEmpty()) {
return EntryImpl.empty(key);
}
- long lastRev = maxRevision(revs, revUpperBound);
+ long revision = keyRevisions[maxRevisionIndex];
- // lastRev can be -1 if maxRevision return -1.
- if (lastRev == -1) {
- return EntryImpl.empty(key);
+ Value value = getValue(key, revision);
+
+ if (revUpperBound <= compactionRevision && (!isLastIndex(keyRevisions,
maxRevisionIndex) || value.tombstone())) {
+ throw new CompactedException(revUpperBound, compactionRevision);
}
- return doGetValue(key, lastRev);
+ return EntryImpl.toEntry(key, revision, value);
}
private List<Entry> doGet(byte[] key, long revLowerBound, long
revUpperBound) {
@@ -943,7 +948,7 @@ public class SimpleInMemoryKeyValueStorage implements
KeyValueStorage {
@Override
public void saveCompactionRevision(long revision) {
- assert revision >= 0;
+ assert revision >= 0 : revision;
synchronized (mux) {
assertCompactionRevisionLessThanCurrent(revision, rev);
@@ -954,7 +959,7 @@ public class SimpleInMemoryKeyValueStorage implements
KeyValueStorage {
@Override
public void setCompactionRevision(long revision) {
- assert revision >= 0;
+ assert revision >= 0 : revision;
synchronized (mux) {
assertCompactionRevisionLessThanCurrent(revision, rev);
@@ -970,8 +975,8 @@ public class SimpleInMemoryKeyValueStorage implements
KeyValueStorage {
}
}
- private static long[] toLongArray(List<Long> list) {
- if (list.isEmpty()) {
+ private static long[] toLongArray(@Nullable List<Long> list) {
+ if (list == null) {
return LONG_EMPTY_ARRAY;
}
@@ -993,4 +998,16 @@ public class SimpleInMemoryKeyValueStorage implements
KeyValueStorage {
return value.tombstone();
}
+
+ private Value getValue(byte[] key, long revision) {
+ NavigableMap<byte[], Value> valueByKey = revsIdx.get(revision);
+
+ assert valueByKey != null : "key=" + toUtf8String(key) + ", revision="
+ revision;
+
+ Value value = valueByKey.get(key);
+
+ assert value != null : "key=" + toUtf8String(key) + ", revision=" +
revision;
+
+ return value;
+ }
}