gbnet-hs-0.1.0.0: src/GBNet/Peer/Migration.hs
{-# LANGUAGE DataKinds #-}
-- |
-- Module : GBNet.Peer.Migration
-- Description : Pure migration detection helpers
--
-- Finds migration candidates by matching incoming packet sequence numbers
-- against existing connections. Only depends on 'GBNet.Peer.Internal'.
module GBNet.Peer.Migration
( findMigrationCandidate,
migrationTokenFor,
migrationCooldownMs,
)
where
import qualified Data.Map.Strict as Map
import Data.Word (Word64)
import GBNet.Class (MonoTime)
import GBNet.Config (NetworkConfig (..))
import GBNet.Connection (Connection)
import qualified GBNet.Connection as Conn
import GBNet.Packet (Packet (..), PacketHeader (..))
import GBNet.Peer.Internal (NetPeer (..), PeerId)
-- | Migration cooldown in milliseconds.
migrationCooldownMs :: Double
migrationCooldownMs = 5000.0
-- | Find a connection that could match this packet (for migration).
-- Returns (oldPeerId, connection, migrationToken) if found.
findMigrationCandidate :: Packet -> MonoTime -> NetPeer -> Maybe (PeerId, Connection, Word64)
findMigrationCandidate pkt _now peer =
findCandidate (Map.toList (npConnections peer))
where
incomingSeq = sequenceNum (pktHeader pkt)
maxDistance = ncMaxSequenceDistance (npConfig peer)
findCandidate [] = Nothing
findCandidate ((pid, conn) : rest) =
let remoteSeq = Conn.connRemoteSeq conn
diff = abs (fromIntegral incomingSeq - fromIntegral remoteSeq :: Int)
in if diff <= fromIntegral maxDistance
then Just (pid, conn, migrationTokenFor conn)
else findCandidate rest
-- | Generate a migration token for a connection.
-- This should be stable across the connection lifetime.
migrationTokenFor :: Connection -> Word64
migrationTokenFor = Conn.connClientSalt