Close sqlite databases more robustly.

Had a report of close throwing ErrorBusy on CIFS.

Retrying up to 16 seconds is a balance between hopefully waiting long
enough for the problem to clear up and waiting so long that git-annex seems
to hang.

The new dependency is free; persistent depends on unliftio-core.
This commit is contained in:
Joey Hess 2019-09-26 12:24:03 -04:00
parent ab8a6a82e1
commit 9628ae2e67
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
3 changed files with 51 additions and 2 deletions

View file

@ -5,6 +5,7 @@ git-annex (7.20190913) UNRELEASED; urgency=medium
* git-lfs: Added support for http basic auth. * git-lfs: Added support for http basic auth.
* git-lfs: Only do endpoint discovery once when concurrency is enabled. * git-lfs: Only do endpoint discovery once when concurrency is enabled.
* Test: Use more robust directory removal when built with directory-1.2.7. * Test: Use more robust directory removal when built with directory-1.2.7.
* Close sqlite databases more robustly.
-- Joey Hess <id@joeyh.name> Thu, 19 Sep 2019 11:11:19 -0400 -- Joey Hess <id@joeyh.name> Thu, 19 Sep 2019 11:11:19 -0400

View file

@ -1,10 +1,12 @@
{- Persistent sqlite database handles. {- Persistent sqlite database handles.
- -
- Copyright 2015-2018 Joey Hess <id@joeyh.name> - Copyright 2015-2019 Joey Hess <id@joeyh.name>
- -
- Licensed under the GNU AGPL version 3 or higher. - Licensed under the GNU AGPL version 3 or higher.
-} -}
{-# LANGUAGE TypeFamilies, FlexibleContexts #-}
module Database.Handle ( module Database.Handle (
DbHandle, DbHandle,
DbConcurrency(..), DbConcurrency(..),
@ -23,6 +25,8 @@ import Database.Persist.Sqlite
import qualified Database.Sqlite as Sqlite import qualified Database.Sqlite as Sqlite
import Control.Monad import Control.Monad
import Control.Monad.IO.Class (liftIO) import Control.Monad.IO.Class (liftIO)
import Control.Monad.IO.Unlift (MonadUnliftIO, withRunInIO)
import Control.Monad.Logger (MonadLogger)
import Control.Concurrent import Control.Concurrent
import Control.Concurrent.Async import Control.Concurrent.Async
import Control.Exception (throwIO, BlockedIndefinitelyOnMVar(..)) import Control.Exception (throwIO, BlockedIndefinitelyOnMVar(..))
@ -193,7 +197,7 @@ runSqliteRobustly tablename db a = do
go conn retries = do go conn retries = do
r <- try $ runResourceT $ runNoLoggingT $ r <- try $ runResourceT $ runNoLoggingT $
withSqlConn (wrapConnection conn) $ withSqlConnRobustly (wrapConnection conn) $
runSqlConn a runSqlConn a
case r of case r of
Right v -> return v Right v -> return v
@ -237,3 +241,46 @@ runSqliteRobustly tablename db a = do
nullselect = T.pack $ "SELECT null from " ++ tablename ++ " limit 1" nullselect = T.pack $ "SELECT null from " ++ tablename ++ " limit 1"
briefdelay = threadDelay 1000 -- 1/1000th second briefdelay = threadDelay 1000 -- 1/1000th second
-- Like withSqlConn, but more robust.
withSqlConnRobustly
:: (MonadUnliftIO m, MonadLogger m, IsPersistBackend backend, BaseBackend backend ~ SqlBackend)
=> (LogFunc -> IO backend)
-> (backend -> m a)
-> m a
withSqlConnRobustly open f = do
logFunc <- askLogFunc
withRunInIO $ \run -> bracket
(open logFunc)
closeRobustly
(run . f)
-- Sqlite can throw ErrorBusy while closing a database; this catches
-- the exception and retries.
closeRobustly
:: (IsPersistBackend backend, BaseBackend backend ~ SqlBackend)
=> backend
-> IO ()
closeRobustly conn = go maxretries briefdelay
where
briefdelay = 1000 -- 1/1000th second
-- Try up to 14 times; with the delay doubling each time,
-- the maximum delay before giving up is 16 seconds.
maxretries = 14 :: Int
go retries delay = do
r <- try $ close' conn
case r of
Right () -> return ()
Left ex@(Sqlite.SqliteException { Sqlite.seError = e })
| e == Sqlite.ErrorBusy -> do
threadDelay delay
let delay' = delay * 2
let retries' = retries - 1
if retries' < 1
then rethrow "while closing database connection" ex
else go retries' delay'
| otherwise -> rethrow "while closing database connection" ex
rethrow msg e = throwIO $ userError $ show e ++ "(" ++ msg ++ ")"

View file

@ -344,6 +344,7 @@ Executable git-annex
persistent-sqlite (>= 2.8.1), persistent-sqlite (>= 2.8.1),
persistent (>= 2.8.1), persistent (>= 2.8.1),
persistent-template, persistent-template,
unliftio-core,
microlens, microlens,
aeson, aeson,
vector, vector,