module Ecluse.Composition.MirrorQueue (
MirrorRuntimePlan (..),
planMirrorRuntime,
MirrorQueuePlan (..),
planMirrorQueue,
mirrorQueuePlanWarning,
memoryQueueBootWarning,
memoryQueueDropWarning,
) where
import Data.Text qualified as T
import Ecluse.Composition.BootError (BootError (..))
import Ecluse.Config (
AppConfig (..),
Config (..),
Mount (mountRegistries),
QueueSettings (qsUrl),
regMirrorTarget,
unUrl,
)
import Ecluse.Config.Ambient (AmbientAws (..), parseEndpointUrl)
import Ecluse.Config.QueueTarget (QueueTarget (..), parseQueueTarget)
import Ecluse.Core.Text (nonBlank)
import Ecluse.Runtime.Queue.Sqs (SqsConfig (sqsEndpoint), SqsEndpoint (..), defaultSqsConfig)
data MirrorRuntimePlan
=
NoMirroring
|
MirrorWith MirrorQueuePlan
deriving stock (MirrorRuntimePlan -> MirrorRuntimePlan -> Bool
(MirrorRuntimePlan -> MirrorRuntimePlan -> Bool)
-> (MirrorRuntimePlan -> MirrorRuntimePlan -> Bool)
-> Eq MirrorRuntimePlan
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: MirrorRuntimePlan -> MirrorRuntimePlan -> Bool
== :: MirrorRuntimePlan -> MirrorRuntimePlan -> Bool
$c/= :: MirrorRuntimePlan -> MirrorRuntimePlan -> Bool
/= :: MirrorRuntimePlan -> MirrorRuntimePlan -> Bool
Eq, Int -> MirrorRuntimePlan -> ShowS
[MirrorRuntimePlan] -> ShowS
MirrorRuntimePlan -> String
(Int -> MirrorRuntimePlan -> ShowS)
-> (MirrorRuntimePlan -> String)
-> ([MirrorRuntimePlan] -> ShowS)
-> Show MirrorRuntimePlan
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> MirrorRuntimePlan -> ShowS
showsPrec :: Int -> MirrorRuntimePlan -> ShowS
$cshow :: MirrorRuntimePlan -> String
show :: MirrorRuntimePlan -> String
$cshowList :: [MirrorRuntimePlan] -> ShowS
showList :: [MirrorRuntimePlan] -> ShowS
Show)
planMirrorRuntime :: AmbientAws -> Config -> Either [BootError] MirrorRuntimePlan
planMirrorRuntime :: AmbientAws -> Config -> Either [BootError] MirrorRuntimePlan
planMirrorRuntime AmbientAws
ambient Config
config
| Bool
noneMirror = MirrorRuntimePlan -> Either [BootError] MirrorRuntimePlan
forall a b. b -> Either a b
Right MirrorRuntimePlan
NoMirroring
| Bool
otherwise = MirrorQueuePlan -> MirrorRuntimePlan
MirrorWith (MirrorQueuePlan -> MirrorRuntimePlan)
-> Either [BootError] MirrorQueuePlan
-> Either [BootError] MirrorRuntimePlan
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> AmbientAws -> AppConfig -> Either [BootError] MirrorQueuePlan
planMirrorQueue AmbientAws
ambient (Config -> AppConfig
configApp Config
config)
where
noneMirror :: Bool
noneMirror = (Mount -> Bool) -> Map Ecosystem Mount -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (Maybe MirrorTarget -> Bool
forall a. Maybe a -> Bool
isNothing (Maybe MirrorTarget -> Bool)
-> (Mount -> Maybe MirrorTarget) -> Mount -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MountRegistries -> Maybe MirrorTarget
regMirrorTarget (MountRegistries -> Maybe MirrorTarget)
-> (Mount -> MountRegistries) -> Mount -> Maybe MirrorTarget
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Mount -> MountRegistries
mountRegistries) (Config -> Map Ecosystem Mount
configMounts Config
config)
data MirrorQueuePlan
=
SqsBackend SqsConfig
|
MemoryBackend
deriving stock (MirrorQueuePlan -> MirrorQueuePlan -> Bool
(MirrorQueuePlan -> MirrorQueuePlan -> Bool)
-> (MirrorQueuePlan -> MirrorQueuePlan -> Bool)
-> Eq MirrorQueuePlan
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: MirrorQueuePlan -> MirrorQueuePlan -> Bool
== :: MirrorQueuePlan -> MirrorQueuePlan -> Bool
$c/= :: MirrorQueuePlan -> MirrorQueuePlan -> Bool
/= :: MirrorQueuePlan -> MirrorQueuePlan -> Bool
Eq, Int -> MirrorQueuePlan -> ShowS
[MirrorQueuePlan] -> ShowS
MirrorQueuePlan -> String
(Int -> MirrorQueuePlan -> ShowS)
-> (MirrorQueuePlan -> String)
-> ([MirrorQueuePlan] -> ShowS)
-> Show MirrorQueuePlan
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> MirrorQueuePlan -> ShowS
showsPrec :: Int -> MirrorQueuePlan -> ShowS
$cshow :: MirrorQueuePlan -> String
show :: MirrorQueuePlan -> String
$cshowList :: [MirrorQueuePlan] -> ShowS
showList :: [MirrorQueuePlan] -> ShowS
Show)
planMirrorQueue :: AmbientAws -> AppConfig -> Either [BootError] MirrorQueuePlan
planMirrorQueue :: AmbientAws -> AppConfig -> Either [BootError] MirrorQueuePlan
planMirrorQueue AmbientAws
ambient AppConfig
env = case QueueSettings -> Maybe Url
qsUrl (AppConfig -> QueueSettings
cfgQueue AppConfig
env) of
Maybe Url
Nothing -> MirrorQueuePlan -> Either [BootError] MirrorQueuePlan
forall a b. b -> Either a b
Right MirrorQueuePlan
MemoryBackend
Just Url
queueUrl ->
let url :: Text
url = Url -> Text
unUrl Url
queueUrl
in case Text -> Maybe Text
nonBlank (Text -> Maybe Text) -> Maybe Text -> Maybe Text
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< AmbientAws -> Maybe Text
ambientAwsEndpointUrlSqs AmbientAws
ambient of
Just Text
override -> case (Either BootError Text
regionE, Text -> Either BootError SqsEndpoint
endpointE Text
override) of
(Right Text
region, Right SqsEndpoint
endpoint) ->
MirrorQueuePlan -> Either [BootError] MirrorQueuePlan
forall a b. b -> Either a b
Right (SqsConfig -> MirrorQueuePlan
SqsBackend (Text -> Text -> SqsConfig
defaultSqsConfig Text
url Text
region){sqsEndpoint = Just endpoint})
(Either BootError Text
r, Either BootError SqsEndpoint
e) -> [BootError] -> Either [BootError] MirrorQueuePlan
forall a b. a -> Either a b
Left ([Either BootError ()] -> [BootError]
forall a b. [Either a b] -> [a]
lefts [Either BootError Text -> Either BootError ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void Either BootError Text
r, Either BootError SqsEndpoint -> Either BootError ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void Either BootError SqsEndpoint
e])
Maybe Text
Nothing -> case Text -> Maybe QueueTarget
parseQueueTarget Text
url of
Just (SqsTarget Text
region) -> MirrorQueuePlan -> Either [BootError] MirrorQueuePlan
forall a b. b -> Either a b
Right (SqsConfig -> MirrorQueuePlan
SqsBackend (Text -> Text -> SqsConfig
defaultSqsConfig Text
url Text
region))
Just (PubSubTarget Text
_project Text
_topic) -> [BootError] -> Either [BootError] MirrorQueuePlan
forall a b. a -> Either a b
Left [Text -> BootError
QueueProviderUnavailable Text
"pubsub"]
Maybe QueueTarget
Nothing -> [BootError] -> Either [BootError] MirrorQueuePlan
forall a b. a -> Either a b
Left [Text -> BootError
QueueUrlUnrecognised Text
url]
where
regionE :: Either BootError Text
regionE :: Either BootError Text
regionE = case Text -> Text
T.strip (Text -> Text) -> Maybe Text -> Maybe Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> AmbientAws -> Maybe Text
ambientAwsRegion AmbientAws
ambient of
Just Text
region | Bool -> Bool
not (Text -> Bool
T.null Text
region) -> Text -> Either BootError Text
forall a b. b -> Either a b
Right Text
region
Maybe Text
_ -> BootError -> Either BootError Text
forall a b. a -> Either a b
Left BootError
QueueRegionMissing
endpointE :: Text -> Either BootError SqsEndpoint
endpointE :: Text -> Either BootError SqsEndpoint
endpointE Text
override = case Text -> Maybe (Bool, Text, Int)
parseEndpointUrl Text
override of
Maybe (Bool, Text, Int)
Nothing -> BootError -> Either BootError SqsEndpoint
forall a b. a -> Either a b
Left (Text -> BootError
QueueEndpointMalformed Text
override)
Just (Bool
secure, Text
host, Int
port) ->
SqsEndpoint -> Either BootError SqsEndpoint
forall a b. b -> Either a b
Right SqsEndpoint{endpointSecure :: Bool
endpointSecure = Bool
secure, endpointHost :: Text
endpointHost = Text
host, endpointPort :: Int
endpointPort = Int
port}
mirrorQueuePlanWarning :: MirrorQueuePlan -> Maybe Text
mirrorQueuePlanWarning :: MirrorQueuePlan -> Maybe Text
mirrorQueuePlanWarning = \case
SqsBackend SqsConfig
_ -> Maybe Text
forall a. Maybe a
Nothing
MirrorQueuePlan
MemoryBackend -> Text -> Maybe Text
forall a. a -> Maybe a
Just Text
memoryQueueBootWarning
memoryQueueBootWarning :: Text
memoryQueueBootWarning :: Text
memoryQueueBootWarning =
Text
"no ECLUSE_QUEUE__URL is set, so the mirror queue is IN-MEMORY, NON-DURABLE, and BEST-EFFORT. "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"Jobs are dropped on cap overflow and lost on restart or redeploy; each is re-mirrored on the next "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"demand (no data loss, only deferred mirroring). Point ECLUSE_QUEUE__URL at a durable queue (SQS) "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"for a production mirror that must not shed under load."
memoryQueueDropWarning :: Int -> Text
memoryQueueDropWarning :: Int -> Text
memoryQueueDropWarning Int
dropped =
Text
"mirror queue at capacity: dropped a mirror job (drop-newest); "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall b a. (Show a, IsString b) => a -> b
show Int
dropped
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" job(s) dropped so far. Each is re-mirrored on the next demand; raise "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"ECLUSE_QUEUE__MEMORY_MAX_DEPTH to shed fewer under load."