link to bug report blob
delete from tree
This commit is contained in:
parent
57107cf213
commit
a89db2c604
2 changed files with 4 additions and 297 deletions
|
@ -188,7 +188,10 @@ runSqliteRobustly tablename db a = do
|
||||||
-- sometimes take a while to become usable; select statements will
|
-- sometimes take a while to become usable; select statements will
|
||||||
-- fail with ErrorBusy for some time. So, loop until a select
|
-- fail with ErrorBusy for some time. So, loop until a select
|
||||||
-- succeeds; once one succeeds the connection will stay usable.
|
-- succeeds; once one succeeds the connection will stay usable.
|
||||||
-- <http://thread.gmane.org/gmane.comp.db.sqlite.general/93116>
|
--
|
||||||
|
-- I reported this bug, but the sqlite developers did not respond.
|
||||||
|
-- Bug report is archived in blob 500f777a6ab6c45ca5f9790e0a63575f8e3cb88f
|
||||||
|
-- in git-annex git repo.
|
||||||
settle conn = do
|
settle conn = do
|
||||||
r <- tryNonAsync $ do
|
r <- tryNonAsync $ do
|
||||||
stmt <- Sqlite.prepare conn nullselect
|
stmt <- Sqlite.prepare conn nullselect
|
||||||
|
|
|
@ -1,296 +0,0 @@
|
||||||
From mairix@mairix Mon Jan 1 12:34:56 1970
|
|
||||||
X-source-folder: /home/joey/mail/.git/annex/objects/KF/18/SHA256E-s3002403--01161ac42b9e452562e18425176f95cca7466e08f21e6c755f752bd0ede64b14.gz/SHA256E-s3002403--01161ac42b9e452562e18425176f95cca7466e08f21e6c755f752bd0ede64b14.gz
|
|
||||||
Date: Thu, 19 Feb 2015 12:32:55 -0400
|
|
||||||
From: Joey Hess <id@joeyh.name>
|
|
||||||
To: sqlite-users@sqlite.org
|
|
||||||
Subject: bug report: SELECT fails with BUSY in WAL mode database with
|
|
||||||
concurrent writer
|
|
||||||
Message-ID: <20150219163255.GA13383@kitenet.net>
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: multipart/signed; micalg=pgp-sha1;
|
|
||||||
protocol="application/pgp-signature"; boundary="8GpibOaaTibBMecb"
|
|
||||||
Content-Disposition: inline
|
|
||||||
User-Agent: Mutt/1.5.23 (2014-03-12)
|
|
||||||
Status: RO
|
|
||||||
Content-Length: 8268
|
|
||||||
Lines: 278
|
|
||||||
|
|
||||||
|
|
||||||
--8GpibOaaTibBMecb
|
|
||||||
Content-Type: multipart/mixed; boundary="nFreZHaLTZJo0R7j"
|
|
||||||
Content-Disposition: inline
|
|
||||||
|
|
||||||
|
|
||||||
--nFreZHaLTZJo0R7j
|
|
||||||
Content-Type: text/plain; charset=us-ascii
|
|
||||||
Content-Disposition: inline
|
|
||||||
Content-Transfer-Encoding: quoted-printable
|
|
||||||
|
|
||||||
[ Not subscribed; please CC me. ]
|
|
||||||
|
|
||||||
The attached Testcase.hs is a haskell program using sqlite, that
|
|
||||||
demonstrates what I think may be a bug in WAL mode. Based on the
|
|
||||||
documentation, readers in WAL mode are supposed to not be blocked by
|
|
||||||
concurrent writers. However, this test case demonstrates that a SELECT
|
|
||||||
can fail with busy in a WAL mode database that is getting a large volume
|
|
||||||
of writes.
|
|
||||||
|
|
||||||
To build:
|
|
||||||
|
|
||||||
apt-get install haskell-platform
|
|
||||||
cabal update
|
|
||||||
cabal install persistent-sqlite persistent-template esqueleto IfElse
|
|
||||||
ghc --make Testcase
|
|
||||||
|
|
||||||
I've been building it on Debian unstable. Note that by default,
|
|
||||||
persisten-sqlite includes its own embedded copy of sqlite, which is rather
|
|
||||||
out of date. It can be modified to build with the system library.
|
|
||||||
I have reproduced the crash when the test case is linked to version 3.8.7.4;
|
|
||||||
I have not yet tried a newer version.
|
|
||||||
|
|
||||||
To run:
|
|
||||||
|
|
||||||
rm test.db* (if ran before)
|
|
||||||
Run one Testcase process, and wait for it to print the Migrating line.
|
|
||||||
Then immediately run a second Testcase process. One of the two processes
|
|
||||||
will quickly crash:
|
|
||||||
|
|
||||||
=2E.Testcase: user error (SQLite3 returned ErrorBusy while attempting to pe=
|
|
||||||
rfor
|
|
||||||
|
|
||||||
While running, it outputs '.' every time it successfully changes the
|
|
||||||
database. It's expected that some write attempts fail, as there are multiple
|
|
||||||
concurrent writers; if a write fails, it prints '!' and ignores the failure.
|
|
||||||
|
|
||||||
The crash comes when a *read* fails. WAL documentation indicates that
|
|
||||||
writers should not block reads, but this test case seems to demonstrate
|
|
||||||
otherwise!
|
|
||||||
|
|
||||||
Also attached is a TestcaseReader.hs. This only does reads, no writes.
|
|
||||||
TestcaseReader can be run while Testcase is running, and will also
|
|
||||||
demonstrate the problem:
|
|
||||||
|
|
||||||
user error (SQLite3 returned ErrorBusy while attempting to perform prepare =
|
|
||||||
"SELECT \"fscked\".\"key\"\nFROM \"fscked\"\nWHERE \"fscked\".\"key\" =3D ?=
|
|
||||||
\n": database is locked)
|
|
||||||
|
|
||||||
finally succeeded after 1 retries
|
|
||||||
all 1..100000 followup selects succeeded
|
|
||||||
|
|
||||||
The interest thing about this is that it shows that the failing
|
|
||||||
SELECT is always the first one made on a new database connection.
|
|
||||||
I've seen it need to retry 60+ times to get that first SELECT to
|
|
||||||
succeed, but once a SELECT does succeed, it seems it's past
|
|
||||||
the danger zone and the database can be used without problems.
|
|
||||||
|
|
||||||
--=20
|
|
||||||
see shy jo
|
|
||||||
|
|
||||||
--nFreZHaLTZJo0R7j
|
|
||||||
Content-Type: text/x-haskell; charset=us-ascii
|
|
||||||
Content-Disposition: attachment; filename="Testcase.hs"
|
|
||||||
Content-Transfer-Encoding: quoted-printable
|
|
||||||
|
|
||||||
-- sqlite WAL database testcase
|
|
||||||
--
|
|
||||||
-- Joey Hess <id@joey.name> ; copyright: BSD3
|
|
||||||
|
|
||||||
{-# LANGUAGE QuasiQuotes, TypeFamilies, TemplateHaskell #-}
|
|
||||||
{-# LANGUAGE OverloadedStrings, GADTs, FlexibleContexts #-}
|
|
||||||
{-# LANGUAGE MultiParamTypeClasses, GeneralizedNewtypeDeriving #-}
|
|
||||||
{-# LANGUAGE BangPatterns, ScopedTypeVariables #-}
|
|
||||||
|
|
||||||
import Database.Persist.TH
|
|
||||||
import Database.Esqueleto
|
|
||||||
import Control.Monad
|
|
||||||
import Control.Monad.IfElse
|
|
||||||
import Control.Monad.IO.Class (liftIO)
|
|
||||||
import System.Directory
|
|
||||||
import Database.Persist.Sqlite hiding ((=3D=3D.))
|
|
||||||
import qualified Database.Sqlite as Sqlite
|
|
||||||
import qualified Data.Text as T
|
|
||||||
import Control.Monad.Catch as X hiding (Handler)
|
|
||||||
import qualified Control.Monad.Catch as M
|
|
||||||
import Control.Exception (AsyncException)
|
|
||||||
import System.IO
|
|
||||||
import System.Posix.Process
|
|
||||||
|
|
||||||
share [mkPersist sqlSettings, mkMigrate "migrateFsck"] [persistLowerCase|
|
|
||||||
Fscked
|
|
||||||
key String
|
|
||||||
foo String
|
|
||||||
UniqueKey key
|
|
||||||
|]
|
|
||||||
|
|
||||||
main :: IO ()
|
|
||||||
main =3D do
|
|
||||||
pid <- getProcessID
|
|
||||||
|
|
||||||
let db =3D T.pack "test.db"
|
|
||||||
unlessM (doesFileExist (T.unpack db)) $ do
|
|
||||||
void $ runSqlite db $
|
|
||||||
runMigration migrateFsck
|
|
||||||
enableWAL (T.unpack db)
|
|
||||||
|
|
||||||
forM_ [1..40000] $ \n -> do
|
|
||||||
_ <- inDb db n -- this is the line that unexpectedly crashes!
|
|
||||||
addDb db n (show pid)
|
|
||||||
|
|
||||||
toK :: Int -> String
|
|
||||||
toK =3D take 2 . show
|
|
||||||
|
|
||||||
addDb :: T.Text -> Int -> String -> IO ()
|
|
||||||
addDb db i s =3D do
|
|
||||||
r <- tryNonAsync $ runSqlite db $=20
|
|
||||||
unlessM (inDb' sk) $
|
|
||||||
insert_ $ Fscked sk s
|
|
||||||
liftIO $ do
|
|
||||||
case r of
|
|
||||||
Left _ -> putStr "!"
|
|
||||||
Right _ -> putStr "."
|
|
||||||
hFlush stdout
|
|
||||||
where
|
|
||||||
sk =3D toK i
|
|
||||||
|
|
||||||
inDb :: T.Text -> Int -> IO Bool
|
|
||||||
inDb db =3D runSqlite db . inDb' . toK
|
|
||||||
|
|
||||||
inDb' :: String -> SqlPersistM Bool
|
|
||||||
inDb' sk =3D do
|
|
||||||
r <- select $ from $ \r -> do
|
|
||||||
where_ (r ^. FsckedKey =3D=3D. val sk)
|
|
||||||
return (r ^. FsckedKey)
|
|
||||||
return $ not $ null r
|
|
||||||
|
|
||||||
enableWAL :: FilePath -> IO ()
|
|
||||||
enableWAL db =3D do
|
|
||||||
conn <- Sqlite.open (T.pack db)
|
|
||||||
stmt <- Sqlite.prepare conn (T.pack "PRAGMA journal_mode=3DWAL;")
|
|
||||||
void $ Sqlite.step stmt
|
|
||||||
void $ Sqlite.finalize stmt
|
|
||||||
Sqlite.close conn
|
|
||||||
|
|
||||||
catchNonAsync :: MonadCatch m =3D> m a -> (SomeException -> m a) -> m a
|
|
||||||
catchNonAsync a onerr =3D a `catches`
|
|
||||||
[ M.Handler (\ (e :: AsyncException) -> throwM e)
|
|
||||||
, M.Handler (\ (e :: SomeException) -> onerr e)
|
|
||||||
]
|
|
||||||
|
|
||||||
tryNonAsync :: MonadCatch m =3D> m a -> m (Either SomeException a)
|
|
||||||
tryNonAsync a =3D go `catchNonAsync` (return . Left)
|
|
||||||
where
|
|
||||||
go =3D do
|
|
||||||
v <- a
|
|
||||||
return (Right v)
|
|
||||||
|
|
||||||
--nFreZHaLTZJo0R7j
|
|
||||||
Content-Type: text/x-haskell; charset=us-ascii
|
|
||||||
Content-Disposition: attachment; filename="TestcaseReader.hs"
|
|
||||||
|
|
||||||
-- sqlite WAL database reader testcase
|
|
||||||
--
|
|
||||||
-- Joey Hess <id@joey.name> ; copyright: BSD3
|
|
||||||
|
|
||||||
{-# LANGUAGE QuasiQuotes, TypeFamilies, TemplateHaskell #-}
|
|
||||||
{-# LANGUAGE OverloadedStrings, GADTs, FlexibleContexts #-}
|
|
||||||
{-# LANGUAGE MultiParamTypeClasses, GeneralizedNewtypeDeriving #-}
|
|
||||||
{-# LANGUAGE BangPatterns, ScopedTypeVariables #-}
|
|
||||||
|
|
||||||
import Database.Persist.TH
|
|
||||||
import Database.Esqueleto
|
|
||||||
import Control.Monad
|
|
||||||
import Control.Monad.IO.Class (liftIO)
|
|
||||||
import Database.Persist.Sqlite hiding ((==.))
|
|
||||||
import qualified Data.Text as T
|
|
||||||
import Control.Monad.Catch as X hiding (Handler)
|
|
||||||
import qualified Control.Monad.Catch as M
|
|
||||||
import Control.Exception (AsyncException)
|
|
||||||
import System.IO
|
|
||||||
|
|
||||||
share [mkPersist sqlSettings, mkMigrate "migrateFsck"] [persistLowerCase|
|
|
||||||
Fscked
|
|
||||||
key String
|
|
||||||
foo String
|
|
||||||
UniqueKey key
|
|
||||||
|]
|
|
||||||
|
|
||||||
main :: IO ()
|
|
||||||
main = do
|
|
||||||
let db = T.pack "test.db"
|
|
||||||
void $ runSqlite db loop
|
|
||||||
where
|
|
||||||
sk = toK 100
|
|
||||||
loop = do
|
|
||||||
r <- go (0 :: Int)
|
|
||||||
if r
|
|
||||||
then do
|
|
||||||
-- try some more selects, to see if it ever
|
|
||||||
-- begins to fail after initial connection
|
|
||||||
forM_ [1..100000] $ \n -> do
|
|
||||||
_ <- inDb' (toK n)
|
|
||||||
liftIO $ do
|
|
||||||
putStr " "
|
|
||||||
hFlush stdout
|
|
||||||
liftIO $ putStrLn "all 1..100000 followup selects succeeded"
|
|
||||||
else loop
|
|
||||||
go n = do
|
|
||||||
r <- tryNonAsync $ inDb' sk
|
|
||||||
case r of
|
|
||||||
Right _
|
|
||||||
| n > 0 -> do
|
|
||||||
liftIO $ putStrLn $ "finally succeeded after " ++ show n ++ " retries"
|
|
||||||
return False
|
|
||||||
| otherwise -> return True
|
|
||||||
Left e -> do
|
|
||||||
liftIO $ putStrLn $ show e
|
|
||||||
go (n+1)
|
|
||||||
|
|
||||||
toK :: Int -> String
|
|
||||||
toK = take 2 . show
|
|
||||||
|
|
||||||
inDb' :: String -> SqlPersistM Bool
|
|
||||||
inDb' sk = do
|
|
||||||
r <- select $ from $ \r -> do
|
|
||||||
where_ (r ^. FsckedKey ==. val sk)
|
|
||||||
return (r ^. FsckedKey)
|
|
||||||
return $ not $ null r
|
|
||||||
|
|
||||||
catchNonAsync :: MonadCatch m => m a -> (SomeException -> m a) -> m a
|
|
||||||
catchNonAsync a onerr = a `catches`
|
|
||||||
[ M.Handler (\ (e :: AsyncException) -> throwM e)
|
|
||||||
, M.Handler (\ (e :: SomeException) -> onerr e)
|
|
||||||
]
|
|
||||||
|
|
||||||
tryNonAsync :: MonadCatch m => m a -> m (Either SomeException a)
|
|
||||||
tryNonAsync a = go `catchNonAsync` (return . Left)
|
|
||||||
where
|
|
||||||
go = do
|
|
||||||
v <- a
|
|
||||||
return (Right v)
|
|
||||||
|
|
||||||
--nFreZHaLTZJo0R7j--
|
|
||||||
|
|
||||||
--8GpibOaaTibBMecb
|
|
||||||
Content-Type: application/pgp-signature; name="signature.asc"
|
|
||||||
Content-Description: Digital signature
|
|
||||||
|
|
||||||
-----BEGIN PGP SIGNATURE-----
|
|
||||||
Version: GnuPG v1
|
|
||||||
|
|
||||||
iQIVAwUBVOYQNckQ2SIlEuPHAQI2Bg//Qh/j4jobOdSXtbWMe2YAd06nysfOdkyr
|
|
||||||
5czFH2TWWtA4PPERuV5BbGpXmjCqhsPqJJwBGp+rtSHr222/08XgydqAzec/RWDp
|
|
||||||
U6nHvFoGLPSuw/5JEBGdY08Vdpgeh/25FIBvWMdToDdrxnDepmiM/wKQ+4jTIDt7
|
|
||||||
FikvGevyI1LOnB5dl5lf+UZQRnoDLQmL6znhnZ2ICqYQ5Y4B89SUHhji0ZaLQphr
|
|
||||||
Or04lP5vZ2AN6Ltnx7VueallPgy3RH5/Lr5FlsNnLeDM2TRtKkWJSHUDbrZoZBJ1
|
|
||||||
gOBqc9YWXPsmLXJlHRUUZK9fGtJ88VYfsmduadurIQ6JcGlqNiy5ersi7mB71Iiq
|
|
||||||
6cyaxFsdVUggTUo+qBU4behAMmkxYHDJWe6Uvp8pXA7nF1iZnuxzCczyL79KoSIL
|
|
||||||
DLxXdK+JAzR3nxX+2ZMxpeiD4sYPy73RLcxW6ItyVwAuLhHVcOX1yp+VPhCxEr5s
|
|
||||||
WVQUEhevdCJbl2jz797tSGpR8umHpuUqd5npfVMUDthfj05VhXPsrnEG0vv77iPF
|
|
||||||
Sjk/iS14LQiW6pZfCFxLDx6cejYj+eVm3p4A/wiZqO/x/Gz6SLlUpMLr30B8/WfO
|
|
||||||
3MZUCTRH1ROlQXa4WgHmRBOakM4GWpTdko81DrQeUdK0KHTevpCJhF4EWJi5iuYK
|
|
||||||
vmQDUvGVomA=
|
|
||||||
=VqgN
|
|
||||||
-----END PGP SIGNATURE-----
|
|
||||||
|
|
||||||
--8GpibOaaTibBMecb--
|
|
||||||
|
|
Loading…
Reference in a new issue