This is an automated email from the ASF dual-hosted git repository.
sk0x50 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 bb268f4c0 IGNITE-16670 Added the ability to skip tombstones when
iterating through the meta storage. Fixes #918
bb268f4c0 is described below
commit bb268f4c00b240edfa637d8b0a6e397d17472fc6
Author: Denis Chudov <[email protected]>
AuthorDate: Tue Jul 5 01:18:08 2022 +0300
IGNITE-16670 Added the ability to skip tombstones when iterating through
the meta storage. Fixes #918
Signed-off-by: Slava Koptilin <[email protected]>
---
.../client/ItMetaStorageRaftGroupTest.java | 2 +-
.../client/ItMetaStorageServiceTest.java | 16 ++++----
.../metastorage/client/MetaStorageService.java | 33 +++++++++++++++
.../metastorage/client/MetaStorageServiceImpl.java | 27 +++++++++---
.../metastorage/common/command/RangeCommand.java | 32 +++++++++++++++
.../metastorage/server/KeyValueStorage.java | 10 +++--
.../server/persistence/RangeCursor.java | 21 ++++++----
.../server/persistence/RocksDbKeyValueStorage.java | 8 ++--
.../server/raft/MetaStorageListener.java | 4 +-
.../server/AbstractKeyValueStorageTest.java | 48 +++++++++++++++++++++-
.../server/SimpleInMemoryKeyValueStorage.java | 21 ++++++----
.../internal/metastorage/MetaStorageManager.java | 15 ++++++-
.../DistributedConfigurationStorageTest.java | 2 +-
13 files changed, 196 insertions(+), 43 deletions(-)
diff --git
a/modules/metastorage-client/src/integrationTest/java/org/apache/ignite/internal/metastorage/client/ItMetaStorageRaftGroupTest.java
b/modules/metastorage-client/src/integrationTest/java/org/apache/ignite/internal/metastorage/client/ItMetaStorageRaftGroupTest.java
index 74b94bc7c..a9e36c008 100644
---
a/modules/metastorage-client/src/integrationTest/java/org/apache/ignite/internal/metastorage/client/ItMetaStorageRaftGroupTest.java
+++
b/modules/metastorage-client/src/integrationTest/java/org/apache/ignite/internal/metastorage/client/ItMetaStorageRaftGroupTest.java
@@ -228,7 +228,7 @@ public class ItMetaStorageRaftGroupTest {
final AtomicInteger replicatorStoppedCounter = new AtomicInteger(0);
- when(mockStorage.range(EXPECTED_RESULT_ENTRY1.key().bytes(), new
byte[]{4})).thenAnswer(invocation -> {
+ when(mockStorage.range(EXPECTED_RESULT_ENTRY1.key().bytes(), new
byte[]{4}, false)).thenAnswer(invocation -> {
List<org.apache.ignite.internal.metastorage.server.Entry> entries
= new ArrayList<>(
List.of(EXPECTED_SRV_RESULT_ENTRY1,
EXPECTED_SRV_RESULT_ENTRY2));
diff --git
a/modules/metastorage-client/src/integrationTest/java/org/apache/ignite/internal/metastorage/client/ItMetaStorageServiceTest.java
b/modules/metastorage-client/src/integrationTest/java/org/apache/ignite/internal/metastorage/client/ItMetaStorageServiceTest.java
index 0139583d4..afe7e53b9 100644
---
a/modules/metastorage-client/src/integrationTest/java/org/apache/ignite/internal/metastorage/client/ItMetaStorageServiceTest.java
+++
b/modules/metastorage-client/src/integrationTest/java/org/apache/ignite/internal/metastorage/client/ItMetaStorageServiceTest.java
@@ -540,7 +540,7 @@ public class ItMetaStorageServiceTest {
long expRevUpperBound = 10;
- when(mockStorage.range(expKeyFrom.bytes(), expKeyTo.bytes(),
expRevUpperBound)).thenReturn(mock(Cursor.class));
+ when(mockStorage.range(expKeyFrom.bytes(), expKeyTo.bytes(),
expRevUpperBound, false)).thenReturn(mock(Cursor.class));
metaStorageSvc.range(expKeyFrom, expKeyTo, expRevUpperBound).close();
}
@@ -556,7 +556,7 @@ public class ItMetaStorageServiceTest {
ByteArray expKeyTo = new ByteArray(new byte[]{3});
- when(mockStorage.range(expKeyFrom.bytes(),
expKeyTo.bytes())).thenReturn(mock(Cursor.class));
+ when(mockStorage.range(expKeyFrom.bytes(), expKeyTo.bytes(),
false)).thenReturn(mock(Cursor.class));
metaStorageSvc.range(expKeyFrom, expKeyTo).close();
}
@@ -570,7 +570,7 @@ public class ItMetaStorageServiceTest {
public void testRangeWitNullAsKeyTo() throws Exception {
ByteArray expKeyFrom = new ByteArray(new byte[]{1});
- when(mockStorage.range(expKeyFrom.bytes(),
null)).thenReturn(mock(Cursor.class));
+ when(mockStorage.range(expKeyFrom.bytes(), null,
false)).thenReturn(mock(Cursor.class));
metaStorageSvc.range(expKeyFrom, null).close();
}
@@ -582,7 +582,7 @@ public class ItMetaStorageServiceTest {
public void testRangeHasNext() {
ByteArray expKeyFrom = new ByteArray(new byte[]{1});
- when(mockStorage.range(expKeyFrom.bytes(),
null)).thenAnswer(invocation -> {
+ when(mockStorage.range(expKeyFrom.bytes(), null,
false)).thenAnswer(invocation -> {
var cursor = mock(Cursor.class);
when(cursor.hasNext()).thenReturn(true);
@@ -600,7 +600,7 @@ public class ItMetaStorageServiceTest {
*/
@Test
public void testRangeNext() {
- when(mockStorage.range(EXPECTED_RESULT_ENTRY.key().bytes(),
null)).thenAnswer(invocation -> {
+ when(mockStorage.range(EXPECTED_RESULT_ENTRY.key().bytes(), null,
false)).thenAnswer(invocation -> {
var cursor = mock(Cursor.class);
when(cursor.hasNext()).thenReturn(true);
@@ -619,7 +619,7 @@ public class ItMetaStorageServiceTest {
*/
@Test
public void testRangeNextNoSuchElementException() {
- when(mockStorage.range(EXPECTED_RESULT_ENTRY.key().bytes(),
null)).thenAnswer(invocation -> {
+ when(mockStorage.range(EXPECTED_RESULT_ENTRY.key().bytes(), null,
false)).thenAnswer(invocation -> {
var cursor = mock(Cursor.class);
when(cursor.hasNext()).thenReturn(true);
@@ -644,7 +644,7 @@ public class ItMetaStorageServiceTest {
Cursor cursorMock = mock(Cursor.class);
- when(mockStorage.range(expKeyFrom.bytes(),
null)).thenReturn(cursorMock);
+ when(mockStorage.range(expKeyFrom.bytes(), null,
false)).thenReturn(cursorMock);
Cursor<Entry> cursor = metaStorageSvc.range(expKeyFrom, null);
@@ -899,7 +899,7 @@ public class ItMetaStorageServiceTest {
*/
@Test
public void testCursorsCleanup() throws Exception {
- when(mockStorage.range(EXPECTED_RESULT_ENTRY.key().bytes(),
null)).thenAnswer(invocation -> {
+ when(mockStorage.range(EXPECTED_RESULT_ENTRY.key().bytes(), null,
false)).thenAnswer(invocation -> {
var cursor = mock(Cursor.class);
when(cursor.hasNext()).thenReturn(true);
diff --git
a/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/MetaStorageService.java
b/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/MetaStorageService.java
index f211a7539..d597f78dc 100644
---
a/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/MetaStorageService.java
+++
b/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/MetaStorageService.java
@@ -252,6 +252,23 @@ public interface MetaStorageService {
@NotNull
Cursor<Entry> range(@NotNull ByteArray keyFrom, @Nullable ByteArray keyTo,
long revUpperBound);
+ /**
+ * Retrieves entries for the given key range in lexicographic order.
Entries will be filtered out by upper bound of given revision
+ * number.
+ *
+ * @param keyFrom Start key of range (inclusive). Couldn't be
{@code null}.
+ * @param keyTo End key of range (exclusive). Could be {@code
null}.
+ * @param revUpperBound The upper bound for entry revision. {@code -1}
means latest revision.
+ * @param includeTombstones Whether to include tombstone entries.
+ * @return Cursor built upon entries corresponding to the given range and
revision.
+ * @throws OperationTimeoutException If the operation is timed out.
+ * @throws CompactedException If the desired revisions are removed
from the storage due to a compaction.
+ * @see ByteArray
+ * @see Entry
+ */
+ @NotNull
+ Cursor<Entry> range(@NotNull ByteArray keyFrom, @Nullable ByteArray keyTo,
long revUpperBound, boolean includeTombstones);
+
/**
* Retrieves entries for the given key range in lexicographic order. Short
cut for {@link #range(ByteArray, ByteArray, long)} where
* {@code revUpperBound == -1}.
@@ -267,6 +284,22 @@ public interface MetaStorageService {
@NotNull
Cursor<Entry> range(@NotNull ByteArray keyFrom, @Nullable ByteArray keyTo);
+ /**
+ * Retrieves entries for the given key range in lexicographic order. Short
cut for
+ * {@link #range(ByteArray, ByteArray, long, boolean)} where {@code
revUpperBound == -1}.
+ *
+ * @param keyFrom Start key of range (inclusive). Couldn't be
{@code null}.
+ * @param keyTo End key of range (exclusive). Could be {@code
null}.
+ * @param includeTombstones Whether to include tombstone entries.
+ * @return Cursor built upon entries corresponding to the given range and
revision.
+ * @throws OperationTimeoutException If the operation is timed out.
+ * @throws CompactedException If the desired revisions are removed
from the storage due to a compaction.
+ * @see ByteArray
+ * @see Entry
+ */
+ @NotNull
+ Cursor<Entry> range(@NotNull ByteArray keyFrom, @Nullable ByteArray keyTo,
boolean includeTombstones);
+
/**
* Subscribes on meta storage updates matching the parameters.
*
diff --git
a/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/MetaStorageServiceImpl.java
b/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/MetaStorageServiceImpl.java
index aebd7d5af..d6c6fb472 100644
---
a/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/MetaStorageServiceImpl.java
+++
b/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/MetaStorageServiceImpl.java
@@ -209,10 +209,21 @@ public class MetaStorageServiceImpl implements
MetaStorageService {
/** {@inheritDoc} */
@Override
public @NotNull Cursor<Entry> range(@NotNull ByteArray keyFrom, @Nullable
ByteArray keyTo, long revUpperBound) {
+ return range(keyFrom, keyTo, revUpperBound, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public @NotNull Cursor<Entry> range(
+ @NotNull ByteArray keyFrom,
+ @Nullable ByteArray keyTo,
+ long revUpperBound,
+ boolean includeTombstones
+ ) {
return new CursorImpl<>(
metaStorageRaftGrpSvc,
metaStorageRaftGrpSvc.run(
- new RangeCommand(keyFrom, keyTo, revUpperBound,
localNodeId, uuidGenerator.randomUuid())),
+ new RangeCommand(keyFrom, keyTo, revUpperBound,
localNodeId, uuidGenerator.randomUuid(), includeTombstones)),
MetaStorageServiceImpl::singleEntryResult
);
}
@@ -220,11 +231,17 @@ public class MetaStorageServiceImpl implements
MetaStorageService {
/** {@inheritDoc} */
@Override
public @NotNull Cursor<Entry> range(@NotNull ByteArray keyFrom, @Nullable
ByteArray keyTo) {
+ return range(keyFrom, keyTo, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public @NotNull Cursor<Entry> range(@NotNull ByteArray keyFrom, @Nullable
ByteArray keyTo, boolean includeTombstones) {
return new CursorImpl<>(
- metaStorageRaftGrpSvc,
- metaStorageRaftGrpSvc.run(
- new RangeCommand(keyFrom, keyTo, localNodeId,
uuidGenerator.randomUuid())),
- MetaStorageServiceImpl::singleEntryResult
+ metaStorageRaftGrpSvc,
+ metaStorageRaftGrpSvc.run(
+ new RangeCommand(keyFrom, keyTo, localNodeId,
uuidGenerator.randomUuid())),
+ MetaStorageServiceImpl::singleEntryResult
);
}
diff --git
a/modules/metastorage-common/src/main/java/org/apache/ignite/internal/metastorage/common/command/RangeCommand.java
b/modules/metastorage-common/src/main/java/org/apache/ignite/internal/metastorage/common/command/RangeCommand.java
index 5280022ca..57acc95dc 100644
---
a/modules/metastorage-common/src/main/java/org/apache/ignite/internal/metastorage/common/command/RangeCommand.java
+++
b/modules/metastorage-common/src/main/java/org/apache/ignite/internal/metastorage/common/command/RangeCommand.java
@@ -48,6 +48,9 @@ public final class RangeCommand implements WriteCommand {
@NotNull
private final IgniteUuid cursorId;
+ /** Whether to include tombstone entries. */
+ private final boolean includeTombstones;
+
/**
* Constructor.
*
@@ -80,12 +83,34 @@ public final class RangeCommand implements WriteCommand {
long revUpperBound,
@NotNull String requesterNodeId,
@NotNull IgniteUuid cursorId
+ ) {
+ this(keyFrom, keyTo, revUpperBound, requesterNodeId, cursorId, false);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param keyFrom Start key of range (inclusive).
+ * @param keyTo End key of range (exclusive).
+ * @param revUpperBound The upper bound for entry revision. {@code -1}
means latest revision.
+ * @param requesterNodeId Id of the node that requests range.
+ * @param cursorId Id of cursor that is associated with the
current command.
+ * @param includeTombstones Whether to include tombstones.
+ */
+ public RangeCommand(
+ @NotNull ByteArray keyFrom,
+ @Nullable ByteArray keyTo,
+ long revUpperBound,
+ @NotNull String requesterNodeId,
+ @NotNull IgniteUuid cursorId,
+ boolean includeTombstones
) {
this.keyFrom = keyFrom.bytes();
this.keyTo = keyTo == null ? null : keyTo.bytes();
this.revUpperBound = revUpperBound;
this.requesterNodeId = requesterNodeId;
this.cursorId = cursorId;
+ this.includeTombstones = includeTombstones;
}
/**
@@ -123,4 +148,11 @@ public final class RangeCommand implements WriteCommand {
public IgniteUuid getCursorId() {
return cursorId;
}
+
+ /**
+ * Returns the boolean value indicating whether this range command is
supposed to include tombstone entries into the cursor.
+ */
+ public boolean includeTombstones() {
+ return includeTombstones;
+ }
}
diff --git
a/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/KeyValueStorage.java
b/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/KeyValueStorage.java
index 56d6e33f4..81598771e 100644
---
a/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/KeyValueStorage.java
+++
b/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/KeyValueStorage.java
@@ -170,11 +170,12 @@ public interface KeyValueStorage extends AutoCloseable {
/**
* Returns cursor by entries which correspond to the given keys range.
*
- * @param keyFrom Start key of range (inclusive).
- * @param keyTo Last key of range (exclusive).
+ * @param keyFrom Start key of range (inclusive).
+ * @param keyTo Last key of range (exclusive).
+ * @param includeTombstones Whether to include tombstone entries.
* @return Cursor by entries which correspond to the given keys range.
*/
- Cursor<Entry> range(byte[] keyFrom, byte @Nullable [] keyTo);
+ Cursor<Entry> range(byte[] keyFrom, byte @Nullable [] keyTo, boolean
includeTombstones);
/**
* Returns cursor by entries which correspond to the given keys range and
bounded by revision number.
@@ -182,9 +183,10 @@ public interface KeyValueStorage extends AutoCloseable {
* @param keyFrom Start key of range (inclusive).
* @param keyTo Last key of range (exclusive).
* @param revUpperBound Upper bound of revision.
+ * @param includeTombstones Whether to include tombstone entries.
* @return Cursor by entries which correspond to the given keys range.
*/
- Cursor<Entry> range(byte[] keyFrom, byte[] keyTo, long revUpperBound);
+ Cursor<Entry> range(byte[] keyFrom, byte[] keyTo, long revUpperBound,
boolean includeTombstones);
/**
* Creates subscription on updates of entries corresponding to the given
keys range and starting from the given revision number.
diff --git
a/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/persistence/RangeCursor.java
b/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/persistence/RangeCursor.java
index a20d12eae..c16e29f24 100644
---
a/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/persistence/RangeCursor.java
+++
b/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/persistence/RangeCursor.java
@@ -52,6 +52,9 @@ class RangeCursor implements Cursor<Entry> {
/** Key of the last returned entry. */
private byte[] lastRetKey;
+ /** Whether to include tombstone entries. */
+ private final boolean includeTombstones;
+
/**
* {@code true} if the iteration is finished.
*/
@@ -60,16 +63,18 @@ class RangeCursor implements Cursor<Entry> {
/**
* Constructor.
*
- * @param storage Storage.
- * @param keyFrom {@link #keyFrom}.
- * @param keyTo {@link #keyTo}.
- * @param rev {@link #rev}.
+ * @param storage Storage.
+ * @param keyFrom {@link #keyFrom}.
+ * @param keyTo {@link #keyTo}.
+ * @param rev {@link #rev}.
+ * @param includeTombstones {@link #includeTombstones}.
*/
- RangeCursor(RocksDbKeyValueStorage storage, byte[] keyFrom, byte @Nullable
[] keyTo, long rev) {
+ RangeCursor(RocksDbKeyValueStorage storage, byte[] keyFrom, byte @Nullable
[] keyTo, long rev, boolean includeTombstones) {
this.storage = storage;
this.keyFrom = keyFrom;
this.keyTo = keyTo;
this.rev = rev;
+ this.includeTombstones = includeTombstones;
this.it = createIterator();
}
@@ -147,9 +152,11 @@ class RangeCursor implements Cursor<Entry> {
Entry entry = storage.doGetValue(key, lastRev);
- assert !entry.empty() : "Iterator should not
return empty entry.";
+ if (!entry.tombstone() || includeTombstones) {
+ assert !entry.empty() : "Iterator should not
return empty entry.";
- nextRetEntry = entry;
+ nextRetEntry = entry;
+ }
}
}
} finally {
diff --git
a/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/persistence/RocksDbKeyValueStorage.java
b/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/persistence/RocksDbKeyValueStorage.java
index 33ed663da..989dddf71 100644
---
a/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/persistence/RocksDbKeyValueStorage.java
+++
b/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/persistence/RocksDbKeyValueStorage.java
@@ -685,14 +685,14 @@ public class RocksDbKeyValueStorage implements
KeyValueStorage {
/** {@inheritDoc} */
@Override
- public Cursor<Entry> range(byte[] keyFrom, byte[] keyTo) {
- return new RangeCursor(this, keyFrom, keyTo, rev);
+ public Cursor<Entry> range(byte[] keyFrom, byte[] keyTo, boolean
includeTombstones) {
+ return new RangeCursor(this, keyFrom, keyTo, rev, includeTombstones);
}
/** {@inheritDoc} */
@Override
- public Cursor<Entry> range(byte[] keyFrom, byte[] keyTo, long
revUpperBound) {
- return new RangeCursor(this, keyFrom, keyTo, revUpperBound);
+ public Cursor<Entry> range(byte[] keyFrom, byte[] keyTo, long
revUpperBound, boolean includeTombstones) {
+ return new RangeCursor(this, keyFrom, keyTo, revUpperBound,
includeTombstones);
}
/** {@inheritDoc} */
diff --git
a/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/raft/MetaStorageListener.java
b/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/raft/MetaStorageListener.java
index 8bb646035..c85b5a9d8 100644
---
a/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/raft/MetaStorageListener.java
+++
b/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/raft/MetaStorageListener.java
@@ -250,8 +250,8 @@ public class MetaStorageListener implements
RaftGroupListener {
IgniteUuid cursorId = rangeCmd.getCursorId();
Cursor<Entry> cursor = (rangeCmd.revUpperBound() != -1)
- ? storage.range(rangeCmd.keyFrom(), rangeCmd.keyTo(),
rangeCmd.revUpperBound()) :
- storage.range(rangeCmd.keyFrom(), rangeCmd.keyTo());
+ ? storage.range(rangeCmd.keyFrom(), rangeCmd.keyTo(),
rangeCmd.revUpperBound(), rangeCmd.includeTombstones()) :
+ storage.range(rangeCmd.keyFrom(), rangeCmd.keyTo(),
rangeCmd.includeTombstones());
cursors.put(
cursorId,
diff --git
a/modules/metastorage-server/src/test/java/org/apache/ignite/internal/metastorage/server/AbstractKeyValueStorageTest.java
b/modules/metastorage-server/src/test/java/org/apache/ignite/internal/metastorage/server/AbstractKeyValueStorageTest.java
index 77b983ce7..34b265dd7 100644
---
a/modules/metastorage-server/src/test/java/org/apache/ignite/internal/metastorage/server/AbstractKeyValueStorageTest.java
+++
b/modules/metastorage-server/src/test/java/org/apache/ignite/internal/metastorage/server/AbstractKeyValueStorageTest.java
@@ -1958,7 +1958,7 @@ public abstract class AbstractKeyValueStorageTest {
assertEquals(3, storage.updateCounter());
// Range for latest revision without max bound.
- Cursor<Entry> cur = storage.range(key1, null);
+ Cursor<Entry> cur = storage.range(key1, null, false);
Iterator<Entry> it = cur.iterator();
@@ -2006,7 +2006,7 @@ public abstract class AbstractKeyValueStorageTest {
}
// Range for latest revision with max bound.
- cur = storage.range(key1, key3);
+ cur = storage.range(key1, key3, false);
it = cur.iterator();
@@ -2043,6 +2043,50 @@ public abstract class AbstractKeyValueStorageTest {
}
}
+ @Test
+ public void rangeCursorSkippingTombstones() {
+ byte[] key1 = key(1);
+ byte[] val1 = keyValue(1, 1);
+
+ byte[] key2 = key(2);
+ byte[] val2 = keyValue(2, 2);
+
+ assertEquals(0, storage.revision());
+ assertEquals(0, storage.updateCounter());
+
+ storage.put(key1, val1);
+
+ assertEquals(1, storage.revision());
+ assertEquals(1, storage.updateCounter());
+
+ storage.remove(key1);
+
+ assertEquals(2, storage.revision());
+ assertEquals(2, storage.updateCounter());
+
+ storage.put(key2, val2);
+
+ assertEquals(3, storage.revision());
+ assertEquals(3, storage.updateCounter());
+
+ // Range that includes tombstones.
+ Cursor<Entry> cur = storage.range(key1, null, true);
+
+ assertEquals(2, cur.stream().count());
+
+ // Range that doesn't include tombstones.
+ cur = storage.range(key1, null, false);
+
+ Entry e = cur.next();
+
+ assertArrayEquals(key2, e.key());
+
+ assertFalse(e.tombstone());
+
+ // Check that there are no more elements in cursor.
+ assertFalse(cur.hasNext());
+ }
+
@Test
public void watchCursorLexicographicTest() throws Exception {
assertEquals(0, storage.revision());
diff --git
a/modules/metastorage-server/src/test/java/org/apache/ignite/internal/metastorage/server/SimpleInMemoryKeyValueStorage.java
b/modules/metastorage-server/src/test/java/org/apache/ignite/internal/metastorage/server/SimpleInMemoryKeyValueStorage.java
index ffea7e0e5..71f202314 100644
---
a/modules/metastorage-server/src/test/java/org/apache/ignite/internal/metastorage/server/SimpleInMemoryKeyValueStorage.java
+++
b/modules/metastorage-server/src/test/java/org/apache/ignite/internal/metastorage/server/SimpleInMemoryKeyValueStorage.java
@@ -353,16 +353,16 @@ public class SimpleInMemoryKeyValueStorage implements
KeyValueStorage {
/** {@inheritDoc} */
@Override
- public Cursor<Entry> range(byte[] keyFrom, byte[] keyTo) {
+ public Cursor<Entry> range(byte[] keyFrom, byte[] keyTo, boolean
includeTombstones) {
synchronized (mux) {
- return new RangeCursor(keyFrom, keyTo, rev);
+ return new RangeCursor(keyFrom, keyTo, rev, includeTombstones);
}
}
/** {@inheritDoc} */
@Override
- public Cursor<Entry> range(byte[] keyFrom, byte[] keyTo, long
revUpperBound) {
- return new RangeCursor(keyFrom, keyTo, revUpperBound);
+ public Cursor<Entry> range(byte[] keyFrom, byte[] keyTo, long
revUpperBound, boolean includeTombstones) {
+ return new RangeCursor(keyFrom, keyTo, revUpperBound,
includeTombstones);
}
/** {@inheritDoc} */
@@ -642,12 +642,15 @@ public class SimpleInMemoryKeyValueStorage implements
KeyValueStorage {
private byte[] lastRetKey;
+ private final boolean includeTombstones;
+
private boolean finished;
- RangeCursor(byte[] keyFrom, byte[] keyTo, long rev) {
+ RangeCursor(byte[] keyFrom, byte[] keyTo, long rev, boolean
includeTombstones) {
this.keyFrom = keyFrom;
this.keyTo = keyTo;
this.rev = rev;
+ this.includeTombstones = includeTombstones;
this.it = createIterator();
}
@@ -718,11 +721,13 @@ public class SimpleInMemoryKeyValueStorage implements
KeyValueStorage {
Entry entry = doGetValue(key, lastRev);
- assert !entry.empty() : "Iterator should not
return empty entry.";
+ if (!entry.tombstone() || includeTombstones) {
+ assert !entry.empty() : "Iterator should
not return empty entry.";
- nextRetEntry = entry;
+ nextRetEntry = entry;
- break;
+ break;
+ }
}
}
}
diff --git
a/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/MetaStorageManager.java
b/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/MetaStorageManager.java
index c76cbe5c5..163762ffb 100644
---
a/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/MetaStorageManager.java
+++
b/modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/MetaStorageManager.java
@@ -666,12 +666,25 @@ public class MetaStorageManager implements
IgniteComponent {
* @see MetaStorageService#range(ByteArray, ByteArray)
*/
public @NotNull Cursor<Entry> range(@NotNull ByteArray keyFrom, @Nullable
ByteArray keyTo) throws NodeStoppingException {
+ return range(keyFrom, keyTo, false);
+ }
+
+ /**
+ * Retrieves entries for the given key range in lexicographic order.
+ *
+ * @see MetaStorageService#range(ByteArray, ByteArray, boolean)
+ */
+ public @NotNull Cursor<Entry> range(
+ @NotNull ByteArray keyFrom,
+ @Nullable ByteArray keyTo,
+ boolean includeTombstones
+ ) throws NodeStoppingException {
if (!busyLock.enterBusy()) {
throw new NodeStoppingException();
}
try {
- return new CursorWrapper<>(metaStorageSvcFut.thenApply(svc ->
svc.range(keyFrom, keyTo)));
+ return new CursorWrapper<>(metaStorageSvcFut.thenApply(svc ->
svc.range(keyFrom, keyTo, includeTombstones)));
} finally {
busyLock.leaveBusy();
}
diff --git
a/modules/runner/src/test/java/org/apache/ignite/internal/configuration/storage/DistributedConfigurationStorageTest.java
b/modules/runner/src/test/java/org/apache/ignite/internal/configuration/storage/DistributedConfigurationStorageTest.java
index 6407ec67a..5d672c3bb 100644
---
a/modules/runner/src/test/java/org/apache/ignite/internal/configuration/storage/DistributedConfigurationStorageTest.java
+++
b/modules/runner/src/test/java/org/apache/ignite/internal/configuration/storage/DistributedConfigurationStorageTest.java
@@ -105,7 +105,7 @@ public class DistributedConfigurationStorageTest extends
ConfigurationStorageTes
ByteArray keyFrom = invocation.getArgument(0);
ByteArray keyTo = invocation.getArgument(1);
- return new CursorAdapter(metaStorage.range(keyFrom.bytes(),
keyTo == null ? null : keyTo.bytes()));
+ return new CursorAdapter(metaStorage.range(keyFrom.bytes(),
keyTo == null ? null : keyTo.bytes(), false));
});
} catch (NodeStoppingException e) {
throw new RuntimeException(e);