Что вам нужно реализовать, это стек FIFO или простая очередь. В Oracle лучше всего (если вы не хотите реализовать реальную очередь с AQ) для такой вещи - SELECT ... FOR UPDATE
с предложением SKIP LOCKED
. SKIP LOCKED
позволяет легко управлять стеком с несколькими пользователями.
Вот простой интерфейс:
create or replace package task_mgmt is
function get_next_task return tasks.id%type;
procedure complete_task (p_id in tasks.id%type);
procedure release_task (p_id in tasks.id%type);
end task_mgmt;
/
Это реализация скелетная:
create or replace package body task_mgmt is
function get_next_task return tasks.id%type
is
return_value tasks.id%type;
cursor c_tsk is
select id
from tasks
where status = 'open'
order by date_created, id
for update skip locked;
begin
open c_tsk;
fetch c_tsk into return_value;
update tasks
set status = 'progress'
, assigned = user
where current of c_tsk;
close c_tsk;
return return_value;
end get_next_task;
procedure complete_task (p_id in tasks.id%type)
is
begin
update tasks
set status = 'complete'
, date_completed = sysdate
where id = p_id;
commit;
end complete_task;
procedure release_task (p_id in tasks.id%type)
is
begin
rollback;
end ;
end task_mgmt;
/
Обновление статуса, когда пользователи выскакивает стек создает блокировку. Из-за предложения SKIP LOCKED
следующий пользователь не увидит эту задачу. Это намного чище, чем удаление и повторная установка записей.
Вот некоторые данные:
create table tasks (
id number not null
, descr varchar2(30) not null
, date_created date default sysdate not null
, status varchar2(10) default 'open' not null
, assigned varchar2(30)
, date_completed date
, constraint task_pk primary key (id)
)
/
insert into tasks (id, descr, date_created) values (1000, 'Do something', date '2015-05-28')
/
insert into tasks (id, descr, date_created) values (1010, 'Look busy', date '2015-05-28')
/
insert into tasks (id, descr, date_created) values (1020, 'Get coffee', date '2015-06-12')
/
Давайте поп!Вот Session один:
SQL> var tsk1 number;
SQL> exec :tsk1 := task_mgmt.get_next_task ;
PL/SQL procedure successfully completed.
SQL> print :tsk1
TSK1
----------
1000
SQL>
Между тем в Сессия два:
SQL> var tsk2 number;
SQL> exec :tsk2 := task_mgmt.get_next_task ;
PL/SQL procedure successfully completed.
SQL> print :tsk2
TSK2
----------
1010
SQL>
Назад в Session один:
SQL> exec task_mgmt.complete_task (:tsk1);
PL/SQL procedure successfully completed.
SQL> exec :tsk1 := task_mgmt.get_next_task ;
PL/SQL procedure successfully completed.
SQL> print :tsk1
TSK
----------
1020
SQL>
Основной недостаток этого подхода состоит в том, что оно требует, чтобы пользователи поддерживали сеансы с состоянием, пока они работают на e задача. Это не так, тогда вам нужен API, в котором get_next_task()
является дискретной транзакцией и забывает о блокировке.
Кстати, это, вероятно, лучше, чтобы пользователи могли захватить задачу, а не назначать их через триггер входа в систему (или что вы имеете в виду под «Том входит в систему и получить задачи 1, 2, 3.») , Задачи вытягивания - это то, как работает очередь SO Review.
Кроме того, просто назначьте одну задачу за раз. Таким образом, вы можете получить эффективное распределение работы. Вы хотите избежать ситуации, когда Том имеет три задачи на своей тарелке, один из которых он не собирается завершать, и Боб ничего не может сделать. То есть, если вы не Боб.
Это зависит от того, как вы определяете «войти/выйти из системы». Простой подход: во время каждого запроса вы предполагаете, что пользователь «входит в систему». Если пользователь уже назначил задачи, все в порядке. Если есть открытые задачи, назначьте некоторые задачи этому пользователю (т. Е. Обновите задачу в db и запомните пользователя и временную метку), если у пользователя не будет достаточно назначенных задач. Если пользователь выйдет из системы или не был замечен некоторое время, вы можете удалить пользователя и его временную метку из таблицы задач. Таким образом, все, что вам нужно сделать сейчас, это: обновить задачи и их назначения во время каждого запроса. –