(26-04-2012, 07:22 PM)Sephi-Chan a écrit : Tu utilises quoi comme base de données ? Mnesia ? Tu as des articles sur la question ? Tu écris les données en base au fur et à mesure ou bien une fois de temps en temps (en récupération d'erreur, notamment) ?
J'utilise postgresql avec un petit ORM nommé boss_db, mais il n'est pas très complet.
J'utilise également bitcask pour stocker sur un node n'importe quel terme erlang. quand tu as un process qui crève, lorsqu'il sera redémarré il récupère où il en était comme si de rien n'était. J'ai pas trop expérimenté là dessus ceci-dit, je me suis juste créé le code pour pouvoir le faire mais ça n'a pas été éprouvé.
(26-04-2012, 07:22 PM)Sephi-Chan a écrit : Je t'avoue que je n'ai pas encore abordé OTP et les outils qu'il fournit.C'est un peu plus dur que le langage de base car il faut comprendre la logique. Mais une fois pigé, ça simplifie pas mal de choses. ça demande par contre pas mal de code "boilerplate", un peu chiant. Mais c'est du solide.
(26-04-2012, 07:22 PM)Sephi-Chan a écrit : Quels deadlocks ? Erlang n'est pas supposé éviter ça ?Et bien, imagine deux process A et B. Le process A envoie un message à B et se met en attente de réponsede la part de B. pendant la construction de sa réponse, B a besoin d'informations de A, il lui envoie donc un message et se met en attente de réponse.
B ne recevra jamais de réponse de la part de A, car A est bloqué dans un receive qui ne terminera jamais puisque lui aussi attend une réponse de B avant de pouvoir faire autre chose.
Il est donc possible d'envoyer les messages sans attendre la réponse, et la traiter quand elle reviendra, mais le design que j'ai évoqué est donc à éviter. je n'ai pas eu pour le moment besoin de faire des allers-retours de ce type, mais ça pourrait bien arriver. Il y a pas mal de mécanismes pour les mettre en place mais il faut simplement y penser.
Or, comme montré dans l'autre topic, le côté asynchrone est masqué, on oublie rapidement qu'un petit appel de fonction va en fait passer des messages dans plusieurs processus.
Erlang n'évite pas les deadlocks mais permet facilement de les éviter.
(26-04-2012, 07:22 PM)Sephi-Chan a écrit : Ouais j'ai vu ça. Mais du coup je me demande quand utiliser de simples records, des record Mnesia, ou des dictionnaires, voir même d'autres trucs qui existent.
Mnesia utilise les records classiques en fait. Les records c'est bien quand tu as des données membres à récuperer rapidement, comme dans l'état d'un process. un joueur gardera son ID, son nom, son email, le record de son perso, etc..
Un dict ça sera bien quand tu a besoin de stocker des clés indéfiniment. Comme en ruby en fait : tu fais des objets avec leurs variables membres et si tu veux passer une liste d'options, un hash convient.
(J'utilise pas vraiment les dict mais plutot les proplists : [{name, "niahoo"},{age,99.4},{country,"France"}])
(26-04-2012, 07:22 PM)Sephi-Chan a écrit : Qu'est-ce que tu suggères pour s'occuper de ça ?
je me suis fait un petit bout de code qui utilise 'global', un storage de processus.
global te permet d'associer un Pid à une clé, et quand le process creve, la clé est supprimée.
Code :
-module(pidstore).
-export([get_pid/2,get_pid/1,register/2]).
%% sert à démarrer des process s'ils ne sont pas enregistrés.
%% on se sert du module 'global' comme process registry
%% mais le présent module sert justement à faire abstraction
get_pid(Key) -> global:whereis_name(Key).
get_pid(Key, MFA) ->
case get_pid(Key)
of undefined -> newpid(Key, MFA)
; Pid -> {ok, Pid}
end.
newpid(Key, {M,F,A}=_MFA) when is_list(A) ->
Ret = erlang:apply(M,F,A),
case Ret
of {ok, Pid} ->
ok = ?MODULE:register(Key, Pid)
; Any -> 'youbraaad!'
end,
Ret.
register(Key, Pid) when is_pid(Pid) ->
io:format("registering ~p as ~p on pidstore ~n", [Key,Pid]),
yes = global:register_name(Key, Pid),
ok.
On lui fournit un clé et un MFA (module, function, args), si 'global' ne renvoie pas de Pid (==undefined) alors j'applique apply(M,F,A) qui doit normalement me renvoyer {ok, Pid}, je réengegistre le Pid avec la même clé et c'est reparti.
Comme tu peux le lire ce n'est pas fini mais ça fonctionne comme ça.
Mais là, si deux process demandent la même clé en même temps et qu'elle n'est pas enregistrée, deux process vont être créés avec la même clé, le premier sera écrasé par le suivant mais (je crois) que global s'en fout, il ne va pas le 'kill'.
Ma prochaine étape est donc de mettre tout ceci au sein d'un process pour qu'il réponde aux demandes à la queue.
j'ai pas demandé l'avis d'experts, s'il le faut, c'est pourri. Il existe nprocreg et GProc mais ils sont destinés à remplacer les dictionnairs des processus, or je veux du code qui fasse uniquement associer un Pid à une clé, et c'est tout ce que global fait. Mais je ne sais pas si utiliser global est déconseillé.