packages feed

valiant-0.1.0.0: bench/BenchPool.hs

{-# LANGUAGE BangPatterns #-}

-- | Pool-specific benchmarks: size scaling, contention, recycling methods.
module BenchPool (benchmarks) where

import Control.Concurrent.Async (mapConcurrently)
import Criterion.Main
import Valiant
import TestSupport (requireDatabaseUrl)

benchmarks :: [Benchmark]
benchmarks =
  [ bgroup "pool"
    [ bgroup "acquire-release"
      -- Measure pure pool overhead: acquire a connection, do a trivial
      -- query, release. Varies pool size to show scaling.
      [ bench "pool-size-1" $ whnfIO (poolAcquireRelease 1)
      , bench "pool-size-4" $ whnfIO (poolAcquireRelease 4)
      , bench "pool-size-16" $ whnfIO (poolAcquireRelease 16)
      ]
    , bgroup "contention"
      -- N threads compete for a pool of size M. Shows wait time under load.
      [ bench "8 threads, pool-4" $ whnfIO (poolContention 8 4 10)
      , bench "16 threads, pool-4" $ whnfIO (poolContention 16 4 10)
      , bench "32 threads, pool-4" $ whnfIO (poolContention 32 4 10)
      , bench "32 threads, pool-8" $ whnfIO (poolContention 32 8 10)
      , bench "32 threads, pool-16" $ whnfIO (poolContention 32 16 10)
      , bench "32 threads, pool-32" $ whnfIO (poolContention 32 32 10)
      ]
    , bgroup "recycling"
      -- Compare recycling methods: Fast (no check), Verified (empty query
      -- if idle > threshold), Clean (DISCARD ALL).
      [ bench "RecycleFast" $ whnfIO (poolRecycling RecycleFast)
      , bench "RecycleVerified" $ whnfIO (poolRecycling RecycleVerified)
      , bench "RecycleClean" $ whnfIO (poolRecycling RecycleClean)
      ]
    ]
  ]

-- | Acquire a connection from a fresh pool, run SELECT 1, release.
poolAcquireRelease :: Int -> IO ()
poolAcquireRelease size = do
  url <- requireDatabaseUrl
  pool <- newPool defaultPoolConfig
    { poolConnString = url
    , poolSize = size
    , poolAcquireTimeout = 5
    }
  _ <- withResource pool $ \conn ->
    simpleQuery conn "SELECT 1"
  closePool pool

-- | N threads each acquire from pool (size M) and run K queries.
poolContention :: Int -> Int -> Int -> IO ()
poolContention nThreads poolSize nQueries = do
  url <- requireDatabaseUrl
  pool <- newPool defaultPoolConfig
    { poolConnString = url
    , poolSize = poolSize
    , poolAcquireTimeout = 10
    }
  _ <- mapConcurrently (\_ ->
    withResource pool $ \conn ->
      mapM_ (\_ -> do _ <- simpleQuery conn "SELECT 1"; pure ()) [1 :: Int .. nQueries]
    ) [1 .. nThreads]
  closePool pool

-- | Benchmark different recycling methods.
poolRecycling :: RecyclingMethod -> IO ()
poolRecycling method = do
  url <- requireDatabaseUrl
  pool <- newPool defaultPoolConfig
    { poolConnString = url
    , poolSize = 4
    , poolAcquireTimeout = 5
    , poolRecyclingMethod = method
    , poolHealthCheckAge = 0 -- force check every time for RecycleVerified
    }
  -- Acquire and release 10 times to exercise recycling
  mapM_ (\_ -> do _ <- withResource pool $ \conn -> simpleQuery conn "SELECT 1"; pure ()) [1 :: Int .. 10]
  closePool pool