On Fri, Jan 20, 2017 at 9:13 PM, Rasmus Lerdorf <ras...@lerdorf.com> wrote:

> I have been chasing a really odd fd leak which I haven't been able to
> reproduce manually. The code involved is about as simple as you can get:
>
> class C {
>     public static function fn($arg) {
>         $pdo = new PDO('sqlite:/var/dbs/file.db');
>         $query = $pdo->prepare("SELECT * FROM tb WHERE id = ?");
>         $query->execute([$arg]);
>         $result = $query->fetch(PDO::FETCH_ASSOC);
>
>         if (!$result) {
>             throw new RuntimeException("not found" );
>         }
>
>         return [
>             "a" => $result['a'],
>             "b" => $result['b']
>         ];
>     }
> }
>
> The symptoms are:
>
>    - It always starts with a max_execution timeout in the *$pdo->prepare()*
>    call
>    - Many hours after this timeout the httpd process runs out of FDs and
>    lsof shows the httpd process with ~1000 fds open on */var/dbs/file.db*
>
> The file.db itself is only read from PHP and isn't being updated in the
> background by anything. mtime is months ago, but this happens sporadically
> on a single server out of dozens about once every couple of days. These
> servers are getting hit hard so it takes hundreds of millions of requests
> to trigger whatever condition is causing this.
>
> It feels like it should be something related to timeouts and the
> *sqlite_handle_closer()
> *call:
>
> https://github.com/php/php-src/blob/PHP-7.0/ext/pdo_
> sqlite/sqlite_driver.c#L155-L175
>
> but I have been staring at this code and comparing it to the other pdo
> drivers and I don't see what makes *pdo_sqlite* special here.
> This is on PHP 7.0.10 but this code hasn't changed significantly in 7.1 and
> master. Anyone see anything in PDO that could possibly prevent the
> *sqlite3_close()* call from being executed, especially in edge conditions
> around an execution timeout?
>
> -Rasmus
>

>From the docs for sqlite3_close():

> If the database connection is associated with unfinalized prepared
statements or unfinished sqlite3_backup objects then sqlite3_close() will
leave the database connection open and return SQLITE_BUSY
<http://www.sqlite.org/rescode.html#busy>. If sqlite3_close_v2() is called
with unfinalized prepared statements and/or unfinished sqlite3_backups,
then the database connection becomes an unusable "zombie" which will
automatically be deallocated when the last prepared statement is finalized
or the last sqlite3_backup is finished. The sqlite3_close_v2() interface is
intended for use with host languages that are garbage collected, and where
the order in which destructors are called is arbitrary.

That sounds like it could be the source of the issue.

Nikita

Reply via email to