You are not logged in.
Hi,
I've extended your "06 - Remote JSON REST Service" example, so that server-side calls some external libraries using COM in order to return value when requested.
Simply, I need to expose results of some local COM calls over HTTP.
Thus I modified your example to use TSQLite3HttpServer and TSQLite3HttpClient instead of Named Pipes.
Now the function which uses COM is called in TServerForm.Button1Click (yeah, I've added a Form to the server).
Now when I run the server and click it's Button1, it works as expected - obtains the values from COM call.
Unfortunately, if I call ServerForm.Button1Click from my Service (descendant of TSQLRestServer) it fails, saying CoInitialize was not called.
Is that kind of thread/context problem?
It is really weird, once again - if I click the button itself, it works. If I run the client which - over HTTP - requests the server to call the OnClick code, it fails on CreateComObject saying "CoInitialize" was not called. If I click the button itself again, it works fine again.
Offline
You should call CoInitialize for each thread which uses COM.
It's by Microsoft's design, not mine:
Remarks
You need to initialize the COM library on a thread before you call any of the library functions except CoGetMalloc, to get a pointer to the standard allocator, and the memory allocation functions.
The HTTP server has its own thread(s) so you'll need to call CoInitialize for each thread.
You may do this by overriding the THttpApiServer.Create constructor (if you use THttpApiServer for the HTTP server class). Without forgetting to call CoUninitialize in an overriden constructor.
Offline
The HTTP server has its own thread(s) so you'll need to call CoInitialize for each thread.
You may do this by overriding the THttpApiServer.Create constructor (if you use THttpApiServer for the HTTP server class). Without forgetting to call CoUninitialize in an overriden constructor.
I'm using TSQLite3HttpServer which, as I can see, tries to use THttpApiServer if possible, if not - it falls back to the higher-level ones.
That would require me not only to override methods in one class (HttpApiServer) but also the others (just in case the first one won't be used).
Not to mention the need to modify TSQLite3HttpServer in order to use "my" classes.
I wonder if there's any better "place" (method) to hook in thread creation?
Offline
Hi,
first of all it seems the call to CoInitialize need to be done in Execute; method.
second of all, for some reason it need to be called for each request - otherwise I get the same error as previous on the second request (even though CoUninitialized is called in thread Destructor.)
are there any low-level tricks behind handling Request, which may cause this behavior?
once again,
1. CoInitialize is called at the very beginning of Execute; method
2. CoUninitialize is called in Destructor
3. calling CoCreateObject in the registered REST Server method works only for the first time - second call throws exception saying "CoInitialize was not called"
4. it works if I move CoInitialize to REST Server method...
any ideas?
Offline
I've modified TSQLite3HttpServer.Create to use one more parameter : const AServerInstance: THttpServerGeneric = nil.
see this (modified) part of code
{$ifndef USETCPPREFIX}
try
// first try to use fastest http.sys
if AServerInstance = nil then
fHttpServer := THttpApiServer.Create(false)
else
fHttpServer:= AServerInstance;
if fHttpServer.InheritsFrom(THttpApiServer) then
for i := 0 to high(aServers) do begin
and here is "my" server:
TmyServer = class(THttpApiServer)
protected
destructor Destroy; override;
procedure Execute; override;
end;
procedure TMyServer.Execute;
begin
CoInitialize(nil);
inherited;
end;
destructor TmyServer.Destroy;
begin
CoUninitialize();
inherited;
end;
the above example works on the first request.
Now if I change the parent class to THttpServer (so it won't use kernel.sys) the example does not work at all.
Even though the CoInitialize is called (I've debugged it), when calling CoCreateObject in request it fails saying CoInitialize wasn't called ;]
any ideas here?
I've built a trivial sample to prove that it's not about threads:
and, yes, it works. each call to GetValue works as expected.
type
ttestthread = class(TThread)
private
fClient: IeClient;
protected
destructor Destroy; override;
procedure Execute; override;
end;
procedure ttestthread.Execute;
var s: string;
begin
CoInitialize(nil);
fClient.Init;
while true do
begin
Sleep(1000);
fClient.GetValue('Random', s);
s:= s + 'abc';
end;
end;
destructor ttestthread.Destroy;
begin
CoUninitialize();
inherited;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
client: IeClient;
t: ttestthread;
begin
client:= Te.Create;
if not client.Init() then
raise Exception.Create('Could not init Client!');
t:=ttestthread.Create(true);
t.fClient:= client;
t.Resume;
end;
any ideas what are differences between this and THttpServer?
some context management? Thread pooling?
Offline
I have the same problem.
I've tried some:
1. overrided BeginCurrentThread/EndCurrentThread from TSQLRestServer, calling CoInitialize(nil)/CoUnitialize respectively.
2. implemented HttpServer.OnHttpThreadStart/OnHttpThreadTerminate for TSQLHttpServer, calling CoInitialize(nil)/CoUnitialize respectively.
I'm using ADOConnection directly.
TServRest = class(TSQLRestServer)
public
procedure BeginCurrentThread(Sender: TThread); override;
procedure EndCurrentThread(Sender: TThread); override;
end;
TServServer = class(TSQLHttpServer)
private
procedure DoThreadStart(Sender: TThread);
procedure DoThreadTerminate(Sender: TThread);
public
constructor Create(const aPort: AnsiString;
const aServers: array of TSQLRestServer; const aDomainName: AnsiString='+';
aHttpServerKind: TSQLHttpServerOptions=HTTP_DEFAULT_MODE; ServerThreadPoolCount: Integer=32;
aHttpServerSecurity: TSQLHttpServerSecurity=secNone; const aAdditionalURL: AnsiString='';
const aQueueName: SynUnicode=''; aHeadersUnFiltered: boolean=false); overload;
end;
constructor TServServer.Create;
begin
inherited Create(aPort, aServers, aDomainName, aHttpServerKind, ServerThreadPoolCount,
aHttpServerSecurity, aAdditionalURL, aQueueName, aHeadersUnFiltered);
HttpServer.OnHttpThreadStart := DoThreadStart;
HttpServer.OnHttpThreadTerminate := DoThreadTerminate;
end;
Offline
Actually, it's working now using BeginCurrentThread/EndCurrentThread.
I've changed the code to call SynOleDb.CoInit/CoUninit instead from ActiveX unit—maybe because it's thread safe.
Also, I've instantiate the ADOConnection, inside a MainDataModule, using a TSynLocker.
I have just one question: the MainDataModule is a threadvar. Is it really needed using a TSynLocker on BeginCurrentThread/EndCurrentThread?
Offline
If it is a threadvar, no need to use a lock to access it.
That was my first thought until receiving a "BORDBK50.DLL error" (I believe was that DLL, but sometime I got error, sometimes not - I was trying to reproduce to post here but it didn't fail). Maybe it was because I was calling ActiveX.CoInitialize() instead SynOleDB version... anyway, it isn't in production yet, I will do mor tests. Thanks.
Offline
But the whole DataModule is a threadvar.
On OnCreate event there is just calls to conn.Close; fill user and passwd; conn.Open.
In my mind it should be thread-safe... Didn't raise exception anymore, it fixed itself
Offline
Good to know about this solution.
I have a legacy application, where I created a connection pool.
Each thread has its own connection.
But this application is not using mORMot.
Out of curiosity, when is the datamodule destroyed?
Is automatically destroyed when the thread ends?
Offline
Good to know about this solution.
I have a legacy application, where I created a connection pool.
Each thread has its own connection.
But this application is not using mORMot.
That's why I posted this today https://synopse.info/forum/viewtopic.ph … 382#p33382 - for help/inspire more people with the same issues.
Out of curiosity, when is the datamodule destroyed?
Is automatically destroyed when the thread ends?
It's created on BeginCurrentThread and destroyed on EndCurrentThread, overrided methods.
Offline
Interesting.
My connection pool is not much different.
The difference is that I don't use a threadvar.
I maintain a list of connections indexed by the thread id.
When starting the thread the connection is created for your id.
At the end of the thread I destroy the connection by id.
Offline
What you created is some kind of a connection pool: one connection per thread.
You don't recreate the connection for each request.
Of course, I understand that the connections are not reused from one thread to another.
But if you use a thread pool, like the mORMot HTTP server, then it is not an issue to not reuse the connections.
Also note that in the long-term, some DB connections tends to break, or may have resource leaks.
A typical problem is when the connection is not used for a while, there may be a timeout on the server side, and the connection is closed. Then you get an exception on the client side, the next time you use it.
With a few number of clients, we have seen it often. For instance, during the night there is almost no service usage, so first time in the morning, the DB connections raise errors.
What we do in SynDB is to release and reinitialize a connection after some time or number of requests, even if the thread is still the same.
Offline
What you created is some kind of a connection pool: one connection per thread.
You don't recreate the connection for each request.
As my service was defined as sicSingle, it wouldn't be one connection per request?
EDIT: ah, Ok. I think only the implementation of the interface will be create per request, not threads.
Of course, I understand that the connections are not reused from one thread to another.
But if you use a thread pool, like the mORMot HTTP server, then it is not an issue to not reuse the connections.Also note that in the long-term, some DB connections tends to break, or may have resource leaks.
A typical problem is when the connection is not used for a while, there may be a timeout on the server side, and the connection is closed. Then you get an exception on the client side, the next time you use it.
That was exactly what I would test today... and got an exception:
{
"errorCode":500,
"error":
{"EOleException":{"EOleException":"Connection failure"}}
}
With a few number of clients, we have seen it often. For instance, during the night there is almost no service usage, so first time in the morning, the DB connections raise errors.
What we do in SynDB is to release and reinitialize a connection after some time or number of requests, even if the thread is still the same.
You know that I can't use SynDB due the legacy code... Do you have any tip so I can implement something similar?
Which method should I override to put a timer? Maybe an external service doing fake pings?
Last edited by mdbs99 (2020-11-13 12:04:56)
Offline
Maybe the easiest way is inform the server to release threads in a period of time, then I can setup this period as being less than the current timeout of DBMS.
I will search about it, but if you @ab or anybody can tell me first, I will appreciate.
Offline
In my connection pool I store it when it was last requested.
This allows you to recreate the connection by inactivity, and by demand.
But I don't know if this would work in this case.
You have to check where and when the connection is requested by mORMot.
In any case, it may be easier to implement a timer to check the expired connections in the pooll, instead of checking and releasing the threads.
Last edited by macfly (2020-11-13 14:55:59)
Offline
@macfly
If I understood correct the concept of your pool, you could still get an exception by inactivity. I mean, if no client sent a request, you can't be able to remove any other inactivity connection... but only by a timer (do you use?).
Anyway, I still thinking that would be nice and easier if the server finished threads by end of a (configurable) period.
At least in my case, I don't care if next request will be the first that will create all threads again, as all were gone eventually. Does mORMot can do that?
Offline
@mdbs99
I do not use mORMot in this application. I use a persistence layer that I created myself.
There is a method that requests a connection whenever necessary, so I don't need a timer. It occurs on demand.
If there is no activity for a while, the connection will die, but on the other hand it is not being used.
When a new request occurs, its expiration will be checked and if it is expired it is renewed.
In the case of mORMot is need to analyze how and when the connection is requested, - but I believe that it will not be possible to do the same.
In this case the timer would be better.
At least in my case, I don't care if next request will be the first that will create all threads again, as all were gone eventually. Does mORMot can do that?
Yes, this is the question.
You will have to dig deep into the code and tests to see how and when threads and connections are created and released by mORMot.
Offline
...When a new request occurs, its expiration will be checked and if it is expired it is renewed.
That it was I meant when said "...if no client sent a request, you can't be able to remove any other inactivity connection", i.e., first you need a request then you could do other things like renew connections.
In the case of mORMot is need to analyze how and when the connection is requested, - but I believe that it will not be possible to do the same.
In this case the timer would be better.
Yes, this is the question.
You will have to dig deep into the code and tests to see how and when threads and connections are created and released by mORMot.
But I'm writing here to get some help from who knows the internal framework
@ab?
Last edited by mdbs99 (2020-11-13 17:19:57)
Offline
That it was I meant when said "...if no client sent a request, you can't be able to remove any other inactivity connection", i.e., first you need a request then you could do other things like renew connections.
Yes, in my scenario, with on-demand verification, the connection is renewed per thread.
One thread does not interfere with the connection of the other, including renewal.
But is this a problem? What error could occur if a connection that is not being used is idle on a thread that is also not in use?
Offline
...
But is this a problem? What error could occur if a connection that is not being used is idle on a thread that is also not in use?
The DBMS will kill connection in a period of time on idle, but the thread won't know about it; then the client will get an exception.
Offline
I mentioned above that I save the time that connection was last requested and renew before reaching the timeout.
So this problem does not occur in my case.
The question is whether this would apply to mORMot.
I don't know if in mORMot there is a way to intercept when the connection is requested.
Offline
I mentioned above that I save the time that connection was last requested and renew before reaching the timeout.
So this problem does not occur in my case.
I'm not sure if I can use like you did. In my mind, to do that I will always need to check the connection before, for each method on server.
I can save the time that the thread got the connection, then code a function that will check that time and renew the connection before give it to the program... but doing that every time is error prone, IMHO.
Would be nice 1) mORMot kill threads in a period of time or 2) I kill my threads in a period of time.
But I'm waiting @ab say something, as I need that working but I would like to do in a best way considering how the framework works.
The question is whether this would apply to mORMot.
I don't know if in mORMot there is a way to intercept when the connection is requested.
Other problem is that I'm not using mORMot DB (SynOleDB). If I did, I wouldn't have any problem, as Arnaud already check this internally, renewing connections time to time.
Offline
Just to mention: TADOConnection has a KeepConnection property, which means that. And it works.
But I need my services more stronger than that. For example, if the network has down/broke, KeepConnection is not enough and an exception will occur.
So, when I got this exception was because it was running in a develop database, which is renew every day, dropping all connections of course.
Last edited by mdbs99 (2020-11-13 18:59:11)
Offline