Skip to content

Using Endpoint.for to connect to sentinels hangs the execution #70

@jlledom

Description

@jlledom

When working on my project, I found what looks like a bug. If I want to connect to sentinels, using Endpoint.for makes the execution to get stuck. However, a combination of URI::Generic.build and Async::Redis::Endpoint.new works as expected. Asked the AI to write a small script to reproduce the bug and here it is:

# frozen_string_literal: true

# Demonstrates bug with Endpoint.for when creating sentinel endpoints
# The connection hangs when using Endpoint.for(nil, host, port:, ...)
# but works correctly when building URI explicitly first

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  gem 'async-redis', '~> 0.13.0'
end

def test_with_endpoint_for
  puts "Testing with Endpoint.for (BUGGY - will hang)..."
  Async do
    sentinels = [{ host: 'localhost', port: 26379 },
                 { host: 'localhost', port: 26380 },
                 { host: 'localhost', port: 26381 }]

    database = '6'
    credentials = nil
    ssl_context = nil
    master_options = {database:, credentials:, ssl_context:}.compact

    sentinel_credentials = nil
    master_name = 'redis-master'
    role = :master

    # BUGGY: Using Endpoint.for
    endpoints = sentinels.map do |sentinel|
      Async::Redis::Endpoint.for(nil, sentinel[:host], port: sentinel[:port], credentials: sentinel_credentials, ssl_context:)
    end

    client = Async::Redis::SentinelClient.new(endpoints, master_name:, master_options:, role:)

    puts "Attempting to ping..."
    result = client.ping 'test'
    puts "Success: #{result}"
  end
end

def test_with_uri_build
  puts "\nTesting with URI::Generic.build + Endpoint.new (WORKS)..."
  Async do
    sentinels = [{ host: 'localhost', port: 26379 },
                 { host: 'localhost', port: 26380 },
                 { host: 'localhost', port: 26381 }]

    database = '6'
    credentials = nil
    ssl_context = nil
    master_options = {database:, credentials:, ssl_context:}.compact

    sentinel_credentials = nil
    master_name = 'redis-master'
    role = :master

    # CORRECT: Build URI explicitly first
    endpoints = sentinels.map do |sentinel|
      scheme = ssl_context ? 'rediss' : 'redis'
      sentinel_uri = URI::Generic.build(scheme:, host: sentinel[:host], port: sentinel[:port])
      Async::Redis::Endpoint.new(sentinel_uri, nil, credentials: sentinel_credentials, ssl_context:)
    end

    client = Async::Redis::SentinelClient.new(endpoints, master_name:, master_options:, role:)

    puts "Attempting to ping..."
    result = client.ping 'test'
    puts "Success: #{result}"
  end
end

# Uncomment the one you want to test:

# This works correctly:
test_with_uri_build

# This will hang:
test_with_endpoint_for

This script reproduces the bug consistently in my machine, I also verified that calling Endpoint.for blocks the test suite in CircleCI.

I'm sorry for not being able to investigate more and send a proper PR. I'll do it later on if I find the time.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions