Co to znamená
Lease přechází na jiného, zatímco proces, který jej držel, stále běží a je zdravý. Obvyklou příčinou je přestavba: volba přečte zaznamenaný spustitelný soubor leasu, zjistí, že soubor, který jmenuje, byl nahrazen, a server běžící s kódem, který už není na disku, považuje za zastaralý. Nahrazený proces se nezabíjí — klient uprostřed odpovědi by neměl přijít o server, protože někdo spustil cargo build — takže si ponechává svůj socket, nástěnku peerů, historii provádění a generaci vrstvy, kterou načetl před hodinami.
Přestává být tím serverem. Jeho index odpovídá leaseholder: false; lease::probe, kterým prochází každý čtenář zapamatované adresy, ji odmítá; nový initialize je odmítnut s 409 lease_lost jmenujícím launcher jako cestu k aktuálnímu serveru; a jeho vlastní health.report varuje, že server, který popisuje, je jiný proces. Relace, které už měl, zůstávají jeho vlastní a proces končí spolu s nimi.
Princip
src/lease.rs zaznamená ztrátu jednou v lost(): příznak platný pro celý proces, který se nikdy neruší, publikovaný dokument leasu je vyčištěn a obsluha signálu dostane pokyn přestat odpojovat soubor, který je nyní někoho jiného. was_lost() je jediná otázka, kterou klade zbytek programu, a záměrně to není held().is_none() — proces, který lease nikdy nezískal (test, přemostěný klient, jednorázový příkaz), musí dál obsluhovat toho, kdo se zeptal. src/shared.rs je to, co si toho všimne: vlastní čtečka serveru porovná token souboru s vlastním každých REAP_INTERVAL a při prvním rozdílu zavolá lost().
Tři odpovědi plynou z toho jednoho faktu. src/http/router.rs zapisuje leaseholder do indexu pod lease::LEASEHOLDER_KEY a lease::probe ho čte pod toutéž konstantou, takže zapisovač a čtenář se nemohou rozejít. src/http/mcp.rs odmítne initialize, který nenese id relace, když lease zmizel. src/capability/builtin/health.rs přidá nález ke kontrole server a varuje, protože stav, který hlásí, je přesný o procesu, který není tento.
Server příliš starý na to, aby na otázku vůbec odpověděl, sonda přijme. Na tomto endpointu jej nelze odlišit od aktuálního a odmítnout jej by bylo horší selhání: živý server považovaný za mrtvý se převezme, a tak jeden checkout přijde ke dvěma.
Jak to vidět
just build
# start a server and note its address
apps/majordomus-cli/target/debug/majordomus mcp < /dev/null &
url="$(sed -n 's/.*listening on \(http:\/\/127\.0\.0\.1:[0-9]*\).*/\1/p' <<<"$(jobs)" )"
curl -s "$url/" | jq .leaseholder # true
# take the lease away, as a rebuild's election would
rm .ai/local/state/mcp/server.json
sleep 1
curl -s "$url/" | jq .leaseholder # false
curl -s -o /dev/null -w '%{http_code}\n' -X POST "$url/mcp" \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"late","version":"0"}}}'
# 409
curl -s "$url/api/v1/health" | jq '.checks[] | select(.id=="server") | .findings'Co nepokrývá
Klientovi, který už drží zastaralou adresu, se řekne, že tam žádný server není; není přesměrován na ten, který tam je. Nalezení aktuálního serveru je volba launcheru, kam tato znalost patří. Server starší než toto pole sonda přijme, z důvodu výše, takže záruka platí od této verze dále, ne pro každý proces na stroji. A nic zde neprodlužuje ani nezkracuje život nahrazeného serveru: skončí, když skončí jeho poslední relace, což u nečinného klienta je časový limit nečinnosti relace.
Proč existuje
Změřeno v tomto repozitáři 2026-09-10. Za jeden checkout odpovídaly dva servery: držitel lease a ten, jehož spustitelný soubor byl o hodinu dříve nahrazen. Oba hlásily stejné jméno, stejné repository_id, stejné počty objektů a schopností — a různé otisky registru. Relace, jejíž prostředí neslo starší adresu, četla nástěnku peerů s jedním peerem, zatímco nástěnka, kterou sdílely všechny ostatní relace, měla dva, a žádná odpověď neříkala, kterou nástěnku čte. Převzetí fungovalo přesně podle návrhu; nikdo nevlastnil jeho následky. Doktrína je project.shared-server-resilience a apps/majordomus-cli/tests/lease_lost.rs dokazuje stav před i po v jednom procesu, protože lease, který byl převzat, se nevrací.