From 523d9827f6176713c85ce0f961a937b1b3afd225 Mon Sep 17 00:00:00 2001 From: Songmu Date: Fri, 25 Feb 2022 19:03:04 +0900 Subject: [PATCH] fix the blocking behavior when tmpdir disappears when ther server is stopping --- cpanfile | 2 +- lib/Test/RedisServer.pm | 25 +++++++++++++++++++++++++ t/no_save.t | 20 ++++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 t/no_save.t diff --git a/cpanfile b/cpanfile index 3394c8b..3754f3e 100644 --- a/cpanfile +++ b/cpanfile @@ -5,10 +5,10 @@ requires 'Time::HiRes'; on configure => sub { requires 'Module::Build::Tiny', '0.035'; requires 'perl', '5.008_001'; + requires 'Redis'; }; on test => sub { - requires 'Redis'; requires 'Test::More', '0.98'; requires 'Test::TCP'; }; diff --git a/lib/Test/RedisServer.pm b/lib/Test/RedisServer.pm index da99356..0d3398b 100644 --- a/lib/Test/RedisServer.pm +++ b/lib/Test/RedisServer.pm @@ -10,6 +10,7 @@ use File::Temp; use POSIX qw(SIGTERM WNOHANG); use Time::HiRes qw(sleep); use Errno (); +use Redis; has auto_start => ( is => 'rw', @@ -36,6 +37,11 @@ has tmpdir => ( lazy_build => 1, ); +has _redis => ( + is => 'rw', + isa => 'Redis', +); + no Mouse; sub BUILD { @@ -133,6 +139,13 @@ sub start { }; } + # This is sometimes needed to send commands to RedisServer during the stop process. + # Generally, we would like to generate it lazily and not have it as a property + # of the object. However, if you try to create the object at the stop, + # the object generation may fail, such as missing the socket file. Therefore, + # we will make the object and store it as property here. + $self->_redis( Redis->new($self->connect_info) ); + $self->pid($pid); } @@ -163,6 +176,18 @@ sub stop { local $?; # waitpid may change this value :/ return unless defined $self->pid; + # If the tmpdir has disappeared, clear the save config to prevent saving + # in the server terminating process. The newer Redis will save on stop + # for robustness, but will keep blocking if the directory is missing. + # + # It is unlikely that tmpdir will disappear first, but if both the RedisServer + # object and the tmpdir are defined globally, it may happen because the order + # in which they are DESTLOYed is uncertain. + if (! -f $self->tmpdir) { + $self->_redis->config_set('appendonly', 'no'); + $self->_redis->config_set('save', ''); + } + $sig ||= SIGTERM; kill $sig, $self->pid; diff --git a/t/no_save.t b/t/no_save.t new file mode 100644 index 0000000..fed9e3c --- /dev/null +++ b/t/no_save.t @@ -0,0 +1,20 @@ +use strict; +use warnings; +use Test::More; +use File::Path 'remove_tree'; + +use Test::RedisServer; + +eval { Test::RedisServer->new } or plan skip_all => 'redis-server is required in PATH to run this test'; + +my $tmpdir = File::Temp->newdir; +my $server = Test::RedisServer->new(tmpdir => $tmpdir); +ok $server->pid, 'pid ok'; + +remove_tree($tmpdir); +ok ! -f $tmpdir; + +$server->stop; +pass 'redis exit ok'; + +done_testing;