具有 git 语义的数据库 table 的最佳模式?

Best patterns for database table with git semantics?

我们有一些中型到大型 tables(即数十万行)的数据,我们希望允许用户有效地 "fork" 类似于 Git,并可能随着时间的推移进行合作。我们希望他们能够多次 "fork" 它们,并对不同的分叉进行编辑并比较两个分叉之间的各种聚合数据。

基本上,这些数据开始时是一堆只读 table,我们希望让用户能够查看包含他们编辑的 table。挑战在于我们还想定期查询此 table 中的行(即仅显示 column3 = 'left' 的行),甚至可能根据特定列加入另一个 table . (即 user_table.column3 = other_table.column10 的 INNER JOIN)——即我们希望能够像对待完全物化的 table 一样对待这些 table关系操作。

最愚蠢的解决方案(我们今天所做的)就是简单地制作 table 的完整副本,但挑战在于至少在我们当前的化身中这是昂贵的:我们正在使用 PostgreSQL,这些复制操作可能需要 2-20 分钟。我们希望这是一个实时操作,就像具有写时复制行为的东西。

我们确实记录了用户所做的更改(即更改日志),因此我们最终可以将它们应用到 "original" table 但最好有一个模式,或者在一个理想的世界,一个库或存储层,只是为我们做这件事。

我们今天碰巧使用 PostgreSQL 和 Python,但我对这里的 NoSQL 系统持开放态度,因为我可以想象这会导致一些非常讨厌的事情 SQL 如果这足够概括的话。另外,我们愿意牺牲一些关系能力来实现上述目标。在这个 space 中是否有已知的模式 and/or 实现?是在 PostgreSQL 中,还是在其他存储系统中?事实证明,这对 google 来说是一件非常困难的事情。

当然可以用它做一些事情,尽管结果可能(或可能不会)不稳定,具体取决于您将在数据库中使用多少其他 hack。

如果我们将列 ownerdeleted 添加到源 table,创建一个视图,将 INSTEAD OF 触发器添加到视图并仅授予用户权限到视图而不是源 table 我们得到这个:

CREATE SEQUENCE source_seq;
CREATE TABLE source
(
    id INT DEFAULT nextval('source_seq')
    ,value VARCHAR
    ,owner name DEFAULT session_user
    ,deleted boolean DEFAULT FALSE
);
CREATE VIEW source_emp AS
    SELECT id, value
    FROM source AS s1
    WHERE ((owner IS NULL AND NOT EXISTS (SELECT * FROM source AS s2 WHERE s1.id = s2.id AND s2.owner = session_user )) OR owner = session_user)
    AND NOT deleted
CREATE OR REPLACE FUNCTION source_change()
RETURNS TRIGGER
LANGUAGE plpgsql
SECURITY DEFINER
AS $function$
   BEGIN      
      IF TG_OP = 'UPDATE' THEN
       INSERT INTO source(id,value,owner,deleted) 
       SELECT NEW.id,NEW.value,session_user, FALSE
       WHERE NOT EXISTS(SELECT * FROM source WHERE owner = session_user AND id = OLD.id);
       UPDATE source SET id = NEW.id, value = NEW.value, owner = session_user WHERE owner = session_user AND id = OLD.id;              
       RETURN NEW;
       ELSIF TG_OP = 'DELETE' THEN
       INSERT INTO source(id,value,owner,deleted) 
       SELECT OLD.id,NULL,session_user, TRUE
       WHERE NOT EXISTS(SELECT * FROM source WHERE owner = session_user AND id = OLD.id);
       UPDATE source SET value = NULL, deleted = TRUE WHERE owner = session_user AND id = OLD.id;
       RETURN NULL;
      END IF;
      RETURN NEW;
    END;
$function$;

CREATE TRIGGER source_trig
    INSTEAD OF UPDATE OR DELETE ON
      source_emp FOR EACH ROW EXECUTE PROCEDURE source_change();

现在,如果用户尝试:

  • INSERT :他获取仅在视图中对他可见的记录
  • 更新:他得到原始记录的副本并编辑这些副本或更新他的副本
  • 删除:他将源标记为已删除,仅供他个人使用。

如果您不想触及原来的 table,您可以创建一个包含额外两列的新列并更改视图,使其与原来的 table 合并。