[Work in Progress] Cluster-wide Multi-key commands#369
[Work in Progress] Cluster-wide Multi-key commands#369nilanshu-sharma wants to merge 1 commit intomainfrom
Conversation
Signed-off-by: Nilanshu Sharma <nilanshu_sharma@apple.com>
|
✅ Pull request no significant performance differences ✅ SummaryNew baseline 'pull_request' is WITHIN the 'main' baseline thresholds. Full Benchmark ComparisonComparing results between 'main' and 'pull_request'ValkeyBenchmarksClient: GET benchmark metricsMalloc (total): results within specified thresholds, fold down for details.
Client: GET benchmark | parallel 20 | 20 concurrent connections metricsMalloc (total): results within specified thresholds, fold down for details.
Connection: GET benchmark metricsMalloc (total): results within specified thresholds, fold down for details.
Connection: GET benchmark – NoOpTracer metricsMalloc (total): results within specified thresholds, fold down for details.
Connection: Pipeline array benchmark metricsMalloc (total): results within specified thresholds, fold down for details.
Connection: Pipeline benchmark metricsMalloc (total): results within specified thresholds, fold down for details.
HashSlot – {user}.whatever metricsMalloc (total): results within specified thresholds, fold down for details.
ValkeyCommandEncoder – Command with 7 words metricsMalloc (total): results within specified thresholds, fold down for details.
ValkeyCommandEncoder – Simple GET metricsMalloc (total): results within specified thresholds, fold down for details.
ValkeyCommandEncoder – Simple MGET 15 keys metricsMalloc (total): results within specified thresholds, fold down for details.
|
adam-fowler
left a comment
There was a problem hiding this comment.
I've had an initial look at this.
- There is a lot of memory allocation.
- The public protocol API needs some work. I'm not certain how it can be improved but if feels a bit hacky
- Have you considered how this is going to work for pipelined commands?
| slotIndices[slot]!.append(i) | ||
| } | ||
|
|
||
| return slotOrder.map { (indices: slotIndices[$0]!, slot: $0) } |
There was a problem hiding this comment.
| return slotOrder.map { (indices: slotIndices[$0]!, slot: $0) } | |
| return slotIndices.map { (indices: $0.value, slot: $0.key) } |
| slotIndices[slot] = [] | ||
| } | ||
| slotIndices[slot]!.append(i) | ||
| } |
There was a problem hiding this comment.
Do the slots indices need to be ordered? If not you can replace this code with
for (i, key) in keys.enumerated() {
let slot = HashSlot(key: key)
slotIndices[slot, default: []].append(i)
}|
|
||
| // Map each raw Result<RESPToken, ValkeyClientError> to the typed Response. | ||
| let slotResults = partitions.enumerated().map { i, partition in | ||
| (indices: partition.indices, result: rawResults[i].convertFromRESP(to: Command.Response.self)) |
There was a problem hiding this comment.
Is it worth not converting to the response type and parsing the RESP in the assemble function. This could avoid multiple allocations, if a response is converted to an Array. In the case of MGET you end up rebuilding a RESPToken anyway.
| /// - Parameter indices: Positions into this command's full key list that | ||
| /// belong to a single hash slot. | ||
| /// - Returns: A new command of the same type scoped to those keys. | ||
| func subCommand(for indices: [Int]) -> Self |
There was a problem hiding this comment.
| func subCommand(for indices: [Int]) -> Self | |
| func createSubCommand(for indices: [Int]) -> Self |
| /// indices for that slot and the sub-command result. | ||
| /// - Returns: The fully assembled response in original key order. | ||
| /// - Throws: ``ValkeyClientError`` if any sub-result is a failure. | ||
| static func assemble( |
There was a problem hiding this comment.
| static func assemble( | |
| static func assembleResults( |
| let partitions = partitionBySlot(keys: keys) | ||
|
|
||
| // Build one sub-command per slot, cast to the type-erased protocol. | ||
| let subCommands: [any ValkeyCommand] = partitions.map { command.subCommand(for: $0.indices) } |
There was a problem hiding this comment.
Is it worth having an early out when there is only one partition which calls the standard code path?
No description provided.