This post is also available in: English
За да възпроизведа проблемите, описани в статията; както и за да тествам евентуалните решения, драснах набързо един тестова пакет. Има си кусури – примерно не се държи правилно ако config таблицата е празна – но за решение „набързо“ се получи добре. Всъщност толкова добре, че реших да го сложа тук, да не го загубя 🙂
Идеята е следната: с едно елементарно скриптче се пускат произволен брой sql*plus сесии (аз пробвах с 20 и 40).
for i in `seq 1 20`;
do
sqlplus /nolog @loader.sql &
done;
Всяка от тях извиква процедурата start_single_process и толкова.
Зареждането започва с извикване (от друга – контролна – сесия) на start_load. Добре е да се зададе някакъв wait, пък бил той и 0.01 сек. Иначе става тегаво.
По всяко време зареждането може да бъде прекъснато с pause_load. Извикването на stop_load кара sql*plus сесиите да приключат.
create or replace package multi_ses_load is
c_status_start constant number := 1; -- the LOADER sessions should run
c_status_pause constant number := 0; -- the LOADER sessions should stop
c_status_exit constant number := 2; -- the LOADER sessions should exit
-- every LOADER process should call this procedure. The procedure will wait for the START signal
procedure start_single_process;
-- issies the START signal. Parameters:
-- part_no: in which partition should we load data
-- wait: how many seconds to wait between 2 inserts
-- refresh_iterations: how often to refresh the data from the config table
procedure start_load(in_part_no in number,
in_wait_sec in number default 0,
in_refresh_iterations in number default 10);
-- changes in which partition should we load data
procedure change_part(in_part_no in number);
-- changes how many seconds to wait between 2 inserts
procedure change_wait(in_wait_sec in number);
-- changes how often to refresh the data flrom the config table
procedure change_refresh(in_refresh_iterations in number);
-- stops the load process until another START signal
procedure pause_load;
-- restarts the load without changing any parameters
procedure restart_load;
-- makes the loading processes to exit
procedure stop_load;
end multi_ses_load;
create or replace package body multi_ses_load is
c_alert_name constant varchar2(20) := 'SESS_ALERT';
-- every LOADER process should call this procedure. The procedure will wait for the START signal
procedure start_single_process is
v_alert_msg VARCHAR2(1800);
v_alert_status PLS_INTEGER;
v_part_no number;
v_wait_sec number;
v_refresh_iterations number;
v_curr_stat number;
v_ses_clob clob;
v_my_sid number;
v_seed number;
procedure read_config_vars is
begin
-- reed the config variables form teh config table
select curr_stat, part_no, wait_sec, refresh_iterations
into v_curr_stat, v_part_no, v_wait_sec, v_refresh_iterations
from ses_load_parames;
end;
begin
-- init the random generator. we will use the current SID and number of seconds since mignight for seed
Select Sid
into v_my_sid
from v$mystat
where rownum = 1;
v_seed := (sysdate - trunc(sysdate)) * 1000 * (24 * 60 * 60) + v_my_sid;
dbms_random.initialize(v_seed);
-- read the config vars
read_config_vars;
while v_curr_stat in (c_status_start, c_status_pause) -- if the current status is START or PAUSE, we proceed;
loop
if v_curr_stat = c_status_pause then
-- if the status us PAUSE, wait for the signal
-- we should register the current session as a consumer for the alert
dbms_alert.register(c_alert_name);
-- now we can wait for the alert
dbms_alert.waitone(c_alert_name, v_alert_msg, v_alert_status);
-- refresh the config vars - they may be changed
read_config_vars;
if v_curr_stat = c_status_exit then
exit;
end if;
end if;
-- do 'v_refresh_iterations' inserts
for i in 1 .. v_refresh_iterations loop
-- insert a row into 'v_part_no' partition
insert into ...
values (dbms_random.string('P', 32), ...); -- here we should take care of the partition placement
commit;
dbms_lock.sleep(v_wait_sec); -- wiat for 'v_wait_sec' seconds
end loop;
-- refresh the config vars after whe have done enough iterations
read_config_vars;
end loop;
end;
-- issues the START signal. Parameters:
-- part_no: in which partition should we load data
-- wait: how many seconds to wait between 2 inserts
-- refresh_iterations: how often to refresh the data flrom the config table
procedure start_load(in_part_no in number,
in_wait_sec in number default 0,
in_refresh_iterations in number default 10) is
begin
-- we set the parameters in the config table
update ses_load_parames
set curr_stat = c_status_start, part_no = in_part_no, wait_sec = in_wait_sec,
refresh_iterations = in_refresh_iterations;
commit;
-- set teh LOADER processes free :)
dbms_alert.signal(c_alert_name, 'Газ!');
commit;
end;
-- changes in which partition should we load data
procedure change_part(in_part_no in number) is
begin
update ses_load_parames
set part_no = in_part_no;
commit;
end;
-- changes how many seconds to wait between 2 inserts
procedure change_wait(in_wait_sec in number) is
begin
update ses_load_parames
set wait_sec = in_wait_sec;
commit;
end;
-- changes how often to refresh the data flrom the config table
procedure change_refresh(in_refresh_iterations in number) is
begin
update ses_load_parames
set refresh_iterations = in_refresh_iterations;
commit;
end;
-- stops the load process until another START signal
procedure pause_load is
begin
update ses_load_parames
set curr_stat = c_status_pause;
commit;
end;
-- restarts the load without changing any parameters
procedure restart_load is
begin
-- we set the parameters in the config table
update ses_load_parames
set curr_stat = c_status_start;
commit;
-- set the LOADER processes free :)
dbms_alert.signal(c_alert_name, 'Аре пак на пангара!');
commit;
end;
-- makes the loading processes to exit
procedure stop_load is
begin
-- put the exit flag in teh table
update ses_load_parames
set curr_stat = c_status_exit;
commit;
-- is some process is in PAUSE, we should alert him to reread the config vars
dbms_alert.signal(c_alert_name, 'Аре да си одиме, момчета!');
commit;
end;
end multi_ses_load;
Sorry, the comment form is closed at this time.