packages feed

pqi-conformance 0.1.1.0 → 0.1.2.1

raw patch · 5 files changed

+94/−3 lines, 5 files

Files

CHANGELOG.md view
@@ -1,3 +1,15 @@+# v0.1.2.1++- Fixed the publishing.++# v0.1.2.0++- Added another test.++# v0.1.1.1++- Fixed the docs.+ # v0.1.1.0  ## Non-breaking
pqi-conformance.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: pqi-conformance-version: 0.1.1.0+version: 0.1.2.1 category: Database, PostgreSQL, Testing synopsis: Differential conformance tests for pqi adapters description:@@ -115,6 +115,7 @@     Pqi.Conformance.Operation.ExitPipelineMode     Pqi.Conformance.Operation.Fformat     Pqi.Conformance.Operation.Finish+    Pqi.Conformance.Operation.Finish.PostFinishSend     Pqi.Conformance.Operation.Flush     Pqi.Conformance.Operation.Fmod     Pqi.Conformance.Operation.Fname
src/library/Pqi/Conformance.hs view
@@ -43,6 +43,7 @@ import qualified Pqi.Conformance.Operation.ExitPipelineMode as ExitPipelineMode import qualified Pqi.Conformance.Operation.Fformat as Fformat import qualified Pqi.Conformance.Operation.Finish as Finish+import qualified Pqi.Conformance.Operation.Finish.PostFinishSend as Finish.PostFinishSend import qualified Pqi.Conformance.Operation.Flush as Flush import qualified Pqi.Conformance.Operation.Fmod as Fmod import qualified Pqi.Conformance.Operation.Fname as Fname@@ -116,7 +117,6 @@ import qualified Pqi.Conformance.Operation.UnescapeBytea as UnescapeBytea import qualified Pqi.Conformance.Operation.UnsafeFreeResult as UnsafeFreeResult import qualified Pqi.Conformance.Operation.User as User-import Pqi.Conformance.Prelude import Test.Hspec  -- | The full conformance battery: every per-operation spec under one shared@@ -133,6 +133,7 @@     NewNullConnection.spec adapter     IsNullConnection.spec adapter     Finish.spec adapter+    Finish.PostFinishSend.spec adapter     Reset.spec adapter     ResetStart.spec adapter     ResetPoll.spec adapter
+ src/library/Pqi/Conformance/Operation/Finish/PostFinishSend.hs view
@@ -0,0 +1,78 @@+-- | Regression coverage for a hang discovered via @hasql-pool@'s+-- @Specs.Use@ integration suite: sending a command on a connection after+-- 'Pqi.finish' has closed it could block forever on the native adapter+-- instead of failing promptly.+--+-- Root cause: 'Pqi.finish' closed the transport socket but never updated the+-- connection's status away from @ConnectionOk@. Nothing downstream of+-- 'Pqi.finish' short-circuited on a closed connection, so later protocol+-- operations kept attempting to use the closed socket's file descriptor. The+-- first such send after close typically failed fast with "Bad file+-- descriptor", but in a process doing other concurrent socket I\/O (exactly+-- the situation in a real test suite or application — e.g. testcontainers+-- and a connection pool's own background threads keep other sockets open),+-- that closed file descriptor number could already have been reused for an+-- unrelated socket by the time of a second attempt. Writing to the (silently+-- reused) fd then blocked waiting for buffer space that a completely+-- unrelated peer would never free — which is exactly what @hasql@'s+-- @cleanUpAfterInterruption@ flow does after a first send fails: it always+-- re-attempts a further write to deallocate prepared statements.+--+-- This mirrors hasql-pool's @Sessions.closeConn@ helper, which deliberately+-- calls 'Pqi.finish' on a connection and then keeps using it (to test that+-- the pool notices and evicts it) — a supported use of the low-level+-- @onLibpqConnection@ escape hatch, and the code path in which the hang was+-- first observed (hasql-pool's @Specs.UseSpec@, "Connection errors cause+-- eviction of connection").+--+-- The primary check here is deterministic and does not depend on winning+-- any race: 'Pqi.status' must report 'Pqi.ConnectionBad' once 'Pqi.finish'+-- has run, so that the @ConnectionOk@ guards already present in the query+-- functions (see @Pqi.Native.Query.sendAsync@, @withReady@) reject any+-- further operation outright instead of touching the closed socket at all.+-- A secondary, best-effort check replays a send immediately after finish+-- under a timeout, so that if the file-descriptor race above is ever hit+-- again, it fails the suite instead of hanging it.+module Pqi.Conformance.Operation.Finish.PostFinishSend+  ( spec,+  )+where++import Control.Exception (SomeException, try)+import qualified Pqi+import Pqi.Conformance.Prelude+import System.Timeout (timeout)+import Test.Hspec++-- | Generous relative to a healthy roundtrip (milliseconds), but far below+-- what a genuinely hung send would ever recover within.+sendTimeoutMicros :: Int+sendTimeoutMicros = 3_000_000++spec :: Pqi.Adapter -> SpecWith ByteString+spec adapter =+  describe "finish" do+    describe "sending after finish" do+      -- Deliberately not a 'differential' check against the FFI reference:+      -- postgresql-libpq leaves 'Pq.status' looking stale (@ConnectionOk@)+      -- after 'PQfinish' too (querying a freed @PGconn@ is undefined+      -- behaviour in C, not something conformance testing can safely pin+      -- down as a reference). Regardless of what the reference does, a+      -- well-behaved adapter should mark itself unusable so nothing+      -- downstream is tempted to touch the closed socket again.+      it "reports the connection as bad, rather than leaving it looking usable" \conninfo -> do+        connection <- Pqi.connectdb adapter conninfo+        _ <- Pqi.exec connection "select 1"+        Pqi.finish connection+        status <- Pqi.status connection+        status `shouldBe` Pqi.ConnectionBad++      it "does not block indefinitely on a send issued right after finish" \conninfo -> do+        connection <- Pqi.connectdb adapter conninfo+        _ <- Pqi.exec connection "select 1"+        Pqi.finish connection+        result <- timeout sendTimeoutMicros (try @SomeException (Pqi.exec connection "select 1"))+        -- 'Nothing' means the send never completed within the bound — i.e.+        -- it hung. Succeeding or throwing are both acceptable outcomes here;+        -- only hanging is not.+        isJust result `shouldBe` True
src/library/Pqi/Conformance/Operation/PipelineSync/Parity.hs view
@@ -13,7 +13,6 @@ import qualified Pqi import qualified Pqi as Lq import Pqi.Conformance.Harness-import Pqi.Conformance.Observation import Pqi.Conformance.Prelude import Pqi.Conformance.Scenario (observed, takeCommandResults, takeResult) import Test.Hspec