ISIGest
RTC Expired
Posts: 121
|
|
« on: November 20, 2014, 01:02:57 PM » |
|
Hi, I've set the AutoSessionLive to 99 (seconds) and I've an autoping function at 10seconds interval. All work fine...but if the client call a function to server and the server tackes longer than 99 seconds, the client session was closed sorry for my english...
|
|
|
Logged
|
|
|
|
D.Tkalcec (RTC)
|
|
« Reply #1 on: November 20, 2014, 01:46:58 PM » |
|
Sessions can not expire while they are being used and Session expiration time is updated before a Session is released, which prevents Sessions from expiring shortly after use, even if they were being used for an extended period of time. Unless you are manually calling "Session.UnLock" before you start executing your long-running process on the Server, the Session will not expire.
If you are calling "Session.UnLock" on the Server before you start executing your longer running process, then you need to take the time of your Server processing into account when defining the AutoSessionLive parameter, because any Client using Sessions can make your Session expire when it is not in use.
Another thing to keep in mind is that holding a Session locked while a long running process is being executed on the Server, the Client could lose or drop a connection to the Server (because of poor network or a Proxy closing idle connections). If that happens and the Client opens a new connection to the Server, then tries to lock the same Session while the Server is still keeping that Session under lock (while processing the last request), the 2nd Session lock attempt would fail, so the Client might think that the Session has expired (even though it is still valid, but in use by the Server).
Best Regards, Danijel Tkalcec
|
|
|
Logged
|
|
|
|
ISIGest
RTC Expired
Posts: 121
|
|
« Reply #2 on: November 20, 2014, 02:30:40 PM » |
|
This is my disconnection when the client work for long time:
|
|
|
Logged
|
|
|
|
D.Tkalcec (RTC)
|
|
« Reply #3 on: November 20, 2014, 02:39:59 PM » |
|
Since you are NOT manually unlocking the Session, the Session will remain locked and will NOT expire as long as your code is executing on the Server. And once your code completes execution, the Session expiration time will be updated based on the time when your process has finished execution, which means that the earliest time that your Session could expire, is 99 seconds after the Server has finished processing your last remote function (since your AutoSessionsLive parameter is 99 seconds). But ... the Client does not distinguish between an expired and a locked Session, so it is possible that the Session is still locked on the Server when a connection drops, because of which the Client, after establishing a new connection and trying to lock the same Session again, gets a "401 Session locked" response, which results in the Client to request a new Session, since the same Session can not be used from more than one connection at a time.
Best Regards, Danijel Tkalcec
|
|
|
Logged
|
|
|
|
ISIGest
RTC Expired
Posts: 121
|
|
« Reply #4 on: November 20, 2014, 02:55:48 PM » |
|
The problem is on client... The client call a function, get the result and begin work for long time....(and this lock the autoping) if the work is more of 99 seconds the connection is lost.
|
|
|
Logged
|
|
|
|
ISIGest
RTC Expired
Posts: 121
|
|
« Reply #5 on: November 20, 2014, 03:15:03 PM » |
|
I try to use a TTimer to ping server manually but the Call is not fired while the client process the result from other function calls.
|
|
|
Logged
|
|
|
|
D.Tkalcec (RTC)
|
|
« Reply #6 on: November 20, 2014, 03:36:48 PM » |
|
TTimer component won't work at all if your Main Thread is busy. And you can not use the TRtcHttpClient component to post a new request or make a new remote function call if you are still executing some code from inside a RTC event triggered after receiving the last result. But there is no reason for using a TTimer component, since the AutoSessionsPing property already does that for you. What you do need, is to change the way you are processing the Result on the Client side, so the AutoSessionsPing property can continue working in the background while you are handling the result.
If you are using the TRtcClientModule component in blocking mode (the Execute method), you will need to set the MultiThreaded property on the TRtcHttpClient component to TRUE, so that all the RTC events and communication code can run from background theads while you are processing the results you have received from the Server. Naturally, this also means that any RTC events from which you might need access to the Main Thread will have to be manually synchronized with the Main Thread, so make sure you know how to use the "inMainThread" and "Sync" methods.
if you are using the TRtcClientModule component in event-driven non-blocking mode (the Call method), in addition to setting the MultiThreaded property on the TRtcHttpClient component to TRUE (and all the other things explained above), you will also need to use the "PostInteractive" procedure from the OnResult event of the TRtcResult component to exit the RTC event, so the component can continue being used in the background for other tasks (like the AutoSessionsPing feature), while you execute your code from the Main Thread.
Best Regards, Danijel Tkalcec
|
|
|
Logged
|
|
|
|
ISIGest
RTC Expired
Posts: 121
|
|
« Reply #7 on: November 20, 2014, 03:45:06 PM » |
|
Ok I use TRtcClientModule in non-blocking mode with Call method and Multithread := True. If I use the PostInteractive to continue the background the "OnResult" evet will be fired in MainThread and I don't want this!!! If I process my OnResult in MainThread the application will be locked
|
|
|
Logged
|
|
|
|
D.Tkalcec (RTC)
|
|
« Reply #8 on: November 20, 2014, 04:04:11 PM » |
|
An alternative to using the PostInteractive method, if you do NOT want your code to run in the Main Thread, would be to use the TRtcQuickJob component, which allows you to execute your code from a background thread, without manually writing your own TThread class descendant.
If your code is already written so that it works from a background thread, moving it to the OnExecute event of the TRtcQuickJob component should not be an issue. But please keep is that the Result and Data objects which you have access to from the OnResult event on the TRtcResult component will be destroyed by the TRtcClientModule component immediately after exiting the event, which will most likely happen even before the OnExecute event of the TRtcQuickJob component starts running, so you need to send a copyOf these objects to the OnExecute event of the TRtcQuickJob component.
If you only need the Result received from the Server, simply use "Result.copyOf" as a paremeter to the Post method of the TRtcQuickJob component. If you also need the Data object, then you can create a TRtcRecord or a TRtcArray, so you can put Data.copyOf and Result.copyOf inside as separate elements.
Also, please note that the object(s) you send to the OnExecute method by passing them as a parameter to the Post method of the TRtcQuickJob component will be destroyed automatically after the OnExecuge event completes execution, so you should NOT keep pointers to these objects anywhere else in memory.
Best Regards, Danijel Tkalcec
|
|
|
Logged
|
|
|
|
ISIGest
RTC Expired
Posts: 121
|
|
« Reply #9 on: November 20, 2014, 04:23:45 PM » |
|
Can you post a simple method to call QuickJob.Post(myData); from OnResult with Data.copyOf and Result.CopyOf??
|
|
|
Logged
|
|
|
|
D.Tkalcec (RTC)
|
|
« Reply #10 on: November 20, 2014, 04:32:38 PM » |
|
Example using TRtcRecord ...
var mydata:TRtcRecord; begin mydata:=TRtcRecord.Create; mydata.asObject['data']:=Data.copyOf; mydata.asObject['result']:=Result.copyOf; QuickJob.Post(mydata); end;
Example using TRtcArray ...
var mydata:TRtcArray; begin mydata:=TRtcArray.Create; mydata.asObject[0]:=Data.copyOf; mydata.asObject[1]:=Result.copyOf; QuickJob.Post(mydata); end;
Best Regards, Danijel Tkalcec
|
|
|
Logged
|
|
|
|
ISIGest
RTC Expired
Posts: 121
|
|
« Reply #11 on: November 20, 2014, 04:42:34 PM » |
|
Thanks for help but in OnExecute event I cannot get the Data and Result values...I got an access violation: var LData, LResult: TRtcValue; begin LData := TRtcValue(Data.asRecord.asObject['Data']); LResult := TRtcValue(Data.asRecord.asObject['Result']);
if not(LResult.asRecord.isType['DataSets'] = rtc_Record) then <--- I GOT ACCESS VIOLATION Exit;
if LResult.asRecord.asRecord['DataSets'].Count = 0 then Exit;
|
|
|
Logged
|
|
|
|
D.Tkalcec (RTC)
|
|
« Reply #12 on: November 20, 2014, 04:57:20 PM » |
|
Never use explicit typecasts, unless you know the exact class being used. Since TRtcValue is a container, it will NOT be copied when using the "copyOf" method. Instead, the "copyOf" method will only return the object contained inside the bottom-level TRtcValue instance, which is why your implementation caused an Access Violation.
If you want to access Data and Result the same way you do in the OnResult event, you can create your own TRtcValue containers for LData and LResult ...
var LData, LResult: TRtcValue; begin LData:=TRtcValue.Create; // Create TRtcValue container for "Data" LResult:=TRtcValue.Create; // Create TRtcValue container for "Result" LData.asObject:=Data.asRecord.asObject['Data']; // assign "Data" object to the container for "Data" LResult.asObject:=Data.asRecord.asObject['Result']; // assign "Result" object to the container for "Result" Data.asRecord.asObject['Data']:=nil; // remove "Data" pointer from the original container Data.asRecord.asObject['Result']:=nil; // remove "Result" pointer from the original container try // Use LData and LResult here, just like you would use Data and Result in the OnResult event ... finally LResult.Free; LData.Free; end; end;
Alternatively, if you do not want to have a try/finally block, but want the Data to be destroyed automatically with the Data instance, you can also assigning your TRtcValue containers in-place of the original objects, like this ...
var LData, LResult: TRtcValue; begin LData:=TRtcValue.Create; // Create TRtcValue container for "Data" LData.asObject:=Data.asRecord.asObject['Data']; // assign "Data" object to the "LData" container Data.asRecord.asObject['Data']:=nil; // remove "Data" pointer from the original container Data.asRecord.asObject['Data']:=LData; // put "LData" container in place
LResult:=TRtcValue.Create; // Create TRtcValue container for "Result" LResult.asObject:=Data.asRecord.asObject['Result']; // assign "Result" object to the "LResult" container Data.asRecord.asObject['Result']:=nil; // remove "Result" pointer from the original container Data.asRecord.asObject['Result']:=LResult; // put "LResult" container in place
// Now, you can use LData and LResult here, just like you would use Data and Result in the OnResult event. // Since LData and LResult are now inside Data, they will be destroyed automatically with Data. end;
Or ... if you know you have a TRtcRecord in Result and a TRtcFunctionInfo in Data, you could do this:
var LResult:TRtcRecord; LData:TRtcFunctionInfo; begin LResult:=nil; LData:=nil;
if Data.asRecord.isType['Result']=rtc_Record then LResult:=Data.asRecord.asRecord['Result'] else Exit; // ... or handle the case when you receive the wrong type
if Data.asRecord.isType['Data']=rtc_Function then LData:=Data.asRecord.asFunction['Data'] else Exit; // ... or handle the case when you receive the wrong type
// Now you can use LResult directly as a rtc_Record object and LData as a rtc_Function object ... { Example: if not(LResult.isType['DataSets'] = rtc_Record) then Exit; // ERROR?? if LResult.asRecord['DataSets'].Count = 0 then Exit; } end;
Best Regards, Danijel Tkalcec
|
|
|
Logged
|
|
|
|
|