gpt4 book ai didi

haskell - 并发访问持久化数据库的规则是什么

转载 作者:行者123 更新时间:2023-12-03 08:49:52 24 4
gpt4 key购买 nike

似乎有关并发访问的规则没有记录(在 Haskell 方面)并且只是假设开发人员熟悉正在使用的特定后端。对于生产需求,这是一个完全合理的假设,但对于临时原型(prototype)设计和开发来说,如果 persistent-* 包更加独立,那就太好了。

那么,管理对persistent-sqlite 和family 的并发访问的规则是什么?隐含地,如果我们有连接池,则必须允许某种程度的并发,但创建单个连接池并调用 replicateM x $ forkIO (useThePool connectionPool) 很简单。给出以下错误。

user error (SQLite3 returned ErrorBusy while attempting to perform step.)

编辑:一些示例代码现在如下。

在下面的代码中,我 fork 了 6 个线程(任意数量 - 我的实际应用程序执行 3 个线程)。每个线程不断地存储和查找一条记录(来自其他线程访问的唯一记录,但这并不重要),打印其中一个字段。
{-# LANGUAGE TemplateHaskell, QuasiQuotes
, TypeFamilies, FlexibleContexts, GADTs
, OverloadedStrings #-}
import Control.Concurrent (forkIO, threadDelay)
import Database.Persist
import Database.Persist.Sqlite hiding (get)
import Database.Persist.TH
import Control.Monad
import Control.Monad.IO.Class

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persist|
SomeData
myId Int
myData Double
MyId myId
|]

main = withSqlitePool "TEST" 40 $ \pool -> do
runSqlPool (runMigration migrateAll) pool
mapM_ forkIO [runSqlPool (dbThread i) pool | i <- [0..5]]
threadDelay maxBound

dbThread :: Int -> SqlPersist IO ()
dbThread i = forever $ do
x <- getBy (MyId i)
insert (SomeData i (fromIntegral i))
liftIO (print x)
liftIO (threadDelay 100000) -- Just to calm down the CPU,
-- not needed for demonstrating
-- the problem

注意 40 的值, TEST ,并且此示例的所有记录都是任意的。许多值(value)观,包括更现实的值(value)观,都会导致相同的行为。

另请注意,虽然当您在数据库事务(由 forever 开始)内嵌套非终止操作(通过 runSqlPool )时,它可能会明显被破坏,但这不是核心问题。您可以反转这些操作并使交易变得任意小,但最终仍会出现周期性异常。

输出通常是这样的:
$ ./so
Nothing
so: user error (SQLite3 returned ErrorBusy while attempting to perform step.)
so: user error (SQLite3 returned ErrorBusy while attempting to perform step.)
so: user error (SQLite3 returned ErrorBusy while attempting to perform step.)
so: user error (SQLite3 returned ErrorBusy while attempting to perform step.)
so: user error (SQLite3 returned ErrorBusy while attempting to perform step.)
so: user error (SQLite3 returned ErrorConstraint while attempting to perform step.)

最佳答案

值得注意的是,SQLite 在许多系统上存储在类似 NFS 的卷(vboxsf、NFS、SMB、mvfs 等)上时存在锁定问题,这会导致 SQLite 甚至在您成功打开数据库之前就给出该错误。这些卷可能会错误地实现 fcntl() 读/写锁。 (http://www.sqlite.org/faq.html#q5)

假设这不是问题,还值得一提的是,SQLite 本身并不真正支持并发“连接”(http://www.sqlite.org/faq.html#q6),因为它使用文件系统锁来确保两个写入不会同时发生。 (见 http://www.sqlite.org/lockingv3.html 的第 3.0 节)

假设所有这些都是已知的,您还可以检查您的环境中可用的 sqlite3 版本,因为在 3.x 系列中获取不同类型的锁的方式发生了一些变化:http://www.sqlite.org/sharedcache.html

编辑:
来自persist-sqlite3 库的一些附加信息This package includes a thin sqlite3 wrapper based on the direct-sqlite package, as well as the entire C library
“薄”包装让我决定看一看它有多薄;查看代码,除了翻译/发出错误和中断执行所需的保护之外,持久性包装器似乎没有任何保护措施来防止对池的语句失败,尽管我必须提供我不满意的警告 haskell 。

看来您必须防止池中的语句失败并重新尝试,或者您将初始化时的池大小限制为 1(这似乎不太理想。)

关于haskell - 并发访问持久化数据库的规则是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9055214/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com