a-piece-of-flake-0.0.1: src/PieceOfFlake/CmdRun.hs
module PieceOfFlake.CmdRun where
import Data.Version (showVersion)
import Data.ByteString.Char8 qualified as S8
import Paths_a_piece_of_flake ( version )
import PieceOfFlake.Acid ( loadFromDb, openFlakeDb, runPersistQueue )
import PieceOfFlake.CmdArgs
( WsCmdArgs(staticCache, indexQueryCacheSize, acidFlakes,
ringBufferSize, logLevel, fetcherSecretPath, noSubmitionHeartbeat, fetcherHeartbeatPeriod),
CmdArgs(..),
FetcherCmdArgs(fetcherSecretPath, FetcherCmdArgs, logLevel),
SubmitListOfFlakesArgs(logLevel) )
import PieceOfFlake.Fetcher ( runFetcher )
import PieceOfFlake.Flake.Repo
( FlakeRepo(flakeIndex, wsArgs, acidFlakes, acidQueue, repoStats,
flakes),
mkFlakeRepo,
removeOldBadFlakes,
sendEmptyFlakeSubmition,
resubmitFlakesFetchingByZombie )
import PieceOfFlake.Http ( runWebService )
import PieceOfFlake.Index
( mkFlakeIndex, loadIndexFromScratch, consumeIndexQueue )
import PieceOfFlake.Page ( Ypp(Ypp) )
import PieceOfFlake.Prelude
import PieceOfFlake.Stats ( mkRepoStats )
import PieceOfFlake.SubmitList ( runSubmitList )
import PieceOfFlake.WebService
( FetcherSecret(..), NoSubmitionHeartbeatSec )
import StmContainers.Map ( newIO )
import UnliftIO.Concurrent ( forkFinally )
import UnliftIO.Retry ( fibonacciBackoff, recoverAll )
initRepo :: PoF m => FetcherSecret -> WsCmdArgs -> m FlakeRepo
initRepo fsec wsa = do
flakesMap <- liftIO newIO
fi <- mkFlakeIndex wsa.indexQueryCacheSize
acidFlakeStorage <- openFlakeDb wsa.acidFlakes
rs <- mkRepoStats wsa.ringBufferSize
loadIndexFromScratch rs fi flakesMap . reverse =<< loadFromDb acidFlakeStorage
mkFlakeRepo fsec wsa fi flakesMap acidFlakeStorage rs
launchBackgroundThreads :: PoF m =>
Tagged NoSubmitionHeartbeatSec Second -> FlakeRepo -> m ()
launchBackgroundThreads period fr = do
forkForever "Lost flake resubmition" $ do
resubmitFlakesFetchingByZombie fr
threadDelay . (* 3) $ untag fr.wsArgs.fetcherHeartbeatPeriod
forkForever "Flake persistence" $ do
recoverAll
(fibonacciBackoff 10000)
(\_ -> runPersistQueue fr.acidFlakes fr.acidQueue)
forkForever "Flake text search indexer"
$ consumeIndexQueue fr.repoStats fr.flakes fr.flakeIndex
forkForever "Empty Submition" $ do
threadDelay $ untag period
sendEmptyFlakeSubmition fr
forkForever "Old Bad Flake Collector" $ removeOldBadFlakes fr
where
forkForever thrName f = do
tid <- forkFinally
(forever f)
(\case
Left e -> $(logError) $ thrName <> " thread ended: " <> show e
Right () -> $(logInfo) $ thrName <> " thread ended without errors")
$(logInfo) $ thrName <> " thread is forked with tid: " <> show tid
-- commented lines below are excuted via: @ghciwatch --enable-eval@
-- $> import PieceOfFlake.CmdArgs
-- $> execWithArgs runCmd . (:[]) =<< (fromMaybe "web" <$> PieceOfFlake.Prelude.lookupEnv "E")
runCmd :: CmdArgs -> IO ()
runCmd = \case
WebService ws ->
withLogs ws.logLevel $ do
$(logInfo) $ "Start WebService " <> show ws
fr <- (`initRepo` ws) =<< loadFetcherSecret ws.fetcherSecretPath
launchBackgroundThreads ws.noSubmitionHeartbeat fr
runWebService ws $ Ypp fr ws.staticCache
FetcherJob fa@FetcherCmdArgs {} ->
withLogs fa.logLevel $ do
$(logInfo) $ "Start Fetcher " <> show fa
runFetcher fa =<< loadFetcherSecret fa.fetcherSecretPath
SubmitListOfFlakes sla ->
withLogs sla.logLevel $ runSubmitList sla
PieceOfFlakeVersion ->
putStrLn $ "Version " <> showVersion version
withLogs :: MonadIO m => LogLevel -> LoggingT m a -> m a
withLogs minLogL m =
runFlushingStdoutLoggingT $ filterLogger (\_ l -> l >= minLogL) m
where
runFlushingStdoutLoggingT = (`runLoggingT` flushingOutput stdout)
where
-- without explicit flushing - syslog does to get logs
flushingOutput h loc src level msg = S8.hPutStr h ls >> hFlush h
where
ls = defaultLogStrBS loc src level msg
defaultLogStrBS a b c d = fromLogStr $ defaultLogStr a b c d
loadFetcherSecret :: MonadIO m => Tagged FetcherSecret FilePath -> m FetcherSecret
loadFetcherSecret a = readFileTxt (untag a) <&> FetcherSecret