perl5-dbi / DBD-Oracle

Oracle database driver for the DBI module
http://search.cpan.org/dist/DBD-Oracle
18 stars 25 forks source link

Indefinite block on exit of forked client (Library version 19.20 only) #169

Closed justinschoeman closed 1 month ago

justinschoeman commented 1 year ago

Hi,

I was running 1.80 (installed through CPAN) with Oracle Client Library 18.5. Everything worked perfectly with this setup.

I then upgraded Oracle Client Library to 19.20, and did 'cpan -f DBD::Oracle' to upgrade that. This installed version 1.83.

After that, whenever a forked client (which opens its own db handle) exits, the process blocks forever on:

#0  0x00007f49995998d9 in pthread_cond_destroy@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#1  0x00007f498c8c5ea2 in sltspcdestroy () from /opt/oracle/instantclient_19_20/libclntshcore.so.19.1
#2  0x00007f498dce8f6b in kpucpstopthr () from /opt/oracle/instantclient_19_20/libclntsh.so.19.1
#3  0x00007f4990ac5ba7 in kpufhndl0 () from /opt/oracle/instantclient_19_20/libclntsh.so.19.1
#4  0x00007f49919fee91 in ora_db_destroy (dbh=dbh@entry=0x1c9e780, imp_dbh=imp_dbh@entry=0x1c806f0)
    at dbdimp.c:1262
#5  0x00007f49919f680b in XS_DBD__Oracle__db_DESTROY (my_perl=0x1776010, cv=<optimized out>)
    at ./Oracle.xsi:387

I manually re-installed version 1.80 (still with 19.20) and had exactly the same result.

As a temporary workaround, I have edited dbdimp.c around line 1266:

                        } else if ( (imp_dbh->envhp == imp_drh->envhp) && (SvTRUE(perl_get_sv("DBI::PERL_ENDING",0))) ) {
                                //OCIHandleFree_log_stat(imp_dbh, imp_dbh->envhp, OCI_HTYPE_ENV, status);
                                //if ( status == OCI_SUCCESS ) {
                                        imp_dbh->envhp = NULL;
                                        imp_drh->envhp = NULL;
                                //}
                        }

To comment out the final environment free call.

With the above change, forked children exit correctly - but there is a potential memory leak (not sure though, as this seems to only be on the final exit path, so OS should clean it up anyway?)

Thanks, Justin

justinschoeman commented 1 year ago

I have now tested with client library 21.11, and it has the same issue.

justinschoeman commented 1 year ago

To aid in replicating, this is the offending snippet:

  my $i = fork();
  if(!defined $i) {   ...  ; return 0; }
  if($i == 0) {
    LOG(10, "child started");
    # child must not close master dbh on exit
    $mdbh->{InactiveDestroy} = 1;
    # No signal handler for the child?
    $SIG{CHLD} = 'DEFAULT';
    $req->{'child'} = $$;
    # get child db handle
    $req->{'dbh'} = DBI->connect(..., { RaiseError => 0, AutoCommit => 1});
    if(!$req->{'dbh'}) { ...; exit -1; }

... child code here ...

    $req->{'dbh'}->disconnect;
    delete $req->{'dbh'};
    LOG(10, "Child done ($ret): " . Dumper($req));
    exit $ret;
  }

The final 'Child done' is logged, and the 'exit $ret' blocks indefinitely.

justinschoeman commented 1 year ago

More information. Got this SEGFAULT today:

kpedbg_dmp_stack()+396<-kpeDbgCrash()+204<-kpeDbgSignalHandler()+113<-skgesig_sigactionHandler()+258<-__sighandler()<-Perl_csighandler()+34<-__sighandler()<-pthread_cond_timedwait()+306<-sltspctimewait()+131<-kpucpincrtime()+113<-start_thread()+197

Not sure if it has anything to do with the above hackaround though.

The program + DBD::Oracle has been running flawlessly for more than 10 years on client library 11 and then 18. Something is deeply fubar with 19 and on though.

justinschoeman commented 1 month ago

No longer an issue since 1.83