ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY MM DD HH24:MI:SS';
drop table customers;
drop table worklog;
drop sequence worklog_seq;
drop package body barber_shop;
drop package barber_shop;
create table customers
(id number primary key,
entered_shop timestamp,
needs_cut number);
declare
begin
for i in 1..2000 loop
insert into customers values (i,null,0);
end loop;
commit;
end;
/
create table worklog
( id number
, event varchar2(100)
, log_time timestamp
)
/
create sequence worklog_seq start with 1 increment by 1 cache 20
/
create or replace package barber_shop
as
procedure sync;
procedure add_customers(p_avg_customers_per_sec number,p_max_duration number);
procedure add_barber(p_avg_cut_time number,p_max_duration number);
end barber_shop;
/
create or replace package body barber_shop
as
procedure sync as
n1 number(38);
m_handle varchar2(60);
begin
dbms_lock.allocate_unique
( lockname => 'Synchronize'
, lockhandle => m_handle
);
n1 := dbms_lock.request
( lockhandle => m_handle
, lockmode => dbms_lock.x_mode
, timeout => dbms_lock.maxwait
, release_on_commit => true -- the default is false !!
);
end sync;
procedure wait_for_synchronisation as
n1 number;
m_handle varchar2(60);
begin
dbms_lock.allocate_unique('Synchronize',m_handle);
n1 := dbms_lock.request(m_handle,dbms_lock.s_mode,dbms_lock.maxwait,true);
end wait_for_synchronisation;
procedure log (p_event in varchar2) as
pragma autonomous_transaction;
begin
insert into worklog values (worklog_seq.nextval,p_event,systimestamp);
commit;
end log ;
procedure finish_work(p_customer_id in number) as
begin
update customers set needs_cut=0,entered_shop=null where id=p_customer_id;
end finish_work ;
procedure cut_hair(p_avg_cut_time number) as
begin
dbms_lock.sleep(p_avg_cut_time);
end cut_hair;
procedure add_customers(p_avg_customers_per_sec number,p_max_duration number) as
l_start_time date;
begin
wait_for_synchronisation; -- to let all processes start at the exact same time
l_start_time := sysdate;
loop
exit when sysdate > l_start_time + numtodsinterval(p_max_duration,'second');
update customers set needs_cut=1,entered_shop=systimestamp
where id in (
select id from (select id from customers where needs_cut=0 order by dbms_random.random)
where rownum<=(dbms_random.value*(p_avg_customers_per_sec*2+1))
);
commit;
log('customers entered shop at ' || systimestamp);
dbms_lock.sleep(1);
end loop;
end add_customers;
procedure add_barber(p_avg_cut_time number,p_max_duration number) as
cursor c is select * from customers where needs_cut=1 order by entered_shop for update skip locked;
l_rec customers%rowtype;
l_time timestamp;
l_start_time date;
begin
wait_for_synchronisation; -- to let all processes start at the exact same time
l_start_time := sysdate;
loop
exit when sysdate > l_start_time + numtodsinterval(p_max_duration,'second');
open c;
loop
fetch c into l_rec;
exit when c%NOTFOUND;
log('customer needs cut? ' || l_rec.needs_cut || ' ' || l_rec.entered_shop);
log('waited: ' || to_char(extract(second from (systimestamp-l_rec.entered_shop))));
cut_hair(dbms_random.value*p_avg_cut_time*2);
finish_work(l_rec.id);
end loop;
commit;
close c;
end loop;
end add_barber;
end barber_shop;
/
show err