【发布时间】:2016-03-22 21:13:36
【问题描述】:
我需要关于用作原子更改日志的数据结构的建议。
我正在尝试实现以下算法。有流入的 更改更新内存中的映射。在类似 Haskell 的伪代码中是
update :: DataSet -> SomeListOf Change -> Change -> STM (DataSet, SomeListOf Change)
update dataSet existingChanges newChange = do
...
return (dataSet, existingChanges ++ [newChange])
其中 DataSet 是一个映射(目前它是来自 stm-containers 包的映射,https://hackage.haskell.org/package/stm-containers-0.2.10/docs/STMContainers-Map.html)。从任意数量的线程调用整个“更新”。由于域语义,某些更改可能会被拒绝,我使用 throwSTM 来丢弃事务的影响。在成功提交的情况下,“newChange”被添加到列表中。
存在调用以下函数的单独线程:
flush :: STM (DataSet, SomeListOf Change) -> IO ()
这个函数应该将 DataSet 的当前快照与更改列表(它必须是一致的对)一起拍摄并刷新到文件系统,即
flush data = do
(dataSet, changes) <- atomically $ readTVar data_
-- write them both to FS
-- ...
atomically $ writeTVar data_ (dataSet, [])
我需要有关用于“SomeListOf Change”的数据结构的建议。我不想用[Change],因为它“太有序”,怕冲突太多,会迫使整个事务重试。如果我在这里错了,请纠正我。
我不能使用 Set (https://hackage.haskell.org/package/stm-containers-0.2.10/docs/STMContainers-Set.html),因为我仍然需要保留 一些 顺序,例如事务提交的顺序。我可以为它使用 TChan,它看起来很匹配(恰好是事务提交的顺序),但我不知道如何实现“刷新”功能,以便它可以提供整个更改日志的一致视图与数据集。
它的当前实现在这里https://github.com/lolepezy/rpki-pub-server/blob/add-storage/src/RRDP/Repo.hs,分别在函数applyActionsToState 和rrdpSyncThread 中。它使用了 TChan,而且似乎以错误的方式进行。
提前谢谢你。
更新:一个合理的答案似乎是这样的
type SomeListOf c = TChan [c]
update :: DataSet -> TChan [Change] -> Change -> STM DataSet
update dataSet existingChanges newChange = do
...
writeTChan changeChan $ reverse (newChange : existingChanges)
return dataSet
flush data_ = do
(dataSet, changes) <- atomically $ (,) <$> readTVar data_ <*> readTChan changeChan
-- write them both to FS
-- ...
但我仍然不确定将整个列表作为频道元素传递是否是一个巧妙的解决方案。
【问题讨论】:
-
我没有仔细阅读你的问题,但是
TChan是一个非常简单的([a], [a])函数式出队;听起来您在其上实现自己的变体可能是有意义的。 -
让我问:预计有多少线程(至少是一个粗略的数字)来访问该结构?一次有多少个?您预计更改列表会增长到多大?
-
还需要将
update与其他STM操作组合起来,还是始终在自己的事务中运行? -
抱歉回复晚了。我预计负载不会很大,但可能会出现突发事件,比如同时请求几十个。数据集应该非常小(数十万个元素)但经常更新。而且我不认为这会成为更大交易的一部分。