runbook : rotation cles Google Bifrost bloquee par max_retries=0
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
# Bifrost -- rotation entre cles provider bloquee par max_retries=0
|
||||
|
||||
**Instance auteur** : hermes-tt (diagnostic Claude, session nyora-notes-tt)
|
||||
**Date** : 2026-08-05
|
||||
**Tags** : bifrost, llm-gateway, rate-limit, google
|
||||
**Statut** : valide (fix applique, limite connue documentee)
|
||||
|
||||
---
|
||||
|
||||
## Probleme
|
||||
|
||||
`search_onedrive_archive` et le backfill embeddings de nyora-notes-tt echouaient en continu
|
||||
avec `429 Too Many Requests` sur `POST /v1/embeddings`. Message Google exact :
|
||||
`Quota exceeded for metric: generativelanguage.googleapis.com/embed_content_free_tier_requests,
|
||||
limit: 1000, model: gemini-embedding-1.0`.
|
||||
|
||||
3 cles Google etaient pourtant configurees dans Bifrost (`google-key1/2/3`), toutes actives,
|
||||
poids egal, `allow_all_keys=1` sur la VK consommatrice -- en theorie de quoi absorber 3x le quota.
|
||||
|
||||
---
|
||||
|
||||
## Contexte et contraintes
|
||||
|
||||
Bifrost (`config.db`, table `config_providers`) stocke `network_config_json` par provider,
|
||||
incluant `max_retries`. Table `governance_virtual_key_provider_configs` confirme les
|
||||
permissions cote VK (`allow_all_keys`), mais la rotation reelle entre cles au sein d'un
|
||||
meme provider est geree par la couche retry, pas par les permissions VK seules.
|
||||
|
||||
---
|
||||
|
||||
## Ce qui NE fonctionne PAS
|
||||
|
||||
| Tentative | Erreur obtenue | Raison de l'echec |
|
||||
|-----------|----------------|-------------------|
|
||||
| Configurer 3 cles + `allow_all_keys=1` sur la VK | 429 systematique, une seule cle tentee | `allow_all_keys` autorise l'usage des cles mais ne force pas d'essai multiple -- ca depend de `max_retries` |
|
||||
| Attendre / relancer la requete | Meme 429, meme cle a chaque fois | Sans retry, Bifrost ne re-tire jamais une autre cle du pool |
|
||||
|
||||
Verification en base (`logs.db`, colonne `attempt_trail`) : avant fix,
|
||||
`[{"attempt":0,"key_id":"...","fail_reason":"rate_limit_error","triggered_rotation":false}]`
|
||||
-- un seul essai, `triggered_rotation` a `false`.
|
||||
|
||||
---
|
||||
|
||||
## Solution validee
|
||||
|
||||
`max_retries` etait a `0` dans `network_config_json` du provider `google`
|
||||
(`config_providers.id=46`) -- valeur par defaut sur TOUS les providers de cette instance
|
||||
Bifrost (opencode, openrouter, google, nvidia_nim, groq), probablement pour eviter de
|
||||
multiplier latence/cout ailleurs. Mis a 2 specifiquement pour `google` (pas de changement
|
||||
global), avec `retry_backoff_initial=1` et `retry_backoff_max=10`.
|
||||
|
||||
```bash
|
||||
# 1. Backup obligatoire avant toute modification
|
||||
cp /volume1/docker/bifrost/data/config.json /volume1/docker/bifrost/data/config.json.bak-$(date +%Y%m%d-%H%M%S)
|
||||
cp /volume1/docker/bifrost/data/config.db /volume1/docker/bifrost/data/config.db.bak-$(date +%Y%m%d-%H%M%S)
|
||||
|
||||
# 2. config.json (fichier lu au BOOT -- piege connu, sans ca le fix ne survit pas au restart)
|
||||
python3 -c "
|
||||
import json
|
||||
path = '/volume1/docker/bifrost/data/config.json'
|
||||
d = json.load(open(path))
|
||||
d['providers']['google']['network_config']['max_retries'] = 2
|
||||
d['providers']['google']['network_config']['retry_backoff_initial'] = 1
|
||||
d['providers']['google']['network_config']['retry_backoff_max'] = 10
|
||||
json.dump(d, open(path, 'w'), indent=2, ensure_ascii=False)
|
||||
"
|
||||
|
||||
# 3. config.db (etat vivant lu par le process en cours)
|
||||
python3 -c "
|
||||
import sqlite3, json
|
||||
c = sqlite3.connect('/volume1/docker/bifrost/data/config.db')
|
||||
cur = c.cursor()
|
||||
cur.execute('SELECT network_config_json FROM config_providers WHERE id=46')
|
||||
nc = json.loads(cur.fetchone()[0])
|
||||
nc.update({'max_retries': 2, 'retry_backoff_initial': 1, 'retry_backoff_max': 10})
|
||||
cur.execute('UPDATE config_providers SET network_config_json=? WHERE id=46', [json.dumps(nc)])
|
||||
c.commit()
|
||||
"
|
||||
|
||||
# 4. Redemarrage (config.json + config.db doivent etre coherents AVANT le restart)
|
||||
docker restart bifrost
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
# Apres une requete embeddings ayant echoue, verifier attempt_trail dans logs.db
|
||||
python3 -c "
|
||||
import sqlite3, json
|
||||
c = sqlite3.connect('/volume1/docker/bifrost/data/logs.db')
|
||||
cur = c.cursor()
|
||||
cur.execute(\"SELECT attempt_trail FROM logs WHERE provider='google' ORDER BY timestamp DESC LIMIT 1\")
|
||||
print(cur.fetchone()[0])
|
||||
"
|
||||
# Resultat attendu : plusieurs entrees "attempt":0,1,2... avec des key_id DIFFERENTS
|
||||
# et "triggered_rotation":true sur les echecs intermediaires.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pieges specifiques DSM / NAS
|
||||
|
||||
- **Ne pas se fier a `allow_all_keys` seul** -- ce flag autorise, il ne force pas d'essai.
|
||||
La rotation reelle depend de `max_retries` > 0 sur le provider.
|
||||
- **config.json ET config.db doivent etre synchronises** -- piege deja documente ailleurs
|
||||
(rechargement des cles au restart), s'applique aussi a `max_retries`.
|
||||
- **Limite residuelle non resolue par ce fix** : si les N cles configurees partagent le
|
||||
meme quota (meme projet / meme compte Google Cloud, ce qui semble etre le cas ici --
|
||||
les 3 cles ont echoue ensemble apres le fix), la rotation ne cree AUCUNE capacite
|
||||
supplementaire. Rotation != pool de quota independant. A verifier cle par cle si le
|
||||
probleme se reproduit apres ce fix (voir `attempt_trail` : si les 3 `key_id` echouent
|
||||
systematiquement ensemble, c'est un quota partage, pas un bug de rotation).
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- Google AI rate limits : https://ai.google.dev/gemini-api/docs/rate-limits
|
||||
- Session diagnostic complete : NyoraNotes, note "nyora-notes-tt : 3 bugs corriges..." (05/08/2026)
|
||||
Reference in New Issue
Block a user