@@ -9,7 +9,9 @@ import io.hstream.Producer
99import io.hstream.Record
1010import io.hstream.internal.AppendRequest
1111import io.hstream.internal.HStreamRecord
12- import io.hstream.internal.LookupStreamRequest
12+ import io.hstream.internal.ListShardsRequest
13+ import io.hstream.internal.LookupShardRequest
14+ import io.hstream.internal.Shard
1315import io.hstream.util.GrpcUtils
1416import io.hstream.util.RecordUtils
1517import kotlinx.coroutines.CoroutineScope
@@ -19,39 +21,49 @@ import kotlinx.coroutines.launch
1921import kotlinx.coroutines.sync.Mutex
2022import kotlinx.coroutines.sync.withLock
2123import org.slf4j.LoggerFactory
24+ import java.math.BigInteger
25+ import java.nio.charset.StandardCharsets
2226import java.util.concurrent.CompletableFuture
2327import kotlin.collections.HashMap
2428
2529open class ProducerKtImpl (private val client : HStreamClientKtImpl , private val stream : String ) : Producer {
26- private val serverUrls: HashMap <String , String > = HashMap ()
30+ private val serverUrls: HashMap <Long , String > = HashMap ()
2731 private val serverUrlsLock: Mutex = Mutex ()
32+ private val shards: List <Shard >
2833
29- private suspend fun lookupServerUrl (orderingKey : String , forceUpdate : Boolean = false): String {
34+ init {
35+ val listShardRequest = ListShardsRequest .newBuilder()
36+ .setStreamName(stream)
37+ .build()
38+ val listShardResponse = client.unaryCallBlocked { it.listShards(listShardRequest) }
39+ shards = listShardResponse.shardsList
40+ }
41+
42+ private suspend fun lookupServerUrl (shardId : Long , forceUpdate : Boolean = false): String {
3043 if (forceUpdate) {
31- return updateServerUrl(orderingKey )
44+ return updateServerUrl(shardId )
3245 }
3346 val server: String? = serverUrlsLock.withLock {
34- return @withLock serverUrls[orderingKey ]
47+ return @withLock serverUrls[shardId ]
3548 }
3649 if (server != null ) {
3750 return server
3851 }
39- return updateServerUrl(orderingKey )
52+ return updateServerUrl(shardId )
4053 }
4154
42- private suspend fun updateServerUrl (orderingKey : String ): String {
43- val req = LookupStreamRequest .newBuilder()
44- .setStreamName(stream)
45- .setOrderingKey(orderingKey)
55+ private suspend fun updateServerUrl (shardId : Long ): String {
56+ val req = LookupShardRequest .newBuilder()
57+ .setShardId(shardId)
4658 .build()
4759 val server = client.unaryCallCoroutine {
48- val serverNode = it.lookupStream (req).serverNode
60+ val serverNode = it.lookupShard (req).serverNode
4961 return @unaryCallCoroutine " ${serverNode.host} :${serverNode.port} "
5062 }
5163 serverUrlsLock.withLock {
52- serverUrls[orderingKey ] = server
64+ serverUrls[shardId ] = server
5365 }
54- logger.debug(" updateServerUrl, key:$orderingKey , server:$server " )
66+ logger.debug(" updateServerUrl, key:$shardId , server:$server " )
5567 return server
5668 }
5769
@@ -83,9 +95,24 @@ open class ProducerKtImpl(private val client: HStreamClientKtImpl, private val s
8395 return future
8496 }
8597
98+ protected fun calculateShardIdByPartitionKey (partitionKey : String ): Long {
99+ val hashcode = com.google.common.hash.Hashing .md5().hashString(partitionKey, StandardCharsets .UTF_8 )
100+ val hashValue = BigInteger (hashcode.toString(), 16 )
101+ for (shard in shards) {
102+ val start = BigInteger (shard.startHashRangeKey)
103+ val end = BigInteger (shard.endHashRangeKey)
104+ if (hashValue.compareTo(start) >= 0 && hashValue.compareTo(end) <= 0 ) {
105+ return shard.shardId
106+ }
107+ }
108+
109+ check(false )
110+ return - 1
111+ }
112+
86113 private suspend fun appendWithRetry (
87114 appendRequest : AppendRequest ,
88- orderingKey : String ,
115+ partitionKey : String ,
89116 tryTimes : Int ,
90117 forceUpdate : Boolean = false
91118 ): List <String > {
@@ -96,22 +123,24 @@ open class ProducerKtImpl(private val client: HStreamClientKtImpl, private val s
96123 val status = Status .fromThrowable(e)
97124 if (status.code == Status .UNAVAILABLE .code && tryTimes > 1 ) {
98125 delay(DefaultSettings .REQUEST_RETRY_INTERVAL_SECONDS * 1000 )
99- return appendWithRetry(appendRequest, orderingKey , tryTimes - 1 , true )
126+ return appendWithRetry(appendRequest, partitionKey , tryTimes - 1 , true )
100127 } else {
101128 throw HStreamDBClientException (e)
102129 }
103130 }
104131
105132 check(tryTimes > 0 )
106- val serverUrl = lookupServerUrl(orderingKey, forceUpdate)
133+
134+ val shardId = calculateShardIdByPartitionKey(partitionKey)
135+ val serverUrl = lookupServerUrl(shardId, forceUpdate)
107136 logger.info(" try append with serverUrl [{}], current left tryTimes is [{}]" , serverUrl, tryTimes)
108- try {
109- return client.getCoroutineStub(serverUrl)
137+ return try {
138+ client.getCoroutineStub(serverUrl)
110139 .append(appendRequest).recordIdsList.map(GrpcUtils ::recordIdFromGrpc)
111140 } catch (e: StatusException ) {
112- return handleGRPCException(serverUrl, e)
141+ handleGRPCException(serverUrl, e)
113142 } catch (e: StatusRuntimeException ) {
114- return handleGRPCException(serverUrl, e)
143+ handleGRPCException(serverUrl, e)
115144 }
116145 }
117146
0 commit comments