Skip to content

Commit ff68e84

Browse files
WFQ mux fairness test
The test has been enhanced to cover biased queuing
1 parent e441afc commit ff68e84

1 file changed

Lines changed: 51 additions & 20 deletions

File tree

network-mux/test/Test/Mux.hs

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
{-# LANGUAGE DataKinds #-}
44
{-# LANGUAGE FlexibleContexts #-}
55
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6+
{-# LANGUAGE LambdaCase #-}
67
{-# LANGUAGE NamedFieldPuns #-}
78
{-# LANGUAGE PackageImports #-}
89
{-# LANGUAGE RankNTypes #-}
@@ -29,7 +30,7 @@ import Data.Bits
2930
import Data.ByteString.Lazy qualified as BL
3031
import Data.ByteString.Lazy.Char8 qualified as BL8 (pack)
3132
import Data.Functor.Contravariant ((>$<))
32-
import Data.List (dropWhileEnd, nub)
33+
import Data.List (dropWhileEnd, group, nub)
3334
import Data.List qualified as List
3435
import Data.Map qualified as M
3536
import Data.Maybe (isNothing)
@@ -314,6 +315,33 @@ instance Arbitrary DummyCapability where
314315
]
315316

316317

318+
newtype MiniProtocolWeights = MiniProtocolWeights (MiniProtocolWeight, MiniProtocolWeight)
319+
deriving (Eq, Show)
320+
321+
instance Arbitrary MiniProtocolWeights where
322+
arbitrary = do
323+
wt1 <- arbitrary
324+
wt2 <- arbitrary
325+
let mkWt = MiniProtocolWeight
326+
if wt1 == wt2
327+
then pure $ MiniProtocolWeights (mkWt 1, mkWt 1)
328+
else pure $ MiniProtocolWeights (wt1, wt2)
329+
330+
shrink (MiniProtocolWeights (wt1, wt2)) =
331+
let wt1' = shrink wt1
332+
wt2' = shrink wt2
333+
mkWt = MiniProtocolWeight
334+
in MiniProtocolWeights (mkWt 1, mkWt 1) : nub (zipWith (curry MiniProtocolWeights) wt1' wt2')
335+
336+
337+
newtype MiniProtocolWeight = MiniProtocolWeight Word8
338+
deriving (Eq, Show)
339+
340+
instance Arbitrary MiniProtocolWeight where
341+
arbitrary = MiniProtocolWeight <$> choose (1, 4)
342+
shrink (MiniProtocolWeight wt) = MiniProtocolWeight <$> filter (> 0) (shrink wt)
343+
344+
317345
-- | A pair of two bytestrings which lengths are unevenly distributed
318346
--
319347
data Uneven = Uneven DummyPayload DummyPayload
@@ -1008,8 +1036,10 @@ prop_mux_2_minis_Socket_buf cap a b = ioProperty $
10081036
-- The Mux bearer should alternate between sending data for the two responders.
10091037
--
10101038
prop_mux_starvation :: Uneven
1039+
-> MiniProtocolWeights
10111040
-> Property
1012-
prop_mux_starvation (Uneven response0 response1) =
1041+
prop_mux_starvation (Uneven response0 response1)
1042+
(MiniProtocolWeights (MiniProtocolWeight wt1, MiniProtocolWeight wt2)) =
10131043
let sduLen = Mx.SDUSize 1280 in
10141044
(BL.length (unDummyPayload response0) > 2 * fromIntegral (Mx.getSDUSize sduLen)) &&
10151045
(BL.length (unDummyPayload response1) > 2 * fromIntegral (Mx.getSDUSize sduLen)) ==>
@@ -1072,14 +1102,14 @@ prop_mux_starvation (Uneven response0 response1) =
10721102
miniProtocolDir = Mx.ResponderDirectionOnly,
10731103
miniProtocolLimits = defaultMiniProtocolLimits,
10741104
miniProtocolCapability = Nothing,
1075-
miniProtocolWeight = 1
1105+
miniProtocolWeight = wt1
10761106
}
10771107
serverApp3 = MiniProtocolInfo {
10781108
miniProtocolNum = Mx.MiniProtocolNum 3,
10791109
miniProtocolDir = Mx.ResponderDirectionOnly,
10801110
miniProtocolLimits = defaultMiniProtocolLimits,
10811111
miniProtocolCapability = Nothing,
1082-
miniProtocolWeight = 1
1112+
miniProtocolWeight = wt2
10831113
}
10841114

10851115
serverMux <- Mx.new serverTracer [serverApp2, serverApp3]
@@ -1119,28 +1149,29 @@ prop_mux_starvation (Uneven response0 response1) =
11191149

11201150
-- Then look at the message trace to check for starvation.
11211151
trace <- atomically $ readTVar traceHeaderVar
1122-
let es = map Mx.mhNum (take 100 (reverse trace))
1123-
ls = dropWhile (\e -> e == head es) es
1124-
fair = verifyStarvation ls
1152+
let es = map Mx.mhNum (take 100 (reverse trace))
1153+
-- We can't make 100% sure that both servers start responding at the same
1154+
-- time but once they are both up and running messages should alternate
1155+
-- between ReqResp2 and ReqResp3, so we drop the prefix of the protocol
1156+
-- which goes first, and trim the suffix when the first protocol finishes
1157+
ls = dropWhile (\e -> e == head es) es
1158+
ls' = dropWhileEnd (\e -> e == last ls) ls
1159+
fair = label ("shrinkage " ++ labelPr_ ((length ls' * 100) `div` length es) ++ "%") $
1160+
verifyStarvation ls' (\case (Mx.MiniProtocolNum 2) -> wt1; _otherwise -> wt2)
11251161
return $ res_short .&&. res_long .&&. fair
11261162
where
1127-
-- We can't make 100% sure that both servers start responding at the same
1128-
-- time but once they are both up and running messages should alternate
1129-
-- between ReqResp2 and ReqResp3
1130-
verifyStarvation :: Eq a => [a] -> Property
1131-
verifyStarvation [] = property True
1132-
verifyStarvation ms =
1133-
let ms' = dropWhileEnd (\e -> e == last ms)
1134-
(head ms : dropWhile (\e -> e == head ms) ms)
1135-
++ [last ms]
1136-
in
1137-
label ("length " ++ labelPr_ ((length ms' * 100) `div` length ms) ++ "%")
1138-
$ label ("length " ++ label_ (length ms')) $ alternates ms'
1163+
verifyStarvation :: Eq a => [a] -> (a -> Word8) -> Property
1164+
verifyStarvation [] _atowt = property True
1165+
verifyStarvation ms atowt = label ("length " ++ label_ (length ms)) .
1166+
label ("groups " ++ label_ (length (group ms))) .
1167+
alternates $ group ms
11391168

11401169
where
11411170
alternates [] = True
11421171
alternates (_:[]) = True
1143-
alternates (a : b : as) = a /= b && alternates (b : as)
1172+
alternates (a : b : []) = length a <= fromIntegral (atowt (head a))
1173+
&& length b <= fromIntegral (atowt (head b))
1174+
alternates (a : b : as) = (length a == fromIntegral (atowt (head a))) && alternates (b : as)
11441175

11451176
label_ :: Int -> String
11461177
label_ n = mconcat

0 commit comments

Comments
 (0)