2

私は記録をmnesiaに書き込んでいますが、それは許可された時間(24時間)だけそこに保持する必要があります。24時間後、ユーザーがそれらの一部を変更する前に、システムはそれらを自動的に削除する必要があります。たとえば、ユーザーには、特定の時間に使用する必要のある無料の通話時間(音声通話用)が与えられます。使用しない場合は、24時間後に、システムはこれらのリソース予約をユーザーレコードから削除する必要があります。

さて、これはタイマーをもたらしました。レコード構造の例は次のとおりです。

-record(free_airtime、
            {{
                reference_no、
                timer_object、timer:apply_after/4によって返される%%値
                額
            })。
 

レコード内のタイマーオブジェクトは重要です。ユーザーがタイムアウトする前に予約されたリソースを最終的に使用した場合(またはタイムアウトした場合)、システムはtimer:cancel/1このオブジェクトからタイマーサーバーを解放するために呼び出すことができます。問題は、これらのレコードのタイマーを処理する2つの方法があります。

オプション1:トランザクション内で処理されるタイマー

reserved_resources(Reference_no、Amnt)->
    F = fun(Ref_no、Amount)->
            ケースmnesia:read({free_airtime、Ref_no})of
                []->
                    case mnesia:write(#free_airtime {reference_no = Ref_no、amount = Amount})== ok of
                        true->
                            ケース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})
                            終わり;
                        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:トランザクション外で処理されるタイマー

reserved_resources(Reference_no、Amnt)->
    F = fun(Ref_no、Amount)->
            ケース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})
            終わり
        終わり、    
    結果=mnesia:activity(transaction、F、[Reference_no、Amnt]、mnesia_frag)を試してください
                    任意->任意
                キャッチ
                    exit:{aborted、{error、exists、XX}}-> {exists、XX}
                    E1:E2-> {エラー、{E1、E2}}
                終わり、
    on_reservation(結果)。

on_reservation(#free_airtime {reference_no = Some_Ref})->
    ケース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}
    終わり;
on_reservation(Any)->Any。

予約のタイムアウトを処理するためのコード:

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がトランザクションを再実行した場合でも、タイマー処理コードを除外する方が安全だと思いました。このコードは2回実行されません(同じものに対して複数のタイマーオブジェクトを使用しないようにします)。記録)。

質問1:これら2つの実装のどちらが正しいですか?および/または間違っていますか?(また)両方が間違っているかどうか教えてください

質問2:モジュールタイマー、本番環境で多数のタイマージョブを処理するのに適していますか?

質問3: mnesia上で実行されるSean Hindeのtimer_mn-1.1と比較して、タイマーモジュール(おそらくEtsテーブル上で実行される)は本番環境で(実際には)機能が劣っていますか?(それ自体がmnesiaを使用しているシステムでSean Hindeのtimer_mnを使用することは、スキーマの変更、ノードの問題などの点で問題があるように見えるため、これを求めています)

誰かがmnesiaのタイマー関連の問題を処理する別の方法を持っているなら、私に更新してくださいありがとうみんな...

4

1 に答える 1

2

質問1:

トランザクション外でタイマーを処理します。Mnesia でトランザクションが衝突すると、単純に繰り返されます。これにより、複数のタイマー参照とタイマーの 2 つのトリガーが得られます。それ自体は問題ありませんが、トランザクションが成功するまで待ってからタイマーをインストールすると問題を回避できます。

2番目の解決策は、私がすることです。TX に問題がなければ、タイマーをインストールできます。タイマーがトリガーされ、オブジェクトへの参照がない場合、それは問題ではありません。この状況が頻繁に発生する場合にのみ心配する必要があります。これは、多数の浮遊タイマーが発生するためです。

質問2:

タイマー モジュールはきちんとしていますが、パフォーマンス ガイドerlang:start_timerでは代わりに BIFを使用することを推奨しています。

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

gen_serverタイミングを処理する別のプロセスを導入します。メッセージを送信するとremove(timer:hours(24), RefNo)、タイマーが起動し、Mnesia または ETS のいずれかTRefにマッピングを取得してインストールします。タイマーがトリガーされると、プロセスはメイン テーブルからエントリを{TRef, RefNo, AuxData}削除するヘルパーを生成できます。RefNo

この時点で、クラッシュについて考える必要があります。削除gen_serverがクラッシュする可能性があります。また、ノード全体がクラッシュする可能性があります。これが発生した場合にタイマーを再インストールする方法はあなた次第ですが、解決できるようにそれが起こっていることを熟考する必要があります. 再び起動し、タイマー情報がディスクからロードされたとします。タイマーを再インストールする予定はありますか?

1 つの方法はAuxData、タイムアウト ポイントに関する情報を含めることです。1時間または15分ごとに、テーブル全体をスキャンして、そこにいるべきではない人を取り除きます. 実際、タイマー構造を削除する主な方法として、これを選択できます。はい、最悪の場合でも 15 分間余分に時間を与えますが、コード的には扱いやすいかもしれません。少なくとも、ノード (およびタイマー) が停止した場合の処理​​が改善されます。

もう 1 つのオプションは、ごまかしてデータ構造に大雑把にタイミングのみを保存することです。これにより、過去 5 分間に期限切れになったすべての RefNoを見つけて、5 分ごとに実行するのが非常に安価になります。まとめて行う方が効果的かもしれません。この種の一括処理は、オペレーティング システム カーネルなどでよく使用されます。

質問 3

については何も知りませんtimer-tm、すみません:)

于 2011-06-17T11:14:08.143 に答える