как нам эффективно справляться с временными ограничениями в записях mnesia?

StackOverflow https://stackoverflow.com/questions/6372639

Вопрос

Я пишу записи в Мнезию, которые должны храниться там только в течение допустимого времени (24 часа).Через 24 часа, прежде чем пользователь изменяет их часть, система должна удалить их автоматически.Пример, пользователю предоставляется бесплатное эфирное время (для голосовых вызовов), которое он должен использовать за определенное время.Если они не используют его, через 24 часа система должна удалить эти ресурсы от записи пользователей.

Теперь это привело к появлению таймеров.пример структуры записи:

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

Объект таймера в записи важен, потому что в случае, если пользователь, наконец, приведет к использованию ресурсов, зарезервированных до того, как они будут истешены (или, если он уйдет), система может вызвать timer:cancel/1 чтобы освободить сервер таймеров от этого объекта.Теперь проблема: у меня есть два способа обработки таймеров для этих записей:

Опция 1:таймеры, обрабатываемые в транзакции

 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).

По поводу вышеуказанного варианта.

Документы Mnesia говорят, что транзакции могут повторить TM -менеджер (по какой -то причине), пока они не станут успешными, и поэтому, когда вы поместите код, который io:format/2 Или любой другой, который не имеет ничего общего с записями или чтениями, он может быть выполнен несколько раз.Это утверждение заставило меня сделать паузу на данный момент и подумать о способе обработки таймеров из транзакции самостоятельно, поэтому я изменил код следующим образом:

Вариант 2:таймеры, обрабатываемые вне транзакции

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.

Код для обработки времени выхода из резервирования:

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).

Теперь я думал, что в варианте 2 я более безопасен, не позволяя коду обработки таймера, даже если Mnesia_TM повторно заново выводит транзакцию по его причинам, этот кусок кода не работает дважды (я избегаю нескольких объектов таймера против одного и того же записывать).

Вопрос 1: Какая из этих двух реализаций правильная?и/или неправильно?Скажи мне (также) не правы оба они оба

Вопрос 2: Таймер модуля, хорошо ли он подходит для обработки большого количества рабочих мест в производстве?

Вопрос 3: По сравнению с Шоном Хайндом timer_mn-1.1, который работает на вершине Mnesia, является ли временным модулем (возможно, работает на вершине таблиц ETS) менее способен (на самом деле) в производстве?(Я спрашиваю об этом, потому что использование Timer_mn Шона Хинде в системе, которая сама использует Mnesia, представляется проблемой в терминах схемы, задачи узлов и т. Д.)

Если у кого -то есть другой способ решения проблем, связанных с таймером с Месией, обновите меня, спасибо, ребята ...

Это было полезно?

Решение

Вопрос 1:

Обрабатывайте таймер вне транзакции.Когда транзакции в Mnesia сталкиваются, они просто повторяются.Это даст вам более одной ссылки на таймер и два триггера таймера.Это не проблема сама по себе, но если вы дождетесь успеха транзакции, прежде чем устанавливать таймер, вы можете избежать этой проблемы.

Второе решение - это то, что я бы сделал.Если с ТХ все в порядке, на него можно установить таймер.Если таймер срабатывает и ссылка на объект отсутствует, это не имеет значения.Вам следует беспокоиться только о том, что такая ситуация случается часто, поскольку в этом случае у вас будет большое количество случайных таймеров.

Вопрос 2:

Модуль таймера удобен, но руководство по производительности рекомендует использовать erlang:start_timer Вместо этого используйте BIF, см.

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

Я бы выделил отдельный процесс gen_server который обрабатывает время.Вы отправляете это remove(timer:hours(24), RefNo) сообщение, а затем он запускает таймер, получает TRef и устанавливает сопоставление {TRef, RefNo, AuxData} либо в Mnesia, либо в ETS.Когда срабатывает таймер, процесс может вызвать помощника, удаляющего RefNo запись из основной таблицы.

На этом этапе вы должны задуматься о сбоях.Удаление gen_server может потерпеть крах.Также может произойти сбой всего узла.Как вы хотите переустанавливать таймеры в этом случае, зависит от вас, но вам следует подумать о том, что это произойдет, чтобы вы могли решить эту проблему.Предположим, мы снова подходим и информация о таймере загружается с диска.Как вы планируете переустанавливать таймеры?

Один из способов — иметь AuxData содержат информацию о моменте тайм-аута.Каждый час или 15 минут вы сканируете всю таблицу, удаляя ребят, которых там быть не должно.Фактически, вы можете выбрать это как основной способ удаления структур таймера.Да, в худшем случае вы дадите людям 15 минут дополнительного времени, но, возможно, с этим будет проще справиться с точки зрения кода.По крайней мере, он лучше справляется со случаем, когда узел (и, следовательно, таймеры) умирают.

Еще один вариант — обмануть и грубо хранить тайминги в структуре данных, что делает поиск очень дешевым. все номера RefNo с истекшим сроком действия за последние 5 минут а затем запускайте это каждые 5 минут.Массовое выполнение задач, вероятно, будет более эффективным.Например, такого рода массовая обработка часто используется ядрами операционных систем.

Вопрос 3

Я ничего не знаю о timer-tm, извини :)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top