Settings for creating a reaper. This type has two parameters:
workload gives the entire workload, whereas item gives an
individual piece of the queue. A common approach is to have workload
be a list of items. This is encouraged by defaultReaperSettings and
mkListAction.
Moduleauto-update-0.2.6Haskell2010
Control.Reaper
This module provides the ability to create reapers: dedicated cleanup threads. These threads will automatically spawn and die based on the presence of a workload to process on. Example uses include:
Killing long-running jobs
Closing unused connections in a connection pool
Pruning a cache of old items (see example below)
For real-world usage, search the WAI family of packages for imports of Control.Reaper.
- 2 types
- 14 values
- Packageauto-update-0.2.6
- Exports16
- LanguageHaskell2010
- LicenceMIT
- SourceReaper.hs
Example: Regularly cleaning a cache
0 declarationsIn this example code, we use a Map to cache fibonacci numbers, and a Reaper to prune the cache.
NOTE: When using this module as a cache you should keep in mind that while
the reaper thread is active running your "reaperAction", the cache will
appear empty to concurrently running threads. Any newly created cache
entries will be on the temporary worklist, and will merged back into the the
main cache only once the "reaperAction" completes (together with the portion
of the extant worklist that the cleaner callback decided to retain).
If you're looking for a cache that supports concurrent purging of stale items, but without exposing a transient empty cache during cleanup, this is not the cache implementation you need. This module was primarily designed for cleaning up stuck processes, or idle threads in a thread pool. The cache use-case was not a primary design focus.
The main function first creates a Reaper, with fields to initialize the
cache (reaperEmpty), add items to it (reaperCons), and prune it (reaperAction).
The reaper will run every two seconds (reaperDelay), but will stop running while
reaperNull is true.
main then loops infinitely (forever). Each second it calculates the fibonacci number
for a value between 30 and 34, first trying the cache (reaperRead and lookup),
then falling back to manually calculating it (fib)
and updating the cache with the result (reaperAdd)
clean simply removes items cached for more than 10 seconds.
This function is where you would perform IO-related cleanup,
like killing threads or closing connections, if that was the purpose of your reaper.
module Main where
import Data.Time (UTCTime, getCurrentTime, diffUTCTime)
import Control.Reaper
import Control.Concurrent (threadDelay)
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Control.Monad (forever)
import System.Random (getStdRandom, randomR)
fib :: Int -> Int
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
type Cache = Map Int (Int, UTCTime)
main :: IO ()
main = do
reaper <- mkReaper defaultReaperSettings
{ reaperEmpty = Map.empty
, reaperCons = \(k, v, time) workload -> Map.insert k (v, time) workload
, reaperAction = clean
, reaperDelay = 1000000 * 2 -- Clean every 2 seconds
, reaperNull = Map.null
}
forever $ do
fibArg <- getStdRandom (randomR (30,34))
cache <- reaperRead reaper
let cachedResult = Map.lookup fibArg cache
case cachedResult of
Just (fibResult, _createdAt) -> putStrLn $ "Found in cache: `fib " ++ show fibArg ++ "` " ++ show fibResult
Nothing -> do
let fibResult = fib fibArg
putStrLn $ "Calculating `fib " ++ show fibArg ++ "` " ++ show fibResult
time <- Data.Time.Clock.getCurrentTime
(reaperAdd reaper) (fibArg, fibResult, time)
threadDelay 1000000 -- 1 second
-- Remove items > 10 seconds old
clean :: Cache -> IO (Cache -> Cache)
clean oldMap = do
currentTime <- Data.Time.Clock.getCurrentTime
let pruned = Map.filter (\(_, createdAt) -> currentTime `diffUTCTime` createdAt < 10.0) oldMap
return (\newData -> Map.union pruned newData)
Settings
2 declarationsDefault ReaperSettings value, biased towards having a list of work
items.
Accessors
6 declarationsThe action to perform on a workload. The result of this is a "workload modifying" function. In the common case of using lists, the result should be a difference list that prepends the remaining workload to the temporary workload. The temporary workload here refers to items added to the workload while the reaper action is running. For help with setting up such an action, see mkListAction.
Default: do nothing with the workload, and then prepend it to the temporary workload. This is incredibly useless; you should definitely override this default.
Number of microseconds to delay between calls of reaperAction.
Default: 30 seconds.
Add an item onto a workload.
Default: list consing.
Check if a workload is empty, in which case the worker thread will shut down.
Default: null.
An empty workload.
Default: empty list.
Label of the thread spawned by the reaper.
Default: .Reaper
Type
6 declarationsA data structure to hold reaper APIs.
Adding an item to the workload
Reading workload.
Modify the workload. The resulting workload is returned.
If there is no reaper thread, the modifier will not be applied and
reaperEmpty will be returned.
If the reaper is currently executing jobs, those jobs will not be in the given workload and the workload might appear empty.
If all jobs are removed by the modifier, the reaper thread will not be
killed. The reaper thread will only terminate if reaperKill is called
or the result of reaperAction satisfies reaperNull.
Stopping the reaper thread if exists. The current workload is returned.
Killing the reaper thread immediately if exists.
Creation
1 declarationCreate a reaper addition function. This function can be used to add new items to the workload. Spawning of reaper threads will be handled for you automatically.
Helper
1 declarationA helper function for creating reaperAction functions. You would
provide this function with a function to process a single work item and
return either a new work item, or Nothing if the work item is
expired.