avoid using a second db connection for writes
authorJoey Hess <joeyh@joeyh.name>
Wed, 20 Oct 2021 16:32:46 +0000 (12:32 -0400)
committerJoey Hess <joeyh@joeyh.name>
Wed, 20 Oct 2021 16:32:46 +0000 (12:32 -0400)
This is a potentially breaking change in a very delicate area. However,
examining the code path for writes, I don't see any benefit to opening a
second db connection for them. If the write throws an exception,
commitDb will retry it with a new db connection.

A potential benefit to not opening a second db connection, beyond using
less resources, is it just might avoid problems in WSL with sqlite that
I have hypothesized are caused by multiple db connections.

Commit 5f9eff3f32e0101ee673c3dde221f0f27f32e3c8 explains why it needs to
shut down the db connection to force the database to be updated on disk:
When closeDb does not get called, garbage collection of DbHandle may not
give the workterThread time to cleanly shut down before git-annex exits,
resulting in a recently written change not reaching disk.

Database/Handle.hs

index f06cd0b08dfe8b1cbc5131b0322012139ecb12bd..9eb32b4e271da3d16985af304088e89974076e1c 100644 (file)
@@ -100,13 +100,13 @@ commitDb h wa = robustly Nothing 100 (commitDb' h wa)
 commitDb' :: DbHandle -> SqlPersistM () -> IO (Either SomeException ())
 commitDb' (DbHandle _ jobs) a = do
        res <- newEmptyMVar
-       putMVar jobs $ ChangeJob $ \runner ->
-               liftIO $ putMVar res =<< tryNonAsync (runner a)
+       putMVar jobs $ ChangeJob $
+               liftIO . putMVar res =<< tryNonAsync a
        takeMVar res
 
 data Job
        = QueryJob (SqlPersistM ())
-       | ChangeJob ((SqlPersistM () -> IO ()) -> IO ())
+       | ChangeJob (SqlPersistM ())
        | CloseJob
 
 workerThread :: T.Text -> TableName -> MVar Job -> IO ()
@@ -128,16 +128,10 @@ workerThread db tablename jobs = newconn
                        Left BlockedIndefinitelyOnMVar -> return (return ())
                        Right CloseJob -> return (return ())
                        Right (QueryJob a) -> a >> loop
-                       -- Change is run in a separate database connection
-                       -- since sqlite only supports a single writer at a
-                       -- time, and it may crash the database connection
-                       -- that the write is made to.
                        Right (ChangeJob a) -> do
-                               liftIO (a (runSqliteRobustly tablename db))
-                               -- Exit this sqlite connection so the
-                               -- change that was just written, using 
-                               -- a different db handle, is immediately
-                               -- visible to queries.
+                               a
+                               -- Exit the sqlite connection so the
+                               -- database gets updated on disk.
                                return newconn
        
        getjob :: IO (Either BlockedIndefinitelyOnMVar Job)