Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

backend-db: Actually catch serialization errors to retry them #131

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from

Conversation

3noch
Copy link
Collaborator

@3noch 3noch commented May 7, 2020

Todo:

  • Tests

@3noch 3noch force-pushed the eac@fix-serializable-retry branch from b322cfa to 355e222 Compare May 7, 2020 18:11
@@ -42,7 +45,7 @@ import qualified Control.Monad.State as S
-- It "disallows" (makes harder) arbitrary IO.
-- It "disallows" (makes harder) catching IO exceptions *inside* the transaction.
newtype Serializable a = Serializable (ReaderT Pg.Connection (LoggingT IO) a)
deriving (Functor, Applicative, Monad, MonadThrow, MonadLogger)
deriving (Functor, Applicative, Monad, MonadCatch.MonadThrow, MonadLogger)
-- NOTE: We *intentionally* leave out
-- - 'MonadCatch' so you can't accidentally mask a serialization error from the outer retry logic.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

um

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha that's confusing.... I'll import MonadThrow unqualified.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than leaving out the typeclass, wouldn't it be better to provide a custom implementation that gives the behavior we want?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What behavior would that be? A catch that just catches SqlSerializationError first and rethrows it before running your handler? I suppose that would ensure this error is never masked.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that wouldn't be technically law-abiding

catch (throwM e) f = f e

Presumably this law is for all e and f. If we "unmasked" SqlSerializationError in catch then any f :: SqlSerializationError -> m a would not result in f e but an exception.

How much do we care...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FTR:

instance MonadCatch Serializable where
  catch m f = MonadCatch.catches m
    [ MonadCatch.Handler $ \(e :: SqlSerializationError) -> MonadCatch.throwM e -- Never let a handler mask this exception
    , MonadCatch.Handler f
    ]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could make it (kinda) law-abiding again by also changing our throwM i think. it would be tricky and almost certainly not worthwhile for what it actually buys. I'm just not sure we actually want MonadCatch here though, particularly since also any other SqlError could cause the transaction to abort and would need to be handled specially if you don't abort the Serializable action also ...

@3noch 3noch force-pushed the eac@fix-serializable-retry branch from 355e222 to e0fd1f2 Compare May 7, 2020 20:44
@3noch 3noch force-pushed the eac@fix-serializable-retry branch from e0fd1f2 to ee08d02 Compare May 7, 2020 20:45
@3noch 3noch force-pushed the eac@fix-serializable-retry branch from 2c53fe5 to 026ff08 Compare May 7, 2020 21:55
-- | Like 'Pg.withTransactionModeRetry' but polymorphic over exception type.
-- Copied from https://github.com/phadej/postgresql-simple/blob/e02684f9c38acf736ac590b36b919000a2b45bc4/src/Database/PostgreSQL/Simple/Transaction.hs#L156-L174
withTransactionModeRetry' :: forall e a. E.Exception e => Pg.TransactionMode -> (e -> Bool) -> Pg.Connection -> IO a -> IO a
withTransactionModeRetry' mode shouldRetry conn act =
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably not worth copying this impl just to get the polymorphic exception handling.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind, upstream uses SqlError. But I could upstream this version.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been merged. So we can point at original repo now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants