mirror of
https://github.com/FirebirdSQL/firebird-qa.git
synced 2025-01-22 13:33:07 +01:00
152 lines
6.5 KiB
Python
152 lines
6.5 KiB
Python
#coding:utf-8
|
|
|
|
"""
|
|
ID: issue-2890
|
|
ISSUE: 2890
|
|
TITLE: mon$memory_usage: Sorting memory should be reported as owned by the statement
|
|
DESCRIPTION:
|
|
We open cursor that executes statement that requires to sort 10'000 rows of 32K width.
|
|
Cursor fetches only half of this rows number and breaks from loop (but we do not close this cursor).
|
|
This causes huge memory consumation and we can compare value in mon$memory_used that was before
|
|
this query start.
|
|
In order to check ticket issue we filer mon$ data by criteria 'where m.mon$stat_group = 3' (i.e. STATEMENT level)
|
|
and also we have to check memory usage only for statement that we are running. Because of this, we add special
|
|
tag in the SQL query '/* HEAVY_SORT_TAG */' - and search for that.
|
|
|
|
Runs this test on firebird.conf with default TempCacheLimit show following values of differences:
|
|
* for SuperServer: ~68.1 Mb;
|
|
* for Classic: ~9.4 Mb
|
|
Because of this, difference of mon$memory_used is compared with DIFFERENT threshold depending on
|
|
FB ServerMode (see act.vars['server-arch']).
|
|
JIRA: CORE-2477
|
|
FBTEST: bugs.core_2477
|
|
NOTES:
|
|
[16.11.2021] pcisar
|
|
This test is too complicated and fragile, and it's IMHO not worth to be implemented
|
|
[24.07.2022] pzotov
|
|
Test was totally re-implemented. No async call of ISQL, waiting/killing etc.
|
|
|
|
Checked on Windows: 3.0.8.33535 (SS/CS), 4.0.1.2692 (SS/CS), 5.0.0.591
|
|
Checked on Linux: 4.0.1.2692 (SS/CS) - needed to increase number of rows to be sorted.
|
|
"""
|
|
|
|
import subprocess
|
|
from pathlib import Path
|
|
import pytest
|
|
from firebird.qa import *
|
|
import time
|
|
|
|
init_script = """
|
|
create or alter view v_mon as
|
|
select *
|
|
from (
|
|
select
|
|
m.mon$stat_group as stat_gr
|
|
,rpad( decode(m.mon$stat_group, 0,'0:database', 1,'1:attachment', 2,'2:transaction', 3,'3:statement', 4,'4:call'), 15,' ') as stat_type
|
|
,m.mon$memory_used as memo_used
|
|
,m.mon$memory_allocated as memo_allo
|
|
,m.mon$max_memory_used as max_memo_used
|
|
,m.mon$max_memory_allocated as max_memo_allo
|
|
,m.mon$stat_id as stat_id
|
|
,coalesce( s.mon$attachment_id, t.mon$attachment_id, a.mon$attachment_id, -999 ) as att_id
|
|
,coalesce( s.mon$transaction_id, t.mon$transaction_id, -999 ) as trn_id
|
|
,coalesce( s.mon$statement_id, -999) as sttm_id
|
|
,coalesce( decode( s.mon$state, 0,'idle', 1,'running', 2,'stalled' ), 'n/a') as stm_state
|
|
,lower(right( coalesce(trim(coalesce(a.mon$remote_process, a.mon$user)), ''), 20 )) as att_process -- isql.exe or Garbage Collector or Cache Writer
|
|
,lower(left( coalesce(cast(s.mon$sql_text as varchar(8100)),''), 128 )) as sql_text
|
|
from mon$memory_usage m
|
|
left join mon$statements s on m.mon$stat_group = 3 and m.mon$stat_id = s.mon$stat_id
|
|
left join mon$transactions t on
|
|
m.mon$stat_group = 2 and m.mon$stat_id = t.mon$stat_id
|
|
or m.mon$stat_group = 3 and m.mon$stat_id = s.mon$stat_id and t.mon$transaction_id = s.mon$transaction_id
|
|
left join mon$attachments a on
|
|
m.mon$stat_group = 1 and m.mon$stat_id = a.mon$stat_id
|
|
or m.mon$stat_group=2 and m.mon$stat_id = t.mon$stat_id and a.mon$attachment_id = t.mon$attachment_id
|
|
or m.mon$stat_group=3 and m.mon$stat_id = s.mon$stat_id and a.mon$attachment_id = s.mon$attachment_id
|
|
|
|
) t
|
|
where t.stat_gr = 3 and sql_text containing 'HEAVY_SORT_TAG'
|
|
order by stat_type, stat_id
|
|
;
|
|
|
|
create or alter view v_gather_mon as
|
|
select
|
|
v.att_process
|
|
,replace(replace(replace(replace(v.sql_text, ascii_char(10),' '), ascii_char(13),' '),' ',' '),' ',' ') as sql_text
|
|
,v.stat_type
|
|
,v.stm_state
|
|
,v.att_id
|
|
,v.trn_id
|
|
,v.sttm_id
|
|
,v.memo_used
|
|
,v.memo_allo
|
|
from v_mon v
|
|
where v.att_id is distinct from current_connection
|
|
;
|
|
commit;
|
|
"""
|
|
|
|
db = db_factory(init=init_script)
|
|
|
|
act = python_act('db')
|
|
|
|
def result_msg(a_diff_value, a_min_threshold):
|
|
return ( ('OK, expected: increased significantly.') if a_diff_value > a_min_threshold else ('BAD! Did not increased as expected. Difference: ' + "{:d}".format(a_diff_value)+'.') )
|
|
|
|
@pytest.mark.version('>=3.0')
|
|
def test_1(act: Action, capsys):
|
|
|
|
##################
|
|
ROWS_TO_BE_SORTED = 10000
|
|
MIN_DIFF_THRESHOLD = 10000000 if 'classic' in act.vars['server-arch'].lower() else 60000000
|
|
##################
|
|
|
|
heavy_sql_sttm = "with a as (select 1 i from rdb$types rows 200) select /* HEAVY_SORT_TAG */ distinct lpad('', 32500, uuid_to_char(gen_uuid())) s from a x, a y"
|
|
#heavy_sql_sttm = "select /* HEAVY_SORT_TAG */ guid_str from sp_produce_guid(5000) order by 1"
|
|
|
|
map_result = {}
|
|
with act.db.connect() as con_worker:
|
|
|
|
cur_worker = con_worker.cursor()
|
|
cur_wrk_ps = cur_worker.prepare(heavy_sql_sttm)
|
|
|
|
for m in ('beg','end'):
|
|
with act.db.connect() as con_monitor:
|
|
cur_monitor=con_monitor.cursor()
|
|
cur_monitor.execute('select * from v_gather_mon')
|
|
|
|
for r in cur_monitor:
|
|
map_result[m] = (r[3], r[7]) # ('idle' | 'stalled', memo_used)
|
|
|
|
assert map_result.get(m), f"No rows returned from v_gather_mon for m='{m}'"
|
|
|
|
if m == 'beg':
|
|
cur_worker.execute(cur_wrk_ps)
|
|
for i in range(0, ROWS_TO_BE_SORTED):
|
|
r = cur_worker.fetchone()
|
|
|
|
# After this loop statement with huge sort will remain in stalled state
|
|
# (its mon$statements.mon$state must be 2).
|
|
# We can now gather mon$ info second time (in a NEW connection)
|
|
# and then evaluate DIFFERENCE.
|
|
|
|
#------------------------------------------------------------------------------------------
|
|
|
|
assert map_result['beg'][0].strip() == 'idle' and map_result['end'][0].strip() == 'stalled'
|
|
|
|
expected_msg = 'DELTA of mon$memory_used increased significantly.'
|
|
diff = map_result['end'][1] - map_result['beg'][1]
|
|
|
|
if diff > MIN_DIFF_THRESHOLD:
|
|
print(expected_msg)
|
|
else:
|
|
print('FAIL on %s, MON$MEMORY_USED increased for less than MIN_DIFF_THRESHOLD = %d' % (act.vars['server-arch'], MIN_DIFF_THRESHOLD) )
|
|
for k,v in sorted(map_result.items()):
|
|
print(k,':',v)
|
|
print('diff = %d' % diff)
|
|
|
|
|
|
act.expected_stdout = expected_msg
|
|
act.stdout = capsys.readouterr().out
|
|
assert act.clean_stdout == act.clean_expected_stdout
|