#1 2011-11-05 14:25:00

Sir Rufo
Member
Registered: 2011-10-14
Posts: 24

Problem with Retrieve ForUpdate, UnLock

There is IMHO a caching problem with the Locks.

This code works perfect on several clients:

// Lock Record
if not MyClient.Retrieve( DataID, DataRec, True ) then
begin
  ShowMessage( 'No Lock' );
  Exit;
end;

try
  // Performing some actions with DataRec
  MyClient.Update( DataRec );
finally
  // UnLock Record
  MyClient.UnLock( DataRec );
end;

but if a client A tries to get the lock while another client B has already the lock, this client A won't get the lock, even if the lock is released by client B.

It took a small amount of time to get the lock from the server, but after client A didn't get the lock the next Retrieve on that ID is received without this little delay.
Thats why I think it is a caching problem.

Next thing on Locking is:

var
  Locked : Boolean;

// Lock Record
Locked := MyClient.Retrieve( DataID, DataRec, True );
try
  if Locked then
  begin
    // Performing some actions with DataRec
    MyClient.Update( DataRec );
  end;
finally
  // UnLock Record
  MyClient.UnLock( DataRec ); // UnLocks, even if we haven't got the Lock
end;

when client A gets no lock, because client B has the lock, then performing on client A then

MyClient.UnLock( DataRec )

will UnLock then record.
ok, i could handle this with the code above, but it is strange anyway

Offline

#2 2011-11-06 09:59:41

Sir Rufo
Member
Registered: 2011-10-14
Posts: 24

Re: Problem with Retrieve ForUpdate, UnLock

ok, i've located the problem for both cases in SQLite3Commons.pas

function TSQLRestClientURI.Retrieve(aID: integer; Value: TSQLRecord;
      ForUpdate: boolean=false): boolean;
var Table: TSQLRecordClass;
    Resp: RawUTF8;
begin
  result := false;
  if (self=nil) or (aID<=0) or (Value=nil) then
    exit;
  Table := Value.RecordClass;

  // try to get the lock from the local Model

  if ForUpdate and not Model.Lock(Table,aID) then
    exit; // error marking as locked by the client

  // try to get the lock from the Server

  with URIGet(Table,aID,Resp,ForUpdate) do
  if Lo=200 then begin
 
    // we successfully get the lock from Server

    Value.FillFrom(Resp);
    Value.fInternalState := Hi;
    if ForceBlobTransfert then
      result := RetrieveBlobFields(Value) else
      result := true;
  end;

end;

But what happened if we didn't get a successful lock from the server?
The record in the Model is still marked as locked.

Because of this, we
a) will never get a lock on this record
b) can Unlock a record, even we didn't receive the lock at any time from Server

The solution for this is really simple:

function TSQLRestClientURI.Retrieve(aID: integer; Value: TSQLRecord;
      ForUpdate: boolean=false): boolean;
var Table: TSQLRecordClass;
    Resp: RawUTF8;
begin
  result := false;
  if (self=nil) or (aID<=0) or (Value=nil) then
    exit;
  Table := Value.RecordClass;
  if ForUpdate and not Model.Lock(Table,aID) then
    exit; // error marking as locked by the client
  with URIGet(Table,aID,Resp,ForUpdate) do
  if Lo=200 then begin
    Value.FillFrom(Resp);
    Value.fInternalState := Hi;
    if ForceBlobTransfert then
      result := RetrieveBlobFields(Value) else
      result := true;
  end
  else
    // if we didn't get the lock from the server, we have to unlock the record in the Model :o)
    Model.UnLock(Table,aID);
end;

Offline

#3 2011-11-06 14:46:00

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,656
Website

Re: Problem with Retrieve ForUpdate, UnLock

You are right!

There is some obvious issue in the code.

I've fixed it as you suggested (adding a "if ForUpdate" and a "try...finally" to avoid a save unlocking):
see http://synopse.info/fossil/info/84bf8e1520

Thanks for the report.

Offline

Board footer

Powered by FluxBB