2

I have a C++ application that connects to an Oracle database via the Qt QSqlDatabase interface. The main application establishes and uses the connection to the database along with starting a child process for unrelated other porpuses. To make this clear: the child process does not use any database relevant stuff.

The problem is now: If the main process gets terminated in an unusual way (it crashes or it gets killed by the user via the Task Manager), I can see that the database session on the Oracle server gets kept alive and does not timeout whatsoever. Absolutely reproducibly, however, the session gets cancelled immediatelly after I kill the child process manually.

As those dangling, orphaned sessions lead to some problems (the simplest beeing that the max session count on the server gets reached), I would really like all sessions to be closed as soon as possible.

My question now is: what is the mechanism that keeps a session alive on the server just because an irrelevant child process is still alive? How can I control this behavior, i.e. tell the oracle client to disconnect any sessions if the main application process dies?

Thanks in advance!

Philip Daubmeier
  • 14,584
  • 5
  • 41
  • 77
  • 1
    Child processes can inherit OS handles. Use Process Explorer to [inspect the file handles](https://i-technet.sec.s-msft.com/bb896653.processexplorer(en-us,MSDN.10).jpg), see if there are any related to the Oracle Server. Post the code spawning the child process, and verify that [bInheritHandles](https://msdn.microsoft.com/en-us/library/windows/desktop/ms724466%28v=vs.85%29.aspx) is FALSE. – sashoalm Apr 07 '15 at 08:48
  • 1
    PS: there is a way how to get file(socket) handle from OCI library but it requires a LOT of black magic. – ibre5041 Apr 07 '15 at 09:34
  • @sashoalm: thanks for your hint, but unfortunatelly this does not seem to be the reason. I start the child process via [QProcess](http://doc.qt.io/qt-5/qprocess.html#start). I just stepped into the Qt source code, QProcess starts the process with bInheritHandles == false (`{ sizeof(SECURITY_ATTRIBUTES), 0, false }`) – Philip Daubmeier Apr 07 '15 at 11:39
  • If killing the child process frees the session, it still has to be something about it. Did you inspect the child's handles? Anything suspicious there? Also what is the nature of the session connection - is it a socket, or something else? – sashoalm Apr 07 '15 at 12:24
  • simply compare output of netstat on both sides(client and server) before and after you fork a new process. Either the file handle is inherited from parent or it has something todo with QSqlDatabase heath check(if any). – ibre5041 Apr 07 '15 at 13:07
  • maybe netstat on Windows is not so detailed as on Linux, maybe you will need to use Filemon or TCPMon or how is it called. – ibre5041 Apr 07 '15 at 13:08
  • After killing a child process is main process remains active with closed connection or terminates too? – ThinkJet Apr 10 '15 at 10:41
  • @ThinkJet: the main process is completely terminated. – Philip Daubmeier Apr 10 '15 at 13:30
  • Is it possible to look if OCI.DLL remains in memory after termination of main process by searching in Process Explorer? Especially if OCI.DLL module used by child process? – ThinkJet Apr 10 '15 at 14:10
  • Another thing to look to: is it possible to rename/move/delete main executable file after terminating it from Task Manager? – ThinkJet Apr 10 '15 at 14:14
  • @ThinkJet: just checked those two things: (1) in procexp, the OCI.DLL module can be found in the main process but not in the child process. after termination of the main process, the child process lives but no OCI.DLL can be found at all (2) yes it is possible to move/rename the main process exe after it has been terminated (while child still lives). – Philip Daubmeier Apr 10 '15 at 15:33

2 Answers2

2

UPDATE https://bugreports.qt.io/browse/QTBUG-9350 and https://bugreports.qt.io/browse/QTBUG-4465

On Windows, the child process inherits sockets and file-descriptors even inheritFileDescriptors is set to false

Seems that the bug was fixed in QT5


A discussion about the issue on an Oracle thread:

https://community.oracle.com/thread/1048626

TL;DR; The oracle server does not "know" that the client has disappeared.

Some solutions: 1.There is a terminated connection detection feature: http://docs.oracle.com/cd/B19306_01/network.102/b14213/sqlnet.htm#sthref474

2.My advice is to try to implement a 'connection pool' if you use QOCI driver. Or you can use ODBC which has support for connection pooling.

valentin
  • 3,498
  • 15
  • 23
  • Thanks for your idea. However, I think this is not the issue in my case: the server _does_ know that the client disappeared - if I kill the child process via the Task Manager the database session on the server immediately gets closed. Therefore my question is why the child process keeps the session alive in the first place. – Philip Daubmeier Apr 10 '15 at 08:39
  • here can be 2 things: 1. server 'knows' : if session is ACTIVE, first will wait to finish then will continue to rollback uncommited work, and the status will be KILLED in v$session. 2. the server does not knows: maybe different types of killing (as on an UNIX system you have signals like ABORT, KILL, TERM), another complications can rise if this signals are traped and how they are handled after that. – valentin Apr 10 '15 at 09:42
  • even all queries where only SELECTs, the task for terminating a session can take a very long time. – valentin Apr 10 '15 at 09:44
  • but again the problem does not seem to be the cleanup of the session. There are two facts that can be observed: (1) as long as the child process lives the session on the server lives as well (even over days). (2) If the child process gets killed the session of the server gets closed, too. – Philip Daubmeier Apr 10 '15 at 10:21
  • so my question remains: why does the child process keep alive a session on the database server even if it does not have any database relevant stuff whatsoever. the child process never opened or had anything to do with a database connection. – Philip Daubmeier Apr 10 '15 at 10:23
  • Thanks for the update, that looks very promising - will try this immediately on monday! Especially the comment in Jeremy Friesners bug reports is interesing: `QProcess::startDetached()` looks like not inheriting handles by default - that may be a satisfactory solution for me. – Philip Daubmeier Apr 10 '15 at 19:12
  • Thanks a lot it worked! `QProcess::startDetached()` is just as fine for our application and it doesnt inherit any handles. Accepted answer! – Philip Daubmeier Apr 13 '15 at 08:51
1

Looks like main process didn't terminated successfully and awaits for child process termination in some place of finalization code before closing database connection.
From other side exceptional situation raised by abnormal termination of child process successfully propagated to parent process which starts finalization process and closes connection to Oracle.

So first suggestion is to check if child process properly reacts on kill() and terminate() calls, and even parent process try to terminate child in case of abnormal termination.

ThinkJet
  • 6,725
  • 24
  • 33
  • No, the parent process is terminated completely in this case (double checked via procexp). The child process can not be terminated by the parent in case of an abnormal crash because it abruptly stops execution (no exception that can be handled but immediate termination). – Philip Daubmeier Apr 10 '15 at 08:37
  • Automatic termination of child processes is possible via [Job Objects](https://msdn.microsoft.com/en-us/library/ms682409%28VS.85%29.aspx) in Windows like discussed in [this question](http://stackoverflow.com/questions/3342941/kill-child-process-when-parent-process-is-killed). If main process killed from Application tab of Task Manageк it's possible to handle this case and perform cleanup as discussed [here](http://stackoverflow.com/questions/18454763/how-to-handle-end-task-from-windows-task-manager-on-a-background-process). – ThinkJet Apr 10 '15 at 10:38
  • An automatic termination of the child process would be great. However, the job object does not seem to do its work... I implemented it like described in your links; SetInformationJobObject and AssignProcessToJobObject return true but if I kill the parent process the child process continues to run... – Philip Daubmeier Apr 10 '15 at 13:29