about summary refs log tree commit homepage
path: root/lib/PublicInbox/DS.pm
DateCommit message (Collapse)
2020-10-30tls: epollbit: account for miscellaneous OpenSSL errors
Apparently they happen (triggered by my -imapd instance), so bail out by closing the underlying socket rather than stopping the event loop and daemon process.
2020-09-27ds: add missing label for systems w/o EPOLLEXCLUSIVE
Oops :x
2020-09-12treewide: avoid `goto &NAME' for tail recursion
While Perl implements tail recursion via `goto' which allows avoiding warnings on deep recursion. It doesn't (as of 5.28) optimize the speed of such dispatches, though it may reduce ephemeral memory usage. Make the code less alien to hackers coming from other languages by using normal subroutine dispatch. It's actually slightly faster in micro benchmarks due to the complexity of `goto &NAME'.
2020-09-01ds: avoid unnecessary timer for waitpid
It doesn't seem necessary, since we won't call dwaitpid() until we see an EOF.
2020-09-01ds: avoid excessive queueing when reaping PIDs
We should not enqueue reap_pids() to run more than once per EventLoop iteration. We'll start reformatting reap_pids to tabs, too, since we're no longer Danga::Socket. We should also be able to remove timer usage for reaping down-the-line once we stop abusing dwaitpid() in -watch.
2020-06-28ds: add_timer: allow passing arg to callback.
This allows callers to avoid creating expensive closures. We no longer pass the `$now' value to callers, as none of the callers used it.
2020-06-28watch: wire up IMAP IDLE reapers to DS
We can avoid synchronous `waitpid(-1, 0)' and save a process when simultaneously watching Maildirs. One DS bug is fixed: ->Reset needs to clear the DS $in_loop flag in forked children so dwaitpid() fails and allows git processes to be reaped synchronously. TestCommon also calls DS->Reset when spawning new processes, since t/imapd.t uses DS->EventLoop while waiting on -watch to write.
2020-06-28ds: remove fields.pm usage
Since the removal of pseudo-hash support in Perl 5.10, the "fields" module no longer provides the space or speed benefits it did in 5.8. It also does not allow for compile-time checks, only run-time checks. To me, the extra developer overhead in maintaining "use fields" args has become a hassle. None of our non-DS-related code uses fields.pm, nor do any of our current dependencies. In fact, Danga::Socket (which DS was originally forked from) and its subclasses are the only fields.pm users I've ever encountered in the wild. Removing fields may make our code more approachable to other Perl hackers. So stop using fields.pm and locked hashes, but continue to document what fields do for non-trivial classes.
2020-06-25ds: flush_write: early return on closed socket
This quiets warnings from IMAP::fetch_blob (called via long_response) failing to access `$self->{ibx}->git' because ->{ibx} gets deleted by IMAP::close.
2020-06-21daemon: use ->can to check for IO::Socket::SSL
Doing a ref($obj) string comparison ties us to IO::Socket::SSL (and OpenSSL) In the future, we may support GnuTLS or other TLS implementations. This was already done in the IMAP code.
2020-04-09triewyde: ficks soem speling errrors
Dikshunarees R gude!
2020-01-27switch to sysseek + sysread for serving static files
The "perlio" layer doesn't do read(2) syscalls over 8192 bytes at the moment, and binmode($fh, ':unix') leaks[1]. So use sysseek and sysread for now, since I can't see retaining compatibility with PerlIO::scalar being worth the trouble. [1] http://nntp.perl.org/group/perl.perl5.porters/256918
2020-01-25ds: tmpio: store offsets per-buffer
We want to be able to inject existing file handles + offsets and even lengths into this in the future, without going through the ->getline interface[1] We also switch to using a 64K buffer size since we can safely discard whatever got truncated on write and full writes can help negotiate a larger TCP window for high-latency, high-bandwidth links. While we're at it, make it obvious that we're using O_APPEND for our tmpfile() interface so we can seek freely for reading while the writer always prints to the end of the file. [1] the getline interface for serving static files may result in us buffering on-FS data into another temporary file, which is a waste.
2020-01-13ds: flatten $EXPMAP, delete entries on close
We can reduce the amount of small arrayrefs in memory by flattening $EXPMAP. This forces us to properly clean up references during deferred close handling, so NNTP (and soon HTTP) connections no longer linger until expiry.
2020-01-13ds: rely on autovivification for $later_queue
No reason to have an empty arrayref lying around when not everybody needs it. Re-indent the later-related subs since we're changing a bunch of lines, anyways.
2020-01-13ds: rely on autovivication for waitpid bits
No need to create an arrayref until we need it, and fix up a comment while we're in the area. Some aesthetic changes while we're at it: - Rename $WaitPids to $wait_pids to make it clear this is unique to our implementation and not in Danga::Socket. - rewrite dwaitpid() to reduce indentation level
2020-01-13ds: rely on autovivification for nextq
Another place we can delay creating arrays until needed.
2020-01-13ds|http|nntp: simplify {wbuf} population
We can rely on autovification to turn `undef' value of {wbuf} into an arrayref. Furthermore, "push" returns the (new) size of the array since at least Perl 5.0 (I didn't look further back), so we can use that return value instead of calling "scalar" again.
2020-01-13ds: guard ToClose against DESTROY side-effects
This does not affect our current code, but theoretically a DESTROY callback could call PublicInbox::DS::close to enqueue elements into the ToClose array. So take a similar strategy as we do with other queues (e.g. $nextq) by swapping references to arrays, rather than operating on the array itself. Since close operations are relatively rare, we can rely on auto-vivification via "push" ops to create the array on an as-needed basis. Since we're in the area, clean up the PostLoopCallback invocation to use the ternary operator rather than a confusing (to me) combination of statements. Finally, add a prototype to strengthen compile-time checking, and move it in front of our only caller to make use of the prototype.
2020-01-13ds: remove Timer->cancel and Timer class+bless
It doesn't seem needed at the moment, and we can re-add it in the future if needed.
2020-01-13ds: add an in_loop() function for Inbox.pm use
Inbox.pm accessing the $in_loop variable directly raises warnings when Inbox is loaded without DS.
2020-01-13ds: add_timer: rename from AddTimer, remove a parameter
The class parameter is pointless, especially for an internal sub which only has one external caller in a test. Add a sub prototype while we're at it to get some compile time checking.
2020-01-13ds: new: avoid redundant check, make clobbering fatal
"fileno(undef)" already dies under "use strict", so there's no need to check for it ourselves. As far as "fileno($closed_io)" or "fileno($fake_io)" goes, we'll let epoll_ctl detect the error, instead. Our design should make DescriptorMap entries impossible to clobber, so make it fatal via confess in case it does happen, because inadvertantly clobbering a FD would be very bad. While we're at it, remove a redundant return statement and rely on implicit returns.
2020-01-11doc: technical/ds.txt: describe PublicInbox::DS divergences
Danga::Socket 1.62 was released a few months back and the maintainer indicated it would be the last release. We've diverged significantly in incompatible ways... While most of this should've already been documented in commit messages, putting it all into one document could make it easier-to-digest. It's also a strange design for anybody used to conventional event loops. Maybe this is an unconventional project :P
2020-01-06treewide: "require" + "use" cleanup and docs
There's a bunch of leftover "require" and "use" statements we no longer need and can get rid of, along with some excessive imports via "use". IO::Handle usage isn't always obvious, so add comments describing why a package loads it. Along the same lines, document the tmpdir support as the reason we depend on File::Temp 0.19, even though every Perl 5.10.1+ user has it. While we're at it, favor "use" over "require", since it it gives us extra compile-time checking.
2019-12-28ds: use MSG_MORE when wbuf is empty during long responses
HTTP::getline_pull and NNTP::long_step will both populate {wbuf} manually to avoid recursion, so we need to account for an empty-but-present {wbuf} while dispatching msg_more().
2019-12-15inbox: fix periodic git process cleanup
We need to use $PublicInbox::DS::in_loop instead of ::running(). The latter is not valid for systems with signalfd or kqueue and is now gone, completely. Not needing periodic cleanups at all to deal with unlinked pack indices will be a tougher task...
2019-12-14ds: move NNTP-only expiration code into DS
We'll be supporting idle timeout for the HTTP code in the future to deal directly with Internet-exposed clients w/o Varnish or nginx.
2019-12-14ds: move EvCleanup code into DS
EvCleanup only existed since Danga::Socket was a separate component, and cleanup code belongs with the event loop.
2019-12-11ds: ->Reset initializes $nextq
I haven't noticed this being a problem in practice, but be consistent with the rest of the singleton stuff. Since we always call Reset() at load time, only do initialization in that sub and not at declaration.
2019-11-27httpd|nntpd: avoid missed signal wakeups
Our attempt at using a self-pipe in signal handlers was ineffective, since pure Perl code execution is deferred and Perl doesn't use an internal self-pipe/eventfd. In retrospect, I actually prefer the simplicity of Perl in this regard... We can use sigprocmask() from Perl, so we can introduce signalfd(2) and EVFILT_SIGNAL support on Linux and *BSD-based systems, respectively. These OS primitives allow us to avoid a race where Perl checks for signals right before epoll_wait() or kevent() puts the process to sleep. The (few) systems nowadays without signalfd(2) or IO::KQueue will now see wakeups every second to avoid missed signals.
2019-09-26ds: workaround a memory leak in Perl 5.16.x
The perl-5.16.3-294.el7_6 RPM package on RHEL/CentOS 7 is affected by a memory leak in Perl when calling `ref' on blessed references. This resulted in a very slow leak that manifests more quickly with a nonstop "git fetch" loop. Use Scalar::Util::blessed to work around the issue. Tested overnight on a CentOS 7 VM. cf. https://rt.perl.org/Public/Bug/Display.html?id=114340
2019-09-14tmpfile: support O_APPEND and use it in DS::tmpio
Might as well share some code for temporary file creation
2019-07-08Merge remote-tracking branch 'origin/nntp-compress'
* origin/nntp-compress: nntp: improve error reporting for COMPRESS nntp: reduce memory overhead of zlib nntp: support COMPRESS DEFLATE per RFC 8054 nntp: move LINE_MAX constant to the top nntp: use msg_more as a method
2019-07-08ds: use WNOHANG with waitpid if inside event loop
While we're usually not stuck waiting on waitpid after seeing a pipe EOF or even triggering SIGPIPE in the process (e.g. git-http-backend) we're reading from, it MAY happen and we should be careful to never hang the daemon process on waitpid calls. v2: use "eq" for string comparison against 'DEFAULT'
2019-07-06nntp: reduce memory overhead of zlib
Using Z_FULL_FLUSH at the right places in our event loop, it appears we can share a single zlib deflate context across ALL clients in a process. The zlib deflate context is the biggest factor in per-client memory use, so being able to share that across many clients results in a large memory savings. With 10K idle-but-did-something NNTP clients connected to a single process on a 64-bit system, TLS+DEFLATE used around 1.8 GB of RSS before this change. It now uses around 300 MB. TLS via IO::Socket::SSL alone uses <200MB in the same situation, so the actual memory reduction is over 10x. This makes compression less efficient and bandwidth increases around 45% in informal testing, but it's far better than no compression at all. It's likely around the same level of compression gzip gives on the HTTP side. Security implications with TLS? I don't know, but I don't really care, either... public-inbox-nntpd doesn't support authentication and it's up to the client to enable compression. It's not too different than Varnish caching gzipped responses on the HTTP side and having responses go to multiple HTTPS clients.
2019-06-30ds: fix return values of do_read and *_tls_step
We need to ensure all these subroutines return false on incomplete.
2019-06-30ds: rely on refcounting to close descriptors
Since we have EPOLL_CTL_DEL implemented for the poll(2) and kqueue backends, we can rely on Perl refcounting to gently close(2) the underlying file descriptors as references get dropped. This may be beneficial in the future if we want to drop a descriptor from the event loop without actually closing it.
2019-06-29httpd/async: switch to buffering-as-fast-as-possible
With DS buffering to a temporary file nowadays, applying backpressure to git-http-backend(1) hurts overall memory usage of the system. Instead, try to get git-http-backend(1) to finish as quickly as possible and use edge-triggered notifications to reduce wakeups on our end.
2019-06-29ds: consolidate IO::Socket::SSL checks
We need to be careful about handling EAGAIN on write(2) failures deal with SSL_WANT_READ vs SSL_WANT_WRITE as appropriate.
2019-06-29ds: handle deferred DS->close after timers
Our hacks in EvCleanup::next_tick and EvCleanup::asap were due to the fact "closed" sockets were deferred and could not wake up the event loop, causing certain actions to be delayed until an event fired. Instead, ensure we don't sleep if there are pending sockets to close. We can then remove most of the EvCleanup stuff While we're at it, split out immediate timer handling into a separate array so we don't need to deal with time calculations for the event loop.
2019-06-29http: use requeue instead of watch_in1
Don't use epoll or kqueue to watch for anything unless we hit EAGAIN, since we don't know if a socket is SSL or not.
2019-06-29ds: move requeue logic over from NNTP
We'll be reusing requeue in other places to reduce trips to the kernel to retrieve "hot" descriptors.
2019-06-29ds: share lazy rbuf handling between HTTP and NNTP
Doing this for HTTP cuts the memory usage of 10K idle-after-one-request HTTP clients from 92 MB to 47 MB. The savings over the equivalent NNTP change in commit 6f173864f5acac89769a67739b8c377510711d49, ("nntp: lazily allocate and stash rbuf") seems down to the size of HTTP requests and the fact HTTP is a client-sends-first protocol where as NNTP is server-sends-first.
2019-06-26ds: cleanup poll test and avoid clobbering imports
On Linux systems with epoll support, we don't want to be clobbering defined subs in the t/ds-poll.t test; so use OO ->method dispatch instead and require users to explicitly import subs via EXPORT_OK.
2019-06-24ds: ->write must not clobber empty wbuf array
We need to account for ->write(CODE) calls doing ->write(SCALARREF), otherwise flush_write may see the wrong ->{wbuf} field.
2019-06-24ds: reduce overhead of tempfile creation
We end up buffering giant things to the FS sometimes, and open() is not a cheap syscall; so being forced to do it twice to get a file description with O_APPEND is gross when we can just use O_EXCL ourselves and loop on EEXIST.
2019-06-24ds: reimplement IO::Poll support to look like epoll
At least the subset of epoll we use. EPOLLET might be difficult to emulate if we end up using it.
2019-06-24ds: split out IO::KQueue-specific code
We don't need to code multiple event loops or have branches in watch() if we can easily make the IO::KQueue-based interface look like our lower-level epoll_* API.
2019-06-24ds: flush_write runs ->write callbacks even if closed
We may need to rely on cleanup code running in enqueued callbacks, so ensure we call it when flush_write happens.