mirror of
https://github.com/swift-server/RediStack.git
synced 2026-05-03 07:32:28 +00:00
Use TimeAmount for any timeout command arguments
Motivation: The goal is to have a strong-typed API for type-safety in arbitrary values, such as trying to use Int to represent time - as '3' could mean any unit of time, leaving many places for errors and bugs. Modifications: Switch all current APIs that accept a `timeout` argument to use `NIO.TimeAmount` instead of a plain `Int`. Result: Developers will have an easier time reasoning about their own code as to what values might mean when working with timeouts in Redis APIs.
This commit is contained in:
@@ -118,10 +118,9 @@ extension RedisClient {
|
||||
/// - Returns: `true` if the expiration was set.
|
||||
@inlinable
|
||||
public func expire(_ key: RedisKey, after timeout: TimeAmount) -> EventLoopFuture<Bool> {
|
||||
let amount = timeout.nanoseconds / 1_000_000_000
|
||||
let args: [RESPValue] = [
|
||||
.init(bulk: key),
|
||||
.init(bulk: amount.description)
|
||||
.init(bulk: timeout.seconds)
|
||||
]
|
||||
return send(command: "EXPIRE", with: args)
|
||||
.convertFromRESPValue(to: Int.self)
|
||||
|
||||
@@ -161,19 +161,19 @@ extension RedisClient {
|
||||
/// - Parameters:
|
||||
/// - source: The key of the list to pop from.
|
||||
/// - dest: The key of the list to push to.
|
||||
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
|
||||
/// - timeout: The max time to wait for a value to use. `0` seconds means to wait indefinitely.
|
||||
/// - Returns: The element popped from the source list and pushed to the destination,
|
||||
/// or `nil` if the timeout was reached.
|
||||
@inlinable
|
||||
public func brpoplpush(
|
||||
from source: RedisKey,
|
||||
to dest: RedisKey,
|
||||
timeout: Int = 0
|
||||
timeout: TimeAmount = .seconds(0)
|
||||
) -> EventLoopFuture<RESPValue?> {
|
||||
let args: [RESPValue] = [
|
||||
.init(bulk: source),
|
||||
.init(bulk: dest),
|
||||
.init(bulk: timeout)
|
||||
.init(bulk: timeout.seconds)
|
||||
]
|
||||
return send(command: "BRPOPLPUSH", with: args)
|
||||
.map { $0.isNull ? nil: $0 }
|
||||
@@ -378,9 +378,10 @@ extension RedisClient {
|
||||
/// See [https://redis.io/commands/blpop](https://redis.io/commands/blpop)
|
||||
/// - Parameters:
|
||||
/// - key: The key of the list to pop from.
|
||||
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
|
||||
/// - Returns: The element that was popped from the list, or `nil` if the timout was reached.
|
||||
@inlinable
|
||||
public func blpop(from key: RedisKey, timeout: Int = 0) -> EventLoopFuture<RESPValue?> {
|
||||
public func blpop(from key: RedisKey, timeout: TimeAmount = .seconds(0)) -> EventLoopFuture<RESPValue?> {
|
||||
return blpop(from: [key], timeout: timeout)
|
||||
.map { $0?.1 }
|
||||
}
|
||||
@@ -397,13 +398,13 @@ extension RedisClient {
|
||||
/// See [https://redis.io/commands/blpop](https://redis.io/commands/blpop)
|
||||
/// - Parameters:
|
||||
/// - keys: The keys of lists in Redis that should be popped from.
|
||||
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
|
||||
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
|
||||
/// - Returns:
|
||||
/// If timeout was reached, `nil`.
|
||||
///
|
||||
/// Otherwise, the key of the list the element was removed from and the popped element.
|
||||
@inlinable
|
||||
public func blpop(from keys: [RedisKey], timeout: Int = 0) -> EventLoopFuture<(RedisKey, RESPValue)?> {
|
||||
public func blpop(from keys: [RedisKey], timeout: TimeAmount = .seconds(0)) -> EventLoopFuture<(RedisKey, RESPValue)?> {
|
||||
return _bpop(command: "BLPOP", keys, timeout)
|
||||
}
|
||||
|
||||
@@ -419,13 +420,13 @@ extension RedisClient {
|
||||
/// See [https://redis.io/commands/blpop](https://redis.io/commands/blpop)
|
||||
/// - Parameters:
|
||||
/// - keys: The keys of lists in Redis that should be popped from.
|
||||
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
|
||||
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
|
||||
/// - Returns:
|
||||
/// If timeout was reached, `nil`.
|
||||
///
|
||||
/// Otherwise, the key of the list the element was removed from and the popped element.
|
||||
@inlinable
|
||||
public func blpop(from keys: RedisKey..., timeout: Int = 0) -> EventLoopFuture<(RedisKey, RESPValue)?> {
|
||||
public func blpop(from keys: RedisKey..., timeout: TimeAmount = .seconds(0)) -> EventLoopFuture<(RedisKey, RESPValue)?> {
|
||||
return self.blpop(from: keys, timeout: timeout)
|
||||
}
|
||||
|
||||
@@ -441,9 +442,10 @@ extension RedisClient {
|
||||
/// See [https://redis.io/commands/brpop](https://redis.io/commands/brpop)
|
||||
/// - Parameters:
|
||||
/// - key: The key of the list to pop from.
|
||||
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
|
||||
/// - Returns: The element that was popped from the list, or `nil` if the timout was reached.
|
||||
@inlinable
|
||||
public func brpop(from key: RedisKey, timeout: Int = 0) -> EventLoopFuture<RESPValue?> {
|
||||
public func brpop(from key: RedisKey, timeout: TimeAmount = .seconds(0)) -> EventLoopFuture<RESPValue?> {
|
||||
return brpop(from: [key], timeout: timeout)
|
||||
.map { $0?.1 }
|
||||
}
|
||||
@@ -460,13 +462,13 @@ extension RedisClient {
|
||||
/// See [https://redis.io/commands/brpop](https://redis.io/commands/brpop)
|
||||
/// - Parameters:
|
||||
/// - keys: The keys of lists in Redis that should be popped from.
|
||||
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
|
||||
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
|
||||
/// - Returns:
|
||||
/// If timeout was reached, `nil`.
|
||||
///
|
||||
/// Otherwise, the key of the list the element was removed from and the popped element.
|
||||
@inlinable
|
||||
public func brpop(from keys: [RedisKey], timeout: Int = 0) -> EventLoopFuture<(RedisKey, RESPValue)?> {
|
||||
public func brpop(from keys: [RedisKey], timeout: TimeAmount = .seconds(0)) -> EventLoopFuture<(RedisKey, RESPValue)?> {
|
||||
return _bpop(command: "BRPOP", keys, timeout)
|
||||
}
|
||||
|
||||
@@ -482,13 +484,13 @@ extension RedisClient {
|
||||
/// See [https://redis.io/commands/brpop](https://redis.io/commands/brpop)
|
||||
/// - Parameters:
|
||||
/// - keys: The keys of lists in Redis that should be popped from.
|
||||
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
|
||||
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
|
||||
/// - Returns:
|
||||
/// If timeout was reached, `nil`.
|
||||
///
|
||||
/// Otherwise, the key of the list the element was removed from and the popped element.
|
||||
@inlinable
|
||||
public func brpop(from keys: RedisKey..., timeout: Int = 0) -> EventLoopFuture<(RedisKey, RESPValue)?> {
|
||||
public func brpop(from keys: RedisKey..., timeout: TimeAmount = .seconds(0)) -> EventLoopFuture<(RedisKey, RESPValue)?> {
|
||||
return self.brpop(from: keys, timeout: timeout)
|
||||
}
|
||||
|
||||
@@ -496,10 +498,10 @@ extension RedisClient {
|
||||
func _bpop(
|
||||
command: String,
|
||||
_ keys: [RedisKey],
|
||||
_ timeout: Int
|
||||
_ timeout: TimeAmount
|
||||
) -> EventLoopFuture<(RedisKey, RESPValue)?> {
|
||||
var args = keys.map(RESPValue.init)
|
||||
args.append(.init(bulk: timeout))
|
||||
args.append(.init(bulk: timeout.seconds))
|
||||
|
||||
return send(command: command, with: args)
|
||||
.flatMapThrowing {
|
||||
|
||||
@@ -367,14 +367,14 @@ extension RedisClient {
|
||||
/// See [https://redis.io/commands/bzpopmin](https://redis.io/commands/bzpopmin)
|
||||
/// - Parameters:
|
||||
/// - key: The key identifying the sorted set in Redis.
|
||||
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
|
||||
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
|
||||
/// - Returns:
|
||||
/// The element and its associated score that was popped from the sorted set,
|
||||
/// or `nil` if the timeout was reached.
|
||||
@inlinable
|
||||
public func bzpopmin(
|
||||
from key: RedisKey,
|
||||
timeout: Int = 0
|
||||
timeout: TimeAmount = .seconds(0)
|
||||
) -> EventLoopFuture<(Double, RESPValue)?> {
|
||||
return bzpopmin(from: [key], timeout: timeout)
|
||||
.map {
|
||||
@@ -396,7 +396,7 @@ extension RedisClient {
|
||||
/// See [https://redis.io/commands/bzpopmin](https://redis.io/commands/bzpopmin)
|
||||
/// - Parameters:
|
||||
/// - keys: A list of sorted set keys in Redis.
|
||||
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
|
||||
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
|
||||
/// - Returns:
|
||||
/// If timeout was reached, `nil`.
|
||||
///
|
||||
@@ -405,7 +405,7 @@ extension RedisClient {
|
||||
@inlinable
|
||||
public func bzpopmin(
|
||||
from keys: [RedisKey],
|
||||
timeout: Int = 0
|
||||
timeout: TimeAmount = .seconds(0)
|
||||
) -> EventLoopFuture<(String, Double, RESPValue)?> {
|
||||
return self._bzpop(command: "BZPOPMIN", keys, timeout)
|
||||
}
|
||||
@@ -423,14 +423,14 @@ extension RedisClient {
|
||||
/// See [https://redis.io/commands/bzpopmax](https://redis.io/commands/bzpopmax)
|
||||
/// - Parameters:
|
||||
/// - key: The key identifying the sorted set in Redis.
|
||||
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
|
||||
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
|
||||
/// - Returns:
|
||||
/// The element and its associated score that was popped from the sorted set,
|
||||
/// or `nil` if the timeout was reached.
|
||||
@inlinable
|
||||
public func bzpopmax(
|
||||
from key: RedisKey,
|
||||
timeout: Int = 0
|
||||
timeout: TimeAmount = .seconds(0)
|
||||
) -> EventLoopFuture<(Double, RESPValue)?> {
|
||||
return self.bzpopmax(from: [key], timeout: timeout)
|
||||
.map {
|
||||
@@ -452,7 +452,7 @@ extension RedisClient {
|
||||
/// See [https://redis.io/commands/bzpopmax](https://redis.io/commands/bzpopmax)
|
||||
/// - Parameters:
|
||||
/// - keys: A list of sorted set keys in Redis.
|
||||
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
|
||||
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
|
||||
/// - Returns:
|
||||
/// If timeout was reached, `nil`.
|
||||
///
|
||||
@@ -461,7 +461,7 @@ extension RedisClient {
|
||||
@inlinable
|
||||
public func bzpopmax(
|
||||
from keys: [RedisKey],
|
||||
timeout: Int = 0
|
||||
timeout: TimeAmount = .seconds(0)
|
||||
) -> EventLoopFuture<(String, Double, RESPValue)?> {
|
||||
return self._bzpop(command: "BZPOPMAX", keys, timeout)
|
||||
}
|
||||
@@ -470,10 +470,10 @@ extension RedisClient {
|
||||
func _bzpop(
|
||||
command: String,
|
||||
_ keys: [RedisKey],
|
||||
_ timeout: Int
|
||||
_ timeout: TimeAmount
|
||||
) -> EventLoopFuture<(String, Double, RESPValue)?> {
|
||||
var args = keys.map(RESPValue.init)
|
||||
args.append(.init(bulk: timeout))
|
||||
args.append(.init(bulk: timeout.seconds))
|
||||
|
||||
return send(command: command, with: args)
|
||||
// per the Redis docs,
|
||||
|
||||
@@ -37,6 +37,16 @@ extension EventLoopFuture where Value == RESPValue {
|
||||
}
|
||||
}
|
||||
|
||||
extension TimeAmount {
|
||||
/// The seconds representation of the TimeAmount.
|
||||
@usableFromInline
|
||||
internal var seconds: Int64 {
|
||||
return self.nanoseconds / 1_000_000_000
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Setting up a Redis connection
|
||||
|
||||
extension Channel {
|
||||
/// Adds the baseline `ChannelHandlers` needed to support sending and receiving messages in Redis Serialization Protocol (RESP) format to the pipeline.
|
||||
///
|
||||
|
||||
@@ -133,7 +133,7 @@ final class ListCommandsTests: RediStackIntegrationTestCase {
|
||||
}
|
||||
|
||||
func test_blpop() throws {
|
||||
let nilPop = try connection.blpop(from: #function, timeout: 1).wait()
|
||||
let nilPop = try connection.blpop(from: #function, timeout: .seconds(1)).wait()
|
||||
XCTAssertNil(nilPop)
|
||||
|
||||
_ = try connection.lpush([10, 20, 30], into: "first").wait()
|
||||
@@ -192,7 +192,7 @@ final class ListCommandsTests: RediStackIntegrationTestCase {
|
||||
}
|
||||
|
||||
func test_brpop() throws {
|
||||
let nilPop = try connection.brpop(from: #function, timeout: 1).wait()
|
||||
let nilPop = try connection.brpop(from: #function, timeout: .seconds(1)).wait()
|
||||
XCTAssertNil(nilPop)
|
||||
|
||||
_ = try connection.lpush([10, 20, 30], into: "first").wait()
|
||||
|
||||
@@ -152,7 +152,7 @@ final class SortedSetCommandsTests: RediStackIntegrationTestCase {
|
||||
}
|
||||
|
||||
func test_bzpopmin() throws {
|
||||
let nilMin = try connection.bzpopmin(from: #function, timeout: 1).wait()
|
||||
let nilMin = try connection.bzpopmin(from: #function, timeout: .seconds(1)).wait()
|
||||
XCTAssertNil(nilMin)
|
||||
|
||||
let min1 = try connection.bzpopmin(from: key).wait()
|
||||
@@ -186,7 +186,7 @@ final class SortedSetCommandsTests: RediStackIntegrationTestCase {
|
||||
}
|
||||
|
||||
func test_bzpopmax() throws {
|
||||
let nilMax = try connection.bzpopmax(from: #function, timeout: 1).wait()
|
||||
let nilMax = try connection.bzpopmax(from: #function, timeout: .seconds(1)).wait()
|
||||
XCTAssertNil(nilMax)
|
||||
|
||||
let max1 = try connection.bzpopmax(from: key).wait()
|
||||
|
||||
Reference in New Issue
Block a user