vapor / redis

Vapor provider for RediStack
MIT License
458 stars 57 forks source link

Cannot connect to remote Redis. #182

Closed notcome closed 3 years ago

notcome commented 3 years ago

I am new to Vapor and just started to set up Redis. I am playing using Xcode's unit testing capability. I noticed that I cannot connect to a remote Redis instance (with ping less than 20ms), consistently, yet one running on localhost works fine.

I have tried my best to reduce this to an extremely simple test case, with code copied directly from your unit tests:

class RedisTests: XCTestCase {
    var redisConfig: RedisConfiguration!

    override func setUpWithError() throws {
        try super.setUpWithError()
        redisConfig = try RedisConfiguration(
            hostname: "localhost",
//            hostname: "10.2.0.4",
            port: 6379,
            password: "skiplist"
        )
    }

    func testApplicationRedis() throws {
        let app = Application()
        defer { app.shutdown() }

        app.redis.configuration = redisConfig
        try app.boot()

        let info = try app.redis.send(command: "INFO").wait()
        XCTAssertContains(info.string, "redis_version")
    }

    override class func setUp() {
        XCTAssert(isLoggingConfigured)
    }
}

let isLoggingConfigured: Bool = {
    var env = Environment.testing
    try! LoggingSystem.bootstrap(from: &env)
    return true
}()

When I run with the hostname set to the remote address, I saw:

[  ERROR ] Error shutting down redis connection pools, possibly because the pool never connected to the Redis server: RedisConnectionPoolError(baseError: RediStack.RedisConnectionPoolError.BaseError.poolHasActiveConnections)
[ ERROR ] command failed [error: (RediStack) trying to send command with a closed connection, rdstk_conn_id: B6C2E41E-D801-45EF-BA1A-F29C36BF1FAD, rdstk_conpool_id: 4A1A951E-A631-4056-8839-D3506F8F2A81]
[ ERROR ] failed to create connection for pool [error: RedisClientError(baseError: RediStack.RedisClientError.(unknown context at $1216413e0).BaseError.connectionClosed), rdstk_conpool_id: 4A1A951E-A631-4056-8839-D3506F8F2A81]
[ ERROR ] connection was closed unexpectedly [rdstk_conn_id: B6C2E41E-D801-45EF-BA1A-F29C36BF1FAD, rdstk_conpool_id: 4A1A951E-A631-4056-8839-D3506F8F2A81]

My other piece of testing code is:

func testApplicationRedis() throws {
    let expectation = XCTestExpectation()
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
        let info = try! self.app.redis.send(command: "INFO").wait()
        XCTAssertContains(info.string, "redis_version")
        expectation.fulfill()
    }
    wait(for: [expectation], timeout: 10)
}

When forcing the try, I saw the following error:

2021-02-02 16:53:33.661266+0800 xctest[22315:4873912] Fatal error: 'try!' expression unexpectedly raised an error: RediStack.RedisConnectionPoolError(baseError: RediStack.RedisConnectionPoolError.BaseError.timedOutWaitingForConnection): file AppTests/AppTests.swift, line 164

The whole asyncAfter + wait approach was trying to deal with the timeout error, but it appears that the connection is only attempted on the first command.

I also noticed that in the simplest case the whole test case is finished in less than 1 second, which makes me wonder how could there be a timeout error.

Of course, I have tested my remote Redis. I used both redis-cli and RediStack:

let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let eventLoop = eventLoopGroup.next()
let connection = try RedisConnection.make(
    configuration: try .init(hostname: "10.2.0.4", password: "skiplist"),
    boundEventLoop: eventLoop
).wait()

let result = try connection.set("my_key", to: "some value")
    .flatMap { return connection.get("my_key") }
    .wait()

print(result) // Optional("some value")

Connection works fine.

Environments

I have

vapor: 4.39.0
redis: 4.1.0
RediStack: 1.1.0

Remote Redis is a latest image inside docker. Local machines runs macOS Big Sur using latest Xcode.

notcome commented 3 years ago

Sorry, my math is bad, the default timeout is 10000000ns, which is 10ms…Setting it to a different value solves the issue.