6

I have a table in mnesia and I need to update individual fields in the records in it. According to Erlang : Mnesia : Updating a single field value in a row if I do something like:

update_a(Tab, Key, Value) ->
  fun() ->
    [P] = mnesia:wread({Tab, Key}),
    mnesia:write(Tab, P#rec{a=Value}, write)
  end.

Now as I understand, the above code reads a record P based on a Key, acquiring a write lock on the record, so that no other transactions modify this record while it is being read and written back (or in short, updated). So far so good.

Now my requirement is that I need to able to read records based on both the Key and one other field in the table and then perform an update on it. A function that will do this looking up is mnesia:match_object. The problem now is that, the function only supports a read lock, not a write lock, according to http://www.erlang.org/doc/man/mnesia.html#match_object-3.

The consequence of this is that, suppose in the above function I were to use mnesia:match_object, I will get a (group of) record(s), all with read locks. After I read the records, I need to perform some checks on the retrieved data and then write back the updated record only if a condition is satisfied. Now, assume there are two parallel transactions T1 and T2 initiated by two different sources running. Both T1 and T2 access the same record, at the same time. Since they are read locked, both T1 and T2 will be able to read the records parallely. Both T1 and T2 will perform the same check on the same record, and if the condition matches, both will proceed to execute the update. But, in my code, if T1 and T2 were to have executed serially, T1 would have made changes to the record and in T2, it would have read these changed records and the condition would have failed and no update would have been made.

In short, I need to write lock records that are returned by mnesia:match_object. The documentation clearly states only read lock is supported. Are there are any alternatives?

UPDATE: I've been experimenting a little bit, and a possible solution I thought could be to use compound keys. Suppose I have data written to a table like:

mnesia:transaction(fun() -> mnesia:write(mytable, #rec{i={1,2}, a=2, b=3}, write) end).

Is there any way to lookup entries, using don't cares?

I tried these, but both returned empty results:

mnesia:transaction(fun()-> mnesia:read(mytable, {1,'_'}, read) end).
mnesia:transaction(fun()-> mnesia:read(mytable, {1,_}, read) end).
4

4 に答える 4

2

このような、

        case mnesia:match_object(#rec{name=Name, _='_'}) of
            [] -> not_found;
            [First|Rest] -> something
        end,
于 2010-08-15T06:01:18.677 に答える
2

それらはreadロックされた状態で返されますが、後で write を使用して更新することはできます。読み取り/書き込み全体は、単なる最適化でした。

この mnesia-trick を複合キーに実装するには、ordered_set テーブルを使用できます。

于 2009-12-01T10:51:41.997 に答える
1

心配する必要はありません。記憶喪失のドキュメントから:

読み取りロックは共有される可能性があります。つまり、1 つのトランザクションが項目の読み取りロックを取得できた場合、他のトランザクションも同じ項目の読み取りロックを取得する可能性があります。ただし、誰かが読み取りロックを持っている場合、誰も同じ項目で書き込みロックを取得できません。誰かが書き込みロックを持っている場合、誰も同じ項目で読み取りロックも書き込みロックも取得できません。

トランザクションがオブジェクトに対して読み取りロックを持っている場合、そのオブジェクトは別のトランザクションで編集できません。

T1 と T2 の 2 つのトランザクションが並行して実行されているとします。

  1. T1 はmnesia:match_objectを実行し、返されたすべてのオブジェクトの読み取りロックを取得します。
  2. T2 は同等mnesia:match_objectの処理を行い、同じオブジェクトの読み取りロックを取得します。
  3. T2 は、(編集するために) オブジェクトの書き込みロックを取得しようとします。
  4. Mnesia は T2 を自動的に中止し、後で再試行します。
  5. T1 はオブジェクトの書き込みロックを取得し、それらを編集します。
  6. T1終了。
  7. Mnesia は T2 を再試行します。

T2 は、T1 の完了 (つまり、ロックの解放) にかかる時間に応じて、数回再試行される場合があることに注意してください。

私のテストによると、 のロック動作mnesia:match_objectは一貫していません。たとえば、mnesia:match_object(mytable, {mytable, 2, '_'}, LockType)キー 2 のレコードのみをロックmnesia:match_object(mytable, {mytable, '_', test}, LockType)しますが、テーブル全体をロックします。

また、ドキュメンテーションが正しくなく、mnesia:match_object(Table, Pattern, write)機能し、「読み取り」と同じパターンに従っているように見えることにも注意してください。キーを指定すると、一致するレコードのみが書き込みロックされます。キーを指定しないと、テーブル全体が書き込みロックされます。

次のようにして、自分でテストできます。

test() ->
    mnesia:transaction(fun()-> 
        Table = mytable,
        Matches = mnesia:match_object(Table, {Table, 2, '_'}, write),
        io:format("matched: ~p~n", [Matches]),
        spawn(fun()->mnesia:transaction(fun()->
                        io:format("trying to read~n",[]),
                        io:format("read: ~p~n", [mnesia:read(Table, 2, read)])
                        end) end),        
        timer:sleep(1000),
        RereadMatches = lists:map(fun(#mytable{id=Id}) -> mnesia:read(Table, Id, write) end, Matches),
        io:format("reread matches: ~p~n", [RereadMatches])
        end).

に渡されるパターンとロック タイプ、および生成されたプロセスで (または を使用して) にmatch_object渡されるキー番号とロック タイプを変更することにより、さまざまなロック動作をテストできます。mnesia:readmnesia:write

補遺:同じトピックに関するUlf Wiger によるこの投稿を参照してください。

補遺 2: Mnesia ユーザー ガイドの「分離」に関するセクションを参照してください。

編集:上記はすべてセット型のテーブルで行われましたがmatch_object、バッグ型のテーブルではロックの動作が異なる場合があります。

于 2009-12-02T22:02:26.383 に答える
0

を使用して、事前にトランザクションでテーブルをロックすることはできませんmnesia:write_lock_table(Tab)か?

于 2009-12-01T10:51:27.657 に答える