Open mvorisek opened 2 years ago
I tried a while to reproduce this but I can't. I amended your test case linked in this issue to run multiple iterations. But the memory usage just always stays the same even with multiple operations. Can you still reproduce this? If you can, how much is the memory usage increase? Knowing the amount of bytes often helps tracking down the leak if automatic tools fail. I do get a bunch of memory errors from the OCI library itself though, but that's not something we can do much about.
Please fork https://github.com/mvorisek/php-src/tree/fix_oci8_mem_leak branch (https://github.com/php/php-src/compare/master...1310df60f3), it contains php-src test case.
There is indeed something weird going on. I don't know the root cause yet, but from my analysis something strange happens in the 6th iteration. When the statement fetch is executed the memory usage increases, which is normal. But the strange thing is that in the 6th iteration it increases by 600 bytes while in the previous iterations it increases with 152 bytes. For other iterations that 152 bytes is released again, and it looks like for iteration 6 also only 152 from the 600 bytes are freed somehow.
Okay. So the row fetching code calls a callback php_oci_lob_create()
and in the 6th iteration (so the 6th time calling that function), the call to zend_hash_index_update_ptr()
inside php_oci_lob_create()
will cause the connection->descriptors
hashtable to grow, which results in the memory increase you're seeing.
From the documentation inside php_oci8_int.h
:
HashTable *descriptors; /* descriptors hash, used to flush all the LOBs using this connection on commit */
but I don't get it, why would we need that for fetching instead of inserting ? I'm going to inspect that code more now.
Okay I can confirm that not adding the descriptor to that table here gets rid of the leak:
It leaks because in the example test code we never commit anything, we just fetch rows. I think we should not add the descriptor to that table if we're just fetching, because otherwise we will never clear that table. In any case, I did find the root cause, but I don't know enough about OCI8 to fix this myself. So I'm marking this as verified and my comments can be useful for the person fixing this.
@nielsdos but why does this leak only with long query/CLOB? With small query/CLOB, there is no leak.
@nielsdos but why does this leak only with long query/CLOB? With small query/CLOB, there is no leak.
I don't think I checked the reason for this, or at least I don't remember a reason.
This issue could be move to proper repository as the extension has been decoupled from core.
Description
The following code:
Resulted in this output:
I have verified, the leak is consistent across PHP 7.4 - master and only
oci8
ext is affected. Withpdo_oci
there is no leak for exactly the same query. See https://github.com/atk4/data/runs/7199098438For smaller query, even with CLOB, there is also no leak.
PHP Version
tested PHP 7.4, 8.0, 8.1 and master
Operating System
tested linux and Windows