Fix a regression introduce in 2.70 when the mogilefs server was

rewritten to be non-blocking: immediate write after connect may fail
if the connection is not ready yet.

Approved by:	bdrewery (mentor)
This commit is contained in:
Mikolaj Golub 2013-11-22 19:11:55 +00:00
parent 9937b95e54
commit a09a390ce9
Notes: svn2git 2021-03-31 03:12:20 +00:00
svn path=/head/; revision=334606
2 changed files with 74 additions and 0 deletions

View file

@ -3,6 +3,7 @@
PORTNAME= MogileFS-Server PORTNAME= MogileFS-Server
PORTVERSION= 2.70 PORTVERSION= 2.70
PORTREVISION= 1
CATEGORIES= sysutils perl5 CATEGORIES= sysutils perl5
MASTER_SITES= CPAN MASTER_SITES= CPAN
MASTER_SITE_SUBDIR= CPAN:DORMANDO MASTER_SITE_SUBDIR= CPAN:DORMANDO

View file

@ -0,0 +1,73 @@
Eric Wong: connection/poolable: do not write before event_write
Blindly attempting to write to a socket before a TCP connection can be
established returns EAGAIN on Linux, but not on FreeBSD 8/9. This
causes Danga::Socket to error out, as it won't attempt to buffer on
anything but EAGAIN on write() attempts.
Now, we buffer writes explicitly after the initial socket creation and
connect(), and only call Danga::Socket::write when we've established
writability. This works on Linux, too, and avoids an unnecessary
syscall in most cases.
Reported-by: Alex Yakovenko <aleksey.yakovenko@gmail.com>
--- lib/MogileFS/Connection/Poolable.pm.orig 2013-08-19 05:52:33.000000000 +0300
+++ lib/MogileFS/Connection/Poolable.pm 2013-11-20 22:57:31.000000000 +0200
@@ -13,6 +13,7 @@ use fields (
'mfs_expire_cb', # Danga::Socket::Timer callback
'mfs_requests', # number of requests made on this object
'mfs_err', # used to propagate an error to start()
+ 'mfs_writeq', # arrayref if connecting, undef otherwise
);
use Socket qw(SO_KEEPALIVE);
use Time::HiRes;
@@ -27,6 +28,9 @@ sub new {
$self->{mfs_hostport} = [ $ip, $port ];
$self->{mfs_requests} = 0;
+ # newly-created socket, we buffer writes until event_write is triggered
+ $self->{mfs_writeq} = [];
+
return $self;
}
@@ -53,6 +57,38 @@ sub mark_idle {
$self->{mfs_requests}++;
}
+sub write {
+ my ($self, $arg) = @_;
+ my $writeq = $self->{mfs_writeq};
+
+ if (ref($writeq) eq "ARRAY") {
+ # if we're still connecting, we must buffer explicitly for *BSD
+ # and not attempt a real write() until event_write is triggered
+ push @$writeq, $arg;
+ $self->watch_write(1); # enable event_write triggering
+ 0; # match Danga::Socket::write return value
+ } else {
+ $self->SUPER::write($arg);
+ }
+}
+
+# Danga::Socket will trigger this when a socket is writable
+sub event_write {
+ my ($self) = @_;
+
+ # we may have buffered writes in mfs_writeq during non-blocking connect(),
+ # this is needed on *BSD but unnecessary (but harmless) on Linux.
+ my $writeq = delete $self->{mfs_writeq};
+ if ($writeq) {
+ $self->watch_write(0); # ->write will re-enable if needed
+ foreach my $queued (@$writeq) {
+ $self->write($queued);
+ }
+ } else {
+ $self->SUPER::event_write();
+ }
+}
+
# the request running on this connection is retryable if this socket
# has ever been marked idle. The connection pool can never be 100%
# reliable for detecting dead sockets, and all HTTP requests made by