Écoute du serveur Cable sur une socket unix, via socket activation systemd - #1
Merged
Merged
Conversation
Replace cable_port and the two SSL options with a single cable_bind option, defaulting to a unix socket in the shared directory, and turn the socket unit on by default so the listening socket survives a restart of the server. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nothing in the gem depends on a specific web server, only on the socket path, so drop the nginx upstream example and the Nginx mention in the socket unit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The check only ran from cable:install, at the very end of the deploy. Run it from deploy:starting too, so an app still setting cable_port or one of the SSL options is stopped before anything is uploaded. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problème
Le serveur Cable écoute sur un port TCP, monté en dur sur
0.0.0.0:puma --no-config --port 28090. Deux conséquences :La gem contenait déjà tout le décor pour faire autrement (
Bind,cable_binds,cable.socket.erb,cable_enable_socket_service), hérité de capistrano-puma, mais ce chemin était mort ::cable_bindn'était jamais défini (ligne commentée) etpuma_optionsl'ignorait complètement. L'unité.socketse serait rendue sans aucunListenStream.Le Puma principal de nos apps, lui, tourne déjà sur une socket unix en socket activation (
shared/tmp/sockets/web.sock), donc le terrain est connu.Ce qui change
:cable_binddevient le seul réglage d'écoute. Il accepte une chaîne de bind Puma, ou un tableau :Par défaut, le serveur écoute donc sur une socket unix dans
shared/tmp/sockets.:cable_port,:cable_ssl_certificateet:cable_ssl_certificate_keydisparaissent, ainsi que les deux branches port/SSL depuma_options: le SSL n'est plus un cas particulier, c'est juste un bind.La socket activation systemd est activée par défaut. L'unité
.socketgarde l'écoute ouverte pendant le redémarrage du service : les connexions sont mises en attente dans le backlog au lieu d'être refusées, et le serveur démarre à la première requête s'il est arrêté. Les websockets tombent quand même à chaque redémarrage (le client JS reconnecte), mais plus de connexion refusée entre les deux.Au passage, sur ce chemin qui n'était donc jamais exécuté :
ListenStreamvaut maintenant l'adresse du bind telle quelle. L'ancienBind#localréécrivait0.0.0.0en127.0.0.1, ce qui empêchait justement Puma d'apparier la socket reçue (il compare[:unix, chemin]/[:tcp, hôte, port]avec ses propres binds) : il aurait ouvert sa propre socket à côté. Supprimé./initial d'un chemin unix (l'ancienne regex\/{1,2}transformaitunix:/tmp/c.sockenListenStream=tmp/c.sock, chemin relatif donc unité invalide). Un bind unix relatif est maintenant refusé avec un message explicite.ReusePort=trueest retiré : avec la socket activation il n'y a qu'un seul listener, et l'option ne sert qu'à masquer un appariement raté.NoDelay=truen'est plus émis que pour les binds TCP (l'option est sans objet sur une socket unix) au lieu d'être conditionné au mode:system.After=, en plus duRequires=déjà là).require "erb"/require "stringio"manquants : le rendu des templates plantait dès que la gem était chargée hors d'un contexte qui les avait déjà requis (c'est ce qui a fait échouer les premiers tests de rendu).Migration des apps
Le but est qu'une app n'ait rien à faire côté Capistrano :
cable:installpuiscable:smart_restartsurdeploy:finished) : mettre la gem à jour et déployer suffit, plus decap production cable:installmanuel ni de dérive entre la gem et l'unité installée ;cable:install;:cable_portou une des deux options SSL, le déploiement s'arrête surdeploy:starting, avant d'avoir rien envoyé, avec le message qui dit quoi écrire à la place — plutôt que de basculer silencieusement l'écoute ailleurs.cable:installrefuse aussi de tourner, pour les installations qui chargent le plugin sans ses hooks.Le seul geste manuel est côté serveur web, qui doit proxifier vers le chemin de la socket au lieu de l'hôte et du port. Cette conf est gérée hors du repo, donc à changer dans la même fenêtre que le déploiement. Si ce n'est pas possible, une app peut garder son port avec
set :cable_bind, "tcp://0.0.0.0:28090"et passer à la socket plus tard : le reste du changement (socket activation, install automatique) s'applique quand même.Tests
test/capistrano/test_bind.rb: parsing des trois schémas, chemin unix absolu conservé avec deux ou trois slashes, options de certificat retirées de l'adresse, binds invalides refusés.test/capistrano/test_systemd.rb: bind par défaut,--bindsur chaque bind configuré (et plus de--port), dossiers de socket à créer, refus des options supprimées, ordreinstallpuissmart_restartsurdeploy:finished, et rendu des deux unités (ListenStreampar bind,NoDelayseulement en TCP,Requires=/After=de l'unité socket).test/test_helper.rbcharge maintenant la gem et pas seulement sa version.bundle exec rake(test + standard) est vert. La suite affiche un avertissement de require circulaire venant de capistrano lui-même, sans rapport avec ces changements.systemd-analyze verify --usersur app3 (exit 0, aucun avertissement), sans rien installer ni redémarrer.Rien n'a été déployé.
🤖 Generated with Claude Code