Postgres -- oops, my fault

scrottie on 2007-05-20T06:28:29

Earlier, I was bitching about Postgres and it's "server closed the connection unexpectedly" message.



Here's a refresher. A fellow programmer wrote a few lines of code to exercize an OO-ification I had done, where logic got moved into an object. He made it use one of two subclasses and switch back and forth. Approximately 8,000 lines after the switch, the database would ... vanish. diff'ing traces turned up nothing, and the query that died was often the same but sometimes not. Stumped. The object that gets created forks and runs some code CPU bound code in the fork but doesn't touch the database. I decided to make it close all of the $dbh's it inherited from the parent process when it forked. Boom -- server starts barfing on queries immediately after the object switch happens and the $dbh is closed in the child. Aha!



Solution: destroy the DESTROY routine so the "tell the server to go away" logic never runs. This code in the child process made the server stop going away:



    do {
        # destroy all $dbh's in the forked process, but do so in a way that doesn't
        # affect the parent's connection to the database
        use PadWalker;
        my $i;
        while(1) {
            $i++;
            my $sf = eval { PadWalker::peek_my($i) } or last;
            if(exists $sf->{'$dbh'}) {
                no strict 'refs';
                my $package = ref ${ $sf->{'$dbh'} };  # DBI::db::DESTROY
                *{$package.'::DESTROY'} = sub { };
                ${ $sf->{'$dbh'} } = undef;
            }
        }
    };




Yes, that's ugly, nasty, and evil. Seems like there would be a better way to do this. I guess one kludgey but less evil work-around would be to have the server disconnect, fork, and then reconnect to the database. So, that's the bug that had me stumped for several coding sessions and nearly a week.



-scott


InactiveDestroy

autarch on 2007-05-20T14:22:20

Check out the DBI docs for this handle attribute. I think it's what you need to fix this problem.

In the past...

Dom2 on 2007-05-21T10:06:32

I've ensured that whenever I fetch a dbh object, I fetch it from a hash keyed by pid. Or, use the pid in a closure and generate a new one if I am not currently the same pid. e.g.

  {
    my ($pid, $dbh);
    sub get_dbh {
      if ($$ != $pid) {
        $dbh = DBI->connect(...);
      }
      return $dbh;
    }
  }
(that's off the top of my head, so probably won't work verbatim)