我们如何有效地处理mnesia记录中与时间相关的约束?

时间:2011-06-16 13:27:28

标签: erlang mnesia mochiweb yaws nitrogen

我正在将记录写入mnesia,应保留在那里 仅限允许的时间(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 docs表示交易可能由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:与Sean Hinde的 timer_mn-1.1 相比, 它运行在mnesia之上,是计时器模块(可能在Ets表之上运行)较少 生产能力(真实)? (我问这个是因为在一个本身正在使用mnesia的系统上使用Sean Hinde的timer_mn     在模式更改方面存在问题,节点问题e.t.c)

如果有人用mnesia处理与计时器相关的问题的另一种方法,请更新我  thanx伙计们......

1 个答案:

答案 0 :(得分:2)

问题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并在Mnesia或ETS中安装映射{TRef, RefNo, AuxData}。当计时器触发时,该进程可以生成一个帮助程序,从主表中删除RefNo条目。

此时,你必须想知道崩溃。删除gen_server可能会崩溃。此外,整个节点可能会崩溃。如果发生这种情况,您希望如何重新安装计时器取决于您,但您应该仔细考虑它,以便您可以解决它。假设我们再次出现并且从磁盘加载定时器信息。您打算如何重新安装计时器?

一种方法是让AuxData包含有关超时点的信息。每小时或15分钟,你扫描所有的表,删除不应该在那里的人。实际上,您可以选择将此作为删除计时器结构的主要方式。是的,在最坏的情况下,你会给人们15分钟的额外时间,但代码处理可能更容易。至少它可以更好地处理节点(以及定时器)死亡的情况。

另一种选择是作弊并且只在数据结构中存储时间粗糙,这使得在过去的5分钟内找到所有过期的RefNo 非常便宜,然后每5分钟运行一次。批量做事可能会更有效。例如,操作系统内核会大量使用这种批量处理。

问题3

我对timer-tm一无所知,抱歉:)