* [PATCH 47/57] nntp: simplify long response logic and fix nesting
2019-06-24 2:52 6% [PATCH 00/57] ds: shrink, TLS support, buffer writes to FS Eric Wong
@ 2019-06-24 2:52 7% ` Eric Wong
0 siblings, 0 replies; 2+ results
From: Eric Wong @ 2019-06-24 2:52 UTC (permalink / raw)
To: meta
We can get rid of the {long_res} field and reuse the write
buffer ordering logic to prevent nesting of responses from
requeue.
On FreeBSD, this fixes a problem of callbacks firing twice
because kqueue as event_step is now our only callback entry
point.
There's a slight change in the stdout "logging" format, in
that we can no longer distinguish between writes blocked
due to slow clients or deferred long responses. Not sure
if this affects anybody parsing logs or not, but preserving
the old format could prove expensive and not worth the
effort.
---
lib/PublicInbox/NNTP.pm | 61 +++++++++++++++++------------------------
1 file changed, 25 insertions(+), 36 deletions(-)
diff --git a/lib/PublicInbox/NNTP.pm b/lib/PublicInbox/NNTP.pm
index 12ce4e68..6acfcc1b 100644
--- a/lib/PublicInbox/NNTP.pm
+++ b/lib/PublicInbox/NNTP.pm
@@ -6,7 +6,7 @@ package PublicInbox::NNTP;
use strict;
use warnings;
use base qw(PublicInbox::DS);
-use fields qw(nntpd article rbuf ng long_res);
+use fields qw(nntpd article rbuf ng);
use PublicInbox::Search;
use PublicInbox::Msgmap;
use PublicInbox::MID qw(mid_escape);
@@ -45,17 +45,7 @@ sub next_tick () {
$nextt = undef;
my $q = $nextq;
$nextq = [];
- foreach my $nntp (@$q) {
- # for request && response protocols, always finish writing
- # before finishing reading:
- if (my $long_cb = $nntp->{long_res}) {
- $nntp->write($long_cb);
- } else {
- # pipelined request, we bypassed socket-readiness
- # checks to get here:
- event_step($nntp);
- }
- }
+ event_step($_) for @$q;
}
sub requeue ($) {
@@ -633,8 +623,7 @@ sub get_range ($$) {
}
sub long_response ($$) {
- my ($self, $cb) = @_;
- die "BUG: nested long response" if $self->{long_res};
+ my ($self, $cb) = @_; # cb returns true if more, false if done
my $fd = fileno($self->{sock});
defined $fd or return;
@@ -642,36 +631,38 @@ sub long_response ($$) {
# clients should not be sending us stuff and making us do more
# work while we are stream a response to them
my $t0 = now();
- $self->{long_res} = sub {
+ my $long_cb; # DANGER: self-referential
+ $long_cb = sub {
+ # wbuf is unset or empty, here; $cb may add to it
my $more = eval { $cb->() };
if ($@ || !$self->{sock}) { # something bad happened...
- delete $self->{long_res};
-
+ $long_cb = undef;
+ my $diff = now() - $t0;
if ($@) {
err($self,
"%s during long response[$fd] - %0.6f",
- $@, now() - $t0);
- }
- if ($self->{sock}) {
- update_idle_time($self);
- requeue($self);
- } else {
- out($self, " deferred[$fd] aborted - %0.6f",
- now() - $t0);
+ $@, $diff);
}
+ out($self, " deferred[$fd] aborted - %0.6f", $diff);
+ $self->close;
} elsif ($more) { # $self->{wbuf}:
+ update_idle_time($self);
+
# no recursion, schedule another call ASAP
# but only after all pending writes are done
- update_idle_time($self);
- requeue($self);
+ my $wbuf = $self->{wbuf} ||= [];
+ push @$wbuf, $long_cb;
+
+ # wbuf may be populated by $cb, no need to rearm if so:
+ requeue($self) if scalar(@$wbuf) == 1;
} else { # all done!
- delete $self->{long_res};
+ $long_cb = undef;
res($self, '.');
out($self, " deferred[$fd] done - %0.6f", now() - $t0);
- requeue($self);
+ requeue($self) unless $self->{wbuf};
}
};
- $self->{long_res}->(); # kick off!
+ $self->write($long_cb); # kick off!
undef;
}
@@ -986,9 +977,8 @@ sub event_step {
my $t0 = now();
my $fd = fileno($self->{sock});
$r = eval { process_line($self, $line) };
- my $d = $self->{long_res} ?
- " deferred[$fd]" : '';
- out($self, "[$fd] %s - %0.6f$d", $line, now() - $t0);
+ my $pending = $self->{wbuf} ? ' pending' : '';
+ out($self, "[$fd] %s - %0.6f$pending", $line, now() - $t0);
}
return $self->close if $r < 0;
@@ -998,7 +988,7 @@ sub event_step {
# maybe there's more pipelined data, or we'll have
# to register it for socket-readiness notifications
- requeue($self) unless ($self->{long_res} || $self->{wbuf});
+ requeue($self) unless $self->{wbuf};
}
sub not_idle_long ($$) {
@@ -1012,8 +1002,7 @@ sub not_idle_long ($$) {
# for graceful shutdown in PublicInbox::Daemon:
sub busy {
my ($self, $now) = @_;
- ($self->{rbuf} ne '' || $self->{long_res} ||
- $self->{wbuf} || not_idle_long($self, $now));
+ ($self->{rbuf} ne '' || $self->{wbuf} || not_idle_long($self, $now));
}
1;
--
EW
^ permalink raw reply related [relevance 7%]
* [PATCH 00/57] ds: shrink, TLS support, buffer writes to FS
@ 2019-06-24 2:52 6% Eric Wong
2019-06-24 2:52 7% ` [PATCH 47/57] nntp: simplify long response logic and fix nesting Eric Wong
0 siblings, 1 reply; 2+ results
From: Eric Wong @ 2019-06-24 2:52 UTC (permalink / raw)
To: meta
I finally took the step of making changes to DS after
wanting to do something along these lines to Danga::Socket
for the past decade or so And down the rabitt-hole I went.
Write buffering now goes to the filesystem (which is quite fast
on Linux and FreeBSD), so memory usage with giant messages is
slightly reduced compared to before. It could be better if we
replace Email::(Simple|MIME) with something which doesn't
require slurping (but that's a big task).
Fields for read (for NNTP) and all write buffers are lazily
allocated, now, so there's some memory savings with 10K clients
Further memory savings were achieved by passing $self to
DS->write(sub {...}), eliminiating the need for most anonymous
subs.
NNTPS and NNTP+STARTTLS are now supported via public-inbox-nntpd
using the --key and --cert parameters (HTTPS coming). I'm very
happy with how I was able to reuse the write-buffering code for
TLS negotiation and not have to add additional fields or code in
hot paths.
I'm pretty happy with this, so far; but there's still plenty
left to be done. I'm not too impressed with the per-client
memory cost of IO::Socket::SSL, even with
SSL_MODE_RELEASE_BUFFERS, and will need to do further analysis
to see what memory reductions are possible.
Eric Wong (57):
ds: get rid of {closed} field
ds: get rid of more unused debug instance methods
ds: use and export monotonic now()
AddTimer: avoid clock_gettime for the '0' case
ds: get rid of on_incomplete_write wrapper
ds: lazy initialize wbuf_off
ds: split out from ->flush_write and ->write
ds: lazy-initialize wbuf
ds: don't pass `events' arg to EPOLL_CTL_DEL
ds: remove support for DS->write(undef)
http: favor DS->write(strref) when reasonable
ds: share send(..., MSG_MORE) logic
ds: switch write buffering to use a tempfile
ds: get rid of redundant and unnecessary POLL* constants
syscall: get rid of unused EPOLL* constants
syscall: get rid of unnecessary uname local vars
ds: set event flags directly at initialization
ds: import IO::KQueue namespace
ds: share watch_chg between watch_read/watch_write
ds: remove IO::Poll support (for now)
ds: get rid of event_watch field
httpd/async: remove EINTR check
spawn: remove `Blocking' flag handling
qspawn: describe where `$rpipe' come from
http|nntp: favor "$! == EFOO" over $!{EFOO} checks
ds: favor `delete' over assigning fields to `undef'
http: don't pass extra args to PublicInbox::DS::close
ds: pass $self to code references
evcleanup: replace _run_asap with `event_step' callback
ds: remove pointless exit calls
http|nntp: be explicit about bytes::length on rbuf
ds: hoist out do_read from NNTP and HTTP
nntp: simplify re-arming/requeue logic
allow use of PerlIO layers for filesystem writes
ds: deal better with FS-related errors IO buffers
nntp: wait for writability before sending greeting
nntp: NNTPS and NNTP+STARTTLS working
certs/create-certs.perl: fix cert validity on 32-bit
daemon: map inherited sockets to well-known schemes
ds|nntp: use CORE::close on socket
nntp: call SSL_shutdown in normal cases
t/nntpd-tls: slow client connection test
daemon: use SSL_MODE_RELEASE_BUFFERS
ds: allow ->write callbacks to syswrite directly
nntp: reduce allocations for greeting
ds: always use EV_ADD with EV_SET
nntp: simplify long response logic and fix nesting
ds: flush_write runs ->write callbacks even if closed
nntp: lazily allocate and stash rbuf
ci: require IO::KQueue on FreeBSD, for now
nntp: send greeting immediately for plain sockets
daemon: set TCP_DEFER_ACCEPT on everything but NNTP
daemon: use FreeBSD accept filters on non-NNTP
ds: split out IO::KQueue-specific code
ds: reimplement IO::Poll support to look like epoll
Revert "ci: require IO::KQueue on FreeBSD, for now"
ds: reduce overhead of tempfile creation
MANIFEST | 7 +
certs/.gitignore | 4 +
certs/create-certs.perl | 132 +++++++
lib/PublicInbox/DS.pm | 635 ++++++++++++------------------
lib/PublicInbox/DSKQXS.pm | 73 ++++
lib/PublicInbox/DSPoll.pm | 58 +++
lib/PublicInbox/Daemon.pm | 152 ++++++-
lib/PublicInbox/EvCleanup.pm | 20 +-
lib/PublicInbox/GitHTTPBackend.pm | 18 +-
lib/PublicInbox/HTTP.pm | 154 +++-----
lib/PublicInbox/HTTPD/Async.pm | 44 ++-
lib/PublicInbox/Listener.pm | 4 +-
lib/PublicInbox/NNTP.pm | 243 +++++-------
lib/PublicInbox/NNTPD.pm | 2 +
lib/PublicInbox/ParentPipe.pm | 3 +-
lib/PublicInbox/Qspawn.pm | 11 +-
lib/PublicInbox/Spawn.pm | 2 -
lib/PublicInbox/Syscall.pm | 27 +-
lib/PublicInbox/TLS.pm | 24 ++
script/public-inbox-nntpd | 3 +-
t/ds-poll.t | 58 +++
t/httpd-corner.t | 38 +-
t/httpd.t | 18 +
t/nntpd-tls.t | 224 +++++++++++
t/nntpd.t | 2 +
t/spawn.t | 11 -
26 files changed, 1251 insertions(+), 716 deletions(-)
create mode 100644 certs/.gitignore
create mode 100755 certs/create-certs.perl
create mode 100644 lib/PublicInbox/DSKQXS.pm
create mode 100644 lib/PublicInbox/DSPoll.pm
create mode 100644 lib/PublicInbox/TLS.pm
create mode 100644 t/ds-poll.t
create mode 100644 t/nntpd-tls.t
--
EW
^ permalink raw reply [relevance 6%]
Results 1-2 of 2 | reverse | options above
-- pct% links below jump to the message on this page, permalinks otherwise --
2019-06-24 2:52 6% [PATCH 00/57] ds: shrink, TLS support, buffer writes to FS Eric Wong
2019-06-24 2:52 7% ` [PATCH 47/57] nntp: simplify long response logic and fix nesting Eric Wong
Code repositories for project(s) associated with this public inbox
https://80x24.org/public-inbox.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).