Skip to content

Commit 1ef315e

Browse files
committed
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.
1 parent 0007a08 commit 1ef315e

File tree

6 files changed

+42
-31
lines changed

6 files changed

+42
-31
lines changed

Sources/RediStack/Commands/BasicCommands.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,9 @@ extension RedisClient {
118118
/// - Returns: `true` if the expiration was set.
119119
@inlinable
120120
public func expire(_ key: RedisKey, after timeout: TimeAmount) -> EventLoopFuture<Bool> {
121-
let amount = timeout.nanoseconds / 1_000_000_000
122121
let args: [RESPValue] = [
123122
.init(bulk: key),
124-
.init(bulk: amount.description)
123+
.init(bulk: timeout.seconds)
125124
]
126125
return send(command: "EXPIRE", with: args)
127126
.convertFromRESPValue(to: Int.self)

Sources/RediStack/Commands/ListCommands.swift

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -161,19 +161,19 @@ extension RedisClient {
161161
/// - Parameters:
162162
/// - source: The key of the list to pop from.
163163
/// - dest: The key of the list to push to.
164-
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
164+
/// - timeout: The max time to wait for a value to use. `0` seconds means to wait indefinitely.
165165
/// - Returns: The element popped from the source list and pushed to the destination,
166166
/// or `nil` if the timeout was reached.
167167
@inlinable
168168
public func brpoplpush(
169169
from source: RedisKey,
170170
to dest: RedisKey,
171-
timeout: Int = 0
171+
timeout: TimeAmount = .seconds(0)
172172
) -> EventLoopFuture<RESPValue?> {
173173
let args: [RESPValue] = [
174174
.init(bulk: source),
175175
.init(bulk: dest),
176-
.init(bulk: timeout)
176+
.init(bulk: timeout.seconds)
177177
]
178178
return send(command: "BRPOPLPUSH", with: args)
179179
.map { $0.isNull ? nil: $0 }
@@ -378,9 +378,10 @@ extension RedisClient {
378378
/// See [https://redis.io/commands/blpop](https://redis.io/commands/blpop)
379379
/// - Parameters:
380380
/// - key: The key of the list to pop from.
381+
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
381382
/// - Returns: The element that was popped from the list, or `nil` if the timout was reached.
382383
@inlinable
383-
public func blpop(from key: RedisKey, timeout: Int = 0) -> EventLoopFuture<RESPValue?> {
384+
public func blpop(from key: RedisKey, timeout: TimeAmount = .seconds(0)) -> EventLoopFuture<RESPValue?> {
384385
return blpop(from: [key], timeout: timeout)
385386
.map { $0?.1 }
386387
}
@@ -397,13 +398,13 @@ extension RedisClient {
397398
/// See [https://redis.io/commands/blpop](https://redis.io/commands/blpop)
398399
/// - Parameters:
399400
/// - keys: The keys of lists in Redis that should be popped from.
400-
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
401+
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
401402
/// - Returns:
402403
/// If timeout was reached, `nil`.
403404
///
404405
/// Otherwise, the key of the list the element was removed from and the popped element.
405406
@inlinable
406-
public func blpop(from keys: [RedisKey], timeout: Int = 0) -> EventLoopFuture<(RedisKey, RESPValue)?> {
407+
public func blpop(from keys: [RedisKey], timeout: TimeAmount = .seconds(0)) -> EventLoopFuture<(RedisKey, RESPValue)?> {
407408
return _bpop(command: "BLPOP", keys, timeout)
408409
}
409410

@@ -419,13 +420,13 @@ extension RedisClient {
419420
/// See [https://redis.io/commands/blpop](https://redis.io/commands/blpop)
420421
/// - Parameters:
421422
/// - keys: The keys of lists in Redis that should be popped from.
422-
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
423+
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
423424
/// - Returns:
424425
/// If timeout was reached, `nil`.
425426
///
426427
/// Otherwise, the key of the list the element was removed from and the popped element.
427428
@inlinable
428-
public func blpop(from keys: RedisKey..., timeout: Int = 0) -> EventLoopFuture<(RedisKey, RESPValue)?> {
429+
public func blpop(from keys: RedisKey..., timeout: TimeAmount = .seconds(0)) -> EventLoopFuture<(RedisKey, RESPValue)?> {
429430
return self.blpop(from: keys, timeout: timeout)
430431
}
431432

@@ -441,9 +442,10 @@ extension RedisClient {
441442
/// See [https://redis.io/commands/brpop](https://redis.io/commands/brpop)
442443
/// - Parameters:
443444
/// - key: The key of the list to pop from.
445+
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
444446
/// - Returns: The element that was popped from the list, or `nil` if the timout was reached.
445447
@inlinable
446-
public func brpop(from key: RedisKey, timeout: Int = 0) -> EventLoopFuture<RESPValue?> {
448+
public func brpop(from key: RedisKey, timeout: TimeAmount = .seconds(0)) -> EventLoopFuture<RESPValue?> {
447449
return brpop(from: [key], timeout: timeout)
448450
.map { $0?.1 }
449451
}
@@ -460,13 +462,13 @@ extension RedisClient {
460462
/// See [https://redis.io/commands/brpop](https://redis.io/commands/brpop)
461463
/// - Parameters:
462464
/// - keys: The keys of lists in Redis that should be popped from.
463-
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
465+
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
464466
/// - Returns:
465467
/// If timeout was reached, `nil`.
466468
///
467469
/// Otherwise, the key of the list the element was removed from and the popped element.
468470
@inlinable
469-
public func brpop(from keys: [RedisKey], timeout: Int = 0) -> EventLoopFuture<(RedisKey, RESPValue)?> {
471+
public func brpop(from keys: [RedisKey], timeout: TimeAmount = .seconds(0)) -> EventLoopFuture<(RedisKey, RESPValue)?> {
470472
return _bpop(command: "BRPOP", keys, timeout)
471473
}
472474

@@ -482,24 +484,24 @@ extension RedisClient {
482484
/// See [https://redis.io/commands/brpop](https://redis.io/commands/brpop)
483485
/// - Parameters:
484486
/// - keys: The keys of lists in Redis that should be popped from.
485-
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
487+
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
486488
/// - Returns:
487489
/// If timeout was reached, `nil`.
488490
///
489491
/// Otherwise, the key of the list the element was removed from and the popped element.
490492
@inlinable
491-
public func brpop(from keys: RedisKey..., timeout: Int = 0) -> EventLoopFuture<(RedisKey, RESPValue)?> {
493+
public func brpop(from keys: RedisKey..., timeout: TimeAmount = .seconds(0)) -> EventLoopFuture<(RedisKey, RESPValue)?> {
492494
return self.brpop(from: keys, timeout: timeout)
493495
}
494496

495497
@usableFromInline
496498
func _bpop(
497499
command: String,
498500
_ keys: [RedisKey],
499-
_ timeout: Int
501+
_ timeout: TimeAmount
500502
) -> EventLoopFuture<(RedisKey, RESPValue)?> {
501503
var args = keys.map(RESPValue.init)
502-
args.append(.init(bulk: timeout))
504+
args.append(.init(bulk: timeout.seconds))
503505

504506
return send(command: command, with: args)
505507
.flatMapThrowing {

Sources/RediStack/Commands/SortedSetCommands.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -367,14 +367,14 @@ extension RedisClient {
367367
/// See [https://redis.io/commands/bzpopmin](https://redis.io/commands/bzpopmin)
368368
/// - Parameters:
369369
/// - key: The key identifying the sorted set in Redis.
370-
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
370+
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
371371
/// - Returns:
372372
/// The element and its associated score that was popped from the sorted set,
373373
/// or `nil` if the timeout was reached.
374374
@inlinable
375375
public func bzpopmin(
376376
from key: RedisKey,
377-
timeout: Int = 0
377+
timeout: TimeAmount = .seconds(0)
378378
) -> EventLoopFuture<(Double, RESPValue)?> {
379379
return bzpopmin(from: [key], timeout: timeout)
380380
.map {
@@ -396,7 +396,7 @@ extension RedisClient {
396396
/// See [https://redis.io/commands/bzpopmin](https://redis.io/commands/bzpopmin)
397397
/// - Parameters:
398398
/// - keys: A list of sorted set keys in Redis.
399-
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
399+
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
400400
/// - Returns:
401401
/// If timeout was reached, `nil`.
402402
///
@@ -405,7 +405,7 @@ extension RedisClient {
405405
@inlinable
406406
public func bzpopmin(
407407
from keys: [RedisKey],
408-
timeout: Int = 0
408+
timeout: TimeAmount = .seconds(0)
409409
) -> EventLoopFuture<(String, Double, RESPValue)?> {
410410
return self._bzpop(command: "BZPOPMIN", keys, timeout)
411411
}
@@ -423,14 +423,14 @@ extension RedisClient {
423423
/// See [https://redis.io/commands/bzpopmax](https://redis.io/commands/bzpopmax)
424424
/// - Parameters:
425425
/// - key: The key identifying the sorted set in Redis.
426-
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
426+
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
427427
/// - Returns:
428428
/// The element and its associated score that was popped from the sorted set,
429429
/// or `nil` if the timeout was reached.
430430
@inlinable
431431
public func bzpopmax(
432432
from key: RedisKey,
433-
timeout: Int = 0
433+
timeout: TimeAmount = .seconds(0)
434434
) -> EventLoopFuture<(Double, RESPValue)?> {
435435
return self.bzpopmax(from: [key], timeout: timeout)
436436
.map {
@@ -452,7 +452,7 @@ extension RedisClient {
452452
/// See [https://redis.io/commands/bzpopmax](https://redis.io/commands/bzpopmax)
453453
/// - Parameters:
454454
/// - keys: A list of sorted set keys in Redis.
455-
/// - timeout: The time (in seconds) to wait. `0` means indefinitely.
455+
/// - timeout: The max time to wait for a value to use. `0`seconds means to wait indefinitely.
456456
/// - Returns:
457457
/// If timeout was reached, `nil`.
458458
///
@@ -461,7 +461,7 @@ extension RedisClient {
461461
@inlinable
462462
public func bzpopmax(
463463
from keys: [RedisKey],
464-
timeout: Int = 0
464+
timeout: TimeAmount = .seconds(0)
465465
) -> EventLoopFuture<(String, Double, RESPValue)?> {
466466
return self._bzpop(command: "BZPOPMAX", keys, timeout)
467467
}
@@ -470,10 +470,10 @@ extension RedisClient {
470470
func _bzpop(
471471
command: String,
472472
_ keys: [RedisKey],
473-
_ timeout: Int
473+
_ timeout: TimeAmount
474474
) -> EventLoopFuture<(String, Double, RESPValue)?> {
475475
var args = keys.map(RESPValue.init)
476-
args.append(.init(bulk: timeout))
476+
args.append(.init(bulk: timeout.seconds))
477477

478478
return send(command: command, with: args)
479479
// per the Redis docs,

Sources/RediStack/Extensions/SwiftNIO.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ extension EventLoopFuture where Value == RESPValue {
3737
}
3838
}
3939

40+
extension TimeAmount {
41+
/// The seconds representation of the TimeAmount.
42+
@usableFromInline
43+
internal var seconds: Int64 {
44+
return self.nanoseconds / 1_000_000_000
45+
}
46+
}
47+
48+
// MARK: Setting up a Redis connection
49+
4050
extension Channel {
4151
/// Adds the baseline `ChannelHandlers` needed to support sending and receiving messages in Redis Serialization Protocol (RESP) format to the pipeline.
4252
///

Tests/RediStackIntegrationTests/Commands/ListCommandsTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ final class ListCommandsTests: RediStackIntegrationTestCase {
133133
}
134134

135135
func test_blpop() throws {
136-
let nilPop = try connection.blpop(from: #function, timeout: 1).wait()
136+
let nilPop = try connection.blpop(from: #function, timeout: .seconds(1)).wait()
137137
XCTAssertNil(nilPop)
138138

139139
_ = try connection.lpush([10, 20, 30], into: "first").wait()
@@ -192,7 +192,7 @@ final class ListCommandsTests: RediStackIntegrationTestCase {
192192
}
193193

194194
func test_brpop() throws {
195-
let nilPop = try connection.brpop(from: #function, timeout: 1).wait()
195+
let nilPop = try connection.brpop(from: #function, timeout: .seconds(1)).wait()
196196
XCTAssertNil(nilPop)
197197

198198
_ = try connection.lpush([10, 20, 30], into: "first").wait()

Tests/RediStackIntegrationTests/Commands/SortedSetCommandsTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ final class SortedSetCommandsTests: RediStackIntegrationTestCase {
152152
}
153153

154154
func test_bzpopmin() throws {
155-
let nilMin = try connection.bzpopmin(from: #function, timeout: 1).wait()
155+
let nilMin = try connection.bzpopmin(from: #function, timeout: .seconds(1)).wait()
156156
XCTAssertNil(nilMin)
157157

158158
let min1 = try connection.bzpopmin(from: key).wait()
@@ -186,7 +186,7 @@ final class SortedSetCommandsTests: RediStackIntegrationTestCase {
186186
}
187187

188188
func test_bzpopmax() throws {
189-
let nilMax = try connection.bzpopmax(from: #function, timeout: 1).wait()
189+
let nilMax = try connection.bzpopmax(from: #function, timeout: .seconds(1)).wait()
190190
XCTAssertNil(nilMax)
191191

192192
let max1 = try connection.bzpopmax(from: key).wait()

0 commit comments

Comments
 (0)