@@ -14,6 +14,7 @@ module Cardano.Mock.ChainSync.Server (
1414 withServerHandle ,
1515 stopServer ,
1616 restartServer ,
17+ waitForNextConnection ,
1718
1819 -- * ServerHandle api
1920 ServerHandle (.. ),
@@ -42,8 +43,8 @@ import Control.Concurrent.Class.MonadSTM.Strict (
4243 retry ,
4344 writeTVar ,
4445 )
45- import Control.Exception (bracket )
46- import Control.Monad (forever )
46+ import Control.Exception (IOException , bracket , try )
47+ import Control.Monad (forever , unless )
4748import Control.Tracer
4849import Data.ByteString.Lazy.Char8 (ByteString )
4950import qualified Data.Map.Strict as Map
@@ -101,13 +102,15 @@ import Ouroboros.Network.Snocket
101102import qualified Ouroboros.Network.Snocket as Snocket
102103import Ouroboros.Network.Socket
103104import Ouroboros.Network.Util.ShowProxy (Proxy (.. ), ShowProxy (.. ))
105+ import System.Directory (removeFile )
104106
105107{- HLINT ignore "Use readTVarIO" -}
106108
107109data ServerHandle m blk = ServerHandle
108110 { chainProducerState :: StrictTVar m (ChainProducerState blk )
109111 , threadHandle :: StrictTVar m (Async () )
110112 , forkAgain :: m (Async () )
113+ , socketPath :: FilePath
111114 }
112115
113116replaceGenesis :: MonadSTM m => ServerHandle m blk -> State blk -> STM m ()
@@ -142,9 +145,11 @@ rollback handle point =
142145restartServer :: ServerHandle IO blk -> IO ()
143146restartServer sh = do
144147 stopServer sh
145- -- TODO not sure why, but this delay is necessary. Without it reconnection doesn't happen
146- -- some times.
147- threadDelay 1_000_000
148+ -- On Unix, closing the socket fd does not remove the socket file. If the
149+ -- file still exists when the new server tries to bind, bind() fails with
150+ -- EADDRINUSE and the server thread dies silently. Explicitly remove the
151+ -- file here so the new server can always bind successfully.
152+ _ <- (try @ IOException ) $ removeFile (socketPath sh)
148153 thread <- forkAgain sh
149154 atomically $ writeTVar (threadHandle sh) thread
150155
@@ -153,6 +158,20 @@ stopServer sh = do
153158 srvThread <- atomically $ readTVar $ threadHandle sh
154159 cancel srvThread
155160
161+ -- | Block until db-sync has made a new connection to the (restarted) server.
162+ -- This is detected by watching 'nextFollowerId' in 'ChainProducerState': each
163+ -- new client connection calls 'newFollower', incrementing the counter.
164+ -- Call this after 'restartServer' to ensure the test does not race ahead
165+ -- before db-sync has actually reconnected.
166+ waitForNextConnection :: ServerHandle IO blk -> IO ()
167+ waitForNextConnection sh = do
168+ -- Snapshot the current follower id before the restart connection comes in.
169+ currentId <- atomically $ nextFollowerId <$> readTVar (chainProducerState sh)
170+ -- Block until a new follower (with a higher id) has been registered.
171+ atomically $ do
172+ cps <- readTVar (chainProducerState sh)
173+ unless (nextFollowerId cps > currentId) retry
174+
156175type MockServerConstraint blk =
157176 ( SerialiseNodeToClientConstraints blk
158177 , BlockSupportsLedgerQuery blk
@@ -182,7 +201,7 @@ forkServerThread iom config initSt netMagic path = do
182201 let runThread = async $ runLocalServer iom (configCodec config) netMagic path chainSt
183202 thread <- runThread
184203 threadVar <- newTVarIO thread
185- pure $ ServerHandle chainSt threadVar runThread
204+ pure $ ServerHandle chainSt threadVar runThread path
186205
187206withServerHandle ::
188207 MockServerConstraint blk =>
@@ -262,7 +281,7 @@ runLocalServer iom codecConfig netMagic localDomainSock chainProdState = do
262281 IO (() , Maybe ByteString )
263282 chainSyncServer' _them channel =
264283 runPeer
265- nullTracer -- (showTracing stdoutTracer)
284+ nullTracer
266285 (cChainSyncCodec codecs)
267286 channel
268287 (chainSyncServerPeer $ chainSyncServer state codecConfig blockVersion)
@@ -332,9 +351,10 @@ chainSyncServer state codec _blockVersion =
332351 mupdate <- tryReadChainUpdate r
333352 case mupdate of
334353 Just update -> pure (Left (sendNext r update))
335- Nothing -> pure (Right (sendNext r <$> readChainUpdate r))
336- -- Follower is at the head, have to block and wait for
337- -- the producer's state to change.
354+ Nothing ->
355+ -- Follower is at the head, have to block and wait for
356+ -- the producer's state to change.
357+ pure $ Right $ sendNext r <$> readChainUpdate r
338358
339359 handleFindIntersect ::
340360 FollowerId ->
@@ -352,13 +372,14 @@ chainSyncServer state codec _blockVersion =
352372 ServerStNext (Serialised blk ) (Point blk ) (Tip blk ) m ()
353373 sendNext r (tip, AddBlock b) =
354374 SendMsgRollForward (mkSerialised (encodeDisk codec) b) tip (idle' r)
355- sendNext r (tip, RollBack p) = SendMsgRollBackward (castPoint p) tip (idle' r)
375+ sendNext r (tip, RollBack p) =
376+ SendMsgRollBackward (castPoint p) tip (idle' r)
356377
357378 newFollower :: m FollowerId
358379 newFollower = atomically $ do
359380 cps <- readTVar state
360381 let (cps', rid) = initFollower genesisPoint cps
361- _ <- writeTVar state cps'
382+ writeTVar state cps'
362383 pure rid
363384
364385 improveReadPoint ::
0 commit comments