packages feed

launchdarkly-server-sdk 1.0.1 → 1.0.2

raw patch · 4 files changed

+35/−12 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -2,6 +2,11 @@  All notable changes to the LaunchDarkly Haskell Server-side SDK will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org). +## [1.0.2] - 2020-03-16+### Fixed:+- Added a timeout on SSE reads to 5 minutes.+- Fixed an issue where reading SSE streams would burn CPU in certain edge cases+ ## [1.0.1] - 2020-03-02 ### Fixed: - Client initialization status is now correctly determined by checking the feature store instead of an always in memory value. This is particularly important for usage of daemon mode. In the current implementation daemon mode evaluation always returns the default fallback value because the client never becomes initialized.
launchdarkly-server-sdk.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 8be83973b5569caf1cca5ec68092a736383d9f26014e6f128d1f52e2393825a5+-- hash: 3d20deec805adbfa07184ad9027c2fbf90433f00cb1a39de56f50cf6601c419f  name:           launchdarkly-server-sdk-version:        1.0.1+version:        1.0.2 synopsis:       Server-side SDK for integrating with LaunchDarkly description:    Please see the README on GitHub at <https://github.com/launchdarkly/haskell-server-sdk#readme> category:       Web
src/LaunchDarkly/Server/Client/Internal.hs view
@@ -25,7 +25,7 @@  -- | The version string for this library. clientVersion :: Text-clientVersion = "1.0.1"+clientVersion = "1.0.2"  -- | The status of the client initialization. data Status
src/LaunchDarkly/Server/Network/Streaming.hs view
@@ -2,9 +2,12 @@  import           Data.Text                           (Text) import qualified Data.Text as                        T-import           Data.Attoparsec.Text as             P hiding (Result)+import           Data.Attoparsec.Text as             P hiding (Result, try) import           Control.Monad                       (void, mzero, forever)+import           Control.Monad.Catch                 (Exception, MonadCatch, try)+import           Control.Exception                   (throwIO) import           Data.ByteString                     (ByteString)+import qualified Data.ByteString as                  B import           Control.Applicative                 (many) import           Data.Text.Encoding                  (decodeUtf8, encodeUtf8) import           Data.HashMap.Strict                 (HashMap)@@ -19,6 +22,7 @@ import           Control.Monad.Catch                 (MonadMask) import           Control.Retry                       (RetryPolicyM, RetryStatus, fullJitterBackoff, capDelay, retrying) import           Network.HTTP.Types.Status           (ok200)+import           System.Timeout                      (timeout)  import           LaunchDarkly.Server.Client.Internal (ClientI) import           LaunchDarkly.Server.Store.Internal  (StoreHandle, StoreResult, initializeStore, insertFlag, deleteFlag, deleteSegment, insertSegment)@@ -133,15 +137,29 @@     "delete" -> processDelete store value     _        -> $(logWarn) "unknown event type" -readStream :: (MonadIO m, MonadLogger m) => IO ByteString -> StoreHandle IO -> m ()+data ReadE = ReadETimeout | ReadEClosed deriving (Show, Exception)++tryReadE :: MonadCatch m => m a -> m (Either ReadE a)+tryReadE = try++-- heartbeat expected every 120 seconds+readWithException :: IO ByteString -> IO Text+readWithException body = timeout (1000000 * 300) (brRead body) >>= \case+    Nothing    -> throwIO ReadETimeout+    Just bytes -> if bytes == B.empty then throwIO ReadEClosed else pure (decodeUtf8 bytes)++readStream :: (MonadIO m, MonadLogger m, MonadMask m) => IO ByteString -> StoreHandle IO -> m () readStream body store = loop "" where-    loop initial = liftIO (parseWith (decodeUtf8 <$> brRead body) parseEvent initial) >>= \case-        Done remaining event -> do-            processEvent store (name event) (L.fromStrict $ encodeUtf8 $ buffer event)-            loop remaining-        Fail _ context err ->-            $(logError) $ T.intercalate " " ["failed parsing SSE frame", T.pack $ show context, T.pack err]-        Partial _ -> $(logError) "failed parsing SSE frame unexpected partial"+    loop initial = tryReadE (parseWith (liftIO $ readWithException body) parseEvent initial) >>= \case+        (Left ReadETimeout) -> $(logError) "streaming connection unexpectedly closed"+        (Left ReadEClosed)  -> $(logError) "timeout waiting for SSE event"+        (Right parsed)      -> case parsed of+            Done remaining event -> do+                processEvent store (name event) (L.fromStrict $ encodeUtf8 $ buffer event)+                loop remaining+            Fail _ context err ->+                $(logError) $ T.intercalate " " ["failed parsing SSE frame", T.pack $ show context, T.pack err]+            Partial _ -> $(logError) "failed parsing SSE frame unexpected partial"  -- https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ retryPolicy :: MonadIO m => RetryPolicyM m