PoolCounter
PoolCounter est un service gestionnaire de verrous écrit en C. PoolCounter implémente une fonctionnalité simulaire au mutex, avec une longueur de file d'attente limitée. Si trop de serveurs essaient de faire la même chose au même moment, la file d'attente se remplit et l'application cliente peut implémenter une action de repli à la place, comme renvoyer une entrée de cache bloqué ou afficher un message d'erreur.
PoolCounter a été créé pour MediaWiki afin d'empêcher le blocage du site suite à une utilisation excessive du CPU après que le cache d'un article populaire ait été invalidé (le cas Michael Jackson). Les sémantiques du sémaphore de PoolCounter permettent à MediaWiki de restreindre le nombre de serveurs web qui analysent simultanément une même nouvelle version d'un article après sa modification. PoolCounter a depuis été utilisé également pour d'autres buts comme pour limiter le taux des demandes de redimensionnement des vignettes.
Installation
Serveur
apt install poolcounter.
Voir packages.debian.org et packages.ubuntu.com.
Vous pouvez aussi compiler le serveur à partir de ses sources dans le répertoire mediawiki/services/poolcounter, ou utiliser un serveur Redis standard à la place.
Client
MediaWiki utilise PoolCounter via le client écrit en PHP, configuré via $wgPoolCounterConf, qui prend en charge actuellement deux types de serveurs :
PoolCounter_Client– s'appuie sur un serveur Poolcounter (utilisé par Wikipedia).PoolCounterRedis– s'appuie sur un serveur Redis
Le source du client PHP se trouve dans le répertoire /includes/PoolCounter du noyau MediaWiki.
Il existe aussi un client Python expérimental pour le serveur Poolcounter, utilisé par Thumbor.
Configurer
Pour activer le client PoolCounter, activez-le via $wgPoolCounterConf puis spécifiez l'adresse de votre serveur via $wgPoolCountClientConf.
Le client MediaWiki peut spécifier dynamiquement la taille du pool et attendre l'expiration des temporisations utilisées par le serveur PoolCounter. Le serveur PoolCounter en lui-même ne nécessite pas de configuration.
Architecture
Le serveur est un programme C mono thread basé sur libevent. Il n'utilise pas autoconf, il n'a qu'un makefile suffisant pour un environnement Linux standard. Actuellement il n'a pas de code dans un démon et il s'exécute en arrière plan par systemd.
Dans MediaWiki, le client doit être une sous-classe de PoolCounter et la classe qui détient la logique spécifique de l'application doit être une sous-classe de PoolCounterWork.
Voir l'utilisation de $wgPoolCounterConf pour les détails.
Protocole
Le protocole réseau est basé sur les lignes, avec des paramètres séparés par des espaces (les espaces dans les paramètres sont codés à l'aide de pourcentages). Le client ouvre une connexion, envoie une commande d'acquisition de verrou, fait le travail, envoie une commande de libération de verrou et ferme la connexion.
Commandes
Les commandes suivantes sont définies :
- ACQ4ANY <key> <active worker limit> <total worker limit> <timeout>
- Acquérir un verrou pour un client qui a besoin de faire un certain travail, et qui est capable de lire l'entrée du cache écrite par un autre processus. Si on dépasse la limite des activités dans le groupe, le serveur va garder la connexion et retarder la réponse à cette commande. Lorsqu'un client qui a reçu une réponse LOCKED a terminé son travail et a envoyé une commande RELEASE, tous les processus qui attendent avec ACQ4ANY seront immédiatement réveillés par une réponse DONE et peuvent lire le travail réalisé à partir de la nouvelle entrée du cache.
- ACQ4ME <key> <active worker limit> <total worker limit> <timeout>
- Obtenir un verrou pour les cas où le partage des résultats du travail via un cache n'est pas possible ou non applicable, par exemple lorsqu'une demande de rendu d'un article implique un stub threshold qui n'est pas par défaut. Lorsqu'un verrou de ce type est relâché, un seul processus en attente sera réveillé pour garder identique le nombre des activités en cours.
- RELEASE
- Relâcher le verrou que le client vient d'acquérir récemment.
- STATS [FULL|UPTIME]
- imprime les statistiques.
Réponses
Réponses possibles pour ACQ4ANY / ACQ4ME :
- LOCKED
- acquérir un verrou avec succès. Le client est supposé faire le travail puis envoyer un RELEASE.
- DONE
- envoyer pour réveiller un client en attente
- QUEUE_FULL
- il y a plus d'activités que la <limite du nombre total d'activités>
- TIMEOUT
- il y a plus d'activités que la <limite du nombre d'activités en cours>; aucune place ne s'est dégagée après avoir attendu <timeout> secondes
- LOCK_HELD
- tentative pour obtenir un verrou quand on en possède déja un
Pour RELEASE :
- NOT_LOCKED
- actuellement le client ne possède aucun verrou
- RELEASED
- le verrou a été relâché avec succès
Pour toute commande :
- ERROR <message>
Tests
$ echo 'STATS FULL' | nc -w1 localhost 7531 uptime: 633 days, 15209h 42m 26s total processing time: 85809 days 2059430h 0m 24.000000s average processing time: 0.957994s gained time: 1867 days 44820h 50m 24.000000s waiting time: 390 days 9365h 18m 24.000000s waiting time for me: 389 days 9343h 3m 28.000000s waiting time for anyone: 22h 14m 53.898438s waiting time for good: 520 days 12503h 48m 24.000000s wasted timeout time: 473 days 11375h 2m 44.000000s total_acquired: 7739031655 total_releases: 7736374042 hashtable_entries: 119 processing_workers: 119 waiting_workers: 216 connect_errors: 0 failed_sends: 1 full_queues: 10294544 lock_mismatch: 227 release_mismatch: 0 processed_count: 7739031536
Demander la trace en production
Contrôler rapidement le trafic en production
Sur un serveur d'applications Mediawiki vous pouvez faire :
sudo tcpdump -A 'port 7531 and tcp[tcpflags] & tcp-push != 0'
Support évident de Trivial pour le protocole
Le script Lua suivant est un analyseur basique pour Wireshark qui place uniquement le contenu des paquets réseau Poolcounter dans des chaînes affichables dans une colonne de l'interface utilisateur Wireshark :
--[[
Poolcounter de base contient un analyseur de protocole.
Fournit le contenu du message dans un champ chaîne
qui peut être représenté par une colonne.
Simply renders payload as a string field, which can be then enabled as a column.
Copyright © 2020 Chris Danis & the Wikimedia Foundation
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
--]]
poolcounter_protocol = Proto("PoolCounter", "PoolCounter wire protocol")
pc_command_str = ProtoField.string("poolcounter.cmd")
poolcounter_protocol.fields = {pc_command_str}
function poolcounter_protocol.dissector(buffer, pinfo, tree)
length = buffer:len()
if length == 0 then return end
pinfo.cols.protocol = poolcounter_protocol.name
local subtree = tree:add(poolcounter_protocol, buffer(), "PoolCounter protocol data")
subtree:add(pc_command_str, buffer(0,length-1))
end
local tcp_port = DissectorTable.get("tcp.port")
tcp_port:add(7531, poolcounter_protocol)
Sur les systèmes Linux modernes vous devriez pouvoir enregistrer ceci en tant que ~/.local/lib/wireshark/plugins/poolcounter.lua et ensuite il fonctionnera automatiquement dans tout wireshark or tshark.
Tracer l'exécution de certaines formes de requêtes
Imaginez que vous vous intéressiez à voir le flux complet des conversations entre PoolCounter et ses clients pour une certaine partie de l'espace clé -- dans notre exemple nous utiliserons enwiki:SpecialContributions:a:127.0.0.1.
Comme les réponses du serveur PoolCounter (par exemple LOCKED) ne comprennent pas la clé dont ils parlent, ce n'est pas évident à faire.
Commencer la capture des paquets à partir de l'intervalle de temps qui vous intéresse.
Vous pouvez générer cela sur un Poolcounter hôte (ou sur un appserver hôte que vous utilisez pour tester) avec par exemple :
sudo tcpdump tcp port 7531 -c 500000 -w poolcounter.pcap
Ensuite, nous demanderons à Wireshark d'extraire la liste de ses numéros d'identification des flux TCP internes pour toutes les requêtes qui correspondent à cet espace clé :
tshark -r poolcounter.pcap -Y 'poolcounter.cmd contains "enwiki:SpecialContributions:a:127.0.0.1"' -T fields -e tcp.stream | sort | uniq > ids.txt
Une fois la liste des ID obtenue, elle est transformée par un filtre d'affichage Wireshark :
FILTER=$(sed -e 's/^/tcp.stream eq /' -e :a -e 'N;s/\n/ or tcp.stream eq /;ta' ids.txt)
et puis d'utiliser ce filtre pour sélectionner tout le trafic du protocole PoolCounter à partir de ces flux dans la capture des paquets originaux :
tshark -r poolcounter.pcap -Y "poolcounter and ($FILTER)" -e frame.time_relative -e frame.time -e ip.src -e tcp.stream -e poolcounter.cmd -Tfields
Assistance pour le code
- Maintenu par MediaWiki Platform Team.
- Discussion en direct (IRC): #mediawiki-core connecter
- Suivi des problèmes : Phabricator PoolCounter (rapporter un problème)