fix scan exception when the start key is empty (lower_bound) (#199)

Signed-off-by: birdstorm <samuelwyf@hotmail.com>
This commit is contained in:
birdstorm 2021-06-17 16:56:20 +09:00 committed by GitHub
parent 4a401776d1
commit c6d7f0478c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 73 additions and 50 deletions

View File

@ -78,26 +78,22 @@ public class RawScanIterator extends ScanIterator {
endOfScan = true; endOfScan = true;
return false; return false;
} }
// continue when cache is empty but not null
while (currentCache != null && currentCache.isEmpty()) {
if (cacheLoadFails()) {
return false;
}
}
return notEndOfScan(); return notEndOfScan();
} }
private Kvrpcpb.KvPair getCurrent() { private Kvrpcpb.KvPair getCurrent() {
if (isCacheDrained()) {
return null;
}
--limit; --limit;
return currentCache.get(index++); return currentCache.get(index++);
} }
@Override @Override
public Kvrpcpb.KvPair next() { public Kvrpcpb.KvPair next() {
Kvrpcpb.KvPair kv; return getCurrent();
// continue when cache is empty but not null
for (kv = getCurrent(); currentCache != null && kv == null; kv = getCurrent()) {
if (cacheLoadFails()) {
return null;
}
}
return kv;
} }
} }

View File

@ -50,11 +50,8 @@ public abstract class ScanIterator implements Iterator<Kvrpcpb.KvPair> {
int limit, int limit,
boolean keyOnly) { boolean keyOnly) {
this.startKey = requireNonNull(startKey, "start key is null"); this.startKey = requireNonNull(startKey, "start key is null");
if (startKey.isEmpty()) {
throw new IllegalArgumentException("start key cannot be empty");
}
this.endKey = Key.toRawKey(requireNonNull(endKey, "end key is null")); this.endKey = Key.toRawKey(requireNonNull(endKey, "end key is null"));
this.hasEndKey = !endKey.equals(ByteString.EMPTY); this.hasEndKey = !endKey.isEmpty();
this.limit = limit; this.limit = limit;
this.keyOnly = keyOnly; this.keyOnly = keyOnly;
this.conf = conf; this.conf = conf;
@ -74,7 +71,7 @@ public abstract class ScanIterator implements Iterator<Kvrpcpb.KvPair> {
if (endOfScan || processingLastBatch) { if (endOfScan || processingLastBatch) {
return true; return true;
} }
if (startKey == null || startKey.isEmpty()) { if (startKey == null) {
return true; return true;
} }
try { try {
@ -107,7 +104,8 @@ public abstract class ScanIterator implements Iterator<Kvrpcpb.KvPair> {
startKey = lastKey.next().toByteString(); startKey = lastKey.next().toByteString();
} }
// notify last batch if lastKey is greater than or equal to endKey // notify last batch if lastKey is greater than or equal to endKey
if (hasEndKey && lastKey.compareTo(endKey) >= 0) { // if startKey is empty, it indicates +
if (hasEndKey && lastKey.compareTo(endKey) >= 0 || startKey.isEmpty()) {
processingLastBatch = true; processingLastBatch = true;
startKey = null; startKey = null;
} }

View File

@ -853,7 +853,9 @@ public class RawKVClient implements AutoCloseable {
private List<TiRegion> fetchRegionsFromRange( private List<TiRegion> fetchRegionsFromRange(
BackOffer backOffer, ByteString startKey, ByteString endKey) { BackOffer backOffer, ByteString startKey, ByteString endKey) {
List<TiRegion> regions = new ArrayList<>(); List<TiRegion> regions = new ArrayList<>();
while (startKey.isEmpty() || Key.toRawKey(startKey).compareTo(Key.toRawKey(endKey)) < 0) { while (startKey.isEmpty()
|| endKey.isEmpty()
|| Key.toRawKey(startKey).compareTo(Key.toRawKey(endKey)) < 0) {
TiRegion currentRegion = clientBuilder.getRegionManager().getRegionByKey(startKey, backOffer); TiRegion currentRegion = clientBuilder.getRegionManager().getRegionByKey(startKey, backOffer);
regions.add(currentRegion); regions.add(currentRegion);
startKey = currentRegion.getEndKey(); startKey = currentRegion.getEndKey();

View File

@ -1,5 +1,7 @@
package org.tikv.raw; package org.tikv.raw;
import static org.junit.Assert.*;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
@ -103,16 +105,16 @@ public class RawKVClientTest {
ByteString value2 = ByteString.copyFromUtf8("value2"); ByteString value2 = ByteString.copyFromUtf8("value2");
client.delete(key); client.delete(key);
ByteString res1 = client.putIfAbsent(key, value, ttl); ByteString res1 = client.putIfAbsent(key, value, ttl);
assert res1.isEmpty(); assertTrue(res1.isEmpty());
ByteString res2 = client.putIfAbsent(key, value2, ttl); ByteString res2 = client.putIfAbsent(key, value2, ttl);
assert res2.equals(value); assertEquals(value, res2);
try { try {
Thread.sleep(ttl * 1000); Thread.sleep(ttl * 1000);
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
ByteString res3 = client.putIfAbsent(key, value, ttl); ByteString res3 = client.putIfAbsent(key, value, ttl);
assert res3.isEmpty(); assertTrue(res3.isEmpty());
} }
// tikv-4.0 doest not support ttl // tikv-4.0 doest not support ttl
@ -258,30 +260,47 @@ public class RawKVClientTest {
public void simpleTest() { public void simpleTest() {
if (!initialized) return; if (!initialized) return;
ByteString key = rawKey("key"); ByteString key = rawKey("key");
ByteString key0 = rawKey("key0");
ByteString key1 = rawKey("key1"); ByteString key1 = rawKey("key1");
ByteString key2 = rawKey("key2"); ByteString key2 = rawKey("key2");
ByteString key3 = rawKey("key3"); ByteString key3 = rawKey("key3");
ByteString value = rawValue("value");
ByteString value1 = rawValue("value1"); ByteString value1 = rawValue("value1");
ByteString value2 = rawValue("value2"); ByteString value2 = rawValue("value2");
ByteString value3 = rawValue("value3");
Kvrpcpb.KvPair kv = Kvrpcpb.KvPair.newBuilder().setKey(key).setValue(value).build();
Kvrpcpb.KvPair kv1 = Kvrpcpb.KvPair.newBuilder().setKey(key1).setValue(value1).build(); Kvrpcpb.KvPair kv1 = Kvrpcpb.KvPair.newBuilder().setKey(key1).setValue(value1).build();
Kvrpcpb.KvPair kv2 = Kvrpcpb.KvPair.newBuilder().setKey(key2).setValue(value2).build(); Kvrpcpb.KvPair kv2 = Kvrpcpb.KvPair.newBuilder().setKey(key2).setValue(value2).build();
Kvrpcpb.KvPair kv3 = Kvrpcpb.KvPair.newBuilder().setKey(key3).setValue(value3).build();
try { try {
checkEmpty(key1); checkDeleteRange(ByteString.EMPTY, ByteString.EMPTY);
checkEmpty(key2); checkEmpty(kv);
checkPut(key1, value1); checkEmpty(kv1);
checkPut(key2, value2); checkEmpty(kv2);
List<Kvrpcpb.KvPair> result = new ArrayList<>(); checkEmpty(kv3);
List<Kvrpcpb.KvPair> result2 = new ArrayList<>(); checkPut(kv);
result.add(kv1); checkPut(kv1);
result.add(kv2); checkPut(kv2);
checkScan(key, key3, result, limit); checkPut(kv3);
checkScan(key1, key3, result, limit); // <key, value>, <key1,value1>, <key2,value2>, <key3,value3>
checkScan(key, key1, new ArrayList<>(), limit); // (-, +)
result2.add(kv1); checkScan(ByteString.EMPTY, ByteString.EMPTY, Arrays.asList(kv, kv1, kv2, kv3), limit);
checkScan(key, key2, result2, limit); // (-, key3)
checkScan(ByteString.EMPTY, key3, Arrays.asList(kv, kv1, kv2), limit);
// [key1, +)
checkScan(key1, ByteString.EMPTY, Arrays.asList(kv1, kv2, kv3), limit);
// [key, key3)
checkScan(key, key3, Arrays.asList(kv, kv1, kv2), limit);
// [key1, key3)
checkScan(key1, key3, Arrays.asList(kv1, kv2), limit);
// [key0, key1)
checkScan(key0, key1, new ArrayList<>(), limit);
// [key, key2)
checkScan(key, key2, Arrays.asList(kv, kv1), limit);
checkDelete(key1); checkDelete(key1);
checkDelete(key2); checkDelete(key2);
checkDeleteRange(ByteString.EMPTY, ByteString.EMPTY);
} catch (final TiKVException e) { } catch (final TiKVException e) {
logger.warn("Test fails with Exception: " + e); logger.warn("Test fails with Exception: " + e);
} }
@ -513,7 +532,7 @@ public class RawKVClientTest {
} else { } else {
int i = 0; int i = 0;
for (Map.Entry<ByteString, ByteString> pair : data.entrySet()) { for (Map.Entry<ByteString, ByteString> pair : data.entrySet()) {
assert client.get(pair.getKey()).equals(pair.getValue()); assertEquals(pair.getValue(), client.get(pair.getKey()));
i++; i++;
if (i >= getCases) { if (i >= getCases) {
break; break;
@ -765,27 +784,31 @@ public class RawKVClientTest {
private void checkBatchGet(List<ByteString> keys) { private void checkBatchGet(List<ByteString> keys) {
List<Kvrpcpb.KvPair> result = client.batchGet(keys); List<Kvrpcpb.KvPair> result = client.batchGet(keys);
for (Kvrpcpb.KvPair kvPair : result) { for (Kvrpcpb.KvPair kvPair : result) {
assert data.containsKey(kvPair.getKey()); assertTrue(data.containsKey(kvPair.getKey()));
assert kvPair.getValue().equals(data.get(kvPair.getKey())); assertEquals(data.get(kvPair.getKey()), kvPair.getValue());
} }
} }
private void checkPut(Kvrpcpb.KvPair kv) {
checkPut(kv.getKey(), kv.getValue());
}
private void checkPut(ByteString key, ByteString value) { private void checkPut(ByteString key, ByteString value) {
client.put(key, value); client.put(key, value);
assert client.get(key).equals(value); assertEquals(value, client.get(key));
} }
private void checkBatchPut(Map<ByteString, ByteString> kvPairs) { private void checkBatchPut(Map<ByteString, ByteString> kvPairs) {
client.batchPut(kvPairs); client.batchPut(kvPairs);
for (Map.Entry<ByteString, ByteString> kvPair : kvPairs.entrySet()) { for (Map.Entry<ByteString, ByteString> kvPair : kvPairs.entrySet()) {
assert client.get(kvPair.getKey()).equals(kvPair.getValue()); assertEquals(kvPair.getValue(), client.get(kvPair.getKey()));
} }
} }
private void checkScan( private void checkScan(
ByteString startKey, ByteString endKey, List<Kvrpcpb.KvPair> ans, int limit) { ByteString startKey, ByteString endKey, List<Kvrpcpb.KvPair> expected, int limit) {
List<Kvrpcpb.KvPair> result = client.scan(startKey, endKey, limit); List<Kvrpcpb.KvPair> result = client.scan(startKey, endKey, limit);
assert result.equals(ans); assertEquals(expected, result);
} }
private void checkScan( private void checkScan(
@ -822,7 +845,7 @@ public class RawKVClientTest {
.setValue(kvPair.getValue()) .setValue(kvPair.getValue())
.build()) .build())
.collect(Collectors.toList()); .collect(Collectors.toList());
assert result.get(i).equals(partialResult); assertEquals(partialResult, result.get(i));
i++; i++;
} }
} }
@ -834,7 +857,7 @@ public class RawKVClientTest {
Pair<ByteString, ByteString> range = ranges.get(i); Pair<ByteString, ByteString> range = ranges.get(i);
List<ByteString> partialResult = List<ByteString> partialResult =
new ArrayList<>(data.subMap(range.first, range.second).keySet()); new ArrayList<>(data.subMap(range.first, range.second).keySet());
assert result.get(i).equals(partialResult); assertEquals(partialResult, result.get(i));
} }
} }
@ -848,31 +871,35 @@ public class RawKVClientTest {
logger.info("delete range complete"); logger.info("delete range complete");
List<Kvrpcpb.KvPair> result = client.scan(startKey, endKey); List<Kvrpcpb.KvPair> result = client.scan(startKey, endKey);
logger.info("checking scan complete. number of remaining keys in range: " + result.size()); logger.info("checking scan complete. number of remaining keys in range: " + result.size());
assert result.isEmpty(); assertTrue(result.isEmpty());
} }
private void checkPutTTL(ByteString key, ByteString value, long ttl) { private void checkPutTTL(ByteString key, ByteString value, long ttl) {
client.put(key, value, ttl); client.put(key, value, ttl);
assert client.get(key).equals(value); assertEquals(value, client.get(key));
} }
private void checkGetKeyTTL(ByteString key, long ttl) { private void checkGetKeyTTL(ByteString key, long ttl) {
Long t = client.getKeyTTL(key); Long t = client.getKeyTTL(key);
assert t != null; assertNotNull(t);
assert t <= ttl && t > 0; assertTrue(t <= ttl && t > 0);
} }
private void checkGetTTLTimeOut(ByteString key) { private void checkGetTTLTimeOut(ByteString key) {
assert client.get(key).isEmpty(); assertTrue(client.get(key).isEmpty());
} }
private void checkGetKeyTTLTimeOut(ByteString key) { private void checkGetKeyTTLTimeOut(ByteString key) {
Long t = client.getKeyTTL(key); Long t = client.getKeyTTL(key);
assert t == null; assertNull(t);
}
private void checkEmpty(Kvrpcpb.KvPair kv) {
checkEmpty(kv.getKey());
} }
private void checkEmpty(ByteString key) { private void checkEmpty(ByteString key) {
assert client.get(key).isEmpty(); assertTrue(client.get(key).isEmpty());
} }
private static ByteString rawKey(String key) { private static ByteString rawKey(String key) {