1+ {-# LANGUAGE LambdaCase #-}
2+
13module HStream.Server.Core.Shard
2- ( readShard ,
3- listShards ,
4- splitShards ,
5- )
4+ ( readShard
5+ , listShards
6+ , splitShards
7+ , mergeShards
8+ )
69where
710
8- import Control.Exception (bracket )
9- import Control.Monad (void )
10- import Data.Foldable (foldl' )
11- import qualified Data.Map.Strict as M
12- import qualified Data.Vector as V
13- import GHC.Stack (HasCallStack )
14- import Network.GRPC.HighLevel.Generated
15- import qualified Z.Data.CBytes as CB
16-
17- import Data.Maybe (fromJust )
18- import HStream.Connector.HStore (transToStreamName )
19- import HStream.Server.Exception (InvalidArgument (.. ),
20- StreamNotExist (.. ))
21- import HStream.Server.Handler.Common (decodeRecordBatch )
22- import qualified HStream.Server.HStreamApi as API
23- import HStream.Server.ReaderPool (getReader , putReader )
24- import HStream.Server.Types (ServerContext (.. ))
25- import qualified HStream.Store as S
26- import HStream.ThirdParty.Protobuf as PB
11+ import Control.Exception (bracket )
12+ import Control.Monad (foldM , void )
13+ import Data.Foldable (foldl' )
14+ import qualified Data.HashMap.Strict as HM
15+ import qualified Data.Map.Strict as M
16+ import qualified Data.Vector as V
17+ import GHC.Stack (HasCallStack )
18+ import qualified Z.Data.CBytes as CB
19+
20+ import Control.Concurrent (MVar , modifyMVar , modifyMVar_ ,
21+ readMVar )
22+ import Data.Maybe (fromJust , fromMaybe )
23+ import Data.Text (Text )
24+ import Data.Word (Word64 )
25+ import HStream.Server.Core.Common (decodeRecordBatch )
26+ import qualified HStream.Server.HStreamApi as API
27+ import HStream.Server.ReaderPool (getReader , putReader )
28+ import HStream.Server.Shard (Shard (.. ), ShardKey (.. ),
29+ SharedShardMap , cBytesToKey ,
30+ mergeTwoShard , mkShard ,
31+ mkSharedShardMapWithShards ,
32+ shardKeyToText , splitByKey ,
33+ splitHalf , textToShardKey )
34+ import HStream.Server.Types (ServerContext (.. ),
35+ transToStreamName )
36+ import qualified HStream.Store as S
2737import HStream.Utils
28- import Proto3.Suite (Enumerated (Enumerated ))
38+ import Proto3.Suite (Enumerated (Enumerated ))
39+ import Z.Data.CBytes (CBytes )
2940
3041-------------------------------------------------------------------------------
3142
@@ -36,16 +47,13 @@ listShards
3647 -> IO (V. Vector API. Shard )
3748listShards ServerContext {.. } API. ListShardsRequest {.. } = do
3849 shards <- M. elems <$> S. listStreamPartitions scLDClient streamId
39- V. foldM' getShardInfo V. empty $ V. fromList shards
50+ V. foldM' constructShard V. empty $ V. fromList shards
4051 where
4152 streamId = transToStreamName listShardsRequestStreamName
42- startKey = CB. pack " startKey"
43- endKey = CB. pack " endKey"
44- epoch = CB. pack " epoch"
4553
46- getShardInfo shards logId = do
54+ constructShard shards logId = do
4755 attr <- S. getStreamPartitionExtraAttrs scLDClient logId
48- case getInfo attr of
56+ case getShardInfo attr of
4957 Nothing -> return . V. snoc shards $
5058 API. Shard { API. shardStreamName = listShardsRequestStreamName
5159 , API. shardShardId = logId
@@ -54,19 +62,13 @@ listShards ServerContext{..} API.ListShardsRequest{..} = do
5462 Just (sKey, eKey, ep) -> return . V. snoc shards $
5563 API. Shard { API. shardStreamName = listShardsRequestStreamName
5664 , API. shardShardId = logId
57- , API. shardStartHashRangeKey = sKey
58- , API. shardEndHashRangeKey = eKey
65+ , API. shardStartHashRangeKey = shardKeyToText sKey
66+ , API. shardEndHashRangeKey = shardKeyToText eKey
5967 , API. shardEpoch = ep
6068 -- FIXME: neet a way to find if this shard is active
6169 , API. shardIsActive = True
6270 }
6371
64- getInfo mp = do
65- startHashRangeKey <- cBytesToText <$> M. lookup startKey mp
66- endHashRangeKey <- cBytesToText <$> M. lookup endKey mp
67- shardEpoch <- read . CB. unpack <$> M. lookup epoch mp
68- return (startHashRangeKey, endHashRangeKey, shardEpoch)
69-
7072readShard
7173 :: HasCallStack
7274 => ServerContext
@@ -101,8 +103,90 @@ readShard ServerContext{..} API.ReadShardRequest{..} = do
101103 return $ foldl' (\ acc (_, _, _, record) -> acc <> record) V. empty receivedRecordsVecs
102104
103105splitShards
104- :: HasCallStack
105- => ServerContext
106+ :: ServerContext
106107 -> API. SplitShardsRequest
107108 -> IO (V. Vector API. Shard )
108- splitShards = error " "
109+ splitShards ServerContext {.. } API. SplitShardsRequest {.. } = do
110+ sharedShardMp <- getShardMap scLDClient shardInfo splitShardsRequestStreamName
111+ newShards <- splitShard sharedShardMp
112+ updateShardTable newShards
113+ return . V. map (shardToPb splitShardsRequestStreamName) $ V. fromList newShards
114+ where
115+ splitKey = textToShardKey splitShardsRequestSplitKey
116+
117+ split :: Bool -> ShardKey -> SharedShardMap -> IO (Shard , Shard )
118+ split True key mps = splitHalf scLDClient mps key
119+ split False key mps = splitByKey scLDClient mps key
120+
121+ splitShard sharedShardMp =
122+ modifyMVar shardInfo $ \ info -> do
123+ (s1, s2) <- split splitShardsRequestHalfSplit splitKey sharedShardMp
124+ return (HM. insert splitShardsRequestStreamName sharedShardMp info, [s1, s2])
125+
126+ updateShardTable newShards =
127+ modifyMVar_ shardTable $ \ mp -> do
128+ let dict = fromMaybe M. empty $ HM. lookup splitShardsRequestStreamName mp
129+ dict' = foldl' (\ acc Shard {startKey= sKey, shardId= sId} -> M. insert sKey sId acc) dict newShards
130+ return $ HM. insert splitShardsRequestStreamName dict' mp
131+
132+ mergeShards
133+ :: ServerContext
134+ -> API. MergeShardsRequest
135+ -> IO API. Shard
136+ mergeShards ServerContext {.. } API. MergeShardsRequest {.. } = do
137+ sharedShardMp <- getShardMap scLDClient shardInfo mergeShardsRequestStreamName
138+ (newShard, removedKey) <- mergeShard sharedShardMp
139+ updateShardTable newShard removedKey
140+ return . shardToPb mergeShardsRequestStreamName $ newShard
141+ where
142+ mergeShard sharedShardMp = do
143+ modifyMVar shardInfo $ \ info -> do
144+ let [shardKey1, shardKey2] = V. toList . V. map textToShardKey $ mergeShardsRequestShardKeys
145+ res <- mergeTwoShard scLDClient sharedShardMp shardKey1 shardKey2
146+ return (HM. insert mergeShardsRequestStreamName sharedShardMp info, res)
147+
148+ updateShardTable Shard {startKey= sKey, shardId= sId} removedKey =
149+ modifyMVar_ shardTable $ \ mp -> do
150+ let dict = fromMaybe M. empty $ HM. lookup mergeShardsRequestStreamName mp
151+ dict' = M. insert sKey sId dict
152+ dict'' = M. delete removedKey dict'
153+ return $ HM. insert mergeShardsRequestStreamName dict'' mp
154+
155+ getShardMap :: S. LDClient -> MVar (HM. HashMap Text SharedShardMap ) -> Text -> IO SharedShardMap
156+ getShardMap client shardInfo streamName = do
157+ let streamId = transToStreamName streamName
158+ readMVar shardInfo >>= pure <$> HM. lookup streamName >>= \ case
159+ Just mps -> return mps
160+ Nothing -> loadSharedShardMap client streamId
161+
162+ loadSharedShardMap :: S. LDClient -> S. StreamId -> IO SharedShardMap
163+ loadSharedShardMap client streamId = do
164+ shardIds <- M. elems <$> S. listStreamPartitions client streamId
165+ mkSharedShardMapWithShards =<< foldM createShard [] shardIds
166+ where
167+ createShard acc shardId = do
168+ attrs <- S. getStreamPartitionExtraAttrs client shardId
169+ case getShardInfo attrs of
170+ Nothing -> return acc
171+ Just (sKey, eKey, epoch) -> return $ mkShard shardId streamId sKey eKey epoch : acc
172+
173+ getShardInfo :: M. Map CBytes CBytes -> Maybe (ShardKey , ShardKey , Word64 )
174+ getShardInfo mp = do
175+ startHashRangeKey <- cBytesToKey <$> M. lookup startKey mp
176+ endHashRangeKey <- cBytesToKey <$> M. lookup endKey mp
177+ shardEpoch <- read . CB. unpack <$> M. lookup epoch mp
178+ return (startHashRangeKey, endHashRangeKey, shardEpoch)
179+ where
180+ startKey = CB. pack " startKey"
181+ endKey = CB. pack " endKey"
182+ epoch = CB. pack " epoch"
183+
184+ shardToPb :: Text -> Shard -> API. Shard
185+ shardToPb sName Shard {.. } = API. Shard
186+ { API. shardShardId = shardId
187+ , API. shardStreamName = sName
188+ , API. shardStartHashRangeKey = shardKeyToText startKey
189+ , API. shardEndHashRangeKey = shardKeyToText endKey
190+ , API. shardEpoch = epoch
191+ , API. shardIsActive = True
192+ }
0 commit comments