I've built a windows service to sinchronize to Firebird databases.

I've built a windows service to sinchronize to Firebird databases.
A sample method to sync 2 tables is as follows:

function TCSVSync.SyncStoc(const aID: Integer; var mID: Integer; const aAction: String): Boolean;
begin
Result := False;
if aID <= -1 then Exit;
mID := -1;

if qSlaveStocs.Active then qSlaveStocs.Close;
try
qSlaveStocs.Params.ParamByName('ID').AsInteger := aID;
dbSlave.StartTransaction;
qSlaveStocs.Open();

if qMasterStocs.Active then qMasterStocs.Close;
qMasterStocs.CachedUpdates := True;
qMasterStocs.SQL.Text := ' update or insert into STOC (IDOF,IDVAL,VAL) values (:IDOF,:IDVAL,:VAL) matching (IDOF, IDVAL) returning ID ';
if (qSlaveStocs.RecordCount>0)and(not qSlaveStocs.Eof) then begin
try
dbm.StartTransaction;
try
qMasterStocs.Prepare;
qMasterStocs.Params.ParamByName('IDOF').Value := qSlaveStocs.FieldByName('IDOF').Value;
qMasterStocs.Params.ParamByName('IDVAL').Value := qSlaveStocs.FieldByName('IDVAL').Value;
qMasterStocs.Params.ParamByName('VAL').Value := qSlaveStocs.FieldByName('VAL').Value;
qMasterStocs.Open;
dbm.Commit;

if (not qMasterStocs.Eof) then
mID := qMasterStocs.FieldByName('ID').AsInteger;
except
on E: EFDDBEngineException do begin
dbM.Rollback;
end;
on E: Exception do
end
finally
qMasterStocs.Close
end;
end;
Result := True
finally
if qSlaveStocs.Active then qSlaveStocs.Close;
dbSlave.Commit
end
end;

Some times I get a error that can not detect why if it should not be: Service failed on execute: [FireDAC][Phys][FB]invalid transaction handle (expecting explicit transaction start).
Other times I get the error: [FireDAC][DApt]-400. Update command updated [0] instead of [1] record. Possible reasons: update table does not have PK or row identifier, record has been changed/deleted by another user.
Event if all tables have PK's and if try to execute the same code again it works.
What do I do wrong?

The second question is how to keep service running if any errors occurs?
Here is the main execution method:

procedure TCSVSync.ServiceExecute(Sender: TService);
var
Count,SecBetweenRuns: Integer;
begin
SecBetweenRuns := FSyncPeriod * 60;
Count := SecBetweenRuns; //:= 0;
while not Terminated do
begin
Inc(Count);
if Count >= SecBetweenRuns then
begin
Count := 0;
try
if (not DataConnected) then begin
DataConnect;
if DataConnected then begin
SyncStoc;
end
end;
finally
DataDisconnect
end;
end;
ServiceThread.ProcessRequests(false)
end
end;

Comments

  1. I don't know about Firebird but for the second question you can set the service to restart automatically or the following code:

    while not Terminated do
    begin
    try
    // your code
    except
    on E: Exception do
    //write exception into the log
    end;
    ServiceThread.ProcessRequests(false)
    end

    ReplyDelete
  2. Cristian Peța I do have exception handling inside methods (e.g. in Syncstoc)

    ReplyDelete
  3. R Gosp but probably something outside your try except block fails.
    For example: dbSlave.Commit

    ReplyDelete
  4. Also what if DataConnect trows an exception? The service will simply close.

    ReplyDelete
  5. Attila Kovacs autocommit is off

    ReplyDelete
  6. Cristian Peța a error will log with connection failure and later try again, here it's ok

    ReplyDelete
  7. Dany Marmur Just for a testing purpose

    ReplyDelete
  8. R Gosp I havn't used FireDAC that much but in UniDAC I have seen this error 'invalid transaction handle' when I didn't use 'Retaining'.

    ReplyDelete
  9. Fred S. Using CommitRetaining and RollbackRetaining does not close the transaction but must be closed because each method (such as Syncstoc) works on each table in the slave and master database.
    Although, I'll check this out more thoroughly.

    ReplyDelete

Post a Comment