Async-Redis
view release on metacpan or search on metacpan
t/10-connection/reconnect.t view on Meta::CPAN
# t/10-connection/reconnect.t
use strict;
use warnings;
use Test::Lib;
use Test::Async::Redis ':redis';
use Test2::V0;
use Async::Redis;
use Time::HiRes qw(time sleep);
# Helper: await a Future and return its result (throws on failure)
subtest 'constructor accepts reconnect parameters' => sub {
my $redis = Async::Redis->new(
host => 'localhost',
reconnect => 1,
reconnect_delay => 0.1,
reconnect_delay_max => 30,
reconnect_jitter => 0.25,
);
ok($redis->{reconnect}, 'reconnect enabled');
is($redis->{reconnect_delay}, 0.1, 'reconnect_delay');
is($redis->{reconnect_delay_max}, 30, 'reconnect_delay_max');
is($redis->{reconnect_jitter}, 0.25, 'reconnect_jitter');
};
subtest 'default reconnect values' => sub {
my $redis = Async::Redis->new(host => 'localhost');
ok(!$redis->{reconnect}, 'reconnect disabled by default');
is($redis->{reconnect_delay}, 0.1, 'default reconnect_delay');
is($redis->{reconnect_delay_max}, 60, 'default reconnect_delay_max');
is($redis->{reconnect_jitter}, 0.25, 'default reconnect_jitter');
};
subtest 'callbacks accepted' => sub {
my @events;
my $redis = Async::Redis->new(
host => 'localhost',
on_connect => sub { push @events, ['connect', @_] },
on_disconnect => sub { push @events, ['disconnect', @_] },
on_error => sub { push @events, ['error', @_] },
);
ok(ref $redis->{on_connect} eq 'CODE', 'on_connect stored');
ok(ref $redis->{on_disconnect} eq 'CODE', 'on_disconnect stored');
ok(ref $redis->{on_error} eq 'CODE', 'on_error stored');
};
subtest 'exponential backoff calculation' => sub {
my $redis = Async::Redis->new(
host => 'localhost',
reconnect_delay => 0.1,
reconnect_delay_max => 10,
reconnect_jitter => 0, # disable jitter for predictable testing
);
# Test internal backoff calculation
is($redis->_calculate_backoff(1), 0.1, 'attempt 1: 0.1s');
is($redis->_calculate_backoff(2), 0.2, 'attempt 2: 0.2s');
is($redis->_calculate_backoff(3), 0.4, 'attempt 3: 0.4s');
is($redis->_calculate_backoff(4), 0.8, 'attempt 4: 0.8s');
is($redis->_calculate_backoff(10), 10, 'attempt 10: capped at max');
is($redis->_calculate_backoff(20), 10, 'attempt 20: still capped');
};
subtest 'jitter applied to backoff' => sub {
my $redis = Async::Redis->new(
host => 'localhost',
reconnect_delay => 1,
reconnect_delay_max => 60,
reconnect_jitter => 0.25,
);
# With 25% jitter, delay 1.0 should be in range [0.75, 1.25]
my @delays;
for my $i (1..20) {
push @delays, $redis->_calculate_backoff(1);
}
my $min = (sort { $a <=> $b } @delays)[0];
my $max = (sort { $b <=> $a } @delays)[0];
ok($min >= 0.75, "min delay $min >= 0.75");
ok($max <= 1.25, "max delay $max <= 1.25");
ok($max > $min, "jitter produced variation");
};
# Tests requiring Redis
SKIP: {
my $test_redis = eval {
my $r = Async::Redis->new(
host => $ENV{REDIS_HOST} // 'localhost',
connect_timeout => 2,
);
( run in 1.086 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )