From ca59cc988bf847cefbaf1a0b0f986da5e1388a91 Mon Sep 17 00:00:00 2001 From: pavel-zotov Date: Sat, 17 Aug 2024 23:17:45 +0300 Subject: [PATCH] Added/Updated tests\bugs\gh_6706_test.py: Confirmed bug on 4.0.0.2336. Solved since 4.0.0.2341. Checked on 6.0.0.438, 5.0.2.1478, 4.0.6.3142. --- tests/bugs/gh_6706_test.py | 108 +++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 tests/bugs/gh_6706_test.py diff --git a/tests/bugs/gh_6706_test.py b/tests/bugs/gh_6706_test.py new file mode 100644 index 00000000..103fc041 --- /dev/null +++ b/tests/bugs/gh_6706_test.py @@ -0,0 +1,108 @@ +#coding:utf-8 + +""" +ID: issue-6706 +ISSUE: https://github.com/FirebirdSQL/firebird/issues/6706 +TITLE: Memory leak when running EXECUTE STATEMENT with named parameters [CORE6475] +DESCRIPTION: + We create stored procedure with PARAMS_COUNT input parameters. + Then EXECUTE BLOCK is generated with call of this SP via EXECUTE STATEMENT which applies EXCESS modifier to all arguments. + Value of memory_info().rss is obtained (for appropriate server process), then run execute block MEASURES_COUNT times + and after this - again get memory_info().rss value. + Ratio between current and initial values of memory_info().rss must be less than MAX_RATIO. +NOTES: + [17.08.2024] pzotov + 1. Problem did exist in FB 4.x up to snapshot 4.0.0.2336. + Commit: https://github.com/FirebirdSQL/firebird/commit/4dfb30a45b767994c074bbfcbb8494b8ada19b33 (23-jan-2021, 15:26) + Before this commit ratio for SS was about 5..6 for SS and about 8..9 for CS. + Since 4.0.0.2341 memory consumption was reduced to ~1.6 ... 1.9 + 2. Database must be created with FW = ON otherwise ratio for all snapshots is about 1.5 (and this seems weird). + 3. Test duration is about 35s. + + Checked on 6.0.0.438, 5.0.2.1478, 4.0.6.3142; 4.0.0.2336, 4.0.0.2341. +""" + +import psutil +import pytest +from firebird.qa import * +import time + +########################### +### S E T T I N G S ### +########################### + +# How many input parameters must have procedure: +PARAMS_COUNT = 1000 + +# How many times we call procedures: +MEASURES_COUNT = 1000 + +# Maximal value for ratio between +# new and initial memory_info().rss values: +# +MAX_RATIO = 3 +############# + +db = db_factory(async_write = False) +act = python_act('db') + +#-------------------------------------------------------------------- + +def get_server_pid(con): + with con.cursor() as cur: + cur.execute('select mon$server_pid as p from mon$attachments where mon$attachment_id = current_connection') + fb_pid = int(cur.fetchone()[0]) + return fb_pid + +#-------------------------------------------------------------------- + +@pytest.mark.version('>=4.0.0') +def test_1(act: Action, capsys): + + with act.db.connect() as con: + + sp_ddl = """ + create or alter procedure sp_test( + """ + params_lst = '\n'.join( [ (',' if i else '') +f'p_{i} int' for i in range(PARAMS_COUNT) ] ) + sp_ddl = '\n'.join( ("create or alter procedure sp_test(", params_lst, ") returns(x int) as begin x = 1; suspend; end") ) + con.execute_immediate(sp_ddl) + con.commit() + + server_process = psutil.Process(get_server_pid(con)) + + params_lst = ','.join( [ f':p_{i}' for i in range(PARAMS_COUNT) ] ) + passed_args = ','.join( [ f'excess p_{i} := 1' for i in range(PARAMS_COUNT) ] ) + + srv_memo_rss_init = int(server_process.memory_info().rss / 1024) + srv_memo_vms_init = int(server_process.memory_info().vms / 1024) + + cur = con.cursor() + for k in range(MEASURES_COUNT): + + es_sql = f""" + execute block returns(x int) as + begin + execute statement ('select p.x * {k} from sp_test({params_lst}) p') ({passed_args}) + into x; + suspend; + end + """ + cur.execute(es_sql) + for r in cur: + pass + + srv_memo_rss_curr = int(server_process.memory_info().rss / 1024) + srv_memo_vms_curr = int(server_process.memory_info().vms / 1024) + + memo_ratio = srv_memo_rss_curr / srv_memo_rss_init + + SUCCESS_MSG = 'Ratio between memory values measured before and after loop: acceptable' + if memo_ratio < MAX_RATIO: + print(SUCCESS_MSG) + else: + print( 'Ratio: /* perf_issue_tag */ POOR: %s, more than threshold: %s' % ( '{:.2f}'.format(memo_ratio), '{:.2f}'.format(MAX_RATIO) ) ) + + act.expected_stdout = SUCCESS_MSG + act.stdout = capsys.readouterr().out + assert act.clean_stdout == act.clean_expected_stdout