6
0
mirror of https://github.com/FirebirdSQL/firebird-qa.git synced 2025-01-22 21:43:06 +01:00
firebird-qa/tests/functional/session/test_ext_conn_pool_01.py

783 lines
39 KiB
Python

#coding:utf-8
#
# id: functional.session.ext_conn_pool_01
# title: External Connections Pool, functionality test 01
# decription:
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# ATTENTION! CURRENT FB INSTANCE DOES NOT PARTICIPATE IN THIS TEST WORK! TEMPORARY INSTANCE IS USED!
# RESULT OF THIS TEST HAS NO "LINK: WITH CURRENTLY CHECKED FB SERVERMODE! DIFF OUTPUT MUST BE CHECKED!
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#
# Basic check of External Connections Pool. We verify here following:
# * ability to reuse connections from ECP in case running ES/EDS by "frequent" attachments
# * ability to distinguish connect/disconnect from reuse connections within apropriate
# DB-level trigger (system variable RESETTING = faluse | true)
# * ability to get information about ECP state: total number of active and idle connections.
#
# See $FB_HOME/doc/sql.extensions/README.external_connections_pool and CORE-5832.
# -------------------------------------------------------------------------------------------
# Test retrieves FB_HOME directory and makes copy of firebird.conf and database.conf files.
# Then it searches for free TCP port and overwrites content of firebird.conf: this new port
# will be specified for RemoveServicePort (see 'TMP_FREE_PORT').
# Also, parameters for working with External Connections Pool (hereafter: ECP) will be added:
# ExtConnPoolSize = <not less than 5>
# ExtConnPoolLifeTime = <ECP_LIFE>
# -- where <ECP_LIFE> can be set to 5...10 s (but not less than 3).
#
# New alias is added to databases.conf for test .fdb which must be created as self-security DB:
# tmp_ecp_01 = <test_fdb> {
# SecurityDatabase = tmp_ecp_01
# RemoteAccess = true
# }
# File $FB_HOME/securityN.fdb is copied to the <test_fdb>.
#
# After this test launches new Firebird instance *as application* (see async. call of Popen()) and make
# some actions with just created test DB (alias = tmp_ecp_01). Because this DB is self-secutity, we can
# use it for connect without any conflict with existing FB instance.
#
# When all needed actions with this DB complete, this FB temporary instance will be stopped.
# Test does start and stop of this FB instance _two_ times because of checking following ServerMode:
# * Super
# * SuperClassic
#
# ::: NOTE :::
# Test does NOT check Servermode = Classic because of unpredictable results when ExtConnPoolLifeTime less than 7s.
# In some cases number of IDLE connections can differ from one run to another. The reason is remaining unknown.
# -------------------------------------------------------------------------------------------
# After FB instance launch, test runs ISQL that connects to <test_fdb> using port <TMP_FREE_PORT> and creates
# several DB objects:
# * DB-level triggers on CONNECT / DISCONNECT;
# * table 'audit' for logging these events, with specifying detailed info: whether current
# connect/disconnect is caused by SESSION RESET (variable RESETTING is TRUE) or no;
# * two users who which further perform connect and run several ES/EDS statements:
# 'freq' -- will do ES/EDS 'frequently', i.e. with interval LESS than ExtConnPoolLifeTime;
# 'rare' -- will do ES/EDS 'rarely', with interval GREATER than ExtConnPoolLifeTime;
# * role 'cleaner_ext_pool' with granting to it system privilege MODIFY_EXT_CONN_POOL, in order
# to have ability to clear ext pool after final ES/EDS. Grant this role to both 'freq' and 'rare'
#
# Then we create several connections for user 'freq' (appending them into a list) and for each of them
# do ES/EDS. Number of connections is specified by variable ITER_LOOP_CNT. Delay between subsequent
# ES/EDS for 'freq' is minimal: 1 second.
# After this, we repeate the same for user 'rare', and use delay between subsequent ES/EDS GREATER
# than ExtConnPoolLifeTime for <ADD_DELAY_FOR_RARE> seconds.
# After loop we clear ExtConnPool and close all connections from list.
#
# Finally test terminates Firebird application process and queries to the table 'audit' for check results.
# Number of rows (and unique connection IDs) for user 'freq' must be significantly less than for user 'rare',
# despite the fact that both of them did the same work. This is because engine held idle connections in the pool
# and user FREQ could re-use them when ran subsequent ES/EDS.
#
# ::: CAUTION :::
# Windows Firewall can block attempt to launch FB as application (dialog window appears in this case).
# One may need to configure Firewall so that process <FB_HOME>
# irebird.exe is enable to operate on any port.
#
# Perhaps, following command will be useful: netsh advfirewall set privateprofile state off
#
# Checked on 4.0.0.2235, FB instances were launched as 'Super' and 'SuperClassic'. Time: ~52s.
#
# 22.05.2021: definition of full path and name to security.db was wrong because it supposed that FB major version
# corresponds to numeric suffix of security database (FB 3.x --> security3.fdb; FB 4.x --> security4.fdb).
# But in major version FB 5.x currently remains to use security4.fdb.
# Proper way is either to use Services API (call to get_security_database_path()) or get this info from fbtest
# built-in context variable context['isc4_path'].
# Checked on 5.0.0.47 (Linux, Windows).
#
# tracker_id:
# min_versions: ['4.0']
# versions: 4.0
# qmid: None
import pytest
from firebird.qa import db_factory, python_act, Action
# version: 4.0
# resources: None
substitutions_1 = []
init_script_1 = """"""
db_1 = db_factory(sql_dialect=3, init=init_script_1)
# test_script_1
#---
#
# import sys
# import os
# import socket
# import platform
# import shutil
# import time
# import datetime
# import tempfile
# import difflib
# import subprocess
# import fdb
# from fdb import services
#
# # POSIX: '/opt/fb40/lib/libfbclient.so'
# # Windows: r'C:\\FB SS
# bclient.dll'
# #FB_CLNT = r'C:\\FB SS
# bclient.dll'
#
#
# os.environ["ISC_USER"] = 'SYSDBA'
# os.environ["ISC_PASSWORD"] = 'masterkey'
#
# TMP_DBA_PSWD = 'M@$terkeX'
#
# # Ext. Poll size and lifetime:
# ECP_SIZE = 10
#
# # === !!! do NOT set ECP_LIFE less than 4 !!! ===
# # SuperClassic can fail in that case (approx every 40...50 run): mismatch in last column (number of ECP idle connections):
# # Example of diff:
# ########################################################################################################
# # - SuperClassic FREQ 2 12 BYE 0 7
# # + SuperClassic FREQ 2 12 BYE 0 6
# #
# # - SuperClassic FREQ 4 13 RUN DML 1 6
# # + SuperClassic FREQ 4 13 RUN DML 1 5
# #
# # - SuperClassic FREQ 4 14 MOVE INTO POOL: ACTIVE -> IDLE 1 6
# # + SuperClassic FREQ 4 14 MOVE INTO POOL: ACTIVE -> IDLE 1 5
# #
# # - SuperClassic FREQ 4 15 TAKE FROM POOL: IDLE -> ACTIVE 2 6
# # + SuperClassic FREQ 4 15 TAKE FROM POOL: IDLE -> ACTIVE 2 5
# ########################################################################################################
# #
# ECP_LIFE = 5
#
# # How many seconds will be added to delay = <ECP_LIFE> when user 'RARE' works with database.
# # For Classic it was needed to set this value about 4(!) seconds but this did not help and results remained non-stable
# # For Super and SuperClassic it is enough to add 2 seconds:
# #
# ADD_DELAY_FOR_RARE = 2
#
# # How many connections will be done by users 'FREQ' and (after him) by 'RARE'.
# # Each connection will run _single_ DML using ES/EDS and then immediately is closed
# # Subsequent connection will run its DML after N seconds where:
# # N = 1 -- for user 'FREQ'
# # N = ECP_LIFE + ADD_DELAY_FOR_RARE -- for user 'RARE'
# #
# ITER_LOOP_CNT = 3
#
# svc = fdb.services.connect(host='localhost', user=user_name, password=user_password)
# FB_HOME = svc.get_home_directory()
# FB_BINS = os.path.join( FB_HOME, 'bin'+os.sep if platform.system() == 'Linux' else '' )
# svc.close()
# SEC_FDB = context['isc4_path']
#
#
# #--------------------------------------------
#
# def flush_and_close(file_handle):
# # https://docs.python.org/2/library/os.html#os.fsync
# # If you're starting with a Python file object f,
# # first do f.flush(), and
# # then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
# global os
#
# file_handle.flush()
# if file_handle.mode not in ('r', 'rb'):
# # otherwise: "OSError: [Errno 9] Bad file descriptor"!
# os.fsync(file_handle.fileno())
# file_handle.close()
#
# #--------------------------------------------
#
# def cleanup( f_names_list ):
# global os
# for i in range(len( f_names_list )):
# if type(f_names_list[i]) == file:
# del_name = f_names_list[i].name
# elif type(f_names_list[i]) == str:
# del_name = f_names_list[i]
# else:
# del_name = None
#
# if os.path.isfile( del_name ):
# os.remove( del_name )
#
# #--------------------------------------------
#
# def find_free_port():
# global socket
# from contextlib import closing
# # AF_INET - constant represent the address (and protocol) families, used for the first argument to socket()
# # A pair (host, port) is used for the AF_INET address family, where host is a string representing either a
# # hostname in Internet domain notation like 'daring.cwi.nl' or an IPv4 address like '100.50.200.5', and port is an integer.
# # SOCK_STREAM means that it is a TCP socket.
# # SOCK_DGRAM means that it is a UDP socket.
# with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
# s.bind(('', 0))
# s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# return s.getsockname()[1]
#
# #--------------------------------------------
# def check_server(address, port):
# global socket
# # Create a TCP socket
# s = socket.socket()
# try:
# s.connect((address, port))
# return True
# except socket.error, e:
# return False
# finally:
# s.close()
#
# #--------------------------------------------
#
# def do_shutdown_bring_online( FB_BINS, tcp_port, db_name, dba_pwd ):
# global subprocess
# subprocess.call( [ os.path.join( FB_BINS, 'gfix'), '-user', 'SYSDBA', '-pas', dba_pwd, '-shut', 'full', '-force', '0', 'localhost/%d:%s' % (tcp_port, db_name) ] )
# subprocess.call( [ os.path.join( FB_BINS, 'gfix'), '-user', 'SYSDBA', '-online', db_name ] )
#
# #--------------------------------------------
#
# def get_fb_arch(a_dsn):
# try:
# con1 = fdb.connect(dsn = a_dsn)
# con2 = fdb.connect(dsn = a_dsn)
#
# cur1 = con1.cursor()
#
# sql=(
# "select count(distinct a.mon$server_pid), min(a.mon$remote_protocol), max(iif(a.mon$remote_protocol is null,1,0))"
# +" from mon$attachments a"
# +" where a.mon$attachment_id in (%s, %s) or upper(a.mon$user) = upper('%s')"
# % (con1.attachment_id, con2.attachment_id, 'cache writer')
# )
#
# cur1.execute(sql)
# for r in cur1.fetchall():
# server_cnt=r[0]
# server_pro=r[1]
# cache_wrtr=r[2]
#
# if server_pro == None:
# fba='Embedded'
# elif cache_wrtr == 1:
# fba='SS'
# elif server_cnt == 2:
# fba='CS'
# else:
#
# f1=con1.db_info(fdb.isc_info_fetches)
#
# cur2=con2.cursor()
# cur2.execute('select 1 from rdb$database')
# for r in cur2.fetchall():
# pass
#
# f2=con1.db_info(fdb.isc_info_fetches)
#
# fba = 'SC' if f1 ==f2 else 'SS'
#
# #print(fba, con1.engine_version, con1.version)
# return fba
#
# finally:
# con1.close()
# con2.close()
#
# #-------------------------------------------------
#
# db_conn.close()
#
# fdb_test = os.path.join(context['temp_directory'],'ext-conn-pool-01.fdb')
# cleanup( (fdb_test,) )
#
# dts = datetime.datetime.now().strftime("%y%m%d_%H%M%S")
#
# fbconf_cur = os.path.join(FB_HOME, 'firebird.conf')
# fbconf_bak = os.path.join(context['temp_directory'], 'firebird_'+dts+'.bak')
#
# dbconf_cur = os.path.join(FB_HOME, 'databases.conf')
# dbconf_bak = os.path.join(context['temp_directory'], 'databases_'+dts+'.bak')
#
# shutil.copy2( SEC_FDB, fdb_test )
# f_init_err = 0
#
# #################################
# TMP_FREE_PORT = find_free_port()
# #################################
#
# #fb_arch = get_fb_arch( dsn )
#
# CHECKED_MODE_LIST=( 'Super,', 'SuperClassic', )
#
# # NO SENSE, ALWAYS DIFFERENT VALUES IN 'IDLE' COLUMN >>> CHECKED_MODE_LIST=('Classic',)
#
# for srvidx,srvmode in enumerate( CHECKED_MODE_LIST ):
#
# shutil.copy2( fbconf_cur, fbconf_bak )
# shutil.copy2( dbconf_cur, dbconf_bak )
#
# cfg_params_to_change= {
# 'ServerMode' : srvmode
# ,'RemoteServicePort' : str(TMP_FREE_PORT)
# ,'ExtConnPoolLifeTime' : str(ECP_LIFE)
# ,'ExtConnPoolSize' : str(ECP_SIZE)
# ,'DefaultDbCachePages' : '2048'
# ,'UseFileSystemCache' : 'true'
# ,'IpcName' : 'fb4x_ipc_ecp'
# ,'RemoteServiceName' : 'fb4x_svc_ecp'
# ,'BugcheckAbort' : '1'
# ,'AuthClient' : 'Srp'
# ,'AuthServer' : 'Srp'
# }
#
# f_fbconf=open( fbconf_cur, 'w')
# for k,v in sorted(cfg_params_to_change.items()):
# f_fbconf.write( ''.join( (k, ' = ', v, '\\n') ) )
# flush_and_close( f_fbconf )
#
# alias_data= '''
# # Added temporarily for executing test ext-conn-pool-01.fbt
# tmp_ecp_01 = %(fdb_test)s {
# SecurityDatabase = tmp_ecp_01
# RemoteAccess = true
# }
# ''' % locals()
#
# f_dbconf=open( dbconf_cur, 'w')
# f_dbconf.write(alias_data)
# flush_and_close( f_dbconf )
#
# ########################################################################
# ### L A U N C H F B A S A P P L I C A T I O N ###
# ########################################################################
# fb_process = subprocess.Popen( [ os.path.join( FB_BINS, 'firebird'), '-a'] )
# time.sleep(2)
#
# if not check_server('localhost', TMP_FREE_PORT):
# print('### ERROR ### FB instance not yet started. Increase delay and repeat!')
#
# if srvidx == 0:
# # initial creation of test DB
# #############################
#
# sql_text= '''
# set bail on;
# set wng off;
# set echo on;
#
# connect 'tmp_ecp_01';
#
# create or alter user sysdba password '%(TMP_DBA_PSWD)s' using plugin Srp;
#
# -- !! otherwise next attempt to attach via TCP will fail with Windows
# -- error 32 process can not access file it is used by another process !!
# alter database set linger to 0;
# commit;
#
# connect 'localhost/%(TMP_FREE_PORT)s:tmp_ecp_01' user sysdba password '%(TMP_DBA_PSWD)s';
#
# set list on;
# --select * from mon$database;
# --commit;
#
# create or alter user freq password '123' using plugin Srp;
# create or alter user rare password '123' using plugin Srp;
# commit;
#
# create role cleaner_ext_pool
# set system privileges to MODIFY_EXT_CONN_POOL;
# commit;
#
# grant default cleaner_ext_pool to user freq;
# grant default cleaner_ext_pool to user rare;
# commit;
#
# create table audit(
# id smallint generated by default as identity constraint pk_audit primary key
# ,srvmode varchar(12) -- 'Super' / 'SuperClassic' / 'Classic'
# ,who varchar(10) default current_user -- 'freq' / 'rare' / 'sysdba'
# ,evt varchar(40) not null
# ,att smallint default current_connection
# ,trn smallint default current_transaction
# ,dts timestamp default 'now'
# ,pool_active_count smallint
# ,pool_idle_count smallint
# ,aux_info varchar(100)
# );
#
# create view v_audit as
# select
# srvmode
# ,who
# ,att
# ,id
# ,evt
# ,active_cnt
# ,idle_cnt
# from (
# select
# srvmode
# ,who
# ,cast(dense_rank()over(partition by srvmode,who order by att) as smallint) as att
# ,cast(dense_rank()over(partition by srvmode,who order by id) as smallint) as id
# ,evt
# ,trn
# ,pool_active_count as active_cnt
# ,pool_idle_count as idle_cnt
# from audit
# )
# --order by srvmode, who, att, id
# order by srvmode, who, id
# ;
#
#
# grant select,insert on audit to public;
# grant select on v_audit to public;
# commit;
#
# set term ^;
# create or alter procedure sys_get_fb_arch (
# a_connect_as_user varchar(31) default 'SYSDBA'
# ,a_connect_with_pwd varchar(31) default '%(TMP_DBA_PSWD)s'
# ) returns(
# fb_arch varchar(50)
# ) as
# declare cur_server_pid int;
# declare ext_server_pid int;
# declare att_protocol varchar(255);
# declare v_test_sttm varchar(255);
# declare v_fetches_beg bigint;
# declare v_fetches_end bigint;
# begin
#
# select a.mon$server_pid, a.mon$remote_protocol
# from mon$attachments a
# where a.mon$attachment_id = current_connection
# into cur_server_pid, att_protocol;
#
# if ( att_protocol is null ) then
# fb_arch = 'Embedded';
# else
# begin
# v_test_sttm =
# 'select a.mon$server_pid' -- + 0*(select 1 from rdb$database)'
# ||' from mon$attachments a '
# ||' where a.mon$attachment_id = current_connection';
#
# execute statement v_test_sttm
# on external
# 'localhost:' || rdb$get_context('SYSTEM', 'DB_NAME')
# as
# user a_connect_as_user
# password a_connect_with_pwd
# role left('R' || replace(uuid_to_char(gen_uuid()),'-',''),31)
# into ext_server_pid;
#
# if ( cur_server_pid is distinct from ext_server_pid ) then
# fb_arch = 'Classic';
# else
# begin
# select i.mon$page_fetches
# from mon$io_stats i
# where i.mon$stat_group = 0 -- db_level
# into v_fetches_beg;
#
# in autonomous transaction do
# select i.mon$page_fetches
# from mon$io_stats i
# where i.mon$stat_group = 0 -- db_level
# into v_fetches_end;
#
# fb_arch = iif( v_fetches_beg is not distinct from v_fetches_end,
# 'SuperClassic',
# 'Super'
# );
# end
# end
# suspend;
# end
# ^
#
# create or alter trigger trg_aud_bi for audit active before insert sql security definer as
# declare v_srvmode varchar(30);
# declare p int;
# begin
# new.srvmode = rdb$get_context('USER_SESSION', 'FB_ARCH');
# if ( new.srvmode is null ) then
# begin
# -- Here we get current FB server mode. Procedure 'sys_get_fb_arch' does ES/EDS,
# -- but is uses SYSDBA account and (because of this) table 'audit' will not be
# -- changed in [dis]connect triggers:
# new.srvmode = ( select fb_arch from sys_get_fb_arch('SYSDBA', '%(TMP_DBA_PSWD)s') );
#
# -- 11.01.2021 22:00: WEIRD! If this statement enabled then .py script HANGS on 2nd iter!
# rdb$set_context('USER_SESSION', 'FB_ARCH', new.srvmode);
# end
#
# new.pool_active_count = rdb$get_context('SYSTEM','EXT_CONN_POOL_ACTIVE_COUNT');
# new.pool_idle_count = rdb$get_context('SYSTEM','EXT_CONN_POOL_IDLE_COUNT');
# end
# ^
#
# create or alter trigger trg_connect inactive on connect sql security definer as
# declare p smallint;
# begin
# if (current_user <> 'SYSDBA') then
# begin
#
# insert into audit(
# evt
# ) values (
# iif(resetting, 'TAKE FROM POOL: IDLE -> ACTIVE', 'NEW')
# );
# end
# end
# ^
#
# create or alter trigger trg_disconnect inactive on disconnect sql security definer as
# begin
# if (current_user <> 'SYSDBA') then
# begin
# insert into audit(
# evt
# ) values (
# iif(resetting, 'MOVE INTO POOL: ACTIVE -> IDLE', 'BYE')
# );
# end
# end
# ^
# set term ;^
# commit;
# alter trigger trg_connect active;
# alter trigger trg_disconnect active;
# grant execute on procedure sys_get_fb_arch to public;
# commit;
# ''' % locals()
#
#
# f_sql_cmd = open( os.path.join(context['temp_directory'],'ecp-resetting-DDL.sql'), 'w')
# f_sql_cmd.write( sql_text )
# flush_and_close( f_sql_cmd )
#
# f_sql_log = open( os.path.splitext(f_sql_cmd.name)[0] + '-init.log', 'w')
# f_sql_err = open( os.path.splitext(f_sql_cmd.name)[0] + '-init.err', 'w')
# subprocess.call( [ os.path.join( FB_BINS, 'isql'), '-q', '-i', f_sql_cmd.name, '-user', 'SYSDBA'], stdout=f_sql_log, stderr=f_sql_err )
# flush_and_close( f_sql_log )
# flush_and_close( f_sql_err )
#
# f_init_err = os.path.getsize(f_sql_err.name)
#
# with open( f_sql_err.name,'r') as f:
# for line in f:
# print("Unexpected STDERR, file " + f_sql_err.name + ": "+line)
#
# cleanup( [ i.name for i in (f_sql_cmd, f_sql_log, f_sql_err) ] )
#
# #< srvidx == 0 (process 1st of srvmode list)
#
# ##############################################################################
# do_shutdown_bring_online( FB_BINS, TMP_FREE_PORT, 'tmp_ecp_01', TMP_DBA_PSWD )
# ##############################################################################
#
# if f_init_err == 0:
#
# sql_for_run='''
# execute block as
# declare c int;
# begin
# execute statement ( q'{ insert into audit( evt ) values( 'RUN DML') }' )
# on external 'localhost/%(TMP_FREE_PORT)s:' || rdb$get_context('SYSTEM','DB_NAME')
#
# with autonomous transaction -- <<< !!! THIS IS MANDATORY IF WE WANT TO USE EXT CONN POOL !!! <<<
#
# as user current_user password '123'
# ;
# end
# ''' % locals()
#
#
# ###########################################################################
#
# for usr_name in ('freq','rare'):
# conn_list = []
# for i in range(0, ITER_LOOP_CNT):
# conn_list.append( fdb.connect( dsn = 'localhost/%(TMP_FREE_PORT)s:tmp_ecp_01' % locals(), user = usr_name, password = '123' ) )
#
# for i,c in enumerate(conn_list):
#
# # ::: NOTE :::
# # ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# # On every iteration DIFFERENT connection is used for run ES/EDS,
# # but all of them use the same user/password/role, so apropriate
# # item in the ExtConnPool can be used to run this statement.
# # But this will be so only for user = 'FREQ' because he does such
# # actions 'frequently': each (<ECP_LIFE> - 2) seconds.
# # For user 'RARE' new attachment will be created every time when
# # he runs ES/EDS because he does that 'rarely' and idle connection
# # (from his previous iteration) is removed from ExtConnPool due to
# # expiration of ExtConnPoolLifeTime:
# # ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# c.execute_immediate( sql_for_run )
#
# if i < len(conn_list)-1:
# time.sleep( 1 if usr_name == 'freq' else ECP_LIFE + ADD_DELAY_FOR_RARE )
# else:
# c.execute_immediate( 'ALTER EXTERNAL CONNECTIONS POOL CLEAR ALL' )
#
# c.close()
#
# ### for c in conn_list:
# ### c.close()
#
# ##############################################################################
# do_shutdown_bring_online( FB_BINS, TMP_FREE_PORT, 'tmp_ecp_01', TMP_DBA_PSWD )
# ##############################################################################
#
# if srvidx == len(CHECKED_MODE_LIST)-1:
#
# sql_check='''
# -- set echo on;
# connect 'localhost/%(TMP_FREE_PORT)s:tmp_ecp_01' user sysdba password '%(TMP_DBA_PSWD)s';
# set count on;
# select * from v_audit;
# --select * from v_audit where who = 'FREQ';
# --select * from v_audit where who = 'RARE';
# ''' % locals()
#
# f_sql_cmd = open( os.path.join(context['temp_directory'],'ext_conn-pool-results.sql'), 'w')
# f_sql_cmd.write( sql_check )
# flush_and_close( f_sql_cmd )
#
# f_sql_log = open( os.path.splitext(f_sql_cmd.name)[0] + '.log', 'w')
#
# ##################################
# ### f i n a l q u e r y ###
# ##################################
# subprocess.call( [ os.path.join( FB_BINS, 'isql'), '-q', '-pag', '99999', '-i', f_sql_cmd.name ], stdout=f_sql_log, stderr=subprocess.STDOUT )
#
# flush_and_close( f_sql_log )
#
# with open(f_sql_log.name) as f:
# for line in f:
# print(line)
#
# #< indent 'f_init_err == 0'
#
# ########################################################################
# ### S T O P T E M P O R A R Y F B I N S T A N C E ###
# ########################################################################
#
# fb_process.terminate()
# time.sleep(2)
#
# shutil.move( fbconf_bak, fbconf_cur )
# shutil.move( dbconf_bak, dbconf_cur )
#
# if f_init_err > 0:
# break
#
# if check_server('localhost', TMP_FREE_PORT):
# print('### ERROR ### FB instance was not yet terminated. Increase delay and repeat!')
#
# #<indent for srvidx,srvmode in enumerate( ('Super', 'SuperClassic', 'Classic') )
#
# #time.sleep(1)
# f_list=( f_sql_log, f_sql_err, f_sql_cmd )
#
# # Cleanup
# ##########
# cleanup( [ i.name for i in f_list ] + [fdb_test] )
#
#---
act_1 = python_act('db_1', substitutions=substitutions_1)
expected_stdout_1 = """
SRVMODE WHO ATT ID EVT ACTIVE_CNT IDLE_CNT
============ ========== ======= ======= ======================================== ========== ========
Super FREQ 1 1 NEW 1 0
Super FREQ 2 2 NEW 1 1
Super FREQ 3 3 NEW 1 2
Super FREQ 4 4 NEW 1 3
Super FREQ 4 5 RUN DML 1 4
Super FREQ 4 6 MOVE INTO POOL: ACTIVE -> IDLE 1 4
Super FREQ 4 7 TAKE FROM POOL: IDLE -> ACTIVE 2 4
Super FREQ 1 8 BYE 0 6
Super FREQ 4 9 RUN DML 1 5
Super FREQ 4 10 MOVE INTO POOL: ACTIVE -> IDLE 1 5
Super FREQ 4 11 TAKE FROM POOL: IDLE -> ACTIVE 2 5
Super FREQ 2 12 BYE 0 7
Super FREQ 4 13 RUN DML 1 6
Super FREQ 4 14 MOVE INTO POOL: ACTIVE -> IDLE 1 6
Super FREQ 4 15 TAKE FROM POOL: IDLE -> ACTIVE 2 6
Super FREQ 4 16 BYE 0 0
Super FREQ 3 17 BYE 0 0
Super RARE 1 1 NEW 1 0
Super RARE 2 2 NEW 1 1
Super RARE 3 3 NEW 1 2
Super RARE 4 4 NEW 1 3
Super RARE 4 5 RUN DML 1 4
Super RARE 4 6 MOVE INTO POOL: ACTIVE -> IDLE 1 4
Super RARE 4 7 TAKE FROM POOL: IDLE -> ACTIVE 2 4
Super RARE 4 8 BYE 0 0
Super RARE 1 9 BYE 0 0
Super RARE 5 10 NEW 1 0
Super RARE 5 11 RUN DML 1 1
Super RARE 5 12 MOVE INTO POOL: ACTIVE -> IDLE 1 1
Super RARE 5 13 TAKE FROM POOL: IDLE -> ACTIVE 2 1
Super RARE 5 14 BYE 0 0
Super RARE 2 15 BYE 0 0
Super RARE 6 16 NEW 1 0
Super RARE 6 17 RUN DML 1 1
Super RARE 6 18 MOVE INTO POOL: ACTIVE -> IDLE 1 1
Super RARE 6 19 TAKE FROM POOL: IDLE -> ACTIVE 2 1
Super RARE 6 20 BYE 0 0
Super RARE 3 21 BYE 0 0
SuperClassic FREQ 1 1 NEW 1 0
SuperClassic FREQ 2 2 NEW 1 1
SuperClassic FREQ 3 3 NEW 1 2
SuperClassic FREQ 4 4 NEW 1 3
SuperClassic FREQ 4 5 RUN DML 1 4
SuperClassic FREQ 4 6 MOVE INTO POOL: ACTIVE -> IDLE 1 4
SuperClassic FREQ 4 7 TAKE FROM POOL: IDLE -> ACTIVE 2 4
SuperClassic FREQ 1 8 BYE 0 6
SuperClassic FREQ 4 9 RUN DML 1 5
SuperClassic FREQ 4 10 MOVE INTO POOL: ACTIVE -> IDLE 1 5
SuperClassic FREQ 4 11 TAKE FROM POOL: IDLE -> ACTIVE 2 5
SuperClassic FREQ 2 12 BYE 0 7
SuperClassic FREQ 4 13 RUN DML 1 6
SuperClassic FREQ 4 14 MOVE INTO POOL: ACTIVE -> IDLE 1 6
SuperClassic FREQ 4 15 TAKE FROM POOL: IDLE -> ACTIVE 2 6
SuperClassic FREQ 4 16 BYE 0 0
SuperClassic FREQ 3 17 BYE 0 0
SuperClassic RARE 1 1 NEW 1 0
SuperClassic RARE 2 2 NEW 1 1
SuperClassic RARE 3 3 NEW 1 2
SuperClassic RARE 4 4 NEW 1 3
SuperClassic RARE 4 5 RUN DML 1 4
SuperClassic RARE 4 6 MOVE INTO POOL: ACTIVE -> IDLE 1 4
SuperClassic RARE 4 7 TAKE FROM POOL: IDLE -> ACTIVE 2 4
SuperClassic RARE 4 8 BYE 0 0
SuperClassic RARE 1 9 BYE 0 0
SuperClassic RARE 5 10 NEW 1 0
SuperClassic RARE 5 11 RUN DML 1 1
SuperClassic RARE 5 12 MOVE INTO POOL: ACTIVE -> IDLE 1 1
SuperClassic RARE 5 13 TAKE FROM POOL: IDLE -> ACTIVE 2 1
SuperClassic RARE 5 14 BYE 0 0
SuperClassic RARE 2 15 BYE 0 0
SuperClassic RARE 6 16 NEW 1 0
SuperClassic RARE 6 17 RUN DML 1 1
SuperClassic RARE 6 18 MOVE INTO POOL: ACTIVE -> IDLE 1 1
SuperClassic RARE 6 19 TAKE FROM POOL: IDLE -> ACTIVE 2 1
SuperClassic RARE 6 20 BYE 0 0
SuperClassic RARE 3 21 BYE 0 0
Records affected: 76
"""
@pytest.mark.version('>=4.0')
def test_1(act_1: Action):
pytest.fail("Test not IMPLEMENTED")