@@ -12,7 +12,7 @@ import Cardano.BM.Data.LogItem (
12
12
mkLOMeta ,
13
13
)
14
14
import Cardano.BM.Data.Severity (Severity (.. ))
15
- import Cardano.BM.Trace (Trace , logWarning )
15
+ import Cardano.BM.Trace (Trace )
16
16
import Cardano.Prelude
17
17
import Control.Monad.IO.Unlift (withRunInIO )
18
18
import Control.Monad.Logger (
@@ -94,8 +94,22 @@ sessionErrorToDbError cs sessionErr =
94
94
-- Run DB actions with INTERRUPT HANDLING
95
95
-----------------------------------------------------------------------------------------
96
96
97
- -- | Run a DbAction with explicit transaction and isolation level
98
- -- This version properly handles interrupts (Ctrl+C) and ensures cleanup
97
+ -- | Run a DbAction with explicit transaction control and isolation level
98
+ --
99
+ -- Transaction behavior:
100
+ -- * Begins transaction with specified isolation level
101
+ -- * Runs the action within the transaction
102
+ -- * Commits if action succeeds, rollback only on commit failure or async exceptions
103
+ -- * Returns Either for explicit error handling instead of throwing exceptions
104
+ --
105
+ -- Exception safety:
106
+ -- * Uses 'mask' to prevent async exceptions during transaction lifecycle
107
+ -- * Uses 'onException' to ensure rollback on interrupts (Ctrl+C, SIGTERM, etc.)
108
+ -- * Does NOT rollback on action errors - lets them commit (matches Persistent semantics)
109
+ --
110
+ -- Note: This follows Persistent's philosophy where successful function calls commit
111
+ -- their transactions regardless of the return value. Only async exceptions and
112
+ -- commit failures trigger rollbacks.
99
113
runDbActionWithIsolation ::
100
114
MonadUnliftIO m =>
101
115
DbEnv ->
@@ -104,28 +118,28 @@ runDbActionWithIsolation ::
104
118
m (Either DbError a )
105
119
runDbActionWithIsolation dbEnv isolationLevel action = do
106
120
withRunInIO $ \ runInIO -> do
121
+ -- Use masking to prevent async exceptions during transaction management
107
122
mask $ \ restore -> do
108
- -- Begin transaction
123
+ -- Begin transaction with specified isolation level
109
124
beginResult <- beginTransaction dbEnv isolationLevel
110
125
case beginResult of
111
126
Left err -> pure (Left err)
112
127
Right _ -> do
113
- -- Run the action with exception handling for interrupts
114
- result <-
115
- restore (runInIO $ runReaderT (runExceptT (runDbAction action)) dbEnv)
116
- `onException` do
117
- case dbTracer dbEnv of
118
- Just tracer -> logWarning tracer " rolling back transaction, due to interrupt."
119
- Nothing -> pure ()
120
- rollbackTransaction dbEnv
128
+ -- Run action with async exception protection via onException
129
+ -- If interrupted (Ctrl+C), the onException handler will rollback
130
+ result <- onException
131
+ (restore (runInIO $ runReaderT (runExceptT (runDbAction action)) dbEnv))
132
+ (restore $ rollbackTransaction dbEnv)
121
133
case result of
122
- Left err -> do
123
- rollbackTransaction dbEnv
124
- pure (Left err)
134
+ -- Action returned error but ran successfully - commit the transaction
135
+ -- This matches Persistent's behavior: successful calls always commit
136
+ Left err -> pure (Left err)
125
137
Right val -> do
138
+ -- Attempt to commit the transaction
126
139
commitResult <- commitTransaction dbEnv
127
140
case commitResult of
128
141
Left commitErr -> do
142
+ -- Commit failed - rollback and return the commit error
129
143
rollbackTransaction dbEnv
130
144
pure (Left commitErr)
131
145
Right _ -> pure (Right val)
0 commit comments