forked from informatikr/hedis
-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
One pool per node test #24
Draft
shashitnak
wants to merge
23
commits into
juspay:cluster
Choose a base branch
from
shashitnak:one-pool-per-node-test
base: cluster
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 10 commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
10e07f3
Merge pull request #4 from juspay/cluster
aravindgopall 910977c
Added retry logic for refreshShardMap
638e652
MissingNodeException bugfix
viv3kshukla-juspay 3f49188
Merge pull request #21 from gupta-ujjwal/feature/NodeRetryLogicForRef…
aravindgopall a21fa1b
Merge pull request #22 from imviv3kshukla/fix/MissingNodeException
aravindgopall 974d85e
throwing TimeoutException for timeouts
fc3b373
randomly choosing node for refreshShardMap
b7aea75
using fromException instead of displayException
22d8146
Merge pull request #23 from Candyman770/fix/timeoutException
aravindgopall 039a83d
changing NodeConnection to Pool of NodeConnection and taking Cluster …
shashitnak bc79b73
moving withResource call to the beginning of requestNode
shashitnak 8545c0e
moving NodeConnection HashMap in MVar and adding refreshCluster function
shashitnak 0ee0bf0
refreshShardMap is now updating both MVar's and removing refreshCluster
shashitnak 47613cf
dropping a NodeConnection from HashMap if it throws an exception in c…
shashitnak 4362e64
only adding new nodes to hashmap and not changing the old ones
shashitnak 4fd99de
saving a timestamp for last ShardMap refresh Time and creating entire…
shashitnak e22fbee
moving back to MVar's and fixing deadlock
shashitnak 883936f
moving Pipeline from Connection object to ClusterEnv
shashitnak 7427b2f
moving IORef inside Pool
shashitnak 1230e4c
changing NodeResource from data to newtype
shashitnak 8f27a40
addressing pr comments
shashitnak 5e9065c
fixing redudant import error due to previous change
shashitnak 7eed199
removing clusterConnect function
shashitnak File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
{-# LANGUAGE TupleSections #-} | ||
{-# LANGUAGE RecordWildCards #-} | ||
{-# LANGUAGE OverloadedStrings #-} | ||
{-# LANGUAGE ScopedTypeVariables #-} | ||
module Database.Redis.Connection where | ||
|
||
import Control.Exception | ||
|
@@ -18,14 +19,22 @@ import qualified Data.Time as Time | |
import Network.TLS (ClientParams) | ||
import qualified Network.Socket as NS | ||
import qualified Data.HashMap.Strict as HM | ||
import System.Random (randomRIO) | ||
import System.Environment (lookupEnv) | ||
import Data.Maybe (fromMaybe) | ||
import Text.Read (readMaybe) | ||
|
||
import qualified Database.Redis.ProtocolPipelining as PP | ||
import Database.Redis.Core(Redis, runRedisInternal, runRedisClusteredInternal) | ||
import Database.Redis.Protocol(Reply(..)) | ||
import Database.Redis.Cluster(ShardMap(..), Node, Shard(..)) | ||
import qualified Database.Redis.Cluster as Cluster | ||
import qualified Database.Redis.ConnectionContext as CC | ||
import Control.Concurrent (threadDelay) | ||
import Control.Concurrent.Async (race) | ||
import qualified Database.Redis.Types as T | ||
--import qualified Database.Redis.Cluster.Pipeline as ClusterPipeline | ||
|
||
import Database.Redis.Commands | ||
( ping | ||
, select | ||
|
@@ -45,7 +54,7 @@ import Database.Redis.Commands | |
-- 'connect' function to create one. | ||
data Connection | ||
= NonClusteredConnection (Pool PP.Connection) | ||
| ClusteredConnection (MVar ShardMap) (Pool Cluster.Connection) | ||
| ClusteredConnection (MVar ShardMap) Cluster.Connection | ||
|
||
-- |Information for connnecting to a Redis server. | ||
-- | ||
|
@@ -164,7 +173,7 @@ checkedConnect connInfo = do | |
-- |Destroy all idle resources in the pool. | ||
disconnect :: Connection -> IO () | ||
disconnect (NonClusteredConnection pool) = destroyAllResources pool | ||
disconnect (ClusteredConnection _ pool) = destroyAllResources pool | ||
disconnect (ClusteredConnection _ conn) = Cluster.destroyNodeResources conn | ||
|
||
-- | Memory bracket around 'connect' and 'disconnect'. | ||
withConnect :: (Catch.MonadMask m, MonadIO m) => ConnectInfo -> (Connection -> m c) -> m c | ||
|
@@ -182,8 +191,8 @@ withCheckedConnect connInfo = bracket (checkedConnect connInfo) disconnect | |
runRedis :: Connection -> Redis a -> IO a | ||
runRedis (NonClusteredConnection pool) redis = | ||
withResource pool $ \conn -> runRedisInternal conn redis | ||
runRedis (ClusteredConnection _ pool) redis = | ||
withResource pool $ \conn -> runRedisClusteredInternal conn (refreshShardMap conn) redis | ||
runRedis (ClusteredConnection _ conn) redis = | ||
runRedisClusteredInternal conn (refreshShardMap conn) redis | ||
|
||
newtype ClusterConnectError = ClusterConnectError Reply | ||
deriving (Eq, Show, Typeable) | ||
|
@@ -215,9 +224,10 @@ connectCluster bootstrapConnInfo = do | |
Right infos -> do | ||
let | ||
isConnectionReadOnly = connectReadOnly bootstrapConnInfo | ||
clusterConnection = Cluster.connect withAuth infos shardMapVar timeoutOptUs isConnectionReadOnly refreshShardMapWithNodeConn | ||
pool <- createPool (clusterConnect isConnectionReadOnly clusterConnection) Cluster.disconnect 1 (connectMaxIdleTime bootstrapConnInfo) (connectMaxConnections bootstrapConnInfo) | ||
return $ ClusteredConnection shardMapVar pool | ||
clusterConnection = Cluster.connect withAuth infos shardMapVar timeoutOptUs isConnectionReadOnly refreshShardMapWithNodeConn (connectMaxIdleTime bootstrapConnInfo) (connectMaxConnections bootstrapConnInfo) | ||
-- pool <- createPool (clusterConnect isConnectionReadOnly clusterConnection) Cluster.disconnect 3 (connectMaxIdleTime bootstrapConnInfo) (connectMaxConnections bootstrapConnInfo) | ||
connection <- clusterConnect isConnectionReadOnly clusterConnection | ||
return $ ClusteredConnection shardMapVar connection | ||
where | ||
withAuth host port timeout = do | ||
conn <- PP.connect host port timeout | ||
|
@@ -240,13 +250,24 @@ connectCluster bootstrapConnInfo = do | |
clusterConnect :: Bool -> IO Cluster.Connection -> IO Cluster.Connection | ||
clusterConnect readOnlyConnection connection = do | ||
clusterConn@(Cluster.Connection nodeMap _ _ _ _) <- connection | ||
nodesConns <- sequence $ ( PP.fromCtx . (\(Cluster.NodeConnection ctx _ _) -> ctx ) . snd) <$> (HM.toList nodeMap) | ||
nodesConns <- sequence $ (ctxToConn . snd) <$> (HM.toList nodeMap) | ||
when readOnlyConnection $ | ||
mapM_ (\conn -> do | ||
PP.beginReceiving conn | ||
runRedisInternal conn readOnly | ||
mapM_ (\maybeConn -> case maybeConn of | ||
Just conn -> do | ||
PP.beginReceiving conn | ||
runRedisInternal conn readOnly | ||
Nothing -> return $ Right (T.Status "Connection does not exist") | ||
) nodesConns | ||
return clusterConn | ||
where | ||
ctxToConn :: Cluster.NodeConnection -> IO (Maybe PP.Connection) | ||
ctxToConn (Cluster.NodeConnection pool _ nid) = do | ||
maybeConn <- try $ withResource pool PP.fromCtx | ||
case maybeConn of | ||
Right ppConn -> return $ Just ppConn | ||
Left (_ :: SomeException) -> do | ||
putStrLn ("SomeException Occured in NodeID " ++ show nid) | ||
return Nothing | ||
|
||
shardMapFromClusterSlotsResponse :: ClusterSlotsResponse -> IO ShardMap | ||
shardMapFromClusterSlotsResponse ClusterSlotsResponse{..} = ShardMap <$> foldr mkShardMap (pure IntMap.empty) clusterSlotsResponseEntries where | ||
|
@@ -267,17 +288,34 @@ shardMapFromClusterSlotsResponse ClusterSlotsResponse{..} = ShardMap <$> foldr m | |
|
||
refreshShardMap :: Cluster.Connection -> IO ShardMap | ||
refreshShardMap (Cluster.Connection nodeConns _ _ _ _) = | ||
refreshShardMapWithNodeConn (head $ HM.elems nodeConns) | ||
refreshShardMapWithNodeConn (HM.elems nodeConns) | ||
|
||
refreshShardMapWithNodeConn :: Cluster.NodeConnection -> IO ShardMap | ||
refreshShardMapWithNodeConn (Cluster.NodeConnection ctx _ _) = do | ||
pipelineConn <- PP.fromCtx ctx | ||
refreshShardMapWithConn pipelineConn True | ||
refreshShardMapWithNodeConn :: [Cluster.NodeConnection] -> IO ShardMap | ||
refreshShardMapWithNodeConn [] = throwIO $ ClusterConnectError (Error "Couldn't refresh shardMap due to connection error") | ||
refreshShardMapWithNodeConn nodeConnsList = do | ||
selectedIdx <- randomRIO (0, (length nodeConnsList) - 1) | ||
let (Cluster.NodeConnection pool _ _) = nodeConnsList !! selectedIdx | ||
withResource pool $ \ctx -> do | ||
pipelineConn <- PP.fromCtx ctx | ||
envTimeout <- fromMaybe (10 ^ (3 :: Int)) . (>>= readMaybe) <$> lookupEnv "REDIS_CLUSTER_SLOTS_TIMEOUT" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment, lets combine all such configs in a single place to be more readable / trackable. |
||
raceResult <- race (threadDelay envTimeout) (try $ refreshShardMapWithConn pipelineConn True) -- racing with delay of default 1 ms | ||
case raceResult of | ||
Left () -> do | ||
print $ "TimeoutForConnection " <> show ctx | ||
throwIO $ Cluster.TimeoutException "ClusterSlots Timeout" | ||
Right eiShardMapResp -> | ||
case eiShardMapResp of | ||
Right shardMap -> pure shardMap | ||
Left (err :: SomeException) -> do | ||
print $ "ShardMapRefreshError-" <> show err | ||
throwIO $ ClusterConnectError (Error "Couldn't refresh shardMap due to connection error") | ||
|
||
refreshShardMapWithConn :: PP.Connection -> Bool -> IO ShardMap | ||
refreshShardMapWithConn pipelineConn _ = do | ||
_ <- PP.beginReceiving pipelineConn | ||
slotsResponse <- runRedisInternal pipelineConn clusterSlots | ||
case slotsResponse of | ||
Left e -> throwIO $ ClusterConnectError e | ||
Right slots -> shardMapFromClusterSlotsResponse slots | ||
Right slots -> case clusterSlotsResponseEntries slots of | ||
[] -> throwIO $ ClusterConnectError $ SingleLine "empty slotsResponse" | ||
_ -> shardMapFromClusterSlotsResponse slots |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets move these default values and environment variables into a single Config file. @shashitnak