Skip to content

Commit 06f8d9e

Browse files
bennotienogithub-actions[bot]Tobias Pfaffelmoser
authored
feat: auto-fetch-collections-from-s3 (#109)
* feat: auto-fetch-collections-from-s3 * update documentation * Update ConfigMap with latest script changes * fix: harden qdrant restore script and align restore .env example (#110) Follow-up to feat/auto-fetch-collections-from-s3 with three small fixes surfaced during review. - track_collection: skip the read loop when the jq result is empty. Bash here-strings always append a newline, so `done <<< ""` would otherwise iterate once with an empty item and write a bogus `host,` line to $QDRANT_COLLECTIONS_FILE. Affects `get_coll` against an empty source. - generate_snapshot_file_from_s3: fix word order in the non-error exit message so the filename lands in the correct position. - UPGRADE-OPERATIONS.md restore example: set QDRANT_SOURCE_HOSTS="" to match the new restore-from-S3-only contract used by the k8s manifests; move the port-forward note to QDRANT_RESTORE_HOSTS where it belongs. - Sync configmap-script.yaml via config_map_updater.sh. --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Tobias Pfaffelmoser <tobias.pfaffelmoser@ext.aleph-alpha.com>
1 parent 33897d7 commit 06f8d9e

8 files changed

Lines changed: 220 additions & 28 deletions

UPGRADE-OPERATIONS.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -737,11 +737,11 @@ nano .env
737737
# Qdrant API key
738738
export QDRANT_API_KEY="your-api-key-here"
739739

740-
# Source hosts (where snapshots are stored)
741-
# Port forward qdrant service to local
742-
export QDRANT_SOURCE_HOSTS="http://localhost:6333"
740+
# Source hosts (leave empty for restore - collections are discovered from S3)
741+
export QDRANT_SOURCE_HOSTS=""
743742

744743
# Restore hosts (target Qdrant instances)
744+
# Port forward qdrant service to local
745745
export QDRANT_RESTORE_HOSTS="http://localhost:6334"
746746

747747
# S3 configuration (for fetching snapshots)
@@ -750,8 +750,8 @@ export QDRANT_S3_ACCESS_KEY_ID="your-access-key"
750750
export QDRANT_S3_SECRET_ACCESS_KEY="your-secret-key"
751751
export QDRANT_S3_BUCKET_NAME="qdrant-snapshots"
752752

753-
# Auto-discover peers
754-
export GET_PEERS_FROM_CLUSTER_INFO="true"
753+
# Disable Auto-discover of peers
754+
export GET_PEERS_FROM_CLUSTER_INFO="false"
755755

756756
# Timeout settings
757757
export CURL_TIMEOUT="1800"

qdrant-backup-restore/k8s/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,9 @@ kubectl -n <namespace> logs -lapp=qdrant-backup -f
163163
Update the following environment varibles accordingly;
164164

165165
1. Update the `secretKeyRef.name` on all `env:` entries under `env.[*].valueFrom` to the name of the kubernetes secrets deployed when configuring Qdrant.
166-
2. Update the `QDRANT_SOURCE_HOSTS` to the kubernetes service domain of the source Qdrant deployment.
166+
2. Update the `QDRANT_SOURCE_HOSTS` to empty `""`.
167167
3. Update the `QDRANT_RESTORE_HOSTS` to the target qdrant cluster service domain.
168-
4. `GET_PEERS_FROM_CLUSTER_INFO` should be `true` to discover qdrant cluster peers when backing up collections.
168+
4. `GET_PEERS_FROM_CLUSTER_INFO` should be `false` during restore.
169169
5. `CURL_TIMEOUT` is set at `3000` seconds. This is the max time curl will wait for request to complete. Its increased in scenarios where restoration takes a while.
170170
6. `QDRANT_S3_LINK_EXPIRY_DURATION` is set at `3600`. This is the duration that an s3 presigned url will be active. The url is used during the recovery process.
171171
7. `QDRANT_WAIT_ON_TASK` is set as `true`. This configuation make restoration process synchronous meaning 'wait for snapshot process to finish successfully before moving on'. Its used during backup and recovery.

qdrant-backup-restore/k8s/backup-job.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,30 @@ spec:
2626
- -c
2727
- "cd scripts && ./qdrant_backup_recovery.sh create_snap"
2828
env:
29+
- name: QDRANT_S3_ACCESS_KEY_ID
30+
valueFrom:
31+
secretKeyRef:
32+
name: qdrant-credentials-minio # change this to your qdrant kubernetes secret name
33+
key: QDRANT_S3_ACCESS_KEY_ID
34+
- name: QDRANT_S3_SECRET_ACCESS_KEY
35+
valueFrom:
36+
secretKeyRef:
37+
name: qdrant-credentials-minio # change this to your qdrant kubernetes secret name
38+
key: QDRANT_S3_SECRET_ACCESS_KEY
39+
- name: QDRANT_S3_BUCKET_NAME
40+
valueFrom:
41+
secretKeyRef:
42+
name: qdrant-credentials-minio # change this to your qdrant kubernetes secret name
43+
key: QDRANT_S3_BUCKET_NAME
2944
- name: QDRANT_API_KEY
3045
valueFrom:
3146
secretKeyRef:
3247
name: qdrant-credentials-minio # change this to your qdrant kubernetes secret name
3348
key: QDRANT_API_KEY
49+
- name: QDRANT_S3_ENDPOINT_URL
50+
value: "http://minio.default.svc.cluster.local:9000" # change this to your s3 endpoint url
51+
- name: MC_CONFIG_DIR
52+
value: "mc" # sets the configuration location for mc S3 client
3453
- name: QDRANT_SOURCE_HOSTS
3554
value: "http://qdrant-headless:6333" # change this to your qdrant kubernetes headless service name
3655
- name: QDRANT_RESTORE_HOSTS
@@ -39,6 +58,8 @@ spec:
3958
value: "true"
4059
- name: CURL_TIMEOUT
4160
value: "3000" # increase if more time is needed for a single backup
61+
- name: BACKUP_COLLECTION_ALIASES_ON_S3
62+
value: "true"
4263
resources:
4364
requests:
4465
cpu: "100m"

qdrant-backup-restore/k8s/configmap-script.yaml

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,11 @@ data:
8383
fi
8484
_printf "fetching collections!\n"
8585
86-
local host="${source_hosts[0]}"
86+
local host="${source_hosts[0]:-}"
87+
if [[ -z "$host" ]]; then
88+
_printf "source host is not available for fetching collections. Please ensure QDRANT_SOURCE_HOSTS is set correctly in your environment variables.\n"
89+
exit 1
90+
fi
8791
8892
collections_count=0
8993
@@ -108,6 +112,13 @@ data:
108112
local result="$2"
109113
_result=$(jq -c '.result.collections // [] | .[]' <<< "$result")
110114
115+
# Guard against the bash `<<<` quirk: a here-string of an empty value still
116+
# produces a single newline, so the loop below would iterate once with an
117+
# empty item and write a bogus `host,` line to $QDRANT_COLLECTIONS_FILE.
118+
if [[ -z "$_result" ]]; then
119+
return
120+
fi
121+
111122
while read -r item; do
112123
local collection_name=""
113124
collection_name=$(jq -r '.name' <<< "$item")
@@ -141,7 +152,11 @@ data:
141152
142153
# creates and appends created collection snapshot from a qdrant node to $QDRANT_SNAPSHOTS_FILE
143154
track_created_collection_snapshot() {
144-
local host="${source_hosts[0]}"
155+
local host="${source_hosts[0]:-}"
156+
if [[ -z "$host" ]]; then
157+
_printf "source host is not available for fetching collection snapshots. Please ensure QDRANT_SOURCE_HOSTS is set correctly in your environment variables.\n"
158+
exit 1
159+
fi
145160
local collection_name="$1"
146161
local result=""
147162
local status=""
@@ -342,11 +357,61 @@ data:
342357
fi
343358
}
344359
360+
# discovers collection names from the S3 snapshots prefix and stores them in $QDRANT_COLLECTIONS_FILE
361+
fetch_collections_from_s3() {
362+
local prefix="snapshots"
363+
local result=""
364+
365+
_printf "fetching collections from s3 bucket %s under %s/\n" "$QDRANT_S3_BUCKET_NAME" "$prefix"
366+
367+
result=$(mc ls --json "$QDRANT_S3_ALIAS/$QDRANT_S3_BUCKET_NAME/$prefix/")
368+
369+
if [ "$result" = "" ]; then
370+
_printf "no collections found in s3 path: %s/%s/ ...skipping!\n" "$QDRANT_S3_BUCKET_NAME" "$prefix"
371+
return
372+
fi
373+
374+
collections_count=0
375+
_result=$(jq -c '.' <<< "$result")
376+
377+
while read -r item; do
378+
local status=""
379+
local object_key=""
380+
local collection_name=""
381+
382+
status=$(jq -r '.status?' <<< "$item")
383+
if [ "$status" != "success" ]; then
384+
_printf "failed to list collections from s3, got this instead %s\n" "${item//[[:space:]]/}"
385+
continue
386+
fi
387+
388+
object_key=$(jq -r '.key' <<< "$item") # e.g. "my_collection/" from mc ls on snapshots/
389+
object_key="${object_key%/}" # strip trailing slash (folder marker)
390+
collection_name="${object_key##*/}" # use last path segment as collection name
391+
392+
if [[ -z "$collection_name" || "$collection_name" == "$prefix" ]]; then
393+
continue
394+
fi
395+
396+
((collections_count=collections_count+1))
397+
printf ',%s\n' "$collection_name" >> $QDRANT_COLLECTIONS_FILE
398+
done <<< "$_result"
399+
400+
_printf "%s file updated, found %d collection(s) from s3!\n" "$QDRANT_COLLECTIONS_FILE" "$collections_count"
401+
collections_count=0
402+
}
403+
345404
# generates a list of collection snapshots from a s3 and appends it to $QDRANT_SNAPSHOTS_FILE
346405
generate_snapshot_file_from_s3() {
347406
local datetime="${1:-}"
407+
348408
if [[ ! -s "$QDRANT_COLLECTIONS_FILE" ]]; then
349-
get_collections
409+
fetch_collections_from_s3
410+
fi
411+
412+
if [ ! -f "$QDRANT_COLLECTIONS_FILE" ]; then
413+
_printf "non error exit since file %s does not exist.\n" "$QDRANT_COLLECTIONS_FILE"
414+
exit 0
350415
fi
351416
352417
snapshot_count=0
@@ -496,7 +561,11 @@ data:
496561
497562
# gets the collection aliases from a qdrant node and appends it to $QDRANT_COLLECTION_ALIASES file
498563
get_collection_aliases() {
499-
local host="${source_hosts[0]}"
564+
local host="${source_hosts[0]:-}"
565+
if [[ -z "$host" ]]; then
566+
_printf "source host is not available for fetching collection aliases. Please ensure QDRANT_SOURCE_HOSTS is set correctly in your environment variables.\n"
567+
exit 1
568+
fi
500569
local result=""
501570
local status=""
502571
@@ -597,7 +666,11 @@ data:
597666
# restores collection aliases to a qdrant node and appends progress to $QDRANT_ALIAS_RECOVERY_HISTORY_FILE
598667
recover_collection_aliases() {
599668
600-
local host="${restore_hosts[0]}"
669+
local host="${restore_hosts[0]:-}"
670+
if [[ -z "$host" ]]; then
671+
_printf "restore host is not available for recovering collection aliases. Please ensure QDRANT_RESTORE_HOSTS is set correctly in your environment variables.\n"
672+
exit 1
673+
fi
601674
602675
603676
if [ "$BACKUP_COLLECTION_ALIASES_ON_S3" = "true" ]; then
@@ -667,7 +740,12 @@ data:
667740
668741
# gets qdrant peer host url using cluster info endpoint. Sets the port to $QDRANT_HTTP_PORT the http port.
669742
get_peers_from_cluster_info() {
670-
local host="${source_hosts[0]}"
743+
local host="${source_hosts[0]:-}"
744+
if [[ -z "$host" ]]; then
745+
_printf "source host is not available. Please ensure QDRANT_SOURCE_HOSTS is set correctly in your environment variables. If restoring snapshots set QDRANT_SOURCE_HOSTS and GET_PEERS_FROM_CLUSTER_INFO to empty string.\n"
746+
exit 1
747+
fi
748+
671749
result=$(_curl GET "$host/cluster" --header "api-key: $QDRANT_API_KEY")
672750
entries=$(jq -r '.result.peers // {}' <<< "$result")
673751
peer_uri_entries=$(jq -r 'to_entries[] | "\(.key) \(.value.uri)"' <<< "$entries")

qdrant-backup-restore/k8s/configmap-snapshot.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ metadata:
66
app: qdrant-snapshots-file
77
component: script
88
data:
9+
# this is used to provide a list of snapshots to restore (comma separated list of collection names and snapshot names, host is optional (first column is host))
910
snapshots: |
1011
,_default_256,_default_256-5528114518172461-2026-01-29-11-44-26.snapshot
1112
,midlib,midlib-567156434043892-2026-01-29-11-44-27.snapshot
13+
14+
# this is used to provide a list of collections to restore (comma separated list of collection names, host is optional (first column is host))
15+
collections: |
16+
,_default_256
17+
,_default_128

qdrant-backup-restore/k8s/restore-job.yaml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,21 @@ spec:
4747
name: qdrant-credentials-minio
4848
key: QDRANT_API_KEY
4949
- name: QDRANT_SOURCE_HOSTS
50-
value: "http://qdrant-headless:6333" # change this to your source qdrant kubernetes headless service name
50+
value: "" # leave empty if source host is not available
5151
- name: QDRANT_S3_ENDPOINT_URL
5252
value: "http://minio.default.svc.cluster.local:9000" # change this to your s3 endpoint url
5353
- name: QDRANT_RESTORE_HOSTS
54-
value: "http://qdrantr2-headless:6333" # change this to your target qdrant kubernetes headless service name
54+
value: "http://qdrant-headless:6333" # change this to your target qdrant kubernetes headless service name
5555
- name: GET_PEERS_FROM_CLUSTER_INFO
56-
value: "true"
56+
value: "false"
5757
- name: QDRANT_SNAPSHOT_DATETIME_FILTER
58-
value: "" # change tis to filter snapshts based on date (glob matching is used)
58+
value: "" # set this to filter snapshots based on date (glob matching is used) e.g "2026-01-29" = all snapshots in 29th January 2026, "2026-01" = all snapshots in January 2026.
5959
- name: CURL_TIMEOUT
6060
value: "3000" # increase if more time is needed for a single restore
6161
- name: MC_CONFIG_DIR
6262
value: "mc" # sets the configuration location for mc S3 client
63+
- name: BACKUP_COLLECTION_ALIASES_ON_S3
64+
value: "true"
6365
resources:
6466
requests:
6567
cpu: "100m"

qdrant-backup-restore/k8s/restore-specific-snapshot-job.yaml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,21 @@ spec:
4747
name: qdrant-credentials-minio
4848
key: QDRANT_API_KEY
4949
- name: QDRANT_SOURCE_HOSTS
50-
value: "http://qdrant-headless:6333" # change this to your source qdrant kubernetes headless service name
50+
value: "" # leave empty if source host is not available
5151
- name: QDRANT_S3_ENDPOINT_URL
5252
value: "http://minio.default.svc.cluster.local:9000" # change this to your s3 endpoint url
5353
- name: QDRANT_RESTORE_HOSTS
54-
value: "http://qdrantr2-headless:6333" # change this to your target qdrant kubernetes headless service name
54+
value: "http://qdrant-headless:6333" # change this to your target qdrant kubernetes headless service name
5555
- name: GET_PEERS_FROM_CLUSTER_INFO
56-
value: "true"
56+
value: "false"
5757
- name: CURL_TIMEOUT
5858
value: "3000" # increase if more time is needed for a single restore
5959
- name: QDRANT_SNAPSHOT_DATETIME_FILTER
6060
value: "" # change tis to filter snapshts based on date (glob matching is used)
6161
- name: MC_CONFIG_DIR
6262
value: "mc" # sets the configuration location for mc S3 client
63+
- name: BACKUP_COLLECTION_ALIASES_ON_S3
64+
value: "true"
6365
resources:
6466
requests:
6567
cpu: "100m"
@@ -71,9 +73,14 @@ spec:
7173
- name: qdrant-backup-restore-script
7274
mountPath: /scripts/qdrant_backup_recovery.sh
7375
subPath: qdrant_backup_recovery.sh
76+
# this is used to restore specific collections
7477
- name: qdrant-snapshots-file
75-
mountPath: /scripts/snapshots
76-
subPath: snapshots
78+
mountPath: /scripts/collections
79+
subPath: collections
80+
# this is used to restore specific snapshots
81+
# - name: qdrant-snapshots-file
82+
# mountPath: /scripts/snapshots
83+
# subPath: snapshots
7784
- name: scripts
7885
mountPath: /scripts
7986
volumes:

0 commit comments

Comments
 (0)