2

我正在将记录写入 mnesia,这些记录应仅在允许的时间(24 小时)内保存。24 小时后,在用户修改其中的一部分之前,系统应自动删除它们。例如,为用户提供免费通话时间(用于语音通话),他们应该在给定时间内使用这些通话时间。如果他们不使用它,24 小时后,系统应该从用户记录中删除这些资源预留。

现在,这引入了计时器。记录结构的一个示例是:

-记录(免费通话时间,
            {
                参考编号,
                timer_object,定时器返回的%%值:apply_after/4
                数量
            })。
 

记录中的计时器对象很重要,因为万一用户在超时(或超时)之前最终使用了保留的资源,系统可以调用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
                        真->
                            case timer:apply_after(timer:hours(24),?MODULE,reference_no_timed_out,[Ref_no]) 的
                                {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})
                            结尾;
                        false -> mnesia:abort({error,write_failed})
                    结尾;
                [_] -> mnesia:abort({error,exists,Ref_no})
            结尾
        结尾,
    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),
                    磷;
                [_] -> mnesia:abort({error,exists,Ref_no})
            结尾
        结尾,    
    结果 = 尝试 mnesia:activity(transaction,F,[Reference_no,Amnt],mnesia_frag) 的
                    任何 -> 任何
                抓住
                    退出:{中止,{错误,存在,XX}} - > {存在,XX}
                    E1:E2 -> {错误,{E1,E2}}
                结尾,
    on_reservation(结果)。

on_reservation(#free_airtime{reference_no = Some_Ref})->
    case timer:apply_after(timer:hours(24),?MODULE,reference_no_timed_out,[Some_Ref])
        {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),
            {错误,failed_to_time_object}
    结尾;
on_reservation(任何)-> 任何。

处理预订超时的代码:

reference_no_timed_out(Ref_no)->
    do_somethings_here.....
    然后稍后从数据库中删除此保留..在下面..
    ok = mnesia:activity(transaction,fun(XX) -> mnesia:delete({free_airtime,XX}) end,[Ref_no],mnesia_frag)。

现在我认为在选项 2 中,通过将计时器处理代码排除在外,我会更安全,即使 mnesia_tm 由于其原因重新执行事务,这段代码也不会运行两次(我避免有多个计时器对象针对同一个记录)。

问题1:这两种实现中哪一种是正确的?和/或错误?告诉我(也)他们俩是否都错了

问题 2:模块定时器,它是否非常适合处理生产中的大量定时器作业?

问题 3:与运行在 mnesia 之上的 Sean Hinde 的timer_mn-1.1相比,timer 模块(可能在 Ets 表之上运行)在生产中的能力(实际上)是否更弱?(我问这个是因为在本身使用 mnesia 的系统上使用 Sean Hinde 的 timer_mn 在架构更改、节点问题等方面似乎是一个问题)

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

4

1 回答 1

2

问题一:

处理事务外的计时器。当交易在 Mnesia 中发生冲突时,它们只是简单地重复。这将为您提供不止一个计时器参考和两个计时器触发器。这本身不是问题,但是如果您等到交易成功后再安装计时器,则可以避免该问题。

第二个解决方案是我会做的。如果 TX 没问题,你可以在上面安装一个定时器。如果计时器触发并且没有对对象的引用,那没关系。你只需要担心这种情况是否经常发生,因为你会有大量的流浪计时器。

问题2:

计时器模块很简洁,但性能指南建议您改用erlang:start_timerBIF,请参阅

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

我将引入一个单独的过程作为gen_server处理时间的东西。你向它发送一条remove(timer:hours(24), RefNo)消息,然后它启动一个计时器,获取一个并在 Mnesia 或 ETS 中TRef安装一个映射。{TRef, RefNo, AuxData}当计时器触发时,该进程可以生成一个助手,RefNo从主表中删除条目。

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

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

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

问题 3

我什么都不知道timer-tm,对不起:)

于 2011-06-17T11:14:08.143 回答