2009-12-01 10 views
6

Ho una tabella in mnesia e devo aggiornare i singoli campi nei record in essa contenuti. Secondo Erlang : Mnesia : Updating a single field value in a row se faccio qualcosa di simile:Erlang: Mnesia: Ricerca e aggiornamento basato su campi diversi dalla chiave

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

Ora mi pare di capire, il codice precedente legge un record P sulla base di un Key, l'acquisizione di un blocco di scrittura sul disco, in modo che nessun altro le transazioni modificare questo record, mentre viene letto e scritto (o, in breve, aggiornato). Fin qui tutto bene.

Ora il mio requisito è che ho bisogno di poter leggere i record basati sia sullo Key sia su un altro campo nella tabella e quindi eseguire un aggiornamento su di esso. Una funzione che farà questa ricerca è mnesia:match_object. Il problema ora è che, la funzione supporta solo un blocco di lettura, non un blocco di scrittura, secondo lo http://www.erlang.org/doc/man/mnesia.html#match_object-3.

La conseguenza di ciò è che, suppongo nella funzione di cui sopra dovessi usare mnesia: match_object, otterrò un (gruppo di) record (s), tutti con blocchi di lettura. Dopo aver letto i record, ho bisogno di eseguire alcuni controlli sui dati recuperati e quindi scrivere nuovamente il record aggiornato solo se una condizione è soddisfatta. Ora, supponiamo che ci siano due transazioni parallele T1 e T2 avviate da due sorgenti diverse in esecuzione. Sia T1 che T2 accedono allo stesso record, allo stesso tempo. Dal momento che vengono letti bloccati, sia T1 che T2 saranno in grado di leggere i record in parallelo. Sia T1 che T2 eseguiranno lo stesso controllo sullo stesso record e, se la condizione corrisponde, entrambi procederanno ad eseguire l'aggiornamento. Ma, nel mio codice, se T1 e T2 dovessero essere eseguiti in serie, T1 avrebbe apportato modifiche al record e in T2, avrebbe letto questi record modificati e la condizione avrebbe avuto esito negativo e non sarebbe stato effettuato alcun aggiornamento.

In breve, ho bisogno di scrivere i record di blocco restituiti da mnesia: match_object. La documentazione afferma chiaramente che solo il blocco della lettura è supportato. Ci sono alternative?

UPDATE: ho avuto modo di sperimentare un po ', e una possibile soluzione ho pensato potrebbe essere quella di utilizzare chiavi composte. Supponiamo che io ho i dati scritti in una tabella come:

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

C'è un modo di ricercare voci, il lavoro non se ne frega?

ho provato questi, ma entrambi restituito risultati vuoti:

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

risposta

1

Non devi preoccuparti. Dalla documentazione mnesia:

I blocchi di lettura possono essere condivisi, il che significa che se una transazione riesce ad acquisire un blocco di lettura su un articolo, altre transazioni possono anche acquisire un blocco di lettura sullo stesso articolo. Tuttavia, se qualcuno ha un blocco di lettura nessuno può acquisire un blocco di scrittura sullo stesso oggetto. Se qualcuno ha un blocco di scrittura nessuno può acquisire un blocco di lettura né un blocco di scrittura sullo stesso elemento.

Se una transazione ha un blocco di lettura su un oggetto, quell'oggetto non può essere modificato da un'altra transazione.

Diciamo che sono due operazioni, T1 e T2, che sono in esecuzione in parallelo:

  1. T1 fa mnesia:match_object, e acquisisce un blocco di lettura su tutti gli oggetti restituiti.
  2. T2 fa un equivalente mnesia:match_object e acquisisce un blocco di lettura sugli stessi oggetti.
  3. T2 tenta di acquisire un blocco di scrittura degli oggetti (per modificarlo).
  4. Mnesia interrompe automaticamente T2, per riprovare in seguito.
  5. T1 acquisisce un blocco di scrittura sugli oggetti e li modifica.
  6. finiture T1.
  7. Mnesia riprova T2.

Nota che T2 può essere ripetuto più volte, a seconda di quanto tempo T1 impiega per completare (cioè rilasciare i blocchi).

Secondo i miei test, il comportamento di blocco di mnesia:match_object non è coerente. Ad esempio, mnesia:match_object(mytable, {mytable, 2, '_'}, LockType) bloccherà solo il record con la chiave 2, ma mnesia:match_object(mytable, {mytable, '_', test}, LockType) blocca l'intera tabella.

Si noti inoltre che la documentazione non è corretta, mnesia:match_object(Table, Pattern, write) funziona e sembra seguire lo stesso schema di "lettura", vale a dire. se si specifica la chiave, solo il record corrispondente sarà bloccato in scrittura; se non si specifica la chiave, l'intera tabella sarà bloccata in scrittura.

È possibile verificare voi stessi facendo qualcosa di simile a questo:

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). 

Cambiando il modello e il tipo di blocco passato a match_object, e il numero della chiave e il tipo di blocco passato al mnesia:read nel processo generato (o utilizzando mnesia:write), è possibile verificare i vari comportamenti di blocco.

Addendum: Vedere questo post by Ulf Wiger sullo stesso argomento.

Addendum 2: Vedere section on "Isolation" nella guida dell'utente Mnesia.

Edit: Quanto sopra è stato tutto fatto su un tavolo di tipo set, match_object comportamento di blocco potrebbe essere diversa su un tavolo a sacchetto.

+0

Per me ha molto senso, grazie! – ErJab

0

non puoi semplicemente bloccare la tabella nella vostra transazione preventivamente con mnesia:write_lock_table(Tab)?

+0

Ho considerato questo, ma ho solo bisogno di aggiornare e quindi bloccare un record. La tabella verrà utilizzata da più client contemporaneamente e il blocco dell'intera tabella per un solo aggiornamento del record ridurrà il throughput. – ErJab

2

Anche se restituisce loro un blocco read, è ancora possibile aggiornarli con la scrittura in seguito. L'intera cosa di lettura/wread era solo un'ottimizzazione.

È possibile utilizzare una tabella ordered_set per implementare this mnesia-trick on compound keys.

+0

La mia applicazione richiede l'accesso simultaneo alla tabella da parte di diversi client.Se dovessi utilizzare prima mnesia: match_object per ottenere i record letti e bloccati e quindi aggiornare il blocco sui record, tra il momento in cui recupero i record con match_object e l'aggiornamento a un blocco di scrittura, non c'è la possibilità di un'altra transazione leggendo i record usando mnesia: match_object, dati i miei requisiti di accesso simultanei? – ErJab

+1

No, l'intera mnesia: la transazione è atomica e vede una versione coerente dei dati. La transazione verrà ripetuta un numero di volte fino a quando il commit non verrà eseguito. – Christian

2

Ti piace questa,

 case mnesia:match_object(#rec{name=Name, _='_'}) of 
      [] -> not_found; 
      [First|Rest] -> something 
     end, 
Problemi correlati