You are not logged in.
Pages: 1
Hi @ab, can you post a simple use of TSQLRest.AsynchRedirect ? I tried but without luck.
Thanks in advance.
Esteban
Offline
My idea is to implement an asynchronous long work service with the possibility to the cancel it. What would be the best way do it ?
Thanks in advance.
Esteban
Offline
@ab, Is AsynchRedirect working ?
Thanks.
Esteban
Offline
Yes, it is used on production, with no stability nor performance problem.
We use it namely for two things:
- when calling callbacks for events notifications: one high-performance main process thread call the virtual interface to mark the event, which is then asynchronously notified in the background to the subscribed clients.
- when adding some information to some slow storage, using a persistence service interface (but we also use AsynchBatch for that).
Offline
Good, both things are very useful for me, can you give me an example of how call AsynchRedirect, mainly I can't understood the parameters.
procedure AsynchRedirect(const aGUID: TGUID; const aDestinationInterface: IInvokable; out aCallbackInterface); overload;
- aGUID: is the source service that I want redirect ? What about the code in the method, must be empty ?
- aDestinationInterface: is any interface where the real process is executed ?
- aCallbackInterface: the aDestinationInterface must have it a callback interface ?
Thanks in advance.
Esteban
Offline
In short, aDestinationInterface will be executed after aCallbackInterface is called.
aCallbackInterface will contain a "fake" class implementing aGUID.
Both aDestinationInterface and aCallbackInterface should be of one interface type defined with aGUID.
Offline
Thank you very much @ab, I could make it work !! but neither in this life nor any other I could understand the use of this function. Can you update the documentation ?
Anyway, this is an example for any other that need use this function (adapted from example 31):
interface declaration:
...
type
ILongWorkCallback = interface(IInvokable)
['{425BF199-19C7-4B2B-B1A4-A5BE7A9A4748}']
procedure WorkFinished(const workName: string; timeTaken: integer);
procedure WorkFailed(const workName, error: string);
end;
ILongWorkService = interface(IInvokable)
['{09FDFCEF-86E5-4077-80D8-661801A9224A}']
procedure StartWork(const workName: string; const onFinish: ILongWorkCallback);
function TotalWorkCount: Integer;
end;
IAsynchLongWorkService = interface(IInvokable)
['{CFA559E5-B181-45BB-84AB-7279DDDA51A9}']
procedure StartWork(const workName: string; const onFinish: Integer);
end;
...
initialization
TInterfaceFactory.RegisterInterfaces([
TypeInfo(ILongWorkService),TypeInfo(ILongWorkCallback),TypeInfo(IAsynchLongWorkService)]);
class declaration:
...
type
TLongWorkService = class(TInterfacedObject,ILongWorkService)
protected
fCallbackInterface: IAsynchLongWorkService;
fDestinationInterface: IAsynchLongWorkService;
public
procedure StartWork(const workName: string; const onFinish: ILongWorkCallback);
function TotalWorkCount: Integer;
end;
TAsynchLongWorkService = class(TInterfacedObject,IAsynchLongWorkService)
protected
fCallback: ILongWorkCallback;
fWorkName: string;
fTotalWorkCount: Integer;
public
procedure StartWork(const workName: string; const onFinish: Integer);
function TotalWorkCount: Integer;
end;
...
class implementation:
...
{ TLongWorkAsyncService }
procedure TLongWorkService.StartWork(const workName: string; const onFinish: ILongWorkCallback);
begin
if fCallbackInterface=nil then begin
fDestinationInterface := TAsynchLongWorkService.Create;
ServiceContext.Request.Server.AsynchRedirect(IAsynchLongWorkService,fDestinationInterface,fCallbackInterface);
end;
onFinish._AddRef; // avoid GPF and the interface is available in asynch. routine
fCallbackInterface.StartWork(workName,Integer(onFinish));
end;
function TLongWorkService.TotalWorkCount: Integer;
begin
result := MaxInt; // this shows the asynchronous functioning
end;
...
{ TAsynchLongWorkService }
procedure TAsynchLongWorkService.StartWork(const workName: string; const onFinish: Integer);
var tix: Int64;
begin
InterlockedIncrement(fTotalWorkCount);
fCallback := ILongWorkCallback(onFinish);
fWorkName := workName;
TSQLLog.Add.Log(sllInfo,'%.Execute(%) started',[self,fWorkName]);
tix := GetTickCount64;
Sleep(5000+Random(1000)); // some hard work
if Random(100)>20 then
fCallback.WorkFinished(fWorkName,GetTickCount64-tix) else
fCallback.WorkFailed(fWorkName,'expected random failure');
TSQLLog.Add.Log(sllInfo,'%.Execute(%) notified',[self,fWorkName]);
fCallback._Release; // release the interface out this function
end;
function TAsynchLongWorkService.TotalWorkCount: Integer;
begin
result := fTotalWorkCount;
end;
Best regards.
Esteban
Offline
@ab,
It is the only search result about AsynchRedirect that has sample code.
Can you make documentation more clear or give a simple sample on this subject? because as you mentioned in the doc it seems using AsynchRedirect is the only good way to prevents locks (For example calling server again in the callback)
Offline
Hi, gyus
AsynchRedirect start the threads sequentially.
That is, when a second request is made, new "longworker" is queued to start after the completion of the previous one.
Is there any way to run in parallel?
Offline
Well, responding to myself ...
I followed the example of this post that implements the execution in sequence.
Using example 31 (Project31LongWorkServer), where TLongWorkServiceThread is (of course) a Thread, the worker runs in parallel.
Last edited by macfly (2019-05-10 23:39:04)
Offline
Pages: 1