Skip to content

Commit 2456c3c

Browse files
committed
fix(qdrant): identify local shard owner in replay
1 parent ae32d2c commit 2456c3c

2 files changed

Lines changed: 104 additions & 14 deletions

File tree

addons/qdrant/scripts-ut-spec/qdrant_member_leave_spec.sh

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,27 @@ Describe "Qdrant Member Leave Bash Script Tests"
7979
End
8080
End
8181

82+
Describe "qdrant_shard_on_leaving_peer()"
83+
It "does not attribute a surviving peer local shard to the leaving peer"
84+
collection_info='{"result":{"peer_id":2,"local_shards":[{"shard_id":7}],"remote_shards":[],"shard_transfers":[]}}'
85+
When call qdrant_shard_on_leaving_peer "$collection_info" "7" "1"
86+
The status should be failure
87+
End
88+
89+
It "attributes a local shard using the serving peer identity"
90+
collection_info='{"result":{"peer_id":1,"local_shards":[{"shard_id":7}],"remote_shards":[],"shard_transfers":[]}}'
91+
When call qdrant_shard_on_leaving_peer "$collection_info" "7" "1"
92+
The status should be success
93+
End
94+
95+
It "fails closed when a matching local shard has no serving peer identity"
96+
collection_info='{"result":{"local_shards":[{"shard_id":7}],"remote_shards":[],"shard_transfers":[]}}'
97+
When call qdrant_shard_on_leaving_peer "$collection_info" "7" "1"
98+
The status should eq 2
99+
The stderr should include "local shard without peer_id"
100+
End
101+
End
102+
82103
Describe "memberLeave action-wide deadline"
83104
cleanup_deadline() {
84105
unset QDRANT_MEMBER_LEAVE_ACTION_SECONDS QDRANT_MEMBER_LEAVE_CURL_TIMEOUT
@@ -201,6 +222,28 @@ Describe "Qdrant Member Leave Bash Script Tests"
201222
The output should include "already moving after failed submit"
202223
End
203224

225+
It "treats a failed move submit as success when replay sees the shard local to a surviving peer"
226+
initial_info='{"result":{"peer_id":2,"local_shards":[],"remote_shards":[{"shard_id":7,"peer_id":1}],"shard_transfers":[]}}'
227+
qdrant_curl() {
228+
last_arg="${*: -1}"
229+
case "$last_arg" in
230+
"http://control/collections/demo/cluster")
231+
if [ "$1" = "-sf" ] && [ "$4" = "-X" ]; then
232+
return 1
233+
fi
234+
echo '{"result":{"peer_id":2,"local_shards":[{"shard_id":7}],"remote_shards":[],"shard_transfers":[]}}'
235+
;;
236+
*)
237+
return 1
238+
;;
239+
esac
240+
}
241+
242+
When call qdrant_submit_shard_move_if_needed "demo" "7" "$initial_info"
243+
The status should be success
244+
The output should include "moved off peer 1 after failed submit"
245+
End
246+
204247
It "submits the move to an unused peer instead of an existing replica"
205248
replicated_info='{"result":{"peer_id":3,"local_shards":[{"shard_id":8}],"remote_shards":[{"shard_id":7,"peer_id":1},{"shard_id":7,"peer_id":2}],"shard_transfers":[]}}'
206249
qdrant_curl() {

addons/qdrant/scripts/qdrant-member-leave.sh

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,40 @@ qdrant_shard_on_leaving_peer() {
9898
local shard_id="$2"
9999
local peer_id="$3"
100100
local jq_bin="${JQ:-jq}"
101+
local ownership
101102

102-
echo "$collection_cluster_info" | "$jq_bin" -e \
103+
ownership="$(echo "$collection_cluster_info" | "$jq_bin" -er \
103104
--argjson shard "$shard_id" \
104105
--argjson peer "$peer_id" \
105-
'((.result.remote_shards // [])
106-
| any(.peer_id == $peer and .shard_id == $shard))
107-
or
108-
((.result.local_shards // [])
109-
| any((.peer_id? // $peer) == $peer and .shard_id == $shard))' >/dev/null
106+
'if ((.result.remote_shards // [])
107+
| any(.peer_id == $peer and .shard_id == $shard)) then
108+
"on-leaving-peer"
109+
elif ((.result.local_shards // [])
110+
| any(.shard_id == $shard)) then
111+
if (.result.peer_id | type) == "number" then
112+
if .result.peer_id == $peer then "on-leaving-peer" else "off-leaving-peer" end
113+
else
114+
"unknown-local-peer"
115+
end
116+
else
117+
"off-leaving-peer"
118+
end')" || {
119+
echo "ERROR: failed to determine shard ${shard_id} ownership from collection cluster info" >&2
120+
return 2
121+
}
122+
123+
case "$ownership" in
124+
"on-leaving-peer")
125+
return 0
126+
;;
127+
"off-leaving-peer")
128+
return 1
129+
;;
130+
*)
131+
echo "ERROR: local shard without peer_id for shard ${shard_id}; refusing to infer ownership" >&2
132+
return 2
133+
;;
134+
esac
110135
}
111136

112137
qdrant_remaining_on_leaving_count() {
@@ -351,17 +376,26 @@ qdrant_collection_cluster_info_for_leaving_shard() {
351376
local fallback_info=""
352377
local control_endpoint_uri
353378
local control_endpoint_uris="${control_uris:-$control_uri}"
379+
local shard_owner_rc
354380

355381
for control_endpoint_uri in $control_endpoint_uris; do
356382
col_cluster_info="$(qdrant_collection_cluster_info_from_uri "$control_endpoint_uri" "$col_name")" || return 1
357383
if [ -z "$fallback_info" ]; then
358384
fallback_info="$col_cluster_info"
359385
fi
360-
if qdrant_shard_transfer_exists "$col_cluster_info" "$shard_id" "$leave_peer_id" ||
361-
qdrant_shard_on_leaving_peer "$col_cluster_info" "$shard_id" "$leave_peer_id"; then
386+
if qdrant_shard_transfer_exists "$col_cluster_info" "$shard_id" "$leave_peer_id"; then
362387
printf "%s\n" "$col_cluster_info"
363388
return 0
364389
fi
390+
if qdrant_shard_on_leaving_peer "$col_cluster_info" "$shard_id" "$leave_peer_id"; then
391+
printf "%s\n" "$col_cluster_info"
392+
return 0
393+
else
394+
shard_owner_rc=$?
395+
if [ "$shard_owner_rc" -ne 1 ]; then
396+
return 1
397+
fi
398+
fi
365399
done
366400

367401
printf "%s\n" "$fallback_info"
@@ -373,15 +407,22 @@ qdrant_submit_shard_move_if_needed() {
373407
local col_cluster_info="$3"
374408
local move_target_peer_id
375409
local move_payload
410+
local shard_owner_rc
376411

377412
if qdrant_shard_transfer_exists "$col_cluster_info" "$shard_id" "$leave_peer_id"; then
378413
echo "INFO: shard ${shard_id} in ${col_name} is already moving from peer ${leave_peer_id}"
379414
return 0
380415
fi
381416

382-
if ! qdrant_shard_on_leaving_peer "$col_cluster_info" "$shard_id" "$leave_peer_id"; then
383-
echo "INFO: shard ${shard_id} in ${col_name} is already off peer ${leave_peer_id}"
384-
return 0
417+
if qdrant_shard_on_leaving_peer "$col_cluster_info" "$shard_id" "$leave_peer_id"; then
418+
:
419+
else
420+
shard_owner_rc=$?
421+
if [ "$shard_owner_rc" -eq 1 ]; then
422+
echo "INFO: shard ${shard_id} in ${col_name} is already off peer ${leave_peer_id}"
423+
return 0
424+
fi
425+
return 1
385426
fi
386427

387428
move_target_peer_id="$(qdrant_select_target_peer_id_for_shard \
@@ -410,9 +451,15 @@ qdrant_submit_shard_move_if_needed() {
410451
echo "INFO: shard ${shard_id} in ${col_name} is already moving after failed submit"
411452
return 0
412453
fi
413-
if ! qdrant_shard_on_leaving_peer "$col_cluster_info" "$shard_id" "$leave_peer_id"; then
414-
echo "INFO: shard ${shard_id} in ${col_name} moved off peer ${leave_peer_id} after failed submit"
415-
return 0
454+
if qdrant_shard_on_leaving_peer "$col_cluster_info" "$shard_id" "$leave_peer_id"; then
455+
:
456+
else
457+
shard_owner_rc=$?
458+
if [ "$shard_owner_rc" -eq 1 ]; then
459+
echo "INFO: shard ${shard_id} in ${col_name} moved off peer ${leave_peer_id} after failed submit"
460+
return 0
461+
fi
462+
return 1
416463
fi
417464

418465
echo "ERROR: failed to initiate shard ${shard_id} move in ${col_name}"

0 commit comments

Comments
 (0)