dimanche 10 février 2008

Les callback

La décision de pousser les états dans la base de données apporte au moins un problème particulier: comment prévenir les clients que quelque chose vient de se passer.

J'ai discuté dans les posts précédents la tentative de se débarrasser de (presque) tous les états dans le code, et de ne les avoir que dans la base de données elle même. Presque? En effet, je garde cependant une structure de données partagée et protégée par des sections critiques: le catalogue des connections actuellement valides, et les identifiant de comptes et d'avatars correspondants. Mais à part ça, tout est dans la base.

Que ce passe-t-il donc, lorsque par exemple un joueur veut parler dans la zone? Il envoie son message au serveur, et un thread le traite et le sauve dans la base de données. Et c'est là que l'on se rend compte que la base commence à avoir besoin d'un moyen de prévenir le serveur que quelqu'un a dit quelque chose, et qu'il faudrait peut-être penser à envoyer ledit message à ses voisins. Et heureusement, postgres a pensé à nous.

Le système de notification est fait exactement pour ça. Lorsqu'un processus lance une commande NOTIFY dans la base, toutes les connections qui écoutent vont la recevoir. En ajoutant un thread qui attend les notifications, l'on peut traiter les changements dans la base et expédier aux joueurs.

Le système pourrait fonctionner comme cela:

- Lorsqu'un message d'une conversation est reçu, il est ajouté à une table de la base de données.
- Cet ajout déclenche une "trigger", qui duplique le message dans une table secondaire, et lance un "notify"
- Le thread de notification reçoit le message, lit tous les messages de la table secondaire, les supprime de la base (seulement de la table secondaire, l'on veut quand même garder une trace de ce qui s'est dit), et envoie les messages à leurs destinataires.



Ce petit crobard représente Athos voulant envoyer un message à Porthos et Aramis. Le message est reçu par le serveur, qui l'insère dans la table principale. La trigger insère un duplicata du message dans une table secondaire, qui notifie un thread du serveur, lequel lit les messages actuellement dans la table secondaire, et les envoie à nos trois mousquetaires (Athos sera content de recevoir un accusé de réception).

Et les performances, dans tout ça? Pas très bonnes. Il y a beaucoup de communication entre divers processus (comptez les flèches!), incluant des sauts à travers le réseau (encore plus si la base de données vit la vie sur un serveur séparé). Mais bon, quand le seul problème restant sera celui des performances, l'on pourra toujours penser à réintégrer la base.

2 commentaires:

Anonyme a dit…

Un question se pose tout de même. Supposons qu'effectivement Athos parle, mais que seul Aramis soit en mesure de l'entendre parceque Portos est à l'autre bout de l'île. A moins de doter Aramis d'une bonne voix, Portos ne devrait pas recevoir le message. Qui va s'occuper de savoir à qui renvoyer le message ?

On a ici un problème de localisation, mais ca peut être également un problème de canaux de discussions (quels sont les joueurs "écoutant" ce canal particulier) ou un message système (annonce de reboot serveur malgré le chargement dynamique de modules depuis la base de données ;) ) à destination de tout le monde.

M87 a dit…

C'est un très bon point :)

Si l'on continue sur le principe que tous les états soient dans la base de données, c'est que nécessairement la requête qui récupère les messages doit devenir plus complexe pour contenir également la liste des destinataires. Par exemple:

- En joignant contre la table des canaux souscrits par les avatars
- En joignant contre la table géographique (je m'intéresse à postGIS :) ) des positions des joueurs, et donc des distances
- Et enfin, pour les traitements complexes, en chargeant toutes les informations dans le serveur et en y effectuant les traitements (avec possibilité de cache si les requêtes sont trop gourmandes).