packages feed

pqi-conformance-1.0.17.0: src/library/Pqi/Conformance/Operation/ExecParams/Interrupted.hs

-- | Coverage for a connection reused after an asynchronous exception
-- interrupted 'Pqi.execParams' while it waited for its results: a slow
-- query is cut short by 'timeout', then two quick queries run on the same
-- connection.
--
-- The divergence: @pqi-native@'s synchronous flows sent the query and read
-- up to the next @ReadyForQuery@ without recording that a query was in
-- flight. Once the read was interrupted, the slow query's messages stayed
-- unread, so the next call read them as its own and returned the slow
-- query's result, and every call after that returned the result of the one
-- before it, with no error. @libpq@ cannot be interrupted there at all
-- (@PQexecParams@ is a blocking foreign call, so the exception is delivered
-- once it returns), and the next call gets its own result. @libpq@ also
-- discards any results left over from a previous query at the start of each
-- synchronous call (@PQexecStart@ in @fe-exec.c@).
--
-- Where it surfaced: @pqi-native@ issue #16.
--
-- Why this observation: whether 'timeout' returns early differs by design
-- (the reference cannot be interrupted), so only what the connection does
-- afterwards is compared: the results of the two later queries and the
-- transaction status.
module Pqi.Conformance.Operation.ExecParams.Interrupted
  ( spec,
  )
where

import qualified Pqi
import Pqi.Conformance.Harness
import Pqi.Conformance.Observation
import Pqi.Conformance.Prelude
import Pqi.Conformance.Scenario
import System.Timeout (timeout)
import Test.Hspec

spec :: Pqi.Adapter -> SpecWith ByteString
spec adapter =
  describe "execParams" do
    describe "interrupted" do
      it "the next calls return their own results" \conninfo ->
        differential adapter conninfo scenario

scenario :: Pqi.Connection -> IO (Maybe ResultObservation, Maybe ResultObservation, Pqi.TransactionStatus)
scenario connection = do
  _ <- timeout 200_000 (Pqi.execParams connection "select 'slow' from pg_sleep (1)" [] Pqi.Text)
  one <- observed "select 'one'" [] Pqi.Text connection
  two <- observed "select 'two'" [] Pqi.Text connection
  transactionStatus <- Pqi.transactionStatus connection
  pure (one, two, transactionStatus)