Jerry Dodge
Jerry Dodge

Reputation: 27276

How should I implement an ADO connection in an HTTP TIdServerContext thread?

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

Answers (1)

Remy Lebeau
Remy Lebeau

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

Related Questions