Skip Menu |

This queue is for tickets about the POE-Component-Server-Syslog CPAN distribution.

Report information
The Basics
Id: 16323
Status: rejected
Priority: 0/
Queue: POE-Component-Server-Syslog

People
Owner: SUNGO [...] cpan.org
Requestors: mike.bristow [...] thus.net
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in: 1.03
Fixed in: (no value)



Subject: [patch] blocking call in UDP server
The UDP server has a blocking call in it (gethostbyname). This is undesirable in a event driven framework like POE; the whole system freezes while gethostbyname trundles about finding a DNS server and waiting for a response. The solution attached gives a new option to turn this off; this gives a significant (5 fold, or thereabouts) throughput improvment in my enviroment (syslog sources not in /etc/hosts; dns server on same lan but not on the same host; nscd running). A perhaps better solution would be to use POE::Component::Client::DNS to do the lookups (optionally) so that while DNS servers scratch their heads about the answer the rest of the POE system can still run. However, the quick and dirty "just turn it off!!" option was easier to write. I imagine that the TCP server has the same problem, but that it is less impactful because I expect you do the gethostbyname on accept and not per-message, but (given I'm doing UDP stuff at the point I ran into the problem) I haven't checked. Note: this patch was written against 1.02; I've just imported 1.03 into our CVS repository and done the approprate merge magic. It builds OK and passess the tests on: RedHat-3FC-i386 RedHat-3AS-ia32 RedHat-FC.4-i386 RedHat-EL.4-i386 with the vendor perl.
Index: package.xml =================================================================== RCS file: /cvsroot/packages/POE-Component-Server-Syslog/package.xml,v retrieving revision 1.7 retrieving revision 1.7.2.1 diff -u -r1.7 -r1.7.2.1 --- package.xml 6 Dec 2005 17:51:53 -0000 1.7 +++ package.xml 6 Dec 2005 17:54:50 -0000 1.7.2.1 @@ -1,11 +1,11 @@ <?xml version="1.0"?> -<!-- $Thus: packages/POE-Component-Server-Syslog/package.xml,v 1.7 2005/12/06 17:51:53 michaelb-repoman Exp $ --> +<!-- $Thus: packages/POE-Component-Server-Syslog/package.xml,v 1.7.2.1 2005/12/06 17:54:50 michaelb Exp $ --> <Package xmlns="http://www.eng.demon.net/XML/pbuild"> <Class>cpan</Class> <Name>POE-Component-Server-Syslog</Name> <Version>1.03</Version> - <Release>1</Release> + <Release>2_BUG_5464_2_RC1</Release> <Summary>Perl5 library to provide syslog services to POE</Summary> <Description> This component provides very simple syslog services for POE. Index: POE-Component-Server-Syslog/lib/POE/Component/Server/Syslog/UDP.pm =================================================================== RCS file: /cvsroot/upstream/POE-Component-Server-Syslog/lib/POE/Component/Server/Syslog/UDP.pm,v retrieving revision 1.1.1.1 retrieving revision 1.1.1.1.6.1 diff -u -r1.1.1.1 -r1.1.1.1.6.1 --- POE-Component-Server-Syslog/lib/POE/Component/Server/Syslog/UDP.pm 13 Mar 2005 21:08:42 -0000 1.1.1.1 +++ POE-Component-Server-Syslog/lib/POE/Component/Server/Syslog/UDP.pm 6 Dec 2005 17:54:25 -0000 1.1.1.1.6.1 @@ -10,6 +10,7 @@ sub BINDPORT () { 514 } sub DATAGRAM_MAXLEN () { 1024 } # syslogd defaults to this. as do most # libc implementations of syslog +sub DNSLOOKUP () { 1 } use Params::Validate qw(validate_with); use Carp qw(carp croak); @@ -49,6 +50,11 @@ optional => 1, default => DATAGRAM_MAXLEN, }, + DnisLookup => { + type => &Params::Validate::SCALAR, + optional => 1, + default => DNSLOOKUP, + }, }, ); @@ -111,10 +117,14 @@ if(defined $records and ref $records eq 'ARRAY') { foreach my $record (@$records) { if( ( sockaddr_in( $remote_socket ) )[1]) { - $record->{host} = gethostbyaddr( - ( sockaddr_in( $remote_socket ) )[1], - AF_INET, - ); + if ($_[HEAP]->{DnisLookup}) { + $record->{host} = gethostbyaddr( + ( sockaddr_in( $remote_socket ) )[1], + AF_INET, + ); + } else { + $record->{host} = inet_ntoa(( sockaddr_in( $remote_socket ) )[1]); + } } else { $record->{host} = '[unknown]'; }
three things. 1) what is package.xml that your patch updates? this patch won't even apply because i don't have or provide that file. 2) you're right. using POE::Component::Client::DNS is the right way to go, and as i'm not a fan of doing things half-way, i won't accept a patch for this issue unless it implements Client::DNS usage. 3) the TCP implementation does the same call on socket connection. any patch that addresses this issue has to address it in both implementations. i understand that you're only using udp but any patch to this module set needs to solve and provide tests for fixing the problem in the general case. i'm leaving the issue open because i do need to fix this problem. i will however not be applying this patch. if you provide a patch that addresses the issues above before i get around to fixing the problem, i'll more than happy to apply it. thanks for your report.
From: mike.bristow [...] thus.net
[SUNGO - Tue Dec 6 18:36:20 2005]: Show quoted text
> three things. > > 1) what is package.xml that your patch updates? this patch won't even > apply because i don't have or provide that file.
my stupidity, sorry. FYI: it's the magic we use to turn source into .rpm's on RedHat and .pkg files on Solaris and .depot on HPUX. The layout in our repository is: pkg-POE/ pkg-POE/package.xml & other magic pkg-POE/POE/ pkg-POE/POE/<stuff in the upstream tarball> (POE is less to type). I typed cvs diff in pkg-POE not pkg-POE/POE, hence the exteranous file. Find a better attempt attached. Show quoted text
> 2) you're right. using POE::Component::Client::DNS is the right way to > go, and as i'm not a fan of doing things half-way, i won't accept a > patch for this issue unless it implements Client::DNS usage.
Fair enough. However, if something like this is done it will (also) have a negative impact on performance: instead of POE::Component::Server::Syslog users generating two (or so) POE events per syslog message, they'll generate four (or so). ie, one POE::Component::Server::Syslog event, + one client event becomes two POE::Component::Server::Syslog events (message arrived + DNS lookup complete), one POE::Component::DNS::Client event (DNS lookup complete; I'll assume that the query is done inline without an event) + one client event. In this case it's still probably worth having a "Don't do dns lookups" option for the server because some people don't care about the name of the clients, the IP address is good enough for them, and they will still get a reasonably performance boost for doing so. Show quoted text
> 3) the TCP implementation does the same call on socket connection. any > patch that addresses this issue has to address it in both > implementations. i understand that you're only using udp but any patch > to this module set needs to solve and provide tests for fixing the > problem in the general case.
Actually, I flit between udp and tcp fairly often, depending on the problem of the day. However, tcp implmentations of syslog that I've seen (syslog-ng, and er, that's it) seem to hold open the tcp session for long periods of time and pass multiple messages. This means that the TCP server will call gethostbyname once per client reboot, and the UDP server will do so once per message. Obviously, though, doing 'the right thing' for both is better, but the TCP problem is tiny in comparison - for me. I'm dealing with somewhere about the 10k syslog messages per second for periods of 5-10 minutes - at peak. 10-20/sec is the average, though. The client reboots number in the handful per year, as I only have one client. I intend to add the "don't do DNS lookups" option to the TCP server for completeness, and update the ticket. Then I'll start looking at how to do the POE::Component::Client::DNS stuff instead.
Index: lib/POE/Component/Server/Syslog/UDP.pm =================================================================== RCS file: /cvsroot/upstream/POE-Component-Server-Syslog/lib/POE/Component/Server/Syslog/UDP.pm,v retrieving revision 1.1.1.1 retrieving revision 1.1.1.1.6.1 diff -u -r1.1.1.1 -r1.1.1.1.6.1 --- lib/POE/Component/Server/Syslog/UDP.pm 13 Mar 2005 21:08:42 -0000 1.1.1.1 +++ lib/POE/Component/Server/Syslog/UDP.pm 6 Dec 2005 17:54:25 -0000 1.1.1.1.6.1 @@ -10,6 +10,7 @@ sub BINDPORT () { 514 } sub DATAGRAM_MAXLEN () { 1024 } # syslogd defaults to this. as do most # libc implementations of syslog +sub DNSLOOKUP () { 1 } use Params::Validate qw(validate_with); use Carp qw(carp croak); @@ -49,6 +50,11 @@ optional => 1, default => DATAGRAM_MAXLEN, }, + DnisLookup => { + type => &Params::Validate::SCALAR, + optional => 1, + default => DNSLOOKUP, + }, }, ); @@ -111,10 +117,14 @@ if(defined $records and ref $records eq 'ARRAY') { foreach my $record (@$records) { if( ( sockaddr_in( $remote_socket ) )[1]) { - $record->{host} = gethostbyaddr( - ( sockaddr_in( $remote_socket ) )[1], - AF_INET, - ); + if ($_[HEAP]->{DnisLookup}) { + $record->{host} = gethostbyaddr( + ( sockaddr_in( $remote_socket ) )[1], + AF_INET, + ); + } else { + $record->{host} = inet_ntoa(( sockaddr_in( $remote_socket ) )[1]); + } } else { $record->{host} = '[unknown]'; }
From: mike.bristow [...] thus.net
[SUNGO - Tue Dec 6 18:36:20 2005]: Show quoted text
> 2) you're right. using POE::Component::Client::DNS is the right way to > go, and as i'm not a fan of doing things half-way, i won't accept a > patch for this issue unless it implements Client::DNS usage.
See attached patch. I haven't tested this as fully as it requires, nor have I tackled the TCP server so I know it isn't release-worthy yet.
? lib/POE/Component/Server/Syslog/.UDP.pm.swp Index: META.yml =================================================================== RCS file: /cvsroot/upstream/POE-Component-Server-Syslog/META.yml,v retrieving revision 1.1.1.2 retrieving revision 1.1.1.2.2.1 diff -u -r1.1.1.2 -r1.1.1.2.2.1 --- META.yml 20 Nov 2005 22:53:54 -0000 1.1.1.2 +++ META.yml 18 Dec 2005 09:36:26 -0000 1.1.1.2.2.1 @@ -5,6 +5,7 @@ distribution_type: module requires: POE: 0.24 + POE::Component::Client::DNS: 0 Params::Validate: 0 IO::Socket: 0 Time::ParseDate: 0 Index: lib/POE/Component/Server/Syslog/TCP.pm =================================================================== RCS file: /cvsroot/upstream/POE-Component-Server-Syslog/lib/POE/Component/Server/Syslog/TCP.pm,v retrieving revision 1.1.1.1 retrieving revision 1.1.1.1.6.4 diff -u -r1.1.1.1 -r1.1.1.1.6.4 --- lib/POE/Component/Server/Syslog/TCP.pm 13 Mar 2005 21:08:42 -0000 1.1.1.1 +++ lib/POE/Component/Server/Syslog/TCP.pm 7 Dec 2005 16:52:42 -0000 1.1.1.1.6.4 @@ -10,6 +10,7 @@ sub BINDPORT () { 514 } sub DATAGRAM_MAXLEN () { 1024 } # syslogd defaults to this. as do most # libc implementations of syslog +sub DNSLOOKUP () { 1 } use Params::Validate qw(validate_with); use Carp qw(carp croak); @@ -52,6 +53,11 @@ optional => 1, default => DATAGRAM_MAXLEN, }, + DnsLookup => { + type => &Params::Validate::SCALAR, + optional => 1, + default => DNSLOOKUP, + }, }, ); @@ -98,8 +104,13 @@ my $handle = $_[ARG0]; my $host; - if( ( sockaddr_in( getpeername($handle) ) )[1]) { - $host = gethostbyaddr( ( sockaddr_in( getpeername($handle) ) )[1], AF_INET ); + my $remote = (sockaddr_in(getpeername($handle)))[1]; + if( $remote ) { + if ($_[HEAP]->{DnsLookup}) { + $host = gethostbyaddr( $remote, AF_INET ); + } else { + $host = inet_ntoa($remote); + } } else { $host = '[unknown]'; } @@ -209,6 +220,13 @@ called when the component recieves a message it cannot parse. The erroneous message is passed in as ARG0. +=item * DnsLookup + +An optional boolean value. If true or unspecified, reverse DNS lookups +are done for clients connecting and the name is put in the host element +of the hashref passed to the InputState. If false, then the IP address +will be passed instead. + =back =head2 InputState Index: lib/POE/Component/Server/Syslog/UDP.pm =================================================================== RCS file: /cvsroot/upstream/POE-Component-Server-Syslog/lib/POE/Component/Server/Syslog/UDP.pm,v retrieving revision 1.1.1.1 retrieving revision 1.1.1.1.6.4 diff -u -r1.1.1.1 -r1.1.1.1.6.4 --- lib/POE/Component/Server/Syslog/UDP.pm 13 Mar 2005 21:08:42 -0000 1.1.1.1 +++ lib/POE/Component/Server/Syslog/UDP.pm 18 Dec 2005 09:36:27 -0000 1.1.1.1.6.4 @@ -10,12 +10,14 @@ sub BINDPORT () { 514 } sub DATAGRAM_MAXLEN () { 1024 } # syslogd defaults to this. as do most # libc implementations of syslog +sub DNSLOOKUP () { 1 } use Params::Validate qw(validate_with); use Carp qw(carp croak); use POE; use POE::Filter::Syslog; +use POE::Component::Client::DNS; use Socket; use IO::Socket::INET; @@ -49,6 +51,16 @@ optional => 1, default => DATAGRAM_MAXLEN, }, + DnsLookup => { + type => &Params::Validate::SCALAR, + optional => 1, + default => DNSLOOKUP, + }, + Resolver => { + isa => 'POE::Component::Client::DNS', + optional => 1, + default => undef, + }, }, ); @@ -62,6 +74,7 @@ select_read => \&select_read, shutdown => \&shutdown, + dns_result => \&dns_result, client_input => $args{InputState}, client_error => $args{ErrorState}, @@ -70,6 +83,15 @@ heap => \%args, ); + # We need a Resolver only if we are doing DNS lookups + if ($args{DnsLookup} and not defined $args{Resolver}) { + $args{Resolver} = POE::Component::Client::DNS->spawn( + # need a unique alias in case there are many + # ...::Server::Syslog objects in the same program + Alias => 'POE::Component::Server::Syslog::' . $sess->ID(), + ); + } + return $sess; } @@ -110,16 +132,31 @@ while( ($records = $_[HEAP]->{filter}->get_one()) and (@$records > 0)) { if(defined $records and ref $records eq 'ARRAY') { foreach my $record (@$records) { - if( ( sockaddr_in( $remote_socket ) )[1]) { - $record->{host} = gethostbyaddr( - ( sockaddr_in( $remote_socket ) )[1], - AF_INET, - ); + my ($remote_port, $remote_iaddr) + = sockaddr_in($remote_socket); + if( $remote_iaddr ) { + if ($_[HEAP]->{DnsLookup}) { + # we don't yield: it's done + # either when the resolver posts + # the dns_result event, or in the + # handle_dns_result method + + my $r = $_[HEAP]->{Resolver}->resolve( + type => 'PTR', + host => inet_ntoa($remote_iaddr), + context => $record, + event => 'dns_result', + ); + + handle_dns_result($record, $r,$_[KERNEL]) if $r + } else { + $record->{host} = inet_ntoa($remote_iaddr ); + $_[KERNEL]->yield( 'client_input', $record ); + } } else { - $record->{host} = '[unknown]'; + $record->{host} = '[' . inet_ntoa($remote_iaddr) . ']'; + $_[KERNEL]->yield( 'client_input', $record ); } - - $_[KERNEL]->yield( 'client_input', $record ); } } else { $_[KERNEL]->yield( 'client_error', $message ); @@ -128,6 +165,43 @@ } } +sub dns_result { + my $response = $_[ARG0]; + my $record = $response->{context}; + handle_dns_result($record,$response, $_[KERNEL]); +} + +sub handle_dns_result { + my $record = shift; + my $response = shift; + my $kernel = shift; + + if (not defined $response->{response}) { + # There was some sort of DNS error; probably + # a timeout waiting for a DNS server. + + $response->{host} = '[' . $response->{host} . ']'; + $_[KERNEL]->yield( 'client_input', $record ); + return; + } + + my @ptranswer = grep {$_->type() eq 'PTR'} $response->{response}->answer(); + + unless (@ptranswer == 1) { + # Wrong number of PTR records in the answer. + # If there's zero, the DNS server doesn't know the answer; + # if there's more than one, we don't know which one to use + + $response->{host} = '[' . $response->{host} . ']'; + $_[KERNEL]->yield( 'client_input', $record ); + return; + } + + my $rr = $ptranswer[0]; + $record->{host} = $rr->ptrdname; + $kernel->yield( 'client_input', $record ); +} + sub shutdown { if($_[HEAP]->{handle}) { $_[KERNEL]->select_read($_[HEAP]->{handle}); @@ -195,6 +269,17 @@ called when the component recieves a message it cannot parse. The erroneous message is passed in as ARG0. +=item * DnsLookup + +An optional boolean value. If true or unspecified, reverse DNS lookups +are done for clients connecting and the name is put in the host element +of the hashref passed to the InputState. If false, then the IP address +will be passed instead. + +=item * Resolver + +An optional POE::Component::Client::DNS object to use for DNS lookups. + =back =head2 InputState
From: mike.bristow [...] thus.net
[guest - Sun Dec 18 04:48:11 2005]: Show quoted text
> [SUNGO - Tue Dec 6 18:36:20 2005]:
> > 2) you're right. using POE::Component::Client::DNS is the right way to > > go, and as i'm not a fan of doing things half-way, i won't accept a > > patch for this issue unless it implements Client::DNS usage.
> > See attached patch. I haven't tested this as fully as it requires, nor > have I tackled the TCP server so I know it isn't release-worthy yet.
This attached still needs more testing, but I think it is complete. The following behaviour changes will be observed: 1 bugs I've introduced and not noticed 2 if there is no reverse DNS for a host, the $msg->{host} element will be the IP address in square brackets (eg: '[10.0.0.1]') 3 POE::Component::Client::DNS does not look at /etc/hosts for reverse DNS. This means that if, for example the syslog clients are on a private network with no DNS, then things will go pear shaped rapidly 4 events may be in a differet order from the messages that triggered them. Of these, 3 is rather worrying; it is a sustantial change to the way this component works, and the change will be detrmental to some users. 4 could be fixed, but there is probably little point. I'm not to sure if it's a big deal, and if it is how to fix it. Thoughts are welcome... One possibility is to 'fix' POE::Component::Client::DNS to use /etc/hosts for reverse DNS. It already uses it for forward DNS...

Message body is not shown because it is too large.

As the time line and my previous comments should indicate, I've pretty much rejected this patch and ticket. Finally setting the status to match...