Как нам эффективно обрабатывать временные ограничения на записи Mnesia? - PullRequest
2 голосов
/ 16 июня 2011

я пишу записи в мнезию, которые должны храниться там только на разрешенное время (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: По сравнению с * 1036 Шона Хинда * timer_mn-1.1 , который работает поверх mnesia, является модулем таймера (возможно, работает поверх таблиц Ets) меньше способен (реально) на производство? (Я спрашиваю это, потому что появляется использование timer_mn Шона Хинда в системе, которая сама использует mnesia быть проблемой в терминах изменений схемы, проблем узлов e.t.c)

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

1 Ответ

2 голосов
/ 17 июня 2011

Вопрос 1:

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

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

Вопрос 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's истек в последние 5 минут , а затем запускайте это каждые 5 минут.Делать вещи навалом, вероятно, будет более эффективно.Такого рода массовая обработка часто используется ядрами операционной системы.

Вопрос 3

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

...