我正在将记录写入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伙计们......
答案 0 :(得分:2)
在交易之外处理计时器。当交易在Mnesia中发生碰撞时,它们只是重复出现。这将为您提供多个计时器参考和两个计时器触发器。这本身并不是问题,但如果您在安装计时器之前等到事务成功,则可以避免此问题。
我会做的第二个解决方案。如果TX没问题,您可以在其上安装计时器。如果计时器触发并且没有对象的引用,则无关紧要。你只是担心这种情况是否会发生很多,因为你会有大量的流浪计时器。
计时器模块很整洁,但性能指南建议您使用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分钟运行一次。批量做事可能会更有效。例如,操作系统内核会大量使用这种批量处理。
我对timer-tm
一无所知,抱歉:)