DB 上的状态机,状态转换的执行
State machine represented on DB, enforcement of state transition
我有一个代表工作阶段的有限状态机。
我需要在 Postgres 数据库中表示状态。我想通过禁止从一种状态更新到另一种状态来强制执行代码正确性,除非状态机允许这样做。
实现我的目标的一种天真的方法可能是在 table 上获取 独占锁 ,在事务中检查当前状态和下一个状态,在无效更新的情况下中止错误。
这显然是性能杀手,因为我要在每个状态转换时锁定 Job table。
有没有办法通过约束来实现相同的目标?
触发器是您问题的答案。
让我们考虑简单的 table:
CREATE TABLE world (id serial PRIMARY KEY, state VARCHAR);
insert into world (state) values ('big bang');
insert into world (state) values ('stars formation');
insert into world (state) values ('human era');
触发器将调用的函数。在此处定义您的状态机逻辑。 RAISE EXCEPTION 很有用,因为您可以在此处提供自定义消息。
CREATE FUNCTION check_world_change() RETURNS trigger as $check_world_change$
BEGIN
IF OLD.state = 'big bang' AND NEW.state = 'human era' THEN
RAISE EXCEPTION 'Dont skip stars';
END IF;
IF OLD.state = 'stars formation' AND NEW.state = 'big bang' THEN
RAISE EXCEPTION 'Impossible to reverse order of things';
END IF;
RETURN NEW;
END;
$check_world_change$ LANGUAGE plpgsql;
并为您的 table 定义触发器:
CREATE TRIGGER check_world_change BEFORE UPDATE ON world
FOR EACH ROW EXECUTE PROCEDURE check_world_change();
现在,当您尝试更新其中一行的状态时,您将收到错误消息:
world=# select * from world;
id | state
----+-----------------
2 | stars formation
1 | human era
3 | big bang
(3 rows)
world=# update world set state='human era' where state='big bang';
ERROR: Wrong transition
world=# select * from world;
id | state
----+-----------------
2 | stars formation
1 | human era
3 | big bang
(3 rows)
参考文献:
https://www.postgresql.org/docs/9.5/static/plpgsql-trigger.html
https://www.postgresql.org/docs/9.5/static/sql-createtrigger.html
我有一个代表工作阶段的有限状态机。 我需要在 Postgres 数据库中表示状态。我想通过禁止从一种状态更新到另一种状态来强制执行代码正确性,除非状态机允许这样做。
实现我的目标的一种天真的方法可能是在 table 上获取 独占锁 ,在事务中检查当前状态和下一个状态,在无效更新的情况下中止错误。
这显然是性能杀手,因为我要在每个状态转换时锁定 Job table。
有没有办法通过约束来实现相同的目标?
触发器是您问题的答案。
让我们考虑简单的 table:
CREATE TABLE world (id serial PRIMARY KEY, state VARCHAR);
insert into world (state) values ('big bang');
insert into world (state) values ('stars formation');
insert into world (state) values ('human era');
触发器将调用的函数。在此处定义您的状态机逻辑。 RAISE EXCEPTION 很有用,因为您可以在此处提供自定义消息。
CREATE FUNCTION check_world_change() RETURNS trigger as $check_world_change$
BEGIN
IF OLD.state = 'big bang' AND NEW.state = 'human era' THEN
RAISE EXCEPTION 'Dont skip stars';
END IF;
IF OLD.state = 'stars formation' AND NEW.state = 'big bang' THEN
RAISE EXCEPTION 'Impossible to reverse order of things';
END IF;
RETURN NEW;
END;
$check_world_change$ LANGUAGE plpgsql;
并为您的 table 定义触发器:
CREATE TRIGGER check_world_change BEFORE UPDATE ON world
FOR EACH ROW EXECUTE PROCEDURE check_world_change();
现在,当您尝试更新其中一行的状态时,您将收到错误消息:
world=# select * from world;
id | state
----+-----------------
2 | stars formation
1 | human era
3 | big bang
(3 rows)
world=# update world set state='human era' where state='big bang';
ERROR: Wrong transition
world=# select * from world;
id | state
----+-----------------
2 | stars formation
1 | human era
3 | big bang
(3 rows)
参考文献:
https://www.postgresql.org/docs/9.5/static/plpgsql-trigger.html https://www.postgresql.org/docs/9.5/static/sql-createtrigger.html