kiroku-store-0.13.0.0: bench/CompactionCost.hs
{-# LANGUAGE OverloadedRecordDot #-}
-- A bounded maintenance-path sizing probe, not a statistical regression gate.
module Main (main) where
import Control.Lens ((^.))
import Control.Monad (forM_, unless)
import Data.Aeson (object, (.=))
import Data.Generics.Labels ()
import Data.Text qualified as T
import Data.Vector qualified as V
import GHC.Clock (getMonotonicTimeNSec)
import Hasql.Pool qualified as Pool
import Hasql.Session qualified as Session
import Kiroku.Store hiding (id)
import Kiroku.Test.Postgres (withMigratedTestDatabase, withSharedMigratedPostgres)
import Text.Printf (printf)
main :: IO ()
main = withSharedMigratedPostgres $ do
putStrLn "selections,link_memberships,dead_letters,apply_wall_seconds,coordinator_hold_upper_bound_seconds,microseconds_per_selection,report_digest"
forM_ [10000, 50000, 100000 :: Int] $ \size ->
withMigratedTestDatabase $ \conn -> withStore (defaultConnectionSettings conn) $ \store -> do
let origin = StreamName "cost-origin"
target = StreamName "cost-links"
forM_ [0, 2500 .. size - 1] $ \offset -> do
let events =
[ EventData
Nothing
(EventType "CompactionCost")
(object ["ordinal" .= n, "payload" .= T.replicate 256 "x"])
Nothing
Nothing
Nothing
| n <- [offset + 1 .. min size (offset + 2500)]
]
_ <- require =<< runStoreIO store (appendToStream origin AnyVersion events)
pure ()
events <- require =<< runStoreIO store (readAllForward (GlobalPosition 0) (fromIntegral size))
let linked = V.filter (\e -> let GlobalPosition p = e.globalPosition in p `mod` 10 == 0) events
_ <- require =<< runStoreIO store (linkToStream target (map (\e -> e.eventId) (V.toList linked)))
require
=<< Pool.use
(store ^. #pool)
( Session.script
"INSERT INTO dead_letters(subscription_name,consumer_group_member,global_position,event_id,reason,reason_summary,attempt_count) SELECT 'cost',0,stream_version,event_id,'{}'::jsonb,'cost fixture',1 FROM stream_events WHERE stream_id=0 AND stream_version % 10=0"
)
identity <- require =<< runStoreIO store storeIdentity
op <- either (fail . show) pure (mkCompactionOperation ("cost-" <> T.pack (show size)))
let selections =
[ CompactionSelection
e.eventId
origin
e.originalVersion
e.globalPosition
(let GlobalPosition pos = e.globalPosition in if pos `mod` 10 == 0 then V.singleton (LinkWitness target (StreamVersion (pos `div` 10))) else V.empty)
| e <- V.toList events
]
heads = [StreamHeadWitness origin (StreamVersion (fromIntegral size)), StreamHeadWitness target (StreamVersion (fromIntegral (size `div` 10)))]
manifest <- either (fail . show) pure (mkCompactionManifest (CompactionManifestInput identity op RemoveDeadLetters RefuseCausationDependents heads selections Nothing))
start <- getMonotonicTimeNSec
result <- require =<< runStoreIO store (applyCompaction manifest)
end <- getMonotonicTimeNSec
record <- case result of
Right (CompactionAppliedNow value) -> pure value
other -> fail (show other)
let seconds = fromIntegral (end - start) / 1e9 :: Double
report = record.report
unless (report.selectedEvents == fromIntegral size && report.linkMemberships == fromIntegral (size `div` 10) && report.deadLettersRemoved == fromIntegral (size `div` 10)) (fail "count mismatch")
ledger <- require =<< runStoreIO store (compactionLedger (CompactionLedgerQuery (either (error . show) id (mkCompactionLedgerLimit 10))))
unless (ledger == V.singleton record && report.reportDigest == compactionReportDigest report) (fail "ledger mismatch")
repeated <- require =<< runStoreIO store (applyCompaction manifest)
unless (repeated == Right (CompactionAlreadyApplied record)) (fail "reapply mismatch")
printf "%d,%d,%d,%.6f,%.6f,%.3f,%s\n" size (size `div` 10) (size `div` 10) seconds seconds (seconds * 1e6 / fromIntegral size) (T.unpack (compactionDigestHex report.reportDigest))
require :: (Show e) => Either e a -> IO a
require = either (fail . show) pure