Skip to content
Browse files

Add sidecar processes to reduce this component's memory footprint. Pr…

…ompted by Karen_m's memory issues, and recommended by gcola, Hinrik, and Apocalypse on irc.perl.org #poe. You people rock! :)
  • Loading branch information...
1 parent faa1588 commit 22387e0e157e5cb473314727e0752380da9d8fe6 @rcaputo committed Apr 24, 2011
Showing with 114 additions and 47 deletions.
  1. +12 −47 lib/POE/Component/Resolver.pm
  2. +102 −0 lib/POE/Component/Resolver/Sidecar.pm
View
59 lib/POE/Component/Resolver.pm
@@ -4,14 +4,10 @@ use warnings;
use strict;
use POE qw(Wheel::Run Filter::Reference);
-use Scalar::Util qw(weaken);
use Carp qw(croak);
-use Storable qw(nfreeze thaw);
-use Socket::GetAddrInfo qw(
- :newapi getaddrinfo getnameinfo NI_NUMERICHOST NI_NUMERICSERV
-);
use Time::HiRes qw(time);
use Socket qw(unpack_sockaddr_in AF_INET AF_INET6);
+use Socket::GetAddrInfo qw(:newapi getnameinfo NI_NUMERICHOST NI_NUMERICSERV);
use Exporter;
use base 'Exporter';
@@ -184,7 +180,17 @@ sub _poe_setup_sidecar_ring {
StdoutEvent => 'sidecar_response',
StderrEvent => 'sidecar_error',
CloseEvent => 'sidecar_closed',
- Program => \&_sidecar_code,
+ Program => (
+ ($^O eq "MSWin32")
+ ? \&POE::Component::Resolver::Sidecar::main
+ : [
+ $^X,
+ (map { "-I$_" } @INC),
+ '-MPOE::Component::Resolver::Sidecar',
+ '-e',
+ 'POE::Component::Resolver::Sidecar->main()'
+ ]
+ ),
);
$heap->{sidecar}{$sidecar->PID} = $sidecar;
@@ -195,47 +201,6 @@ sub _poe_setup_sidecar_ring {
}
}
-# Internal helper sub. This is the code that will run within each
-# sidecar process. It accepts requests from the main process, runs
-# the blocking getaddrinfo() for each request, and returns responses.
-#
-# TODO - This would be more efficient as a stand-alone Perl program.
-# The program at large can be quite... large... and forking it for
-# just this snip of code seems inefficient.
-
-sub _sidecar_code {
- my $filter = POE::Filter::Reference->new();
- my $buffer = "";
- my $read_length;
-
- binmode(STDOUT);
- use bytes;
-
- while (1) {
- if (defined $read_length) {
- if (length($buffer) >= $read_length) {
- my $request = thaw(substr($buffer, 0, $read_length, ""));
- $read_length = undef;
-
- my ($request_id, $host, $service, $hints) = @$request;
- my ($err, @addrs) = getaddrinfo($host, $service, $hints);
-
- my $streamable = nfreeze( [ $request_id, $err, \@addrs ] );
- print length($streamable), chr(0), $streamable;
-
- next;
- }
- }
- elsif ($buffer =~ s/^(\d+)\0//) {
- $read_length = $1;
- next;
- }
-
- my $octets_read = sysread(STDIN, $buffer, 4096, length($buffer));
- last unless $octets_read;
- }
-}
-
# Internal helper sub to replay pending requests when their associated
# sidecars are destroyed.
View
102 lib/POE/Component/Resolver/Sidecar.pm
@@ -0,0 +1,102 @@
+package POE::Component::Resolver::Sidecar;
+
+use warnings;
+use strict;
+
+use Storable qw(nfreeze thaw);
+
+use Socket::GetAddrInfo qw(
+ :newapi getaddrinfo getnameinfo NI_NUMERICHOST NI_NUMERICSERV
+);
+
+sub main {
+ my $buffer = "";
+ my $read_length;
+
+ binmode(STDIN);
+ binmode(STDOUT);
+ select STDOUT; $| = 1;
+
+ use bytes;
+
+ while (1) {
+ if (defined $read_length) {
+ if (length($buffer) >= $read_length) {
+ my $request = thaw(substr($buffer, 0, $read_length, ""));
+ $read_length = undef;
+
+ my ($request_id, $host, $service, $hints) = @$request;
+ my ($err, @addrs) = getaddrinfo($host, $service, $hints);
+
+ my $streamable = nfreeze( [ $request_id, $err, \@addrs ] );
+ my $stream = length($streamable) . chr(0) . $streamable;
+
+ my $octets_wrote = syswrite(STDOUT, $stream);
+ die $! unless $octets_wrote == length($stream);
+
+ next;
+ }
+ }
+ elsif ($buffer =~ s/^(\d+)\0//) {
+ $read_length = $1;
+ next;
+ }
+
+ my $octets_read = sysread(STDIN, $buffer, 4096, length($buffer));
+ last unless $octets_read;
+ }
+
+ exit 0;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+POE::Component::Resolver::Sidecar - delegate subprocess to call getaddrinfo()
+
+=head1 SYNOPSIS
+
+Used internally by POE::Component::Resolver.
+
+=head1 DESCRIPTION
+
+POE::Component::Resolver creates subprocesses to call getaddrinfo() so
+that the main program doesn't block during that time.
+
+The actual getaddrinfo() calling code is abstracted into this module
+so it can be run in a separate executable program. This reduces the
+memory footprint of forking the entire main process for just
+getaddrinfo().
+
+It's a strong, useful pattern that other POE::Components have
+implemented before. POE::Quickie does it generically.
+POE::Component::SimpleDBI and POE::Component::EasyDBI do it so their
+DBI subprocesses are relatively lightweight.
+
+=head2 main
+
+The main code to read POE::Component::Resolver requests from STDIN and
+write getaddrinfo() responses to STDOUT.
+
+=head1 SEE ALSO
+
+L<POE::Component::Generic> is one generic implementation of this
+pattern.
+
+L<POE::Quickie> is another generic implementation of this pattern.
+
+=head1 BUGS
+
+None known.
+
+=head1 LICENSE
+
+Except where otherwise noted, this distribution is Copyright 2011 by
+Rocco Caputo. All rights reserved. This distribution is free
+software; you may redistribute it and/or modify it under the same
+terms as Perl itself.
+
+=cut

0 comments on commit 22387e0

Please sign in to comment.
Something went wrong with that request. Please try again.