Add index backend health probe with fs fallback

This commit is contained in:
Carl Niklas Rydberg 2026-02-07 20:45:39 +01:00
parent c10e7e04e6
commit 281f775ebb
3 changed files with 126 additions and 18 deletions

View file

@ -21,6 +21,10 @@ cp config/env.example config/env.local
```sh
./scripts/dev_start_daemon.sh
```
`dev_start_daemon.sh` initializes the store for the selected backend and, when
`STORE_BACKEND=index`, runs a quick startup write probe. If index writes are
unhealthy, it automatically falls back to `fs` (configurable via
`INDEX_BACKEND_PROBE` and `INDEX_BACKEND_FALLBACK` in `config/env.local`).
3. Run startup checks against the daemon socket:

View file

@ -6,6 +6,10 @@ SPACE="app1"
# Optional daemon startup defaults (used by scripts/dev_start_daemon.sh)
STORE_ROOT=".amduat-asl"
STORE_BACKEND="index"
# For index backend, run a startup write probe and fallback to fs if broken.
INDEX_BACKEND_PROBE="1"
INDEX_BACKEND_FALLBACK="fs"
FS_FALLBACK_STORE_ROOT=".amduat-asl-fs"
# AMDUATD_BIN="/path/to/amduatd"
# ASL_BIN="/path/to/amduat-asl"

View file

@ -2,6 +2,13 @@
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
override_store_root="${STORE_ROOT:-}"
override_store_backend="${STORE_BACKEND:-}"
override_space="${SPACE:-}"
override_sock="${SOCK:-}"
override_amduatd_bin="${AMDUATD_BIN:-}"
override_asl_bin="${ASL_BIN:-}"
ENV_FILE="${ROOT_DIR}/config/env.local"
if [[ ! -f "${ENV_FILE}" ]]; then
ENV_FILE="${ROOT_DIR}/config/env.example"
@ -9,16 +16,24 @@ fi
# shellcheck source=/dev/null
source "${ENV_FILE}"
if [[ -n "${override_store_root}" ]]; then STORE_ROOT="${override_store_root}"; fi
if [[ -n "${override_store_backend}" ]]; then STORE_BACKEND="${override_store_backend}"; fi
if [[ -n "${override_space}" ]]; then SPACE="${override_space}"; fi
if [[ -n "${override_sock}" ]]; then SOCK="${override_sock}"; fi
if [[ -n "${override_amduatd_bin}" ]]; then AMDUATD_BIN="${override_amduatd_bin}"; fi
if [[ -n "${override_asl_bin}" ]]; then ASL_BIN="${override_asl_bin}"; fi
STORE_ROOT="${STORE_ROOT:-${ROOT_DIR}/.amduat-asl}"
STORE_BACKEND="${STORE_BACKEND:-index}"
SPACE="${SPACE:-app1}"
SOCK="${SOCK:-${ROOT_DIR}/amduatd.sock}"
if [[ "${STORE_ROOT}" != /* ]]; then
STORE_ROOT="${ROOT_DIR}/${STORE_ROOT}"
fi
if [[ "${SOCK}" != /* ]]; then
SOCK="${ROOT_DIR}/${SOCK}"
fi
INDEX_BACKEND_PROBE="${INDEX_BACKEND_PROBE:-1}"
INDEX_BACKEND_FALLBACK="${INDEX_BACKEND_FALLBACK:-fs}"
FS_FALLBACK_STORE_ROOT="${FS_FALLBACK_STORE_ROOT:-${STORE_ROOT}-fs}"
if [[ "${STORE_ROOT}" != /* ]]; then STORE_ROOT="${ROOT_DIR}/${STORE_ROOT}"; fi
if [[ "${SOCK}" != /* ]]; then SOCK="${ROOT_DIR}/${SOCK}"; fi
if [[ "${FS_FALLBACK_STORE_ROOT}" != /* ]]; then FS_FALLBACK_STORE_ROOT="${ROOT_DIR}/${FS_FALLBACK_STORE_ROOT}"; fi
# Try common local build paths first, then PATH.
AMDUATD_BIN="${AMDUATD_BIN:-}"
@ -60,19 +75,104 @@ if [[ -z "${ASL_BIN}" || ! -x "${ASL_BIN}" ]]; then
exit 1
fi
mkdir -p "${STORE_ROOT}"
if [[ "${STORE_BACKEND}" == "index" ]]; then
if ! "${ASL_BIN}" index state --root "${STORE_ROOT}" >/dev/null 2>&1; then
echo "initializing index-backed ASL store at ${STORE_ROOT}" >&2
"${ASL_BIN}" index init --root "${STORE_ROOT}"
init_store() {
local backend="$1"
local root="$2"
mkdir -p "${root}"
if [[ "${backend}" == "index" ]]; then
if ! "${ASL_BIN}" index state --root "${root}" >/dev/null 2>&1; then
echo "initializing index-backed ASL store at ${root}" >&2
"${ASL_BIN}" index init --root "${root}"
fi
else
if ! "${ASL_BIN}" log inspect --root "${STORE_ROOT}" >/dev/null 2>&1; then
echo "initializing ASL store at ${STORE_ROOT}" >&2
"${ASL_BIN}" init --root "${STORE_ROOT}"
if ! "${ASL_BIN}" log inspect --root "${root}" >/dev/null 2>&1; then
echo "initializing ASL store at ${root}" >&2
"${ASL_BIN}" init --root "${root}"
fi
fi
}
wait_ready() {
local sock="$1"
for _ in $(seq 1 80); do
if [[ -S "${sock}" ]] && curl --globoff --silent --show-error --unix-socket "${sock}" "http://localhost/v2/readyz" >/dev/null 2>&1; then
return 0
fi
sleep 0.1
done
return 1
}
probe_index_write_path() {
local sock="$1"
local space="$2"
local run_id
run_id="$(date +%s)"
local doc="probe-doc-${run_id}"
local topic="probe-topic-${run_id}"
local payload
payload="$(cat <<JSON
{"idempotency_key":"probe-${run_id}","mode":"continue_on_error","nodes":[{"name":"${doc}"},{"name":"${topic}"}],"edges":[{"subject":"${doc}","predicate":"ms.within_domain","object":"${topic}","provenance":{"source_uri":"urn:probe","extractor":"dev-start","observed_at":1,"ingested_at":2,"trace_id":"probe-${run_id}"}}]}
JSON
)"
local out
out="$(curl --globoff --silent --show-error --unix-socket "${sock}" \
-H "Content-Type: application/json" \
-H "X-Amduat-Space: ${space}" \
-X POST --data-binary "${payload}" \
"http://localhost/v2/graph/batch")" || return 1
if [[ "${out}" == *'"ok":true'* ]]; then
return 0
fi
echo "index probe response: ${out}" >&2
return 1
}
run_daemon_foreground() {
local backend="$1"
local root="$2"
echo "starting amduatd: root=${root} sock=${SOCK} backend=${backend} space=${SPACE}" >&2
exec "${AMDUATD_BIN}" --root "${root}" --sock "${SOCK}" --store-backend "${backend}" --space "${SPACE}"
}
init_store "${STORE_BACKEND}" "${STORE_ROOT}"
if [[ "${STORE_BACKEND}" != "index" || "${INDEX_BACKEND_PROBE}" != "1" ]]; then
run_daemon_foreground "${STORE_BACKEND}" "${STORE_ROOT}"
fi
echo "starting amduatd: root=${STORE_ROOT} sock=${SOCK} backend=${STORE_BACKEND} space=${SPACE}" >&2
exec "${AMDUATD_BIN}" --root "${STORE_ROOT}" --sock "${SOCK}" --store-backend "${STORE_BACKEND}" --space "${SPACE}"
echo "starting amduatd (probe mode): root=${STORE_ROOT} sock=${SOCK} backend=${STORE_BACKEND} space=${SPACE}" >&2
"${AMDUATD_BIN}" --root "${STORE_ROOT}" --sock "${SOCK}" --store-backend "${STORE_BACKEND}" --space "${SPACE}" &
daemon_pid=$!
cleanup_probe() {
kill "${daemon_pid}" >/dev/null 2>&1 || true
}
trap cleanup_probe EXIT
fallback_to_fs=0
if ! wait_ready "${SOCK}"; then
echo "index backend startup/readiness failed" >&2
fallback_to_fs=1
elif ! probe_index_write_path "${SOCK}" "${SPACE}"; then
echo "index backend write probe failed" >&2
fallback_to_fs=1
fi
if [[ "${fallback_to_fs}" == "0" ]]; then
trap - EXIT
wait "${daemon_pid}"
exit $?
fi
kill "${daemon_pid}" >/dev/null 2>&1 || true
wait "${daemon_pid}" >/dev/null 2>&1 || true
trap - EXIT
if [[ "${INDEX_BACKEND_FALLBACK}" != "fs" ]]; then
echo "set INDEX_BACKEND_FALLBACK=fs to auto-fallback, or INDEX_BACKEND_PROBE=0 to disable probe" >&2
exit 1
fi
echo "falling back to fs backend: root=${FS_FALLBACK_STORE_ROOT}" >&2
init_store "fs" "${FS_FALLBACK_STORE_ROOT}"
run_daemon_foreground "fs" "${FS_FALLBACK_STORE_ROOT}"