Skip to content

Commit ae5845b

Browse files
committed
raw: fix reverse_scan failing across multiple regions
When reverse scanning a range spanning multiple regions, `retryable_scan` selected the region based on `start_key` (lower bound). However, TiKV reverse scan starts from the upper bound, causing `key_not_in_region` errors when bounds are in different regions. Fix by selecting the region containing `end_key` for reverse scans and adjusting continuation logic to move backward through regions. Also handle the edge case when iteration lands exactly on a region boundary. Fixes #512 Signed-off-by: Hugh <HueCodes@users.noreply.github.com>
1 parent 9d9b680 commit ae5845b

File tree

1 file changed

+95
-14
lines changed

1 file changed

+95
-14
lines changed

src/raw/client.rs

Lines changed: 95 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -759,20 +759,42 @@ impl<PdC: PdClient> Client<PdC> {
759759
});
760760
}
761761
let backoff = DEFAULT_STORE_BACKOFF;
762-
let mut range = range.into().encode_keyspace(self.keyspace, KeyMode::Raw);
762+
let range = range.into().encode_keyspace(self.keyspace, KeyMode::Raw);
763763
let mut result = Vec::new();
764764
let mut current_limit = limit;
765765
let (start_key, end_key) = range.clone().into_keys();
766-
let mut current_key: Key = start_key;
766+
767+
// For forward scan: current_key tracks the lower bound, moving forward
768+
// For reverse scan: current_key tracks the upper bound, moving backward
769+
let mut current_key: Key = if reverse {
770+
// Start from the upper bound for reverse scan
771+
end_key.clone().unwrap_or_default()
772+
} else {
773+
start_key.clone()
774+
};
767775

768776
while current_limit > 0 {
769-
let scan_args = ScanInnerArgs {
770-
start_key: current_key.clone(),
771-
end_key: end_key.clone(),
772-
limit: current_limit,
773-
key_only,
774-
reverse,
775-
backoff: backoff.clone(),
777+
// Build scan arguments based on direction:
778+
// - Forward: scan from current_key (moving lower bound) to end_key (fixed upper bound)
779+
// - Reverse: scan from start_key (fixed lower bound) to current_key (moving upper bound)
780+
let scan_args = if reverse {
781+
ScanInnerArgs {
782+
start_key: start_key.clone(),
783+
end_key: Some(current_key.clone()),
784+
limit: current_limit,
785+
key_only,
786+
reverse,
787+
backoff: backoff.clone(),
788+
}
789+
} else {
790+
ScanInnerArgs {
791+
start_key: current_key.clone(),
792+
end_key: end_key.clone(),
793+
limit: current_limit,
794+
key_only,
795+
reverse,
796+
backoff: backoff.clone(),
797+
}
776798
};
777799
let (res, next_key) = self.retryable_scan(scan_args).await?;
778800

@@ -784,11 +806,29 @@ impl<PdC: PdClient> Client<PdC> {
784806
current_limit -= kvs.len() as u32;
785807
result.append(&mut kvs);
786808
}
787-
if end_key.clone().is_some_and(|ek| ek <= next_key) {
788-
break;
809+
810+
// Determine if we should continue to the next region
811+
if reverse {
812+
// For reverse scan: next_key is the region's start_key (lower boundary)
813+
// Stop if next_key is empty (reached the beginning) or
814+
// if we've reached/passed the lower bound of our scan range
815+
if next_key.is_empty() || next_key <= start_key {
816+
break;
817+
}
818+
// Safety: if next_key >= current_key, we've made no progress
819+
// (shouldn't happen with boundary fix, but prevents infinite loop)
820+
if next_key >= current_key {
821+
break;
822+
}
823+
current_key = next_key;
789824
} else {
825+
// For forward scan: next_key is the region's end_key (upper boundary)
826+
// Stop if next_key is empty (reached the end) or
827+
// if we've reached/passed the upper bound of our scan range
828+
if next_key.is_empty() || end_key.clone().is_some_and(|ek| ek <= next_key) {
829+
break;
830+
}
790831
current_key = next_key;
791-
range = BoundRange::new(std::ops::Bound::Included(current_key.clone()), range.to);
792832
}
793833
}
794834

@@ -807,8 +847,42 @@ impl<PdC: PdClient> Client<PdC> {
807847
) -> Result<(Option<RawScanResponse>, Key)> {
808848
let start_key = scan_args.start_key;
809849
let end_key = scan_args.end_key;
850+
let reverse = scan_args.reverse;
810851
loop {
811-
let region = self.rpc.clone().region_for_key(&start_key).await?;
852+
// For forward scan: select region containing start_key (lower bound)
853+
// For reverse scan: select region containing end_key (upper bound)
854+
// because TiKV reverse scan starts from the upper bound and goes backward.
855+
//
856+
// When reverse=true, new_raw_scan_request swaps the keys so that TiKV receives
857+
// end_key as its start_key. We must select the region containing that key.
858+
let region_lookup_key: &Key = if reverse {
859+
match &end_key {
860+
Some(ek) if !ek.is_empty() => ek,
861+
// If no upper bound, fall back to start_key (though this case
862+
// is documented as unsupported for reverse scan)
863+
_ => &start_key,
864+
}
865+
} else {
866+
&start_key
867+
};
868+
let mut region = self.rpc.clone().region_for_key(region_lookup_key).await?;
869+
870+
// For reverse scan: if the lookup key equals the region's start_key exactly,
871+
// we're at a boundary and need the previous region. This happens when iterating
872+
// backward and current_key lands on a region boundary.
873+
if reverse {
874+
let region_start = region.start_key();
875+
if !region_start.is_empty() && *region_lookup_key == region_start {
876+
// Find the previous region by looking up a key just before this boundary.
877+
// Truncating the last byte gives a lexicographically smaller key.
878+
let mut prev_key: Vec<u8> = region_start.into();
879+
prev_key.pop();
880+
if !prev_key.is_empty() {
881+
region = self.rpc.clone().region_for_key(&prev_key.into()).await?;
882+
}
883+
// If prev_key is empty, we're at the start of the keyspace
884+
}
885+
}
812886
let store = self.rpc.clone().store_for_id(region.id()).await?;
813887
let request = new_raw_scan_request(
814888
(start_key.clone(), end_key.clone()).into(),
@@ -833,7 +907,14 @@ impl<PdC: PdClient> Client<PdC> {
833907
return Err(RegionError(Box::new(err)));
834908
}
835909
}
836-
Ok((Some(r), region.end_key()))
910+
// For forward scan: next region starts at this region's end_key
911+
// For reverse scan: next region ends at this region's start_key
912+
let next_key = if reverse {
913+
region.start_key()
914+
} else {
915+
region.end_key()
916+
};
917+
Ok((Some(r), next_key))
837918
}
838919
Err(err) => Err(err),
839920
};

0 commit comments

Comments
 (0)