diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,13 @@
 # Changelog for persistent
 
+## 2.13.3.0
+
+* [#1341](https://github.com/yesodweb/persistent/pull/1341)
+    * Add `SqlBackendHooks` to allow for instrumentation of queries.
+* [#1327](https://github.com/yesodweb/persistent/pull/1327)
+    * Update `SqlBackend` to use new `StatementCache` interface
+      instead of `IORef (Map Text Statement)`
+
 ## 2.13.2.2
 
 * [#1351](https://github.com/yesodweb/persistent/pull/1351/)
diff --git a/Database/Persist/Sql/Raw.hs b/Database/Persist/Sql/Raw.hs
--- a/Database/Persist/Sql/Raw.hs
+++ b/Database/Persist/Sql/Raw.hs
@@ -1,23 +1,23 @@
 module Database.Persist.Sql.Raw where
 
 import Control.Exception (throwIO)
-import Control.Monad (when, liftM)
+import Control.Monad (liftM, when)
 import Control.Monad.IO.Class (MonadIO, liftIO)
 import Control.Monad.Logger (logDebugNS, runLoggingT)
-import Control.Monad.Reader (ReaderT, ask, MonadReader)
-import Control.Monad.Trans.Resource (MonadResource,release)
-import Data.Acquire (allocateAcquire, Acquire, mkAcquire, with)
+import Control.Monad.Reader (MonadReader, ReaderT, ask)
+import Control.Monad.Trans.Resource (MonadResource, release)
+import Data.Acquire (Acquire, allocateAcquire, mkAcquire, with)
 import Data.Conduit
-import Data.IORef (writeIORef, readIORef, newIORef)
-import qualified Data.Map as Map
+import Data.IORef (newIORef, readIORef, writeIORef)
 import Data.Int (Int64)
 import Data.Text (Text, pack)
 import qualified Data.Text as T
 
 import Database.Persist
+import Database.Persist.Sql.Class
 import Database.Persist.Sql.Types
 import Database.Persist.Sql.Types.Internal
-import Database.Persist.Sql.Class
+import Database.Persist.SqlBackend.Internal.StatementCache
 
 rawQuery :: (MonadResource m, MonadReader env m, BackendCompatible SqlBackend env)
          => Text
@@ -75,9 +75,10 @@
 
 getStmtConn :: SqlBackend -> Text -> IO Statement
 getStmtConn conn sql = do
-    smap <- liftIO $ readIORef $ connStmtMap conn
-    case Map.lookup sql smap of
-        Just stmt -> return stmt
+    let cacheK = mkCacheKeyFromQuery sql
+    mstmt <- statementCacheLookup (connStmtMap conn) cacheK
+    stmt <- case mstmt of
+        Just stmt -> pure stmt
         Nothing -> do
             stmt' <- liftIO $ connPrepare conn sql
             iactive <- liftIO $ newIORef True
@@ -100,8 +101,10 @@
                             then stmtQuery stmt' x
                             else liftIO $ throwIO $ StatementAlreadyFinalized sql
                     }
-            liftIO $ writeIORef (connStmtMap conn) $ Map.insert sql stmt smap
-            return stmt
+
+            liftIO $ statementCacheInsert (connStmtMap conn) cacheK stmt
+            pure stmt
+    (hookGetStatement $ connHooks conn) conn sql stmt
 
 -- | Execute a raw SQL statement and return its results as a
 -- list. If you do not expect a return value, use of
diff --git a/Database/Persist/Sql/Run.hs b/Database/Persist/Sql/Run.hs
--- a/Database/Persist/Sql/Run.hs
+++ b/Database/Persist/Sql/Run.hs
@@ -1,23 +1,25 @@
 {-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE RecordWildCards #-}
 module Database.Persist.Sql.Run where
 
 import Control.Monad.IO.Unlift
-import qualified UnliftIO.Exception as UE
 import Control.Monad.Logger.CallStack
-import Control.Monad.Reader (MonadReader)
+import Control.Monad.Reader (MonadReader, void)
 import qualified Control.Monad.Reader as MonadReader
 import Control.Monad.Trans.Reader hiding (local)
 import Control.Monad.Trans.Resource
 import Data.Acquire (Acquire, ReleaseType(..), mkAcquireType, with)
-import Data.IORef (readIORef)
 import Data.Pool as P
-import qualified Data.Map as Map
 import qualified Data.Text as T
+import qualified UnliftIO.Exception as UE
 
 import Database.Persist.Class.PersistStore
+import Database.Persist.Sql.Raw
 import Database.Persist.Sql.Types
 import Database.Persist.Sql.Types.Internal
-import Database.Persist.Sql.Raw
+import Database.Persist.SqlBackend.Internal.StatementCache
+import Database.Persist.SqlBackend.Internal.SqlPoolHooks
 
 -- | Get a connection from the pool, run the given action, and then return the
 -- connection to the pool.
@@ -93,15 +95,36 @@
     -- cleanup function is complete.
     -> m a
 runSqlPoolWithHooks r pconn i before after onException =
+    runSqlPoolWithExtensibleHooks r pconn i $ SqlPoolHooks
+        { alterBackend = pure
+        , runBefore = \conn _ -> void $ before conn
+        , runAfter = \conn _ -> void $ after conn
+        , runOnException = \b _ e -> void $ onException b e
+        }
+
+-- | This function is how 'runSqlPoolWithHooks' is defined.
+--
+-- It's currently the most general function for using a SQL pool.
+--
+-- @since 2.13.0.0
+runSqlPoolWithExtensibleHooks
+    :: forall backend m a. (MonadUnliftIO m, BackendCompatible SqlBackend backend)
+    => ReaderT backend m a
+    -> Pool backend
+    -> Maybe IsolationLevel
+    -> SqlPoolHooks m backend
+    -> m a
+runSqlPoolWithExtensibleHooks r pconn i SqlPoolHooks{..} =
     withRunInIO $ \runInIO ->
     withResource pconn $ \conn ->
     UE.mask $ \restore -> do
-        _ <- restore $ runInIO $ before conn
-        a <- restore (runInIO (runReaderT r conn))
+        conn' <- restore $ runInIO $ alterBackend conn
+        _ <- restore $ runInIO $ runBefore conn' i
+        a <- restore (runInIO (runReaderT r conn'))
             `UE.catchAny` \e -> do
-                _ <- restore $ runInIO $ onException conn e
+                _ <- restore $ runInIO $ runOnException conn' i e
                 UE.throwIO e
-        _ <- restore $ runInIO $ after conn
+        _ <- restore $ runInIO $ runAfter conn' i
         pure a
 
 rawAcquireSqlConn
@@ -296,5 +319,6 @@
 
 close' :: (BackendCompatible SqlBackend backend) => backend -> IO ()
 close' conn = do
-    readIORef (connStmtMap $ projectBackend conn) >>= mapM_ stmtFinalize . Map.elems
-    connClose $ projectBackend conn
+    let backend = projectBackend conn
+    statementCacheClear $ connStmtMap backend
+    connClose backend
diff --git a/Database/Persist/Sql/Types.hs b/Database/Persist/Sql/Types.hs
--- a/Database/Persist/Sql/Types.hs
+++ b/Database/Persist/Sql/Types.hs
@@ -12,9 +12,8 @@
 import Control.Monad.Logger (NoLoggingT)
 import Control.Monad.Trans.Reader (ReaderT(..))
 import Control.Monad.Trans.Resource (ResourceT)
-import Control.Monad.Trans.Writer (WriterT)
 import Data.Pool (Pool)
-import Data.Text (Text, unpack)
+import Data.Text (Text)
 
 import Data.Time (NominalDiffTime)
 import Database.Persist.Sql.Types.Internal
diff --git a/Database/Persist/Sql/Types/Internal.hs b/Database/Persist/Sql/Types/Internal.hs
--- a/Database/Persist/Sql/Types/Internal.hs
+++ b/Database/Persist/Sql/Types/Internal.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE RankNTypes #-}
-{-# language RecordWildCards #-}
-{-# language DuplicateRecordFields #-}
+{-# LANGUAGE RecordWildCards #-}
 
 -- | Breaking changes to this module are not reflected in the major version
 -- number. Prefer to import from "Database.Persist.Sql" instead. If you neeed
@@ -25,24 +25,28 @@
     , SqlReadT
     , SqlWriteT
     , IsSqlBackend
+    , SqlBackendHooks (..)
     ) where
 
 import Control.Monad.Trans.Class (lift)
-import Control.Monad.Trans.Reader (ReaderT, runReaderT, ask)
+import Control.Monad.Trans.Reader (ReaderT, ask, runReaderT)
 
 import Database.Persist.Class
-    ( HasPersistBackend (..)
-    , PersistQueryRead, PersistQueryWrite
-    , PersistStoreRead, PersistStoreWrite
-    , PersistUniqueRead, PersistUniqueWrite
-    , BackendCompatible(..)
-    )
-import Database.Persist.Class.PersistStore (IsPersistBackend (..))
+       ( BackendCompatible(..)
+       , HasPersistBackend(..)
+       , PersistQueryRead
+       , PersistQueryWrite
+       , PersistStoreRead
+       , PersistStoreWrite
+       , PersistUniqueRead
+       , PersistUniqueWrite
+       )
+import Database.Persist.Class.PersistStore (IsPersistBackend(..))
 import Database.Persist.SqlBackend.Internal
 import Database.Persist.SqlBackend.Internal.InsertSqlResult
+import Database.Persist.SqlBackend.Internal.IsolationLevel
 import Database.Persist.SqlBackend.Internal.MkSqlBackend
 import Database.Persist.SqlBackend.Internal.Statement
-import Database.Persist.SqlBackend.Internal.IsolationLevel
 
 -- | An SQL backend which can only handle read queries
 --
diff --git a/Database/Persist/SqlBackend.hs b/Database/Persist/SqlBackend.hs
--- a/Database/Persist/SqlBackend.hs
+++ b/Database/Persist/SqlBackend.hs
@@ -6,35 +6,46 @@
       SqlBackend
     , mkSqlBackend
     , MkSqlBackendArgs(..)
+    , SqlBackendHooks
+    , emptySqlBackendHooks
     -- * Utilities
 
     -- $utilities
 
     -- ** SqlBackend Getters
+    , getRDBMS
     , getEscapedFieldName
     , getEscapedRawName
     , getEscapeRawNameFunction
     , getConnLimitOffset
     , getConnUpsertSql
+    , getConnVault
+    , getConnHooks
     -- ** SqlBackend Setters
     , setConnMaxParams
     , setConnRepsertManySql
     , setConnInsertManySql
     , setConnUpsertSql
     , setConnPutManySql
+    , setConnVault
+    , modifyConnVault
+    , setConnHooks
+    -- ** SqlBackendHooks
     ) where
 
 import Control.Monad.Reader
+import Data.List.NonEmpty (NonEmpty)
 import Data.Text (Text)
+import Data.Vault.Strict (Vault)
 import Database.Persist.Class.PersistStore (BackendCompatible(..))
+import Database.Persist.Names
 import Database.Persist.SqlBackend.Internal
 import qualified Database.Persist.SqlBackend.Internal as SqlBackend
        (SqlBackend(..))
-import Database.Persist.SqlBackend.Internal.MkSqlBackend as Mk (MkSqlBackendArgs(..))
-import Database.Persist.Types.Base
-import Database.Persist.Names
 import Database.Persist.SqlBackend.Internal.InsertSqlResult
-import Data.List.NonEmpty (NonEmpty)
+import Database.Persist.SqlBackend.Internal.MkSqlBackend as Mk
+       (MkSqlBackendArgs(..))
+import Database.Persist.Types.Base
 
 -- $utilities
 --
@@ -129,7 +140,35 @@
 getConnUpsertSql = do
     asks (SqlBackend.connUpsertSql . projectBackend)
 
+-- | Retrieve the vault from the provided database backend.
+--
+-- @since 2.13.3.0
+getConnVault
+    ::  (BackendCompatible SqlBackend backend, MonadReader backend m)
+    => m Vault
+getConnVault = do
+    asks (SqlBackend.connVault . projectBackend)
 
+-- | Retrieve instrumentation hooks from the provided database backend.
+--
+-- @since 2.13.3.0
+getConnHooks
+    ::  (BackendCompatible SqlBackend backend, MonadReader backend m)
+    => m SqlBackendHooks
+getConnHooks = do
+    asks (SqlBackend.connHooks . projectBackend)
+
+-- | Get a tag displaying what database the 'SqlBackend' is for. Can be
+-- used to differentiate features in downstream libraries for different
+-- database backends.
+-- @since 2.13.3.0
+getRDBMS
+    :: (BackendCompatible SqlBackend backend, MonadReader backend m)
+    => m Text
+getRDBMS = do
+    asks (SqlBackend.connRDBMS . projectBackend)
+
+
 -- | Set the maximum parameters that may be issued in a given SQL query. This
 -- should be used only if the database backend have this limitation.
 --
@@ -188,3 +227,24 @@
     -> SqlBackend
 setConnPutManySql  mkQuery sb =
     sb { connPutManySql = Just mkQuery }
+
+-- | Set the vault on the provided database backend.
+--
+-- @since 2.13.0
+setConnVault :: Vault -> SqlBackend -> SqlBackend
+setConnVault vault sb =
+    sb { connVault = vault }
+
+-- | Modify the vault on the provided database backend.
+--
+-- @since 2.13.0
+modifyConnVault :: (Vault -> Vault) -> SqlBackend  -> SqlBackend
+modifyConnVault f sb =
+    sb { connVault = f $ connVault sb }
+
+-- | Set hooks on the provided database backend.
+--
+-- @since 2.13.0
+setConnHooks :: SqlBackendHooks -> SqlBackend -> SqlBackend
+setConnHooks hooks sb =
+    sb { connHooks = hooks }
diff --git a/Database/Persist/SqlBackend/Internal.hs b/Database/Persist/SqlBackend/Internal.hs
--- a/Database/Persist/SqlBackend/Internal.hs
+++ b/Database/Persist/SqlBackend/Internal.hs
@@ -1,19 +1,21 @@
-{-# language RecordWildCards #-}
-{-# language RankNTypes #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE RecordWildCards #-}
 
 module Database.Persist.SqlBackend.Internal where
 
-import Data.Map (Map)
 import Data.List.NonEmpty (NonEmpty)
 import Data.Text (Text)
+import Data.Vault.Strict (Vault)
+import qualified Data.Vault.Strict as Vault
 import Database.Persist.Class.PersistStore
-import Database.Persist.Types.Base
 import Database.Persist.Names
-import Data.IORef
-import Database.Persist.SqlBackend.Internal.MkSqlBackend
-import Database.Persist.SqlBackend.Internal.Statement
 import Database.Persist.SqlBackend.Internal.InsertSqlResult
 import Database.Persist.SqlBackend.Internal.IsolationLevel
+import Database.Persist.SqlBackend.Internal.MkSqlBackend
+import Database.Persist.SqlBackend.Internal.Statement
+import Database.Persist.SqlBackend.Internal.StatementCache (StatementCache)
+import Database.Persist.SqlBackend.StatementCache
+import Database.Persist.Types.Base
 
 -- | A 'SqlBackend' represents a handle or connection to a database. It
 -- contains functions and values that allow databases to have more
@@ -69,7 +71,7 @@
     -- When left as 'Nothing', we default to using 'defaultPutMany'.
     --
     -- @since 2.8.1
-    , connStmtMap :: IORef (Map Text Statement)
+    , connStmtMap :: StatementCache
     -- ^ A reference to the cache of statements. 'Statement's are keyed by
     -- the 'Text' queries that generated them.
     , connClose :: IO ()
@@ -132,8 +134,23 @@
     -- When left as 'Nothing', we default to using 'defaultRepsertMany'.
     --
     -- @since 2.9.0
+    , connVault :: Vault
+    -- ^ Carry arbitrary payloads for the connection that
+    -- may be used to propagate information into hooks.
+    , connHooks :: SqlBackendHooks
+    -- ^ Instrumentation hooks that may be used to track the
+    -- behaviour of a backend.
     }
 
+newtype SqlBackendHooks = SqlBackendHooks
+    { hookGetStatement :: SqlBackend -> Text -> Statement -> IO Statement
+    }
+
+emptySqlBackendHooks :: SqlBackendHooks
+emptySqlBackendHooks = SqlBackendHooks
+    { hookGetStatement = \_ _ s -> pure s
+    }
+
 -- | A function for creating a value of the 'SqlBackend' type. You should prefer
 -- to use this instead of the constructor for 'SqlBackend', because default
 -- values for this will be provided for new fields on the record when new
@@ -148,6 +165,9 @@
         , connPutManySql = Nothing
         , connUpsertSql = Nothing
         , connInsertManySql = Nothing
+        , connVault = Vault.empty
+        , connHooks = emptySqlBackendHooks
+        , connStmtMap = mkStatementCache $ mkSimpleStatementCache connStmtMap
         , ..
         }
 
diff --git a/Database/Persist/SqlBackend/Internal/MkSqlBackend.hs b/Database/Persist/SqlBackend/Internal/MkSqlBackend.hs
--- a/Database/Persist/SqlBackend/Internal/MkSqlBackend.hs
+++ b/Database/Persist/SqlBackend/Internal/MkSqlBackend.hs
@@ -3,14 +3,14 @@
 module Database.Persist.SqlBackend.Internal.MkSqlBackend where
 
 import Control.Monad.Logger (Loc, LogLevel, LogSource, LogStr)
-import Data.IORef
-import Data.Map (Map)
 import Data.Text (Text)
-import Database.Persist.SqlBackend.Internal.Statement
+import Database.Persist.Names
 import Database.Persist.SqlBackend.Internal.InsertSqlResult
 import Database.Persist.SqlBackend.Internal.IsolationLevel
+import Database.Persist.SqlBackend.Internal.Statement
 import Database.Persist.Types.Base
-import Database.Persist.Names
+import Data.Map (Map)
+import Data.IORef (IORef)
 
 -- | This type shares many of the same field names as the 'SqlBackend' type.
 -- It's useful for library authors to use this when migrating from using the
diff --git a/Database/Persist/SqlBackend/Internal/SqlPoolHooks.hs b/Database/Persist/SqlBackend/Internal/SqlPoolHooks.hs
new file mode 100644
--- /dev/null
+++ b/Database/Persist/SqlBackend/Internal/SqlPoolHooks.hs
@@ -0,0 +1,21 @@
+module Database.Persist.SqlBackend.Internal.SqlPoolHooks
+  ( SqlPoolHooks(..)
+  ) where
+import Control.Exception (SomeException)
+import Database.Persist.SqlBackend.Internal.IsolationLevel
+
+-- | A set of hooks that may be used to alter the behaviour
+-- of @runSqlPoolWithExtensibleHooks@ in a backwards-compatible
+-- fashion.
+data SqlPoolHooks m backend = SqlPoolHooks
+    { alterBackend :: backend -> m backend
+    -- ^ Alter the backend prior to executing any actions with it.
+    , runBefore :: backend -> Maybe IsolationLevel -> m ()
+    -- ^ Run this action immediately before the action is performed.
+    , runAfter :: backend -> Maybe IsolationLevel -> m ()
+    -- ^ Run this action immediately after the action is completed.
+    , runOnException :: backend -> Maybe IsolationLevel -> SomeException -> m ()
+    -- ^ This action is performed when an exception is received. The
+    -- exception is provided as a convenience - it is rethrown once this
+    -- cleanup function is complete.
+    }
diff --git a/Database/Persist/SqlBackend/Internal/StatementCache.hs b/Database/Persist/SqlBackend/Internal/StatementCache.hs
new file mode 100644
--- /dev/null
+++ b/Database/Persist/SqlBackend/Internal/StatementCache.hs
@@ -0,0 +1,23 @@
+module Database.Persist.SqlBackend.Internal.StatementCache where
+
+import Data.Text (Text)
+import Database.Persist.SqlBackend.Internal.Statement
+
+-- | A statement cache used to lookup statements that have already been prepared
+-- for a given query.
+--
+-- @since 2.13.3
+data StatementCache = StatementCache
+    { statementCacheLookup :: StatementCacheKey -> IO (Maybe Statement)
+    , statementCacheInsert :: StatementCacheKey -> Statement -> IO ()
+    , statementCacheClear :: IO ()
+    , statementCacheSize :: IO Int
+    }
+
+newtype StatementCacheKey = StatementCacheKey { cacheKey :: Text }
+-- Wrapping around this to allow for more efficient keying mechanisms
+-- in the future, perhaps.
+
+-- | Construct a `StatementCacheKey` from a raw SQL query.
+mkCacheKeyFromQuery :: Text -> StatementCacheKey
+mkCacheKeyFromQuery = StatementCacheKey
diff --git a/Database/Persist/SqlBackend/SqlPoolHooks.hs b/Database/Persist/SqlBackend/SqlPoolHooks.hs
new file mode 100644
--- /dev/null
+++ b/Database/Persist/SqlBackend/SqlPoolHooks.hs
@@ -0,0 +1,92 @@
+module Database.Persist.SqlBackend.SqlPoolHooks
+  ( SqlPoolHooks
+  , defaultSqlPoolHooks
+  , getAlterBackend
+  , modifyAlterBackend
+  , setAlterBackend
+  , getRunBefore
+  , modifyRunBefore
+  , setRunBefore
+  , getRunAfter
+  , modifyRunAfter
+  , setRunAfter
+  , getRunOnException
+  )
+  where
+
+import Control.Exception
+import Control.Monad.IO.Class
+import Database.Persist.Sql.Raw
+import Database.Persist.SqlBackend.Internal
+import Database.Persist.SqlBackend.Internal.SqlPoolHooks
+import Database.Persist.SqlBackend.Internal.IsolationLevel
+import Database.Persist.Class.PersistStore
+
+-- | Lifecycle hooks that may be altered to extend SQL pool behavior
+-- in a backwards compatible fashion.
+--
+-- By default, the hooks have the following semantics:
+--
+-- - 'alterBackend' has no effect
+-- - 'runBefore' begins a transaction
+-- - 'runAfter' commits the current transaction
+-- - 'runOnException' rolls back the current transaction
+--
+-- @since 2.13.3.0
+defaultSqlPoolHooks :: (MonadIO m, BackendCompatible SqlBackend backend) => SqlPoolHooks m backend
+defaultSqlPoolHooks = SqlPoolHooks
+    { alterBackend = pure
+    , runBefore = \conn mi -> do
+        let sqlBackend = projectBackend conn
+        let getter = getStmtConn sqlBackend
+        liftIO $ connBegin sqlBackend getter mi
+    , runAfter = \conn _ -> do
+        let sqlBackend = projectBackend conn
+        let getter = getStmtConn sqlBackend
+        liftIO $ connCommit sqlBackend getter
+    , runOnException = \conn _ _ -> do
+        let sqlBackend = projectBackend conn
+        let getter = getStmtConn sqlBackend
+        liftIO $ connRollback sqlBackend getter
+    }
+
+getAlterBackend :: SqlPoolHooks m backend -> (backend -> m backend)
+getAlterBackend = alterBackend
+
+modifyAlterBackend :: SqlPoolHooks m backend -> ((backend -> m backend) -> (backend -> m backend)) -> SqlPoolHooks m backend
+modifyAlterBackend hooks f = hooks { alterBackend = f $ alterBackend hooks }
+
+setAlterBackend :: SqlPoolHooks m backend -> (backend -> m backend) -> SqlPoolHooks m backend
+setAlterBackend hooks f = hooks { alterBackend = f }
+
+
+getRunBefore :: SqlPoolHooks m backend -> (backend -> Maybe IsolationLevel -> m ())
+getRunBefore = runBefore
+
+modifyRunBefore :: SqlPoolHooks m backend -> ((backend -> Maybe IsolationLevel -> m ()) -> (backend -> Maybe IsolationLevel -> m ())) -> SqlPoolHooks m backend
+modifyRunBefore hooks f = hooks { runBefore = f $ runBefore hooks }
+
+setRunBefore :: SqlPoolHooks m backend -> (backend -> Maybe IsolationLevel -> m ()) -> SqlPoolHooks m backend
+setRunBefore h f = h { runBefore = f }
+
+
+getRunAfter :: SqlPoolHooks m backend -> (backend -> Maybe IsolationLevel -> m ())
+getRunAfter = runAfter
+
+modifyRunAfter :: SqlPoolHooks m backend -> ((backend -> Maybe IsolationLevel -> m ()) -> (backend -> Maybe IsolationLevel -> m ())) -> SqlPoolHooks m backend
+modifyRunAfter hooks f = hooks { runAfter = f $ runAfter hooks }
+
+setRunAfter :: SqlPoolHooks m backend -> (backend -> Maybe IsolationLevel -> m ()) -> SqlPoolHooks m backend
+setRunAfter hooks f = hooks { runAfter = f }
+
+
+getRunOnException :: SqlPoolHooks m backend -> (backend -> Maybe IsolationLevel -> SomeException -> m ())
+getRunOnException = runOnException
+
+modifyRunOnException :: SqlPoolHooks m backend -> ((backend -> Maybe IsolationLevel -> SomeException -> m ()) -> (backend -> Maybe IsolationLevel -> SomeException -> m ())) -> SqlPoolHooks m backend
+modifyRunOnException hooks f = hooks { runOnException = f $ runOnException hooks }
+
+setRunOnException :: SqlPoolHooks m backend -> (backend -> Maybe IsolationLevel -> SomeException -> m ()) -> SqlPoolHooks m backend
+setRunOnException hooks f = hooks { runOnException = f }
+
+
diff --git a/Database/Persist/SqlBackend/StatementCache.hs b/Database/Persist/SqlBackend/StatementCache.hs
new file mode 100644
--- /dev/null
+++ b/Database/Persist/SqlBackend/StatementCache.hs
@@ -0,0 +1,66 @@
+{-# LANGUAGE RecordWildCards #-}
+module Database.Persist.SqlBackend.StatementCache
+  ( StatementCache
+  , StatementCacheKey
+  , mkCacheKeyFromQuery
+  , MkStatementCache(..)
+  , mkSimpleStatementCache
+  , mkStatementCache
+  ) where
+
+import Data.Foldable
+import Data.IORef
+import qualified Data.Map as Map
+import Database.Persist.SqlBackend.Internal.Statement
+import Database.Persist.SqlBackend.Internal.StatementCache
+import Data.Map (Map)
+import Data.Text (Text)
+
+-- | Configuration parameters for creating a custom statement cache
+--
+-- @since 2.13.3
+data MkStatementCache = MkStatementCache
+    { statementCacheLookup :: StatementCacheKey -> IO (Maybe Statement)
+    -- ^ Retrieve a statement from the cache, or return nothing if it is not found.
+    --
+    -- @since 2.13.3
+    , statementCacheInsert :: StatementCacheKey -> Statement -> IO ()
+    -- ^ Put a new statement into the cache. An immediate lookup of
+    -- the statement MUST return the inserted statement for the given
+    -- cache key. Depending on the implementation, the statement cache MAY
+    -- choose to evict other statements from the cache within this function.
+    --
+    -- @since 2.13.3
+    , statementCacheClear :: IO ()
+    -- ^ Remove all statements from the cache. Implementations of this
+    -- should be sure to call `stmtFinalize` on all statements removed
+    -- from the cache.
+    --
+    -- @since 2.13.3
+    , statementCacheSize :: IO Int
+    -- ^ Get the current size of the cache.
+    --
+    -- @since 2.13.3
+    }
+
+
+-- | Make a simple statement cache that will cache statements if they are not currently cached.
+--
+-- @since 2.13.3
+mkSimpleStatementCache :: IORef (Map Text Statement) -> MkStatementCache
+mkSimpleStatementCache stmtMap =
+    MkStatementCache
+        { statementCacheLookup = \sql -> Map.lookup (cacheKey sql) <$> readIORef stmtMap
+        , statementCacheInsert = \sql stmt ->
+            modifyIORef' stmtMap (Map.insert (cacheKey sql) stmt)
+        , statementCacheClear = do
+            oldStatements <- atomicModifyIORef' stmtMap (\oldStatements -> (Map.empty, oldStatements))
+            traverse_ stmtFinalize oldStatements
+        , statementCacheSize = Map.size <$> readIORef stmtMap
+        }
+
+-- | Create a statement cache.
+--
+-- @since 2.13.0
+mkStatementCache :: MkStatementCache -> StatementCache
+mkStatementCache MkStatementCache{..} = StatementCache { .. }
diff --git a/persistent.cabal b/persistent.cabal
--- a/persistent.cabal
+++ b/persistent.cabal
@@ -1,5 +1,5 @@
 name:            persistent
-version:         2.13.2.2
+version:         2.13.3.0
 license:         MIT
 license-file:    LICENSE
 author:          Michael Snoyman <michael@snoyman.com>
@@ -42,6 +42,7 @@
       , unliftio
       , unliftio-core
       , unordered-containers
+      , vault
       , vector
 
     default-extensions: 
@@ -72,10 +73,14 @@
         Database.Persist.Sql.Util
 
         Database.Persist.SqlBackend
+        Database.Persist.SqlBackend.StatementCache
+        Database.Persist.SqlBackend.SqlPoolHooks
         Database.Persist.SqlBackend.Internal
         Database.Persist.SqlBackend.Internal.InsertSqlResult
         Database.Persist.SqlBackend.Internal.IsolationLevel
+        Database.Persist.SqlBackend.Internal.SqlPoolHooks
         Database.Persist.SqlBackend.Internal.Statement
+        Database.Persist.SqlBackend.Internal.StatementCache
         Database.Persist.SqlBackend.Internal.MkSqlBackend
 
         Database.Persist.Class
