about summary refs log tree commit homepage
path: root/lib/PublicInbox/DS.pm
DateCommit message (Collapse)
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.
2019-06-24ds: always use EV_ADD with EV_SET
kqueue EV_ONESHOT semantics are different than epoll EPOLLONESHOT. epoll only disables watches for that event while keeping the item in the rbtree for future EPOLL_CTL_MOD. kqueue removes the watch from the filter set entirely, necessitating the use of EV_ADD for future modifications.
2019-06-24ds: allow ->write callbacks to syswrite directly
We can bypass buffering when wbuf is empty when it's called from a CODE reference passed to ->write.
2019-06-24nntp: call SSL_shutdown in normal cases
This is in accordance with TLS standards and will be needed to support session caching/reuse in the future. However, we don't issue shutdown(2) since we know not to inadvertantly share our sockets with other processes.
2019-06-24ds|nntp: use CORE::close on socket
IO::Socket::SSL will try to re-bless back to the original class on TLS negotiation failure. Unfortunately, the original class is 'GLOB', and re-blessing to 'GLOB' takes away all the IO::Handle methods, because Filehandle/IO are a special case in Perl5. Anyways, since we already use syswrite() and sysread() as functions on our socket, we might as well use CORE::close(), as well (and it plays nicely with tied classes).
2019-06-24nntp: NNTPS and NNTP+STARTTLS working
It kinda, barely works, and I'm most happy I got it working without any modifications to the main NNTP::event_step callback thanks to the DS->write(CODE) support we inherited from Danga::Socket.
2019-06-24ds: deal better with FS-related errors IO buffers
Instead of ENOMEM (or fragmentation/swap storms), using tempfile buffers opens us up to filesystem and storage-related errors (e.g. ENOSPC, EFBIG, EIO, EROFS). Log these errors, drop the particular client, and try to limp by with whateve we have left.
2019-06-24allow use of PerlIO layers for filesystem writes
It may make sense to use PerlIO::mmap or PerlIO::scalar for DS write buffering with IO::Socket::SSL or similar (since we can't use MSG_MORE), so that means we need to go through buffering in userspace for the common case; while still being easily compatible with slow clients. And it also simplifies GitHTTPBackend slightly. Maybe it can make sense for HTTP input buffering, too...
2019-06-24ds: hoist out do_read from NNTP and HTTP
Both NNTP and HTTP have common needs and we can factor out some common code to make dealing with IO::Socket::SSL easier.
2019-06-24ds: remove pointless exit calls
They're never called; the only way to break out of that loop is the PostEventLoop callback.
2019-06-24ds: pass $self to code references
We can reduce the amount of short-lived anonymous subs we create by passing $self to code references.
2019-06-24ds: favor `delete' over assigning fields to `undef'
This is cleaner in most cases and may allow Perl to reuse memory from unused fields. We can do this now that we no longer support Perl 5.8; since Danga::Socket was written with struct-like pseudo-hash support in mind, and Perl 5.9+ dropped support for pseudo-hashes over a decade ago.
2019-06-24ds: get rid of event_watch field
We don't need to keep track of that field since we always know what events we're interested in when using one-shot wakeups.
2019-06-24ds: remove IO::Poll support (for now)
It may be reinstated at a later time if there's interest; but I want to be able to use one-shot notifications for certain events while retaining level-triggered notifications others. OTOH, I intend to fully support kqueue; via IO::KQueue for now, but via syscall() eventually to take advantage of the syscall reduction kevent(2) can provide over (current) epoll APIs.
2019-06-24ds: share watch_chg between watch_read/watch_write
There was much duplicate logic between watch_read and watch_write. Share that logic, and give us room to enable edge-triggered or one-shot notifications in the future.
2019-06-24ds: import IO::KQueue namespace
Make the rest of our IO::KQueue-using code less verbose and closer to the C equivalent.
2019-06-24ds: set event flags directly at initialization
We can avoid the EPOLL_CTL_ADD && EPOLL_CTL_MOD sequence with a single EPOLL_CTL_ADD.
2019-06-24ds: get rid of redundant and unnecessary POLL* constants
EPOLL* constants already match their POLL* counterparts and there's no way Linux can ever diverge or change the values of those constants. So we'll favor the EPOLL* ones since we use EPOLLEXCLUSIVE, already. For weird stuff like kqueue, we'd need to keep maintaining the mapping, anyways.
2019-06-24ds: switch write buffering to use a tempfile
Data which can't fit into a generously-sized socket buffer, has no business being stored in heap.
2019-06-24ds: share send(..., MSG_MORE) logic
No sense in having similar Linux-specific functionality in both our NNTP.pm and HTTP.pm
2019-06-24ds: remove support for DS->write(undef)
We call ->flush_write directly, now; so we can eliminate a needless check.
2019-06-24ds: don't pass `events' arg to EPOLL_CTL_DEL
There's no point in passing a mask of interesting events when removing an item from the epoll watch set.
2019-06-24ds: lazy-initialize wbuf
We don't need write buffering unless we encounter slow clients requesting large responses. So don't waste a hash slot or (empty) arrayref for it.
2019-06-24ds: split out from ->flush_write and ->write
Get rid of the confusing $need_queue variable and all the associated documentation for it. Instead, make it obvious that we're either skipping the write buffer or flushing the write buffer by splitting the sub in two.
2019-06-24ds: lazy initialize wbuf_off
Since Perl 5.10+, "fields" makes a restricted hash; not a compile-time-defined array (struct) with fixed offsets as it did in Perl <= 5.8. Thus in-use fields cost memory, and since the write buffer offset is rarely needed; stop relying on it.
2019-06-24ds: get rid of on_incomplete_write wrapper
Wrong place to be wrapping this method.