Is there a way to hook into the WndProc of a dbx user session?
Background:
dbx DataSnap uses Indy components for TCP communication. In its simplest form, a DataSnap server is an Indy TCP server accepting connections. When a connection is established, Indy creates a thread for that connection which handles all requests for that connection.
Each of these user connections consume resources. For a server with a couple hundred simultaneous connections, those resources can be expensive. Many of the resources could be pooled, but I don’t want to always acquire and release a resource each time it is needed.
Instead, I’d like to implement a idle timer. After a thread finishes with a resource, the timer would start. If the thread accesses the resource before the timer has elapsed, the resource would still be “assigned” to that thread. But if the timer elapses before the next access, the resource would be released back to the pool. The next time the thread needs the resource, another resource would be acquired from the pool.
I haven’t found a way to do this. I’ve tried using SetTimer but my timer callback never fires. I assume this is because Indy’s WndProc for the thread isn’t dispatching WM_TIMER. I have no control of the “execution loop” for this thread, so I can’t easily check to see if an event has been signaled. In fact, none of my code for this thread executes unless the thread is handling a user request. And in fact, I’m wanting code to execute outside of any user request.
Solutions to the original question or suggestions for alternative approaches would be equally appreciated.
We tried to implement something to share resources across user threads using TCP connections (no HTTP transport, so no SessionManager), but ran into all sorts of problems. In the end we abandoned using individual user threads (set
LifeCycle := TDSLifeCycle.Server) and created our ownFResourcePoolandFUserList(bothTThreadList) in ServerContainerUnit. It only took 1 day to implement, and it works very well.Here’s a simplified version of what we did:
When a user connects, we check
FResourcePoolfor theTResourcethe user needs. If it exists, we increment the resource’sUserCountproperty. When the user is done, we decrement theUserCountproperty and setLastSeen. We have aTTimerthat fires every 60 seconds that frees any resource with aUserCount = 0andLastSeengreater than 60 seconds.The
FUserListis very similar. If a user hasn’t been seen for several hours, we assume that their connection was severed (because our client app does an auto-disconnect if the user has been idle for 90 minutes) so we programmatically disconnect the user on the server-side, which also decrements their use of each resource. Of course, this means that we had to create a session variable ourselves (e.g.,CreateGUID();) and pass that to the client when they first connect. The client passes the session id back to the server with each request so we know whichFUserListrecord is theirs. Although this is a drawback to not using user threads, it is easily managed.