Skip to content

Commit

Permalink
better test coverage for Hypnotoad
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Apr 19, 2014
1 parent aac16e8 commit 7a4f945
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 33 deletions.
2 changes: 2 additions & 0 deletions Changes
@@ -1,5 +1,7 @@

4.94 2014-04-19
- Added prefork and upgrade_timeout attributes to Mojo::Server::Hypnotoad.
- Added configure method to Mojo::Server::Hypnotoad.
- Relaxed name handling in Mojo::Headers a little.
- Fixed small bug in online tests.

Expand Down
96 changes: 63 additions & 33 deletions lib/Mojo/Server/Hypnotoad.pm
Expand Up @@ -10,15 +10,40 @@ use Mojo::Server::Prefork;
use Mojo::Util 'steady_time';
use Scalar::Util 'weaken';

has prefork => sub { Mojo::Server::Prefork->new };
has upgrade_timeout => 60;

sub configure {
my ($self, $name) = @_;

# Hypnotoad settings
my $prefork = $self->prefork;
my $c = $prefork->app->config($name) || {};
$self->upgrade_timeout($c->{upgrade_timeout}) if $c->{upgrade_timeout};

# Prefork settings
$ENV{MOJO_REVERSE_PROXY} = $c->{proxy} if defined $c->{proxy};
$prefork->listen($c->{listen} || ['http://*:8080']);
my $file = catfile dirname($ENV{HYPNOTOAD_APP} // '.'), 'hypnotoad.pid';
$prefork->pid_file($c->{pid_file} || $file);
$prefork->max_clients($c->{clients}) if $c->{clients};
$prefork->max_requests($c->{keep_alive_requests})
if $c->{keep_alive_requests};
defined $c->{$_} and $prefork->$_($c->{$_})
for qw(accept_interval accepts backlog graceful_timeout group),
qw(heartbeat_interval heartbeat_timeout inactivity_timeout lock_file),
qw(lock_timeout multi_accept user workers);
}

sub run {
my ($self, $path) = @_;
my ($self, $app) = @_;

# No Windows support
_exit('Hypnotoad not available for Windows.') if $^O eq 'MSWin32';

# Remember executable and application for later
$ENV{HYPNOTOAD_EXE} ||= $0;
$0 = $ENV{HYPNOTOAD_APP} ||= abs_path $path;
$0 = $ENV{HYPNOTOAD_APP} ||= abs_path $app;

# This is a production server
$ENV{MOJO_MODE} ||= 'production';
Expand All @@ -27,8 +52,9 @@ sub run {
die "Can't exec: $!" if !$ENV{HYPNOTOAD_REV}++ && !exec $ENV{HYPNOTOAD_EXE};

# Preload application and configure server
my $prefork = $self->{prefork} = Mojo::Server::Prefork->new;
$self->_config($prefork->load_app($ENV{HYPNOTOAD_APP}));
my $prefork = $self->prefork;
$prefork->load_app($app);
$self->configure('hypnotoad');
weaken $self;
$prefork->on(wait => sub { $self->_manage });
$prefork->on(reap => sub { $self->_reap(pop) });
Expand All @@ -52,34 +78,13 @@ sub run {
$prefork->run;
}

sub _config {
my ($self, $app) = @_;

# Hypnotoad settings
my $c = $app->config('hypnotoad') || {};
$self->{upgrade_timeout} = $c->{upgrade_timeout} || 60;

# Prefork settings
$ENV{MOJO_REVERSE_PROXY} = $c->{proxy} if defined $c->{proxy};
my $prefork = $self->{prefork}->listen($c->{listen} || ['http://*:8080']);
my $file = catfile dirname($ENV{HYPNOTOAD_APP}), 'hypnotoad.pid';
$prefork->pid_file($c->{pid_file} || $file);
$prefork->max_clients($c->{clients}) if $c->{clients};
$prefork->max_requests($c->{keep_alive_requests})
if $c->{keep_alive_requests};
defined $c->{$_} and $prefork->$_($c->{$_})
for qw(accept_interval accepts backlog graceful_timeout group),
qw(heartbeat_interval heartbeat_timeout inactivity_timeout lock_file),
qw(lock_timeout multi_accept user workers);
}

sub _exit { say shift and exit 0 }

sub _hot_deploy {
my $self = shift;

# Make sure server is running
return unless my $pid = $self->{prefork}->check_pid;
return unless my $pid = $self->prefork->check_pid;

# Start hot deployment
kill 'USR2', $pid;
Expand All @@ -90,7 +95,7 @@ sub _manage {
my $self = shift;

# Upgraded
my $log = $self->{prefork}->app->log;
my $log = $self->prefork->app->log;
if ($ENV{HYPNOTOAD_PID} && $ENV{HYPNOTOAD_PID} ne $$) {
$log->info("Upgrade successful, stopping $ENV{HYPNOTOAD_PID}.");
kill 'QUIT', $ENV{HYPNOTOAD_PID};
Expand All @@ -109,7 +114,7 @@ sub _manage {

# Timeout
kill 'KILL', $self->{new}
if $self->{upgrade} + $self->{upgrade_timeout} <= steady_time;
if $self->{upgrade} + $self->upgrade_timeout <= steady_time;
}
}

Expand All @@ -118,13 +123,13 @@ sub _reap {

# Clean up failed upgrade
return unless ($self->{new} || '') eq $pid;
$self->{prefork}->app->log->info('Zero downtime software upgrade failed.');
$self->prefork->app->log->info('Zero downtime software upgrade failed.');
delete @$self{qw(new upgrade)};
}

sub _stop {
_exit('Hypnotoad server not running.')
unless my $pid = shift->{prefork}->check_pid;
unless my $pid = shift->prefork->check_pid;
kill 'QUIT', $pid;
_exit("Stopping Hypnotoad server $pid gracefully.");
}
Expand All @@ -141,8 +146,8 @@ Mojo::Server::Hypnotoad - ALL GLORY TO THE HYPNOTOAD!
use Mojo::Server::Hypnotoad;
my $toad = Mojo::Server::Hypnotoad->new;
$toad->run('/home/sri/myapp.pl');
my $hypnotoad = Mojo::Server::Hypnotoad->new;
$hypnotoad->run('/home/sri/myapp.pl');
=head1 DESCRIPTION
Expand Down Expand Up @@ -375,14 +380,39 @@ worker processes per CPU core for applications that perform mostly
non-blocking operations, blocking operations often require more and benefit
from decreasing the number of concurrent L</"clients"> (often as low as C<1>).
=head1 ATTRIBUTES
L<Mojo::Server::Hypnotoad> implements the following attributes.
=head2 prefork
my $prefork = $hypnotoad->prefork;
$hypnotoad = $hypnotoad->prefork(Mojo::Server::Prefork->new);
L<Mojo::Server::Prefork> object this server manages.
=head1 upgrade_timeout
my $timeout = $hypnotoad->upgrade_timeout;
$hypnotoad = $hypnotoad->upgrade_timeout(15);
Maximum amount of time in seconds a zero downtime software upgrade may take
before getting canceled, defaults to C<60>.
=head1 METHODS
L<Mojo::Server::Hypnotoad> inherits all methods from L<Mojo::Base> and
implements the following new ones.
=head2 configure
$hypnotoad->configure('hypnotoad');
Configure server from application settings.
=head2 run
$toad->run('script/myapp');
$hypnotoad->run('script/myapp');
Run server for application.
Expand Down
52 changes: 52 additions & 0 deletions t/mojo/hypnotoad.t
Expand Up @@ -15,9 +15,61 @@ use File::Temp 'tempdir';
use FindBin;
use IO::Socket::INET;
use Mojo::IOLoop;
use Mojo::Server::Hypnotoad;
use Mojo::UserAgent;
use Mojo::Util qw(slurp spurt);

# Configure
{
local $ENV{MOJO_REVERSE_PROXY};
my $hypnotoad = Mojo::Server::Hypnotoad->new;
$hypnotoad->prefork->app->config->{myserver} = {
accept_interval => 33,
accepts => 13,
backlog => 43,
clients => 1,
graceful_timeout => 23,
group => 'testers',
heartbeat_interval => 7,
heartbeat_timeout => 9,
keep_alive_requests => 3,
inactivity_timeout => 5,
listen => ['http://*:8081'],
lock_file => '/foo/bar.lock',
lock_timeout => 14,
multi_accept => 16,
pid_file => '/foo/bar.pid',
proxy => 1,
upgrade_timeout => 45,
user => 'tester',
workers => 7
};
is $hypnotoad->upgrade_timeout, 60, 'right default';
$hypnotoad->configure('test');
is_deeply $hypnotoad->prefork->listen, ['http://*:8080'], 'right value';
like $hypnotoad->prefork->pid_file, qr/hypnotoad\.pid$/, 'right value';
$hypnotoad->configure('myserver');
is $hypnotoad->prefork->accept_interval, 33, 'right value';
is $hypnotoad->prefork->accepts, 13, 'right value';
is $hypnotoad->prefork->backlog, 43, 'right value';
is $hypnotoad->prefork->graceful_timeout, 23, 'right value';
is $hypnotoad->prefork->group, 'testers', 'right value';
is $hypnotoad->prefork->heartbeat_interval, 7, 'right value';
is $hypnotoad->prefork->heartbeat_timeout, 9, 'right value';
is $hypnotoad->prefork->inactivity_timeout, 5, 'right value';
is_deeply $hypnotoad->prefork->listen, ['http://*:8081'], 'right value';
is $hypnotoad->prefork->lock_file, '/foo/bar.lock', 'right value';
is $hypnotoad->prefork->lock_timeout, 14, 'right value';
is $hypnotoad->prefork->max_clients, 1, 'right value';
is $hypnotoad->prefork->max_requests, 3, 'right value';
is $hypnotoad->prefork->multi_accept, 16, 'right value';
is $hypnotoad->prefork->pid_file, '/foo/bar.pid', 'right value';
is $hypnotoad->prefork->user, 'tester', 'right value';
is $hypnotoad->prefork->workers, 7, 'right value';
ok $ENV{MOJO_REVERSE_PROXY}, 'reverse proxy enabled';
is $hypnotoad->upgrade_timeout, 45, 'right value';
}

# Prepare script
my $dir = tempdir CLEANUP => 1;
my $script = catdir $dir, 'myapp.pl';
Expand Down

0 comments on commit 7a4f945

Please sign in to comment.