Locking data for writes, but not reads

Christian S <>
Thu Feb 9 10:10:26 CET 2006


2006/2/9, Mark Engelberg <>:
> For example, let's say I have some process that manages a complex data
> structure.  When other processes ask to read some information from the
> structure, it should read this information and deliver this
> information to the requesting process.  In the meantime, it should
> continue processing messages (because it should be able to handle read
> requests in parallel).  However, if it receives a message to modify or
> write to the data structure, it can't allow any more reads to take
> place until the write is completed.

Perhaps the following approach relying on selective receives?

loop(Structure) ->
  receive
    {read, Pid, LookupKey} ->
        ok = do_read(Pid, Structure, LookupKey),
        loop(Structure);
    {open_transaction, Pid, TransactionId} ->
        Pid ! {locked, self(), TransactionId},
        {ok, NewStructure} = transaction(Structure, Structure, TransactionId),
         loop(NewStructure)
  end.

transaction(OldStructure, Structure, TransactionId) ->
  receive
    {read, Pid, LookupKey} ->
      ok = do_read(Pid, OldStructure, LookupKey),
      transaction(OldStructure, Structure, TransactionId);

    {read, Pid, LookupKey, TransactionId} ->
      ok = do_read(Pid, Structure, LookupKey, TransactionId),
      transaction(OldStructure, Structure, TransactionId);

    {update, Pid, Update, TransactionId} ->
      {ok, UpdatedStructure} = do_update(Pid, Structure, Update, TransactionId),
      transaction(OldStructure, UpdatedStructure, TransactionId);

    {close_transaction, Pid, TransactionId} ->
      Pid ! {closed_transaction, self(), TransactionId},
      {ok, Structure}
  end.

In the 'loop' state it accepts ordinary reads and the opening of write
transactions. This is how the user of the data structure process go about
to modify the content:

TId = make_ref(),
Storage ! {transaction, self(), TId},
...reads against modified tree and updates against it...,
Storage ! {close_transaction, self(), TId}

When in a transaction it will allow all messages through that know the
transaction id. Ordinary reads will read the old structure. It is assumed
that each update leave the structure usable for lookups.

> How can this be elegantly done in Erlang?  There seems to be a couple
> of problems.  First, it's not clear how to process the read commands
> in parallel.  Erlang can't spawn off another process to handle a read
> request, because the new process won't have access to the data
> structure stored in the state of the managing process.  The other
> tricky part seems to be that you can't start writing to the data
> structure until all reads are complete.  Is there an easy way to find
> out that all pending reads are complete?
>

The approach I used just allow one to interleave read lookups on
the unmodified structure while performing updates to build a new
structure, no concurrent reads there.

To make it concurrent you should probably keep multiple copies of the
data, write-lock every copy when a transaction is started, collect update
sequences in the write transaction, and on transaction end issue the
updates to each update.

It gets hairy, luckily though, we already have mnesia.



More information about the erlang-questions mailing list