Skip to content

"Too many concurrent executions of the same request" raises when MaxStatementCacheSize > 0 and connection has lot of prepared statements. #9159

Description

@pavel-zotov

Two oddities were encountered during investigation of some application (with probably extremely wrong approach to statements control).
Consider following Python script (let its name be: chk_multiple_prepare_in_same_attachment.py):

import os
import argparse as ap
from firebird.driver import driver_config, connect

parser = ap.ArgumentParser()
parser.add_argument("fb_clnt", help="Path to FB client library")
parser.add_argument("query_cnt", nargs='?', type=int, default=30, help="Number of queries to generate")
parser.add_argument("protocol", nargs='?', default='inet', type = str.lower, choices = ('xnet', 'inet', 'inet4', 'local'), help="Protocol to be used for connection.")
args = parser.parse_args()

assert os.path.isfile(args.fb_clnt), "FB client library not found: '%s'" % args.fb_clnt
assert args.query_cnt > 0

driver_config.fb_client_library.value = args.fb_clnt

#########################
###  S E T T I N G S  ###
#########################
DB_NAME = 'employee'
CHECK_QUERY = "select count(*) from rdb$pages where rdb$page_number = ?"
#########################

dsn = ('' if args.protocol == 'local' else args.protocol + '://') + str(DB_NAME)

with connect(dsn, user='SYSDBA', password='masterkey') as con:
    print(con.info.version)
    con.begin()
    cur = con.cursor()
    cur.execute("select rdb$config_name, rdb$config_value from rdb$config where rdb$config_name = 'MaxStatementCacheSize'")
    k,v = cur.fetchone()
    print(k, '=', v)
    ps_lst = []
    for i in range(args.query_cnt):
        print(f'{i+1:6} / {args.query_cnt}')
        ps_lst.append( cur.prepare( CHECK_QUERY ) )
        cur.execute( ps_lst[-1], (i,) )
    print('Doing rollback...')
    con.rollback()
    print('Done. Now closing connection...')
print('Connection closed. Bye-bye')

/*
It is assumed that:
Python firebird-driver has been installed;
databases.conf has employee alias that points to existing employee.fdb;
security.db contains regular SYSDBA/masterkey
*/

### ISSUE-1 ###

Let us change alias employee`` by adding non-zero MaxStatementCacheSize``` paameter:

employee = $(dir_sampleDb)/employee.fdb
{
    MaxStatementCacheSize = 2M
}

(it seems that one may to set here ANY reasonable value; i've tried 1K, 8K, 16K, 512K, 1M, 2M, 50M and 256M)

Now run script:
%PYTHON3X_HOME%\chk_multiple_prepare_in_same_attachment.py C:\FB\50SS\fbclient.dll 1010
-- where:

  • C:\FB\50SS\fbclient.dll -- full path to running FB 5.x client library;
  • 1010 -- number of parametrized statements to be prepared within same transaction.

Console output will be:

5.0.5.1888
MaxStatementCacheSize = 268435456
     1 / 1010
     2 / 1010
     3 / 1010
...
   999 / 1010
  1000 / 1010
  1001 / 1010

But further:

  1002 / 1010
Traceback (most recent call last):
  File "C:\FBTESTING\qa\misc\chk_multiple_prepare_in_same_attachment-p.py", line 35, in <module>
    ps_lst.append( cur.prepare( CHECK_QUERY ) )
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python3x\Lib\site-packages\firebird\driver\core.py", line 4034, in prepare
    return self._connection._prepare(operation, self._transaction)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python3x\Lib\site-packages\firebird\driver\core.py", line 1853, in _prepare
    stmt = self._att.prepare(tra._tra, sql, self.__sql_dialect)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python3x\Lib\site-packages\firebird\driver\interfaces.py", line 1167, in prepare
    self._check()
  File "C:\Python3x\Lib\site-packages\firebird\driver\interfaces.py", line 141, in _check
    raise self.__report(DatabaseError, self.status.get_errors())
firebird.driver.types.DatabaseError: Too many concurrent executions of the same request

-- and application finishes with abend at this point (and no any message in the firebird.log).

NB (again): this outcome will be the same for any MaxStatementCacheSize.

### ISSUE-2 ###

Now let change alias settings like this:

employee = $(dir_sampleDb)/employee.fdb
{
    MaxStatementCacheSize = 0
}

And run script with some BIG value of parameter N2:
``%PYTHON3X_HOME%\chk_multiple_prepare_in_same_attachment.py C:\FB\50SS\fbclient.dll 50000```
(yes, we want to prepare 50'000 statements within same connection)

This script will complete without errors.
But since the moment after all 50K FREE_STATEMENTS + DETACH_DATABASE completed and up to return control to OS one need to wait about 7 minutes.
This is console when i run this script and redirect its output to console splitter with requirement to show timestamps (mtee /t):

13:31:31.676 49998
13:31:31.679 49999
13:31:31.681 Doing rollback...
13:31:31.694 Done. Now closing connection... ---------- [ 1 ]
13:38:32.032 Connection closed. Bye-bye --------------- [ 2 ]

Trace:

2026-09-22T13:31:31.6820 (15692:0000000004FD2340) EXECUTE_STATEMENT_FINISH
	employee (ATT_44, SYSDBA:NONE, NONE, TCPv6:::1/56397)
	C:\Python3x\python.exe:14480
		(TRA_181, CONCURRENCY | WAIT | READ_WRITE)

Statement 50089:
-------------------------------------------------------------------------------
select count(*) from rdb$pages where rdb$page_number = ?

param0 = integer, "49999"

0 records fetched
      0 ms

2026-09-22T13:31:31.6820 (15692:0000000004FD2340) CLOSE_CURSOR
	employee (ATT_44, SYSDBA:NONE, NONE, TCPv6:::1/56397)
	C:\Python3x\python.exe:14480

Statement 50089:
-------------------------------------------------------------------------------
select count(*) from rdb$pages where rdb$page_number = ?

2026-09-22T13:31:31.6820 (15692:0000000004FD2340) ROLLBACK_TRANSACTION
	employee (ATT_44, SYSDBA:NONE, NONE, TCPv6:::1/56397)
	C:\Python3x\python.exe:14480
		(TRA_181, CONCURRENCY | WAIT | READ_WRITE)
      0 ms, 1 fetch(es), 1 mark(s)

2026-09-22T13:31:31.6940 (15692:0000000004FD2340) FREE_STATEMENT
	employee (ATT_44, SYSDBA:NONE, NONE, TCPv6:::1/56397)
	C:\Python3x\python.exe:14480

Statement 50089:
-------------------------------------------------------------------------------
select count(*) from rdb$pages where rdb$page_number = ?

2026-09-22T13:31:31.6950 (15692:0000000004FD2340) FREE_STATEMENT
	employee (ATT_44, SYSDBA:NONE, NONE, TCPv6:::1/56397)
	C:\Python3x\python.exe:14480

Statement 50088:
-------------------------------------------------------------------------------
select count(*) from rdb$pages where rdb$page_number = ?

2026-09-22T13:31:31.6950 (15692:0000000004FD2340) FREE_STATEMENT
	employee (ATT_44, SYSDBA:NONE, NONE, TCPv6:::1/56397)
	C:\Python3x\python.exe:14480

............................................
SIMILAR ~49999 'FREE_STATEMENT' BLOCKS BELOW
............................................

2026-09-22T13:32:04.9200 (15692:0000000004FD2340) FREE_STATEMENT
	employee (ATT_44, SYSDBA:NONE, NONE, TCPv6:::1/56397)
	C:\Python3x\python.exe:14480

Statement 90:
-------------------------------------------------------------------------------
select count(*) from rdb$pages where rdb$page_number = ?

2026-09-22T13:32:04.9210 (15692:0000000004FD2340) DETACH_DATABASE
	employee (ATT_44, SYSDBA:NONE, NONE, TCPv6:::1/56397)
	C:\Python3x\python.exe:14480

What engine(?) did for ~7 minutes after connection has been closed ?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions