user/dev discussion of public-inbox itself
 help / color / Atom feed
* [PATCH] listener: support publicinboxdaemon.multiaccept in config
@ 2019-05-13  2:56 Eric Wong
  0 siblings, 0 replies; only message in thread
From: Eric Wong @ 2019-05-13  2:56 UTC (permalink / raw)
  To: meta

Similar to the nginx "multi_accept" parameter, this allows
single-worker deployments to accept() multiple clients whenever
the listen socket reports readiness via epoll_wait/poll/kevent.

Unlike nginx, we also parse an integer parameter to determine
how many times in a row we call accept().

Single-process deployments with no shared sockets can safely set
"true", here; meaning accept() will be retried indefinitely as
long as there are clients connecting.

Multi-process deployments with many workers should leave this at
the default ("false", or "0") or try a small positive integer.
Using large values with many workers can lead to imbalanced
connections between workers.

We also can't reliably detect whether a listen socket is shared
between multiple processes.  Sockets can be shared via socket
activation in systemd (or similar) and TTIN/TTOU signals can
adjust worker count of our daemons.
---
 lib/PublicInbox/Config.pm   | 29 +++++++++++++++++++++++++++--
 lib/PublicInbox/Listener.pm | 14 +++++++-------
 2 files changed, 34 insertions(+), 9 deletions(-)

diff --git a/lib/PublicInbox/Config.pm b/lib/PublicInbox/Config.pm
index 09f9179..938685a 100644
--- a/lib/PublicInbox/Config.pm
+++ b/lib/PublicInbox/Config.pm
@@ -13,6 +13,9 @@ use warnings;
 require PublicInbox::Inbox;
 use PublicInbox::Spawn qw(popen_rd);
 
+my $FALSE_RE = qr/\A(?:false|no|off|0)\z/;
+my $TRUE_RE = qr/\A(?:true|yes|on|1)\z/;
+
 sub _array ($) { ref($_[0]) eq 'ARRAY' ? $_[0] : [ $_[0] ] }
 
 # returns key-value pairs of config directives in a hash
@@ -54,6 +57,28 @@ sub new {
 		$self->{css} = _array($css);
 	}
 
+	if (defined(my $ma = $self->{'publicinboxdaemon.multiaccept'})) {
+		# multiaccept=0        accept once (default)
+		# multiaccept=(1..X)   accept an extra X times (or EAGAIN)
+		# multiaccept=true     accept until EAGAIN
+		if ($ma =~ $TRUE_RE) {
+			$ma = -1 if $ma ne '1';
+		} elsif ($ma =~ $FALSE_RE) {
+			$ma = 0;
+		} elsif ($ma =~ /\A(\d+)\z/) {
+			# positive integer value, leave as-is
+		} else {
+			warn
+"publicinboxdaemon.multiaccept=$ma not understood\n";
+			$ma = undef;
+		}
+
+		defined($ma) and eval {
+			no warnings 'once';
+			$PublicInbox::Listener::multi_accept = $ma;
+		};
+	}
+
 	$self;
 }
 
@@ -379,9 +404,9 @@ sub _fill {
 	foreach my $k (qw(obfuscate)) {
 		my $v = $self->{"$pfx.$k"};
 		defined $v or next;
-		if ($v =~ /\A(?:false|no|off|0)\z/) {
+		if ($v =~ $FALSE_RE) {
 			$ibx->{$k} = 0;
-		} elsif ($v =~ /\A(?:true|yes|on|1)\z/) {
+		} elsif ($v =~ $TRUE_RE) {
 			$ibx->{$k} = 1;
 		} else {
 			warn "Ignoring $pfx.$k=$v in config, not boolean\n";
diff --git a/lib/PublicInbox/Listener.pm b/lib/PublicInbox/Listener.pm
index a75a6fd..c4d1a55 100644
--- a/lib/PublicInbox/Listener.pm
+++ b/lib/PublicInbox/Listener.pm
@@ -10,6 +10,8 @@ use Socket qw(SOL_SOCKET SO_KEEPALIVE IPPROTO_TCP TCP_NODELAY);
 use fields qw(post_accept);
 require IO::Handle;
 
+our $multi_accept = 0;
+
 sub new ($$$) {
 	my ($class, $s, $cb) = @_;
 	setsockopt($s, SOL_SOCKET, SO_KEEPALIVE, 1);
@@ -26,16 +28,14 @@ sub new ($$$) {
 sub event_read {
 	my ($self) = @_;
 	my $sock = $self->{sock};
+	my $n = $multi_accept;
+	my ($addr, $c);
 
-	# no loop here, we want to fairly distribute clients
-	# between multiple processes sharing the same socket
-	# XXX our event loop needs better granularity for
-	# a single accept() here to be, umm..., acceptable
-	# on high-traffic sites.
-	if (my $addr = accept(my $c, $sock)) {
+	do {
+		$addr = accept($c, $sock) or return;
 		IO::Handle::blocking($c, 0); # no accept4 :<
 		$self->{post_accept}->($c, $addr, $sock);
-	}
+	} while ($n--);
 }
 
 1;
-- 
EW


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, back to index

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-05-13  2:56 [PATCH] listener: support publicinboxdaemon.multiaccept in config Eric Wong

user/dev discussion of public-inbox itself

Archives are clonable:
	git clone --mirror http://public-inbox.org/meta
	git clone --mirror http://czquwvybam4bgbro.onion/meta
	git clone --mirror http://hjrcffqmbrq6wope.onion/meta
	git clone --mirror http://ou63pmih66umazou.onion/meta

Newsgroups are available over NNTP:
	nntp://news.public-inbox.org/inbox.comp.mail.public-inbox.meta
	nntp://ou63pmih66umazou.onion/inbox.comp.mail.public-inbox.meta
	nntp://czquwvybam4bgbro.onion/inbox.comp.mail.public-inbox.meta
	nntp://hjrcffqmbrq6wope.onion/inbox.comp.mail.public-inbox.meta
	nntp://news.gmane.org/gmane.mail.public-inbox.general

 note: .onion URLs require Tor: https://www.torproject.org/

AGPL code for this site: git clone https://public-inbox.org/ public-inbox