Subject | Memory leak in PHP with Firebird |
---|---|
Author | Dan Wilson |
Post date | 2004-08-17T16:07:13Z |
Using: Apache 2.0.50, PHP 4.3.8, Fedora Core 2
I have been using the Firebird/Interbase interface in PHP with Firebird 1.5 for some weeks now, with great success. However, I am now experiencing a problem that makes me think I have missed a fundamental step somewhere. Most of the PHP scripts issue one or two or so queries, build the html page, and return back to Apache without error. I have one script that needs to perform several hundred thousand queries: in general, it is a loop:
1. Read a record from a temporary file
2. Issue a query to determine if the record exists in the database.
3. If it doesn't exist, issue a query to get the next generator value for the primary key.
4. Issue either an insert or an update query.
I perform a hard commit every 10,000 loops, and I call set_time_limit periodically to make sure the php script doesn't time out. In the current version of the code, I took out the ibase_prepare calls and just call ibase_query without preparing the query first. I know that is less efficient, and I will put the prepare's back once I have the basic code working.
When I run this script, limiting the record loop to 100 records, it completes successfully. If I let it run with the entire temporary file, I eventually get a php error in the Apache error log: Allowed memory size of 8388608 bytes exhausted (tried to allocate 10240 bytes). I could obviously increase the script's memory limit, but I suspect the problem here is a memory leak, and just increasing the limit isn't the best way to handle a leak: I'd rather find and fix the leak.
So, is there something I need to do with the ibase/fb php interface to avoid a leak? Here is a sample portion of the script:
$query = "select OPTION_ID from OPTIONS where OPTION_SYMBOL = ?";
$Q = ibase_query( $trans, $query, $record['Symbol'] );
if ( !$Q )
{
echo "Unexpected query error for $query<br>\n";
return false;
}
$R = ibase_fetch_row( $Q );
if ( $R == false )
{
// security does not exist in database, insert a new record
ibase_free_result( $Q );
$recnum = $this->GetNextGenerator( $generator );
$query = "select STOCK_ID from STOCKS Where STOCK_SYMBOL = ?";
$Q = ibase_query( $trans, $query, $record['UnderSym'] );
if ( !$Q )
{
echo "Unexpected query error for $query<br>\n";
return false;
}
$R = ibase_fetch_row( $Q );
if ( $R == false )
{
echo "Unable to locate underlying symbol $record[UnderSym] for option $record[Symbol]<br>\n";
return false;
}
$stock_id = $R[0];
ibase_free_result( $Q );
$query = <<<EOD
insert into OPTIONS (OPTION_ID, STOCK_ID, OPTION_SYMBOL,
OPTION_STRIKE_PRICE, OPTION_EXPIR_DATE, IS_ACTIVE, SEQ_NUM)
values (?, ?, ?, ?, ?, ?, ?)
EOD;
ibase_query( $trans, $query, $recnum, $stock_id,
$record['Symbol'], $record['StrikePrice'],
date( 'm/d/Y', $record['ExpirDate'] ), 1, $seq_num );
}
else
{
// security already exists, update the current record
$recnum = $R[0];
ibase_free_result( $Q );
$query = "select STOCK_ID from STOCKS Where STOCK_SYMBOL = ?";
$Q = ibase_query( $trans, $query, $record['UnderSym'] );
if ( !$Q )
{
echo "Unexpected query error for $query<br>\n";
return false;
}
$R = ibase_fetch_row( $Q );
if ( $R == false )
{
echo "Unable to locate underlying symbol $record[UnderSym] for option $record[Symbol]<br>\n";
return false;
}
$stock_id = $R[0];
ibase_free_result( $Q );
$query = <<<EOD
update OPTIONS set STOCK_ID = ?, OPTION_STRIKE_PRICE = ?,
OPTION_EXPIR_DATE = ?, IS_ACTIVE = 1, SEQ_NUM = ?
where OPTION_ID = ?
EOD;
ibase_query( $trans, $query, $stock_id, $record['StrikePrice'],
date( 'm/d/Y', $record['ExpirDate'] ), $seq_num, $recnum );
}
return true;
Thanks for any light anyone can shed on this!
Dan.
I have been using the Firebird/Interbase interface in PHP with Firebird 1.5 for some weeks now, with great success. However, I am now experiencing a problem that makes me think I have missed a fundamental step somewhere. Most of the PHP scripts issue one or two or so queries, build the html page, and return back to Apache without error. I have one script that needs to perform several hundred thousand queries: in general, it is a loop:
1. Read a record from a temporary file
2. Issue a query to determine if the record exists in the database.
3. If it doesn't exist, issue a query to get the next generator value for the primary key.
4. Issue either an insert or an update query.
I perform a hard commit every 10,000 loops, and I call set_time_limit periodically to make sure the php script doesn't time out. In the current version of the code, I took out the ibase_prepare calls and just call ibase_query without preparing the query first. I know that is less efficient, and I will put the prepare's back once I have the basic code working.
When I run this script, limiting the record loop to 100 records, it completes successfully. If I let it run with the entire temporary file, I eventually get a php error in the Apache error log: Allowed memory size of 8388608 bytes exhausted (tried to allocate 10240 bytes). I could obviously increase the script's memory limit, but I suspect the problem here is a memory leak, and just increasing the limit isn't the best way to handle a leak: I'd rather find and fix the leak.
So, is there something I need to do with the ibase/fb php interface to avoid a leak? Here is a sample portion of the script:
$query = "select OPTION_ID from OPTIONS where OPTION_SYMBOL = ?";
$Q = ibase_query( $trans, $query, $record['Symbol'] );
if ( !$Q )
{
echo "Unexpected query error for $query<br>\n";
return false;
}
$R = ibase_fetch_row( $Q );
if ( $R == false )
{
// security does not exist in database, insert a new record
ibase_free_result( $Q );
$recnum = $this->GetNextGenerator( $generator );
$query = "select STOCK_ID from STOCKS Where STOCK_SYMBOL = ?";
$Q = ibase_query( $trans, $query, $record['UnderSym'] );
if ( !$Q )
{
echo "Unexpected query error for $query<br>\n";
return false;
}
$R = ibase_fetch_row( $Q );
if ( $R == false )
{
echo "Unable to locate underlying symbol $record[UnderSym] for option $record[Symbol]<br>\n";
return false;
}
$stock_id = $R[0];
ibase_free_result( $Q );
$query = <<<EOD
insert into OPTIONS (OPTION_ID, STOCK_ID, OPTION_SYMBOL,
OPTION_STRIKE_PRICE, OPTION_EXPIR_DATE, IS_ACTIVE, SEQ_NUM)
values (?, ?, ?, ?, ?, ?, ?)
EOD;
ibase_query( $trans, $query, $recnum, $stock_id,
$record['Symbol'], $record['StrikePrice'],
date( 'm/d/Y', $record['ExpirDate'] ), 1, $seq_num );
}
else
{
// security already exists, update the current record
$recnum = $R[0];
ibase_free_result( $Q );
$query = "select STOCK_ID from STOCKS Where STOCK_SYMBOL = ?";
$Q = ibase_query( $trans, $query, $record['UnderSym'] );
if ( !$Q )
{
echo "Unexpected query error for $query<br>\n";
return false;
}
$R = ibase_fetch_row( $Q );
if ( $R == false )
{
echo "Unable to locate underlying symbol $record[UnderSym] for option $record[Symbol]<br>\n";
return false;
}
$stock_id = $R[0];
ibase_free_result( $Q );
$query = <<<EOD
update OPTIONS set STOCK_ID = ?, OPTION_STRIKE_PRICE = ?,
OPTION_EXPIR_DATE = ?, IS_ACTIVE = 1, SEQ_NUM = ?
where OPTION_ID = ?
EOD;
ibase_query( $trans, $query, $stock_id, $record['StrikePrice'],
date( 'm/d/Y', $record['ExpirDate'] ), $seq_num, $recnum );
}
return true;
Thanks for any light anyone can shed on this!
Dan.