Skip to content

Commit 7365bbd

Browse files
authored
Fixed an edge case where a shard that hasn't yet received cluster topology could reject internal queries with 'Query requires unavailable slots' during the first moments after server startup - [MOD-13828] (#8388)
* initialize SlotsTracker with all slots * fix default implementation
1 parent 426985c commit 7365bbd

2 files changed

Lines changed: 33 additions & 6 deletions

File tree

src/redisearch_rs/c_entrypoint/slots_tracker_ffi/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ static OWNER_THREAD: OnceLock<ThreadId> = OnceLock::new();
7272
//
7373
// Only accessible from the thread that first calls any FFI function.
7474
thread_local! {
75-
static TRACKER: RefCell<SlotsTracker> = const { RefCell::new(SlotsTracker::new()) };
75+
static TRACKER: RefCell<SlotsTracker> = RefCell::new(SlotsTracker::new());
7676
}
7777

7878
// ============================================================================

src/redisearch_rs/slots_tracker/src/slots_tracker.rs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ impl Version {
7777
///
7878
/// This structure encapsulates all slot tracking state and provides safe methods
7979
/// for manipulating the three slot sets and version counter.
80-
#[derive(Debug, Clone, PartialEq, Eq, Default)]
80+
#[derive(Debug, Clone, PartialEq, Eq)]
8181
pub struct SlotsTracker {
8282
/// Local responsibility slots - owned by this Redis instance in the cluster topology.
8383
local: SlotSet,
@@ -93,11 +93,20 @@ pub struct SlotsTracker {
9393
version: Version,
9494
}
9595

96+
impl Default for SlotsTracker {
97+
fn default() -> Self {
98+
Self::new()
99+
}
100+
}
101+
96102
impl SlotsTracker {
97-
/// Creates a new SlotsTracker with empty slot sets and version 1.
98-
pub const fn new() -> Self {
103+
/// Creates a new SlotsTracker with full local slot set, empty fully and partially available sets, and version 1.
104+
pub fn new() -> Self {
99105
Self {
100-
local: SlotSet::new(),
106+
local: SlotSet::from_ranges(&[SlotRange {
107+
start: 0,
108+
end: 16383,
109+
}]),
101110
fully_available: SlotSet::new(),
102111
partially_available: SlotSet::new(),
103112
version: Version::new(),
@@ -547,11 +556,15 @@ mod tests {
547556
#[test]
548557
fn test_remove_deleted_slots_only_affects_partially_available() {
549558
let mut tracker = SlotsTracker::new();
559+
// Simulate a shard configured with no local slots (import scenario)
560+
tracker.set_local_slots(&[]);
561+
let v_after_set = tracker.get_version();
550562
tracker.mark_partially_available_slots(&[SlotRange {
551563
start: 200,
552564
end: 210,
553565
}]);
554566
let v1 = tracker.get_version();
567+
assert_eq!(v1, v_after_set.increment());
555568
assert_eq!(tracker, ([], [], [(200, 210)], Some(v1)));
556569
tracker.remove_deleted_slots(&[SlotRange {
557570
start: 205,
@@ -572,7 +585,15 @@ mod tests {
572585
let mut tracker = SlotsTracker::new();
573586
let initial_version = tracker.get_version();
574587
assert_eq!(tracker.get_version(), Version::new());
575-
assert!(tracker.local.is_empty());
588+
// New tracker starts with full slot range (no topology yet)
589+
assert_eq!(
590+
tracker.local,
591+
[SlotRange {
592+
start: 0,
593+
end: 16383
594+
}]
595+
.as_slice()
596+
);
576597
assert!(tracker.fully_available.is_empty());
577598
assert!(tracker.partially_available.is_empty());
578599

@@ -690,6 +711,8 @@ mod tests {
690711
#[test]
691712
fn test_promote_to_local_slots_does_not_increment_version() {
692713
let mut tracker = SlotsTracker::new();
714+
// Simulate a shard configured with no local slots (import scenario)
715+
tracker.set_local_slots(&[]);
693716
tracker.mark_partially_available_slots(&[SlotRange {
694717
start: 100,
695718
end: 200,
@@ -765,6 +788,8 @@ mod tests {
765788
#[test]
766789
fn test_promote_to_local_slots_empty_ranges() {
767790
let mut tracker = SlotsTracker::new();
791+
// Simulate a shard configured with no local slots (import scenario)
792+
tracker.set_local_slots(&[]);
768793
tracker.mark_partially_available_slots(&[SlotRange {
769794
start: 100,
770795
end: 200,
@@ -779,6 +804,8 @@ mod tests {
779804
#[test]
780805
fn test_promote_to_local_slots_multiple_ranges() {
781806
let mut tracker = SlotsTracker::new();
807+
// Simulate a shard configured with no local slots (import scenario)
808+
tracker.set_local_slots(&[]);
782809
tracker.mark_partially_available_slots(&[SlotRange {
783810
start: 100,
784811
end: 200,

0 commit comments

Comments
 (0)