Skip to content

fix(controller): un 429 sur la sonde ne marque plus la connexion en echec - #253

Merged
BryanFRD merged 1 commit into
mainfrom
fix/connection-rate-limit-is-a-retry
Sep 6, 2026
Merged

fix(controller): un 429 sur la sonde ne marque plus la connexion en echec#253
BryanFRD merged 1 commit into
mainfrom
fix/connection-rate-limit-is-a-retry

Conversation

@BryanFRD

@BryanFRD BryanFRD commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Le pendant, pour les FerrVaultConnection, de ce que #249 a fait pour les FerrVaultSecret.

Ce qui s'est passé en production

25 connexions sur 33 figées sur Ready=False, motif Unreachable, message « ferrvault api error 429 ». Les instances FerrVault répondaient normalement, et tous les FerrVaultSecret dépendant de ces connexions ont été entraînés avec elles.

La cause est corrigée côté serveur — FerrLabs/FerrVault-Cloud#874 sort /healthz du limiteur, parce que la sonde n'étant pas authentifiée elle consommait un seau d'IP partagé par les 31 sondes du même pod. Vérifié en production : les 429 sont retombés à zéro.

Mais le raisonnement restait faux ici : toute autre saturation reproduirait le symptôme.

Le correctif

Un 429 sort tôt, avec un report de 20 secondes, et la condition est laissée telle quelle : une connexion prête le reste, une connexion en échec garde la cause de son échec. LastCheckedAt n'est pas touché non plus — l'horodater sur un contrôle qui n'a rien appris serait trompeur.

probe rend désormais l'erreur en plus de son message. C'était nécessaire : un premier jet tentait IsRateLimited(errors.New(message)), ce qui ne peut rien reconnaître puisqu'une chaîne perd le type. La distinction ne se fait que sur l'erreur elle-même.

Ce que le correctif ne fait pas

Il ne rend pas les vraies pannes silencieuses. Un 500, un 401 ou un Secret de jeton absent continuent d'écrire la condition : une connexion cassée qui resterait verte serait l'inverse exact du défaut corrigé, et bien pire. Le test couvre les cinq cas.

Vérification, et sa limite

Go n'est pas installé sur la machine où ceci est écrit, donc ni go build ni go test n'ont tourné localement. Je m'en remets à la CI et je le signale plutôt que de le passer sous silence.

Vérifié à la main :

  • les quatre return de probe portent bien quatre valeurs, cohérentes avec la nouvelle signature ;
  • probe n'a qu'un seul appelant, mis à jour ;
  • rateLimitRequeue n'est pas redéclaré : un premier jet en ajoutait une copie alors que la constante existe déjà dans le même paquet, ce qui n'aurait pas compilé ;
  • time et ferrvault sont déjà importés ;
  • errors.As sur nil renvoie false sans paniquer, donc le chemin nominal est sûr ;
  • les champs APIError{Status, Message} et AuthError{Kind, Message} du test correspondent aux définitions.

@ferrfleet ferrfleet Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Le tri est correct. Probe renvoie bien un *APIError{Status: 429} (internal/ferrvault/client.go:123) et isRetriable ne réessaie que les 5xx et les erreurs de transport, donc le 429 remonte typé jusqu'à IsRateLimited au lieu d'être avalé par la boucle de retry. Les quatre return de probe sont cohérents avec la nouvelle signature, et rateLimitRequeue (20s) est bien la constante déjà déclarée dans le paquet (ferrvaultsecret_controller.go:38). Rien de bloquant.

Nit : le test ne couvre pas ce que le PR change. TestOnlyRateLimitingSkipsTheConditionUpdate appelle ferrvault.IsRateLimited directement, sans jamais passer par Reconcile ni par probe. Supprimer intégralement le nouveau bloc if ferrvault.IsRateLimited(probeErr) laisse le test vert. Ses cinq cas recouvrent par ailleurs TestIsRateLimited (internal/ferrvault/client_test.go:325), qui affirme déjà 429 → true, autres erreurs et nilfalse.

Ce qui manque est le comportement affirmé par le titre du test : un httptest.Server répondant 429 sur /healthz, un fake client portant une connexion avec Ready=True et un LastCheckedAt connu, puis vérifier après Reconcile que la condition et l'horodatage sont inchangés et que Result.RequeueAfter == rateLimitRequeue. probe construit son client via ferrvault.New(conn.Spec.URL, token), donc pointer Spec.URL sur le serveur de test suffit : pas besoin d'injecter une factory comme côté secret. C'est le seul montage qui attraperait une régression sur la ligne ajoutée.

Nit : la sortie tôt n'appelle pas SetConnectionReady. Sur une connexion déjà réconciliée, la jauge garde sa valeur précédente, ce qui est cohérent avec « la condition n'est pas réécrite ». Mais la jauge vit dans le process : après un redémarrage de l'opérateur sous 429 persistant, ferrvault_connection_ready n'a aucune série pour les connexions concernées, et une alerte écrite sur == 0 reste muette. Contrairement au chemin secret, qui fait IncSyncError("RateLimited"), rien n'est compté ici, donc une limitation durable n'existe que dans les logs, alors que c'est précisément l'état où la condition est gelée. Un compteur sur ce chemin lèverait les deux points.

@BryanFRD
BryanFRD merged commit 6e9805f into main Sep 6, 2026
18 checks passed
@BryanFRD
BryanFRD deleted the fix/connection-rate-limit-is-a-retry branch September 6, 2026 08:24
ferrflow Bot added a commit that referenced this pull request Sep 6, 2026
## [5.2.7] - 2026-09-06

### Bug Fixes

- fix(controller): un 429 sur la sonde ne marque plus la connexion en echec (#253)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant