2011-11-08 10 views
2

私はmnesiaテーブルに同じ3つのレコードを挿入/更新する5つのプロセスがあります。これらの各プロセスは、単一のトランザクション内で挿入/更新を行います。Mnesia:予期せずに中断された循環トランザクション

私はこれらの全く同じ3つのレコードを1つのトランザクション内で読み取る5つのプロセスを持っています。

マルチレコードトランザクションの一部としてテーブル全体をロックしない限り、{aborted、{cyclic、node ....}}エラーが発生します。私の直感は、私のユースケースは普通であり、それ自体が中断されたトランザクションを引き起こしてはならないということです。誰かが私の骨頭思考で私を助けることができますか?私がやっているのは、1つのトランザクションで複数の行をキャッシュ(mnesiaテーブル)に挿入することです。 3つのレコードを挿入

この

insert_keylist(Keys) -> 
    F = fun() -> insert_iter(Keys) end, 
    transactional_execute(F). 

insert_iter([]) -> 
    ok; 
insert_iter([{Key, Value} | KVs]) -> 
    insert(Key, Value), 
    insert_iter(Kvs). 

insert(Key, Value) -> 
    F = 
     fun() -> 
     case sc_store:lookup(Key) of 
      {ok, _Value}  -> sc_store:replace(Key, Value); 
      {error, not_found} -> sc_store:insert(Key,Value) 
     end 
     end, 
    transactional_execute(F).  


transactional_execute(F) -> 
    case mnesia:is_transaction() of 
     true -> F(); 
     false -> 
      try mnesia:sync_transaction(F) of 
       {atomic, Result} -> Result; 
       {aborted, Reason} -> {aborted, Reason} 
      catch 
       ErrorCl:Error  -> {error, {ErrorCl, Error}} 
     end 
    end. 

sc_storeのようになります、置き換え挿入して、ルックアップされている次のとおりです。

replace(Key, Value) -> 
    try 
     case mnesia:wread({key_to_value, Key}) of 
      [#key_to_value{} = Rec] -> 
      mnesia:write(Rec#key_to_value{value = Value}); 
      []      -> 
     {error, not_found} 
     end 
    catch 
     _Err:Reason -> 
     {error, Reason} 
    end. 

insert(Key, Value, Type, Scope, TTL, TTLMessage, Ref) -> 
    try 
     NowDT = calendar:now_to_datetime(erlang:now()), 
     ok = mnesia:write(#key_to_value{key = Key, 
       type = Type, 
       scope = Scope, 
       value = Value, 
       create_time_dt = NowDT, 
       ttl_secs = TTL, 
       ttl_message = TTLMessage, 
       ref = Ref}) 
    catch 
     _Error:Reason -> 
     {error, Reason} 
    end. 

lookup(Key) -> 
    try 
     case mnesia:read(key_to_value, Key) of 
     [#key_to_value{type = Type, scope = Scope, value = Value}] -> 
      {ok, {Value, Type, Scope}}; 
     []      -> 
      {error, not_found} 
     end 
    catch 
     _Err:Reason -> {error, Reason} 
    end. 

答えて

2

実際には、メネシアの操作のまわりでtry/catchを使用していたことが判明しました以内内のトランザクション。詳細については、hereを参照してください。

2

Mnesiaは右です!
あなたのコードによれば、関数insert_keylist/1は、ファンクションtransactional_execute/1を呼び出します。関数内で:transactional_execute/1、あなたはすでに取引中であることを尋ね、それが真ならばfun Fを実行します。しかし、楽しいFを見てみると、それはtransactional_execute/1という関数を繰り返し呼び出すことがわかります。反復関数insert_iter/1。したがって、これはトランザクションのループを作成します。したがって、問題が発生しているのは、プロセスが一番上の関数を実行しているためです。insert_keylist/1トランザクション内でネストされたループを作成しているトランザクションでは、実行されることはありませんが、トランザクション中であり、決して執行されることなく尋ね続け、尋ねてループする!

コードを変更する前に、関数内に何が表示されていないのですか:sc_store:replace/2およびsc_store:insert/1。私はこれらの機能が再び自分の中でトランザクションを作成していないと仮定します!私は彼らが直接的に(トランザクション内で実行されなければならない)記憶機能(例えば、mnesia:write/1,mnesia:read/1,mnesia:write/3など)を使用していると仮定します。そうでなければ、これらの関数で別のトランザクションを作成していれば、確かにこれはmnesiaトランザクションの混乱になります!

 
%% This function will always be sure to be in a 
%% mnesia transaction. So no need to call 
%% transactional execute 

insert_iter([])-> ok; 
insert_iter([{Key, Value} | KVs]) -> 
    case sc_store:lookup(Key) of 
     {ok, _Value} -> sc_store:replace(Key, Value); 
     {error, not_found} -> sc_store:insert(Key,Value) 
    end, 
    insert_iter(Kvs). 

機能sc_store:insert/1sc_store:replace/2アクセスは、他のトランザクションを作成せずに、直接関数を記述し、読みmnesiaと仮定して、必要な唯一の変更です:今、以下に示すように、正しいコードができます。

+1

だから、助けてくれてありがとうが、これは正しいとは思わない。 (私はあなたの提案を試みたが、うまくいかなかった)。 Transactional_execute/1はネストされたトランザクションを作成すべきではありません。これは特に停止するように設計されています。コードをトレースするとmnesiaを呼び出すように見えます:sync_transactionは一度だけです。また、複数の挿入プロセス(上記のコード)を繰り返し実行しても何の問題もありません。複数の読み込みプロセス(複数の読み込みプロセスと同じコードを実行する場合は、/insert the value。他のアイデア? – Jr0

+0

実際には、私は* insert *プロセスを単独で実行することでそれを打破することができます。トランザクションの実行コード/ 1が一度呼び出されたことを保証するコードをリファクタリングしました。 )そしてまだそれは壊れている – Jr0

+0

**ホリー・クラップ!**私はそれが私の試みかもしれないと思う...sc:store関数を取り囲む私がそれらを取り除くと、すべてが機能します....私は、試行錯誤の中でそれらの機能を取り囲むことによって、記憶媒体の再試行/デッドロック回避を傍受している可能性がありますか? – Jr0

関連する問題