module Ecluse.Core.Server.Admission.Brake (
BrakeMarks (..),
defaultBrakeMarks,
BrakeState (..),
initialBrakeState,
GcSample (..),
brakeStep,
CollectorReading (..),
gcSharePermille,
SampleWindow,
newSampleWindow,
windowSample,
) where
import Data.Bits (shiftR)
import Ecluse.Core.Server.Admission.Types (BrakeBounds (..), BrakeLevel (..))
data BrakeMarks = BrakeMarks
{ BrakeMarks -> Int
bmGcHighPermille :: Int
, BrakeMarks -> Int
bmGcLowPermille :: Int
, BrakeMarks -> Int
bmKernelHighPermille :: Int
, BrakeMarks -> Int
bmOverflowGuardPermille :: Int
, BrakeMarks -> Int
bmCalmSamples :: Int
, BrakeMarks -> Int
bmGrowPermille :: Int
, BrakeMarks -> Int
bmReturnPermille :: Int
, BrakeMarks -> Int
bmForgetShift :: Int
, BrakeMarks -> Int
bmCooldownSamples :: Int
}
deriving stock (BrakeMarks -> BrakeMarks -> Bool
(BrakeMarks -> BrakeMarks -> Bool)
-> (BrakeMarks -> BrakeMarks -> Bool) -> Eq BrakeMarks
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: BrakeMarks -> BrakeMarks -> Bool
== :: BrakeMarks -> BrakeMarks -> Bool
$c/= :: BrakeMarks -> BrakeMarks -> Bool
/= :: BrakeMarks -> BrakeMarks -> Bool
Eq, Int -> BrakeMarks -> ShowS
[BrakeMarks] -> ShowS
BrakeMarks -> String
(Int -> BrakeMarks -> ShowS)
-> (BrakeMarks -> String)
-> ([BrakeMarks] -> ShowS)
-> Show BrakeMarks
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> BrakeMarks -> ShowS
showsPrec :: Int -> BrakeMarks -> ShowS
$cshow :: BrakeMarks -> String
show :: BrakeMarks -> String
$cshowList :: [BrakeMarks] -> ShowS
showList :: [BrakeMarks] -> ShowS
Show)
defaultBrakeMarks :: BrakeMarks
defaultBrakeMarks :: BrakeMarks
defaultBrakeMarks =
BrakeMarks
{ bmGcHighPermille :: Int
bmGcHighPermille = Int
500
, bmGcLowPermille :: Int
bmGcLowPermille = Int
350
, bmKernelHighPermille :: Int
bmKernelHighPermille = Int
900
, bmOverflowGuardPermille :: Int
bmOverflowGuardPermille = Int
800
, bmCalmSamples :: Int
bmCalmSamples = Int
10
, bmGrowPermille :: Int
bmGrowPermille = Int
125
, bmReturnPermille :: Int
bmReturnPermille = Int
250
, bmForgetShift :: Int
bmForgetShift = Int
8
, bmCooldownSamples :: Int
bmCooldownSamples = Int
10
}
data BrakeState = BrakeState
{ BrakeState -> Int
bsBudget :: Int
, BrakeState -> Maybe Int
bsOutside :: Maybe Int
, BrakeState -> Int
bsLargestRequest :: Int
, BrakeState -> Int
bsCalmSamples :: Int
, BrakeState -> Int
bsCooldown :: Int
, BrakeState -> BrakeLevel
bsLevel :: BrakeLevel
}
deriving stock (BrakeState -> BrakeState -> Bool
(BrakeState -> BrakeState -> Bool)
-> (BrakeState -> BrakeState -> Bool) -> Eq BrakeState
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: BrakeState -> BrakeState -> Bool
== :: BrakeState -> BrakeState -> Bool
$c/= :: BrakeState -> BrakeState -> Bool
/= :: BrakeState -> BrakeState -> Bool
Eq, Int -> BrakeState -> ShowS
[BrakeState] -> ShowS
BrakeState -> String
(Int -> BrakeState -> ShowS)
-> (BrakeState -> String)
-> ([BrakeState] -> ShowS)
-> Show BrakeState
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> BrakeState -> ShowS
showsPrec :: Int -> BrakeState -> ShowS
$cshow :: BrakeState -> String
show :: BrakeState -> String
$cshowList :: [BrakeState] -> ShowS
showList :: [BrakeState] -> ShowS
Show)
initialBrakeState :: BrakeBounds -> BrakeState
initialBrakeState :: BrakeBounds -> BrakeState
initialBrakeState BrakeBounds
bounds =
BrakeState{bsBudget :: Int
bsBudget = Int -> Int -> Int
forall a. Ord a => a -> a -> a
min (BrakeBounds -> Maybe Int -> Int -> Int
budgetCeiling BrakeBounds
bounds Maybe Int
forall a. Maybe a
Nothing Int
0) (BrakeBounds -> Int
bbBootBytes BrakeBounds
bounds), bsOutside :: Maybe Int
bsOutside = Maybe Int
forall a. Maybe a
Nothing, bsLargestRequest :: Int
bsLargestRequest = Int
0, bsCalmSamples :: Int
bsCalmSamples = Int
0, bsCooldown :: Int
bsCooldown = Int
0, bsLevel :: BrakeLevel
bsLevel = BrakeLevel
Calm}
data GcSample = GcSample
{ GcSample -> Maybe Int
gsGcSharePermille :: Maybe Int
, GcSample -> Maybe Int
gsLiveAfterMajor :: Maybe Int
, GcSample -> Int
gsChargedBytes :: Int
, GcSample -> Int
gsLargestCharge :: Int
, GcSample -> Maybe Int
gsKernelPermille :: Maybe Int
}
deriving stock (GcSample -> GcSample -> Bool
(GcSample -> GcSample -> Bool)
-> (GcSample -> GcSample -> Bool) -> Eq GcSample
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: GcSample -> GcSample -> Bool
== :: GcSample -> GcSample -> Bool
$c/= :: GcSample -> GcSample -> Bool
/= :: GcSample -> GcSample -> Bool
Eq, Int -> GcSample -> ShowS
[GcSample] -> ShowS
GcSample -> String
(Int -> GcSample -> ShowS)
-> (GcSample -> String) -> ([GcSample] -> ShowS) -> Show GcSample
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> GcSample -> ShowS
showsPrec :: Int -> GcSample -> ShowS
$cshow :: GcSample -> String
show :: GcSample -> String
$cshowList :: [GcSample] -> ShowS
showList :: [GcSample] -> ShowS
Show)
brakeStep :: BrakeMarks -> BrakeBounds -> BrakeState -> GcSample -> BrakeState
brakeStep :: BrakeMarks -> BrakeBounds -> BrakeState -> GcSample -> BrakeState
brakeStep BrakeMarks
marks BrakeBounds
bounds BrakeState
before GcSample
sample =
BrakeState
{ bsBudget :: Int
bsBudget = Int
grown
, bsOutside :: Maybe Int
bsOutside = Maybe Int
outside
, bsLargestRequest :: Int
bsLargestRequest = Int
largest
, bsCalmSamples :: Int
bsCalmSamples = if BrakeLevel
level BrakeLevel -> BrakeLevel -> Bool
forall a. Eq a => a -> a -> Bool
== BrakeLevel
Calm Bool -> Bool -> Bool
&& Bool -> Bool
not Bool
growNow then Int
calm else Int
0
, bsCooldown :: Int
bsCooldown = if Bool
halveNow then BrakeMarks -> Int
bmCooldownSamples BrakeMarks
marks Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1 else Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (BrakeState -> Int
bsCooldown BrakeState
before Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
, bsLevel :: BrakeLevel
bsLevel = BrakeLevel
level
}
where
outside :: Maybe Int
outside = Maybe Int -> (Int -> Maybe Int) -> Maybe Int -> Maybe Int
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (BrakeState -> Maybe Int
bsOutside BrakeState
before) (Int -> Maybe Int
forall a. a -> Maybe a
Just (Int -> Maybe Int) -> (Int -> Int) -> Int -> Maybe Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. BrakeMarks -> BrakeState -> Int -> Int -> Int
measuredOutside BrakeMarks
marks BrakeState
before (GcSample -> Int
gsChargedBytes GcSample
sample)) (GcSample -> Maybe Int
gsLiveAfterMajor GcSample
sample)
largest :: Int
largest = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max (GcSample -> Int
gsLargestCharge GcSample
sample) (BrakeState -> Int
bsLargestRequest BrakeState
before Int -> Int -> Int
forall a. Num a => a -> a -> a
- BrakeState -> Int
bsLargestRequest BrakeState
before Int -> Int -> Int
forall a. Bits a => a -> Int -> a
`shiftR` BrakeMarks -> Int
bmForgetShift BrakeMarks
marks)
ceilingNow :: Int
ceilingNow = BrakeBounds -> Maybe Int -> Int -> Int
budgetCeiling BrakeBounds
bounds Maybe Int
outside Int
largest
level :: BrakeLevel
level
| BrakeMarks -> BrakeBounds -> GcSample -> Bool
pressed BrakeMarks
marks BrakeBounds
bounds GcSample
sample = BrakeLevel
Braking
| (Int -> Bool) -> Maybe Int -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< BrakeMarks -> Int
bmGcLowPermille BrakeMarks
marks) (GcSample -> Maybe Int
gsGcSharePermille GcSample
sample) = BrakeLevel
Calm
| Bool
otherwise = BrakeLevel
Holding
calm :: Int
calm = BrakeState -> Int
bsCalmSamples BrakeState
before Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1
growNow :: Bool
growNow = BrakeLevel
level BrakeLevel -> BrakeLevel -> Bool
forall a. Eq a => a -> a -> Bool
== BrakeLevel
Calm Bool -> Bool -> Bool
&& Int
calm Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= BrakeMarks -> Int
bmCalmSamples BrakeMarks
marks
kept :: Int
kept = Int -> Int -> Int
forall a. Ord a => a -> a -> a
min Int
ceilingNow (BrakeState -> Int
bsBudget BrakeState
before)
halveNow :: Bool
halveNow = BrakeLevel
level BrakeLevel -> BrakeLevel -> Bool
forall a. Eq a => a -> a -> Bool
== BrakeLevel
Braking Bool -> Bool -> Bool
&& BrakeState -> Int
bsCooldown BrakeState
before Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0
held :: Int
held
| Bool
halveNow = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max (BrakeBounds -> Int
bbFloorBytes BrakeBounds
bounds) (Int
kept Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2)
| Bool
otherwise = Int
kept
grown :: Int
grown
| Bool
growNow = Int -> Int -> Int
forall a. Ord a => a -> a -> a
min Int
ceilingNow (Int
held Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int -> Int -> Int
forall a. Ord a => a -> a -> a
max (BrakeBounds -> Int
bbGrowFloorBytes BrakeBounds
bounds) (Int
held Int -> Int -> Int
forall a. Num a => a -> a -> a
* BrakeMarks -> Int
bmGrowPermille BrakeMarks
marks Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
1000))
| Bool
otherwise = Int
held
budgetCeiling :: BrakeBounds -> Maybe Int -> Int -> Int
budgetCeiling :: BrakeBounds -> Maybe Int -> Int -> Int
budgetCeiling BrakeBounds
bounds Maybe Int
outside Int
largest = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max (BrakeBounds -> Int
bbFloorBytes BrakeBounds
bounds) (Int -> Int) -> Int -> Int
forall a b. (a -> b) -> a -> b
$ case BrakeBounds -> Maybe Int
bbLiveCeilingBytes BrakeBounds
bounds of
Maybe Int
Nothing -> BrakeBounds -> Int
bbBootBytes BrakeBounds
bounds
Just Int
live -> Int -> (Int -> Int) -> Maybe Int -> Int
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Int
live (Int -> Int -> Int
forall a. Ord a => a -> a -> a
min Int
live (Int -> Int) -> (Int -> Int) -> Int -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Int -> Int
forall a. Num a => a -> a -> a
subtract Int
largest) (BrakeBounds -> Maybe Int
bbOverflowLiveBytes BrakeBounds
bounds) Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int -> Int -> Int
forall a. Ord a => a -> a -> a
max (BrakeBounds -> Int
bbFixedLiveBytes BrakeBounds
bounds) (Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe (BrakeBounds -> Int
bbExplainedBytes BrakeBounds
bounds) Maybe Int
outside)
measuredOutside :: BrakeMarks -> BrakeState -> Int -> Int -> Int
measuredOutside :: BrakeMarks -> BrakeState -> Int -> Int -> Int
measuredOutside BrakeMarks
marks BrakeState
before Int
charged Int
live = case BrakeState -> Maybe Int
bsOutside BrakeState
before of
Just Int
previous | Int
measured Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
previous -> Int
previous Int -> Int -> Int
forall a. Num a => a -> a -> a
- (Int
previous Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
measured) Int -> Int -> Int
forall a. Num a => a -> a -> a
* BrakeMarks -> Int
bmReturnPermille BrakeMarks
marks Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
1000
Maybe Int
_ -> Int
measured
where
measured :: Int
measured = Int
live Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
charged
pressed :: BrakeMarks -> BrakeBounds -> GcSample -> Bool
pressed :: BrakeMarks -> BrakeBounds -> GcSample -> Bool
pressed BrakeMarks
marks BrakeBounds
bounds GcSample
sample =
(Int -> Bool) -> Maybe Int -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> BrakeMarks -> Int
bmGcHighPermille BrakeMarks
marks) (GcSample -> Maybe Int
gsGcSharePermille GcSample
sample)
Bool -> Bool -> Bool
|| (Int -> Bool) -> Maybe Int -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> BrakeMarks -> Int
bmKernelHighPermille BrakeMarks
marks) (GcSample -> Maybe Int
gsKernelPermille GcSample
sample)
Bool -> Bool -> Bool
|| Bool
nearOverflow
where
nearOverflow :: Bool
nearOverflow = case (GcSample -> Maybe Int
gsLiveAfterMajor GcSample
sample, BrakeBounds -> Maybe Int
bbOverflowLiveBytes BrakeBounds
bounds) of
(Just Int
live, Just Int
overflow) -> Int
live Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
1000 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
overflow Int -> Int -> Int
forall a. Num a => a -> a -> a
* BrakeMarks -> Int
bmOverflowGuardPermille BrakeMarks
marks
(Maybe Int, Maybe Int)
_ -> Bool
False
data CollectorReading = CollectorReading
{ CollectorReading -> Int64
crCpuNs :: Int64
, CollectorReading -> Int64
crGcCpuNs :: Int64
, CollectorReading -> Word32
crMajorCollections :: Word32
, CollectorReading -> Int
crLiveBytes :: Int
}
deriving stock (CollectorReading -> CollectorReading -> Bool
(CollectorReading -> CollectorReading -> Bool)
-> (CollectorReading -> CollectorReading -> Bool)
-> Eq CollectorReading
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: CollectorReading -> CollectorReading -> Bool
== :: CollectorReading -> CollectorReading -> Bool
$c/= :: CollectorReading -> CollectorReading -> Bool
/= :: CollectorReading -> CollectorReading -> Bool
Eq, Int -> CollectorReading -> ShowS
[CollectorReading] -> ShowS
CollectorReading -> String
(Int -> CollectorReading -> ShowS)
-> (CollectorReading -> String)
-> ([CollectorReading] -> ShowS)
-> Show CollectorReading
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> CollectorReading -> ShowS
showsPrec :: Int -> CollectorReading -> ShowS
$cshow :: CollectorReading -> String
show :: CollectorReading -> String
$cshowList :: [CollectorReading] -> ShowS
showList :: [CollectorReading] -> ShowS
Show)
gcSharePermille :: CollectorReading -> CollectorReading -> Maybe Int
gcSharePermille :: CollectorReading -> CollectorReading -> Maybe Int
gcSharePermille CollectorReading
older CollectorReading
newer
| Int64
cpu Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
<= Int64
0 = Maybe Int
forall a. Maybe a
Nothing
| Bool
otherwise = Int -> Maybe Int
forall a. a -> Maybe a
Just (Int64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int64 -> Int64 -> Int64
forall a. Ord a => a -> a -> a
min Int64
1000 (Int64 -> Int64 -> Int64
forall a. Ord a => a -> a -> a
max Int64
0 (Int64
gc Int64 -> Int64 -> Int64
forall a. Num a => a -> a -> a
* Int64
1000 Int64 -> Int64 -> Int64
forall a. Integral a => a -> a -> a
`div` Int64
cpu))))
where
cpu :: Int64
cpu = CollectorReading -> Int64
crCpuNs CollectorReading
newer Int64 -> Int64 -> Int64
forall a. Num a => a -> a -> a
- CollectorReading -> Int64
crCpuNs CollectorReading
older
gc :: Int64
gc = CollectorReading -> Int64
crGcCpuNs CollectorReading
newer Int64 -> Int64 -> Int64
forall a. Num a => a -> a -> a
- CollectorReading -> Int64
crGcCpuNs CollectorReading
older
data SampleWindow = SampleWindow Int [CollectorReading]
newSampleWindow :: Int -> SampleWindow
newSampleWindow :: Int -> SampleWindow
newSampleWindow Int
periods = Int -> [CollectorReading] -> SampleWindow
SampleWindow (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
1 Int
periods) []
windowSample :: SampleWindow -> Maybe CollectorReading -> Int -> Int -> Maybe Int -> (GcSample, SampleWindow)
windowSample :: SampleWindow
-> Maybe CollectorReading
-> Int
-> Int
-> Maybe Int
-> (GcSample, SampleWindow)
windowSample (SampleWindow Int
periods [CollectorReading]
readings) Maybe CollectorReading
reading Int
charged Int
largest Maybe Int
kernel =
( GcSample
{ gsGcSharePermille :: Maybe Int
gsGcSharePermille = do
newest <- Maybe CollectorReading
reading
oldest <- listToMaybe (reverse spanned)
gcSharePermille oldest newest
, gsLiveAfterMajor :: Maybe Int
gsLiveAfterMajor = do
newest <- Maybe CollectorReading
reading
previous <- listToMaybe readings
guard (crMajorCollections previous /= crMajorCollections newest)
pure (crLiveBytes newest)
, gsChargedBytes :: Int
gsChargedBytes = Int
charged
, gsLargestCharge :: Int
gsLargestCharge = Int
largest
, gsKernelPermille :: Maybe Int
gsKernelPermille = Maybe Int
kernel
}
, Int -> [CollectorReading] -> SampleWindow
SampleWindow Int
periods [CollectorReading]
spanned
)
where
spanned :: [CollectorReading]
spanned = Int -> [CollectorReading] -> [CollectorReading]
forall a. Int -> [a] -> [a]
take (Int
periods Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (Maybe CollectorReading -> [CollectorReading]
forall a. Maybe a -> [a]
maybeToList Maybe CollectorReading
reading [CollectorReading] -> [CollectorReading] -> [CollectorReading]
forall a. Semigroup a => a -> a -> a
<> [CollectorReading]
readings)