Reputation: 27276
I've been working on an HTTP server (TIdHTTPServer
) handling the OnCommandGet
event. I have just created an inherited object of the TIdServerContext
and have overridden its constructor and destructor. Now, I'd like to move my Database connection from a prior custom session object into this context thread. The server's KeepAlive
property is enabled.
The goal is to recycle database connections implemented within each session. Currently every request spawns a new ADO connection, and I'd like to avoid that. I understand KeepAlive
doesn't necessarily mean an entire login/logout session, but rather just maintaining a constant connection to save resources.
My question is, how should I utilize my inherited TIdServerContext
object to maintain its own consistent database connection (TADOConnection
) - which can be used (in the COM rules (CoInitialize
)) by each request?
I'm assuming that whatever code I put in the OnCommandGet
event handler runs in the same thread context as the inherited TIdServerContext
- is this correct? Essentially meaning... Is utilization of both TIdServerContext.OnRun
and TIdHTTPServer.OnCommandGet
in the same thread and COM safe?
Upvotes: 0
Views: 490
Reputation: 596397
OnCommandGet
does indeed run in the same thread that TIdServerContext
runs in.
You cannot assign your own OnRun
handler, because TIdCustomTCPServer
already uses that event to trigger its DoExecute()
method, which TIdHTTPServer
overrides to trigger its OnCommamd...
events.
If you want to reuse DB object(s) across multiple requests from the same client, you can initialize COM and create the DB object(s) in the OnConnect
event, and free the DB object(s) and release COM in the OnDisconnect
event. However, using these events only helps if the client requests a keepalive on each request, which is not a guarantee. The TIdHTTPServer.KeepAlive
property only lets the server honor keepalives if asked for, it does not force clients to ask for them. If not asked for, every request will trigger its own pair of OnConnect/OnDisconnect
events.
ADO is COM-based, so it has thread affinity. Also, COM should be initialized only once per thread. As such, you are better off initializing COM and creating the DB object(s) when the thread starts, and free the DB object(s) and release COM when the thread ends. By default, Indy servers use a new thread for each client connection, so by switching to this model, you still benefit from similar behavior as if you use the OnConnect/OnDisconnect
events. Whether the client uses keepalives or not, the effect is the same.
However, this model gives you more flexibility, because now you can enable thread pooling in the server (by assigning a TIdSchedulerOfThreadPool
to the TIdHTTPServer.Scheduler
property) and can then reuse the DB object(s) across multiple client connections that use the same thread over time.
Derive a new class from TIdThreadWithTask
, override its BeforeExecute()
method to initialize COM and create the DB object(s), and override its AfterExecute()
method to free the DB object(s) and release COM. Assign this class to the scheduler's ThreadClass
property.
To access the DB object(s) in your OnCommand...
event, typecast the AContext.Yarn
property to TIdYarnOfThread
, then typecast its Thread
property to your derived class, then you can access its members as needed.
You can still derive a custom TIdServerContext
class to handle per-client data (Indy does not implement context pooling), and use the DB object(s) on a per-request basis as needed. Just separate the DB object creation/destruction from the context class.
Upvotes: 3