Question

J'écris des enregistrements dans Mnesia qui ne devraient y être conservés que pour un temps autorisé (24 heures). Après 24 heures, avant qu'un utilisateur n'en modifie une partie, le système doit les supprimer automatiquement. Forexample, un utilisateur reçoit du temps d'antenne gratuit (pour les appels vocaux) qu'il doit utiliser dans un temps donné. S'ils ne l'utilisent pas, après 24 heures, le système doit supprimer ces réservations de ressources de l'enregistrement des utilisateurs.

Maintenant, cela a apporté des minuteries. Un exemple de structure d'enregistrement est:

 -record(free_airtime,
            {
                reference_no,
                timer_object,   %% value returned by timer:apply_after/4
                amount
            }).
 

L'objet de minuterie dans l'enregistrement est important car si l'utilisateur met finalement pour utiliser les ressources réservées avant d'être chronométrées (ou s'ils sont à l'heure), le système peut appeler timer:cancel/1 de manière à soulager le serveur de minuterie de cet objet. Maintenant le problème, j'ai deux façons de gérer les minuteries sur ces enregistrements:

Option 1: temporisateurs gérés dans la transaction

 reserve_resources(Reference_no,Amnt)->
    F = fun(Ref_no,Amount) -> 
            case mnesia:read({free_airtime,Ref_no}) of
                [] -> 
                    case mnesia:write(#free_airtime{reference_no = Ref_no,amount = Amount}) == ok of
                        true -> 
                            case timer:apply_after(timer:hours(24),?MODULE,reference_no_timed_out,[Ref_no]) of
                                {ok,Timer_obj} -> 
                                    [Obj] = mnesia:read({free_airtime,Ref_no}),
                                    mnesia:write(Obj#free_airtime{timer_object = Timer_obj});
                                _ -> mnesia:abort({error,failed_to_time_object})
                            end;
                        false -> mnesia:abort({error,write_failed})
                    end;
                [_] -> mnesia:abort({error,exists,Ref_no})
            end
        end,
    mnesia:activity(transaction,F,[Reference_no,Amnt],mnesia_frag).

Sur l'option ci-dessus.

Mnesia Docs dit que les transactions peuvent être répétées par le gestionnaire TM (pour une raison quelconque) jusqu'à ce qu'ils réussissent, et donc lorsque vous mettez du code qui est io:format/2 Ou tout autre qui n'a rien à voir avec les écritures ou les lectures, il peut être exécuté plusieurs fois. Cette déclaration m'a fait faire une pause à ce stade et penser à un moyen de gérer les minuteries de la transaction elle-même, j'ai donc modifié le code comme suit:

Option 2: minuteries manipulées en dehors de la transaction

reserve_resources(Reference_no,Amnt)->
    F = fun(Ref_no,Amount) -> 
            case mnesia:read({free_airtime,Ref_no}) of
                [] -> 
                    P = #free_airtime{reference_no = Ref_no,amount = Amount},
                    ok = mnesia:write(P),
                    P;
                [_] -> mnesia:abort({error,exists,Ref_no})
            end
        end,    
    Result  =   try mnesia:activity(transaction,F,[Reference_no,Amnt],mnesia_frag) of
                    Any -> Any
                catch
                    exit:{aborted,{error,exists,XX}} -> {exists,XX}
                    E1:E2 -> {error,{E1,E2}}
                end,
    on_reservation(Result).

on_reservation(#free_airtime{reference_no = Some_Ref})-> 
    case timer:apply_after(timer:hours(24),?MODULE,reference_no_timed_out,[Some_Ref]) of
        {ok,Timer_obj} -> 
                [Obj] = mnesia:activity(transaction,fun(XX) -> mnesia:read({free_airtime,XX}) end,[Some_Ref],mnesia_frag),
                ok = mnesia:activity(transaction,fun(XX) -> mnesia:write(XX) end,[Obj#free_airtime{timer_object = Timer_obj}],mnesia_frag);
        _ -> 
            ok = mnesia:activity(transaction,fun(XX) -> mnesia:delete({free_airtime,XX}) end,[Some_Ref],mnesia_frag),
            {error,failed_to_time_object}
    end;
on_reservation(Any)-> Any.

Le code pour gérer le temps de la réservation:

reference_no_timed_out(Ref_no)->
    do_somethings_here.....
    then later remove this reservation from the database....below..
    ok = mnesia:activity(transaction,fun(XX) -> mnesia:delete({free_airtime,XX}) end,[Ref_no],mnesia_frag).

Maintenant, je pensais que dans l'option 2, je suis plus sûr en gardant le code de traitement de la minuterie, même lorsque mNESIA_TM réexécute la transaction pour ses raisons, ce code n'est pas exécuté deux fois (j'évite d'avoir plusieurs objets temporaires contre le même enregistrement).

Question 1: Laquelle de ces deux implémentations est correcte? et / ou mal? Dites-moi (aussi) les deux ont tous les deux tort

Question 2: La minuterie du module, est-il bien adapté pour gérer un grand nombre de travaux de minuterie en production?

Question 3: Par rapport à Sean Hinde TIMER_MN-1.1, qui fonctionne au-dessus de Mnesia, le module de minuterie (éventuellement courir sur les tables ETS) est-il moins capable (pour de vrai) en production? (Je demande ceci parce que l'utilisation de TIMER_MN de Sean Hinde sur un système qui utilise lui-même Mnesia semble être un problème en termes de modifications du schéma, de problèmes de nœud, etc.)

Si quelqu'un a une autre façon de gérer les problèmes liés à la minuterie avec mnesia, mettez-moi à jour merci les gars ...

Était-ce utile?

La solution

Question 1:

Gérer la minuterie en dehors de la transaction. Lorsque les transactions entrent en collision à Mnesia, elles sont simplement répétées. Cela vous donnerait plus d'une référence de minuterie et deux déclencheurs de la minuterie. Ce n'est pas un problème en soi, mais si vous attendez le succès de la transaction avant d'installer la minuterie, vous pouvez éviter le problème.

La deuxième solution est ce que je ferais. Si le TX est correct, vous pouvez installer une minuterie dessus. Si la minuterie se déclenche et qu'il n'y a aucune référence à l'objet, cela n'a pas d'importance. Vous ne vous inquiétez que si cette situation se produit beaucoup, car vous auriez alors un grand nombre de chronométreurs errants.

Question 2:

Le module de minuterie est soigné, mais le guide de performance vous recommande d'utiliser le erlang:start_timer BIFS à la place, voir

http://www.erlang.org/doc/efficiency_guide/commoncaveats.html#id58959

J'introduirais un processus distinct en tant que gen_server qui gère les trucs de timing. Vous lui envoyez un remove(timer:hours(24), RefNo) Message puis il démarre une minuterie, obtient un TRef et installe une cartographie {TRef, RefNo, AuxData} en mnesia ou en ets. Lorsque le déclencheur de la minuterie, le processus peut engendrer une aide en retirant le RefNo Entrée de la table principale.

À ce stade, vous devez vous interroger sur les accidents. L'enlèvement gen_server peut s'écraser. De plus, le nœud entier peut s'écraser. La façon dont vous voulez réinstaller les minuteries dans le cas, cela se produit dépend de vous, mais vous devez réfléchir à ce que vous puissiez le résoudre. Supposons que nous revenions et que les informations de la minuterie sont chargées du disque. Comment prévoyez-vous de réinstaller les minuteries?

Une façon est d'avoir AuxData contiennent des informations sur le point de temporisation. Toutes les heures ou 15 minutes, vous scannez toute la table, enlevant des gars qui ne devraient pas être là. En fait, vous pouvez opter pour que ce soit le principal moyen de supprimer les structures de la minuterie. Oui, vous donnerez aux gens 15 minutes de temps supplémentaire dans le pire des cas, mais il peut être plus facile de gérer le code. Au moins, il gère mieux le cas où le nœud (et donc les minuteries) meurent.

Une autre option à nouveau consiste à tricher et à stocker uniquement des horaires rougus dans une structure de données qui le rend très bon marché pour trouver Tous les Refno expirés sont dans les 5 dernières minutes puis exécutez-le toutes les 5 minutes. Faire des trucs en vrac va probablement être plus efficace. Ce type de manipulation en vrac est beaucoup utilisé par les grains de système d'exploitation par exemple.

question 3

Je ne sais rien de timer-tm, Pardon :)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top