Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[to #763] Pick bug fixes of API v2 codec to master #769

Merged
merged 4 commits into from
Oct 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/java/org/tikv/common/apiversion/CodecUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import org.tikv.common.codec.CodecDataOutput;

// TODO(iosmanthus): use ByteString.wrap to avoid once more copying.
class CodecUtils {
public class CodecUtils {

Check warning on line 26 in src/main/java/org/tikv/common/apiversion/CodecUtils.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tikv/common/apiversion/CodecUtils.java#L26

Added line #L26 was not covered by tests
public static ByteString encode(ByteString key) {
CodecDataOutput cdo = new CodecDataOutput();
BytesCodec.writeBytes(cdo, key.toByteArray());
Expand Down
19 changes: 9 additions & 10 deletions src/main/java/org/tikv/common/apiversion/RequestKeyV2Codec.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,22 +81,21 @@ public Region decodeRegion(Region region) {

if (!start.isEmpty()) {
start = CodecUtils.decode(start);
if (ByteString.unsignedLexicographicalComparator().compare(start, keyPrefix) < 0) {
start = ByteString.EMPTY;
} else {
start = decodeKey(start);
}
}

if (!end.isEmpty()) {
end = CodecUtils.decode(end);
if (ByteString.unsignedLexicographicalComparator().compare(end, infiniteEndKey) >= 0) {
end = ByteString.EMPTY;
} else {
end = decodeKey(end);
}
}

if (ByteString.unsignedLexicographicalComparator().compare(start, infiniteEndKey) >= 0
|| (!end.isEmpty()
&& ByteString.unsignedLexicographicalComparator().compare(end, keyPrefix) <= 0)) {
throw new IllegalArgumentException("region out of keyspace" + region.toString());
}

start = start.startsWith(keyPrefix) ? start.substring(keyPrefix.size()) : ByteString.EMPTY;
end = end.startsWith(keyPrefix) ? end.substring(keyPrefix.size()) : ByteString.EMPTY;

return builder.setStartKey(start).setEndKey(end).build();
}
}
11 changes: 10 additions & 1 deletion src/main/java/org/tikv/common/operation/RegionErrorHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,16 @@
// If the region epoch is not ahead of TiKV's, replace region meta in region cache.
for (Metapb.Region meta : currentRegions) {
// The region needs to be decoded to plain format.
meta = regionManager.getPDClient().getCodec().decodeRegion(meta);
try {
meta = regionManager.getPDClient().getCodec().decodeRegion(meta);
} catch (Exception e) {
logger.warn("ignore invalid region: " + meta.toString());

Check warning on line 235 in src/main/java/org/tikv/common/operation/RegionErrorHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tikv/common/operation/RegionErrorHandler.java#L234-L235

Added lines #L234 - L235 were not covered by tests
// if the region is invalid, ignore it since the following situation might appear.
// Assuming a region with range [r000, z), then it splits into:
// [r000, x) [x, z), the right region is invalid for keyspace `r000`.
// We should only care about the valid region.
continue;

Check warning on line 240 in src/main/java/org/tikv/common/operation/RegionErrorHandler.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/tikv/common/operation/RegionErrorHandler.java#L240

Added line #L240 was not covered by tests
}
TiRegion region = regionManager.createRegion(meta, backOffer);
newRegions.add(region);
if (recv.getRegion().getVerID() == region.getVerID()) {
Expand Down
83 changes: 81 additions & 2 deletions src/test/java/org/tikv/common/apiversion/RequestKeyCodecTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@

package org.tikv.common.apiversion;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;

import com.google.common.collect.ImmutableList;
import com.google.protobuf.ByteString;
Expand Down Expand Up @@ -177,5 +176,85 @@ void testV2Codec(RequestKeyV2Codec v2) {
decoded = v2.decodeRegion(region);
assertEquals(start, decoded.getStartKey());
assertEquals(ByteString.EMPTY, decoded.getEndKey());

// test region out of keyspace
{
ByteString m_123 = CodecUtils.encode(ByteString.copyFromUtf8("m_123"));
ByteString m_124 = CodecUtils.encode(ByteString.copyFromUtf8("m_124"));
ByteString infiniteEndKey_0 =
CodecUtils.encode(v2.infiniteEndKey.concat(ByteString.copyFrom(new byte[] {0})));
ByteString t_123 = CodecUtils.encode(ByteString.copyFromUtf8("t_123"));
ByteString y_123 = CodecUtils.encode(ByteString.copyFromUtf8("y_123"));

ByteString[][] outOfKeyspaceCases = {
{ByteString.EMPTY, CodecUtils.encode(v2.keyPrefix)}, // ["", "r000"/"x000")
{ByteString.EMPTY, m_123},
{m_123, m_124},
{m_124, CodecUtils.encode(v2.keyPrefix)},
{CodecUtils.encode(v2.infiniteEndKey), ByteString.EMPTY}, // ["r001"/"x001", "")
{CodecUtils.encode(v2.infiniteEndKey), infiniteEndKey_0},
{infiniteEndKey_0, t_123},
{y_123, ByteString.EMPTY}, // "y_123" is bigger than "infiniteEndKey" for both raw & txn.
};

for (ByteString[] testCase : outOfKeyspaceCases) {
region = Region.newBuilder().setStartKey(testCase[0]).setEndKey(testCase[1]).build();
try {
decoded = v2.decodeRegion(region);
fail(String.format("[%s,%s): %s", testCase[0], testCase[1], decoded.toString()));
} catch (Exception ignored) {
}
}
}

// case: regionStartKey == "" < keyPrefix < regionEndKey < infiniteEndKey
region =
Region.newBuilder()
.setStartKey(ByteString.EMPTY)
.setEndKey(CodecUtils.encode(v2.keyPrefix.concat(ByteString.copyFromUtf8("0"))))
.build();
decoded = v2.decodeRegion(region);
assertTrue(decoded.getStartKey().isEmpty());
assertEquals(ByteString.copyFromUtf8("0"), decoded.getEndKey());

// case: "" < regionStartKey < keyPrefix < regionEndKey < infiniteEndKey < ""
region =
Region.newBuilder()
.setStartKey(CodecUtils.encode(ByteString.copyFromUtf8("m_123")))
.setEndKey(CodecUtils.encode(v2.keyPrefix.concat(ByteString.copyFromUtf8("0"))))
.build();
decoded = v2.decodeRegion(region);
assertEquals(ByteString.EMPTY, decoded.getStartKey());
assertEquals(ByteString.copyFromUtf8("0"), decoded.getEndKey());

// case: "" < regionStartKey < keyPrefix < infiniteEndKey < regionEndKey < ""
region =
Region.newBuilder()
.setStartKey(CodecUtils.encode(ByteString.copyFromUtf8("m_123")))
.setEndKey(CodecUtils.encode(v2.infiniteEndKey.concat(ByteString.copyFromUtf8("0"))))
.build();
decoded = v2.decodeRegion(region);
assertEquals(ByteString.EMPTY, decoded.getStartKey());
assertEquals(ByteString.EMPTY, decoded.getEndKey());

// case: keyPrefix < regionStartKey < infiniteEndKey < regionEndKey < ""
region =
Region.newBuilder()
.setStartKey(CodecUtils.encode(v2.keyPrefix.concat(ByteString.copyFromUtf8("0"))))
.setEndKey(CodecUtils.encode(v2.infiniteEndKey.concat(ByteString.copyFromUtf8("0"))))
.build();
decoded = v2.decodeRegion(region);
assertEquals(ByteString.copyFromUtf8("0"), decoded.getStartKey());
assertTrue(decoded.getEndKey().isEmpty());

// case: keyPrefix < regionStartKey < infiniteEndKey < regionEndKey == ""
region =
Region.newBuilder()
.setStartKey(CodecUtils.encode(v2.keyPrefix.concat(ByteString.copyFromUtf8("0"))))
.setEndKey(ByteString.EMPTY)
.build();
decoded = v2.decodeRegion(region);
assertEquals(ByteString.copyFromUtf8("0"), decoded.getStartKey());
assertTrue(decoded.getEndKey().isEmpty());
}
}
Loading