问题描述
我正在将记录写入失忆症,应该保存在那里仅在允许的时间(24小时)内. 24小时后,在用户修改其中的一部分之前,系统应自动将其删除.例如,给用户免费通话时间(用于语音通话) 他们应该在给定的时间内使用.如果他们不使用它,则24小时后,系统应 从用户记录中删除这些资源保留.
i am writing records into mnesia which should be kept thereonly for an allowed time (24 hours). after 24 hours, before a user modifies part of them,the system should remove them automatically. forexample, a user is given free airtime (for voice calls) which they should use in a given time. if they do not use it, after 24 hours, the system should remove these resource reservation from the users record.
现在,这带来了计时器.记录结构的一个示例是:
Now, this has brought in timers. an example of a record structure is:
-record(free_airtime,
{
reference_no,
timer_object, %% value returned by timer:apply_after/4
amount
}).
记录中的计时器对象很重要,因为如果用户 最终在超时之前使用保留的资源 (或超时),系统可以调用timer:cancel/1
以缓解 此对象的计时器服务器. 现在的问题是,我有两种处理这些记录上的计时器的方法:
The timer object in the record is important because in case the user finally puts to use the resources reserved before they are timed out (or if they time out),the system can call timer:cancel/1
so as to relieve the timer server from this object. Now the problem, i have two ways of handling timers on these records:
选项1:在交易中处理的计时器
Option 1: timers handled within the 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).
关于上述选项.
Mnesia的文档说,TM经理可能会重复进行交易(由于某种原因)直到它们成功为止,因此当您放置io:format/2
或与之无关的任何其他代码时写入或读取,它可能会执行多次.这句话让我在这一点上停顿了一下并想到了一种处理计时器的方法,它可以自行处理事务,因此我将代码修改为如下:
Mnesia docs say that transactions maybe repeated by the tm manager (due to some reason)until they are successful, and so when you put code which is io:format/2
or any other which has nothing to do withwrites or reads, it may get executed several times. This statement made me pause at this pointand think of a way of handling timers out of the transaction it self, so i modified the code asfollows:
选项2:在交易之外处理的计时器
Option 2: timers handled outside the 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.
处理预留时间的代码:
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由于其原因重新执行了事务,也可以将其代码化,这段代码不会运行两次(我避免使用多个计时器对象反对相同的记录).
Now i thought that in option 2, i am safer by keeping the timer processingcode out, even when mnesia_tm re-executes the transaction due to its reasons, this piece of code is not run twice (i avoid having several timer objectsagainst the same record).
问题1:这两个实现中哪一个是正确的?和/或错?告诉我他们俩都错了吗
Question 1: Which of these two implementations is right? and/or wrong? Tell me (also)wether both of them are wrong
问题2:模块计时器,它非常适合处理大量计时器生产中的工作?
Question 2: The module timer, is it well suited for handling large numbers of timerjobs in production?
问题3:与肖恩·欣德(Sean Hinde)的 timer_mn-1.1 ,在mnesia上运行的计时器模块(可能在Ets表的顶部运行)少了有能力(真正的)生产?(之所以这样问是因为出现在本身正在使用Mnesia的系统上使用Sean Hinde的timer_mn 就架构变更而言是一个问题,例如节点问题等等)
Question 3: As compared to Sean Hinde's timer_mn-1.1,which runs on top of mnesia, is the timer module (possibly running on top of Ets tables) lesscapable (for real) in production?(am asking this because using Sean Hinde's timer_mn on a system which itself is using mnesia appears to be a problem in terms schema changes, node problems e.t.c)
如果有人有另一种方法来处理与失忆有关的计时器相关问题,请更新我 谢谢你们...
If any one has another way of handling timer related problems with mnesia, update me thanx guys...
推荐答案
问题1:
在事务之外处理计时器.当交易在Mnesia中发生冲突时,只需重复进行.这将为您提供多个计时器参考和两个计时器触发器.本身不是问题,但是如果在安装计时器之前等到事务成功,就可以避免该问题.
Question 1:
Handle the timer outside the transaction. When transactions collide in Mnesia, they are simply repeated. That would give you more than one timer reference and two triggers of the timer. It is not a problem per se, but if you wait until the success of the transaction before installing the timer, you can avoid the problem.
第二种解决方案是我会做的.如果TX正常,则可以在其上安装计时器.如果计时器触发并且没有对该对象的引用,那就没关系了.您只需要担心这种情况是否会发生很多,因为那时您将有大量的流浪计时器.
The second solution is what I would do. If the TX is okay, you can install a timer on it. If the timer triggers and there is no reference to the object, it doesn't matter. You are only to worry about if this situation happens a lot since you would then have a large number of stray timers.
计时器模块很整洁,但是性能指南建议您改为使用erlang:start_timer
BIF,请参见
The timer module is neat, but the performance guide recommends you use the erlang:start_timer
BIFs instead, see
http://www.erlang.org/doc/efficiency_guide/commoncaveats. html#id58959
我将介绍一个单独的过程作为gen_server
来处理计时内容.您向它发送remove(timer:hours(24), RefNo)
消息,然后它启动计时器,获取TRef
并在Mnesia或ETS中安装映射{TRef, RefNo, AuxData}
.当计时器触发时,该过程可以产生一个助手,从主表中删除RefNo
条目.
I would introduce a separate process as a gen_server
which handles the timing stuff. You send it a remove(timer:hours(24), RefNo)
message and then it starts up a timer, gets a TRef
and installs a mapping {TRef, RefNo, AuxData}
in either Mnesia or ETS. When the timer trigger, the process can spawn a helper removing the RefNo
entry from the main table.
在这一点上,您必须怀疑崩溃.删除gen_server
可能会崩溃.另外,整个节点可能会崩溃.万一发生这种情况,您想如何重新安装计时器,但您应该考虑一下这种情况,以便解决.假设我们再次出现,并从磁盘加载了计时器信息.您如何计划重新安装计时器?
At this point, you must wonder about crashes. The removal gen_server
may crash. Also, the whole node may crash. How you want to reinstall timers in the case this happens is up to you, but you ought to ponder on it happening so you can solve it. Suppose we come up again and the timer information is loaded in from disk. How do you plan on reinstalling the timers?
一种方法是让AuxData
包含有关超时点的信息.每隔一小时或十五分钟,您就会扫描所有桌子,并删除不应该在那里的人.实际上,您可以选择这作为删除计时器结构的主要方法.是的,在最坏的情况下,您将给人们15分钟的额外时间,但是在代码方面可能更容易处理.至少它可以更好地处理节点(以及计时器)死亡的情况.
One way is to have AuxData
contain information about the timeout point. Every hour or 15 minutes, you scan all of the table, removing guys that shouldn't be there. In fact, you could opt for this being the main way to remove timer structures. Yes, you will give people 15 minutes of extra time in the worst case, but it may be easier to handle code-wise. At least it better handles the case where the node (and thus the timers) die.
另一种选择是作弊,并且仅将计时严格地存储在数据结构中,这使得在最近5分钟内找到所有过期的RefNo 非常便宜,然后每5分钟运行一次.批量处理可能会更有效.例如,操作系统内核经常使用这种批量处理.
Another option again is to cheat and only store timings rougly in a data structure which makes it very cheap to find all expired RefNo's in the last 5 minutes and then run that every 5 minutes. Doing stuff in bulk is probably going to be more effective. This kind of bulk-handling is used a lot by operating system kernels for instance.
我对timer-tm
一无所知,对不起:)
I know nothing about timer-tm
, sorry :)
这篇关于我们如何有效处理与记忆相关的时间相关约束?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!