Severe issue with high-volume IMAP server stalling, possible patch
ellie timoney
ellie at fastmail.com
Mon Apr 11 19:32:29 EDT 2016
Hi Jens,
> after some weeks of running a stable service with this patch, I'd like
> to propose merging this (we'd really like to switch back to an unpatched
> Cyrus release in future).
This would be good. Can you attach it as a patch file please? CC me
and I'll get it merged.
Thanks for your hard work figuring this out. :)
Cheers,
ellie
On Mon, Apr 11, 2016, at 05:52 PM, Jens Erat via Cyrus-devel wrote:
> Hi all,
>
> after some weeks of running a stable service with this patch, I'd like
> to propose merging this (we'd really like to switch back to an unpatched
> Cyrus release in future). This is not only an issue to us, but we know
> some other universities running similar Solaris setups and waiting to
> upgrade to Cyrus 2.5 because of our issues.
>
> This is only an issue with Cyrus IMAP 2.5 and probably 3.0; Cyrus 2.4
> also does not properly repeat select if it failed, but it's not an issue
> here as interrupts can occur anywhere.
>
> We didn't have those master loop hick ups any more. In fact, the while
> loop even seems superfluous; just running myselect again if `r==0` would
> be fine, but this might be different with other combinations of
> operating system kernels, C libraries and compilers, and the loop seems
> the safer thing to do. The limit of 100 retries seems a little bit
> arbitrary, but I can't really argument for any number, something around
> five should probably also be fine to cover all scenarios that are not
> completely broken, anyway.
>
> We dumped the interrupted and again counters each time the master loop
> ran (and several other metrics), and never had more than one EINTR or
> EAGAIN in a row (so the next `myselect` immediately returned the FDSET).
>
> Kind regards from Lake Constance, Germany,
> Jens
>
> Am 10.02.2016 um 13:39 schrieb Jens Erat via Cyrus-devel:
> > Hi Bron,
> >
> > digging deeper, we realized there are still issues with the loop you
> > proposed. `select` will only decrement tvptr on Linux (and maybe _some_
> > other systems, definitely not all of them), pselect will never do -- you
> > might end up waiting forever here if nothing happens.
> >
> > On the other hand, we partially got a grasp of understanding what was
> > going on. Because when `pselect` is available, signalling was disabled
> > but during `pselect`, thus with some weird combination of load on the
> > different services, each `pselect` returned with `EINTR` and the message
> > handling code was never reached
> >
> > We ended up putting the whole deadline calculation in a queue, and have
> > this code live at the moment:
> >
> > int interrupted = 0;
> > int again = 0;
> > do {
> > /* how long to wait? - do now so that any scheduled wakeup
> > * calls get accounted for*/
> > gettimeofday(&now, 0);
> > tvptr = NULL;
> > if (schedule && !in_shutdown) {
> > double delay = timesub(&now, &schedule->mark);
> > if (!interrupted && delay > 0.0) {
> > timeval_set_double(&tv, delay);
> > }
> > else {
> > tv.tv_sec = 0;
> > tv.tv_usec = 0;
> > }
> > tvptr = &tv;
> > }
> >
> > #if defined(HAVE_UCDSNMP) || defined(HAVE_NETSNMP)
> > if (tvptr == NULL) blockp = 1;
> > snmp_select_info(&maxfd, &rfds, tvptr, &blockp);
> > #endif
> > errno = 0;
> > r = myselect(maxfd, &rfds, NULL, NULL, tvptr);
> >
> > if (r == -1) switch(errno) {
> > /* Try again to get valid rfds, this time without blocking so we
> > * will definitely process messages without getting interrupted
> > * again. */
> > case EINTR:
> > interrupted++;
> > if (interrupted > 100) {
> > syslog(LOG_WARNING, "Repeatedly interrupted, too
> > many signals?");
> > /* Fake a timeout */
> > r = 0;
> > FD_ZERO(&rfds);
> > }
> > break;
> > /* Try again. */
> > case EAGAIN: again++; break;
> > /* uh oh */
> > default: fatalf(1, "select failed: %m");
> > }
> > } while (r == -1);
> >
> > We also dump interrupted and again together with some other variables at
> > the end of each loop. The server is running fine for two days now, and
> > the dumped information looks fine (we never experienced any delays >4
> > seconds since patching the server, we often found the master loop not
> > completely running for minutes). I will report again if we didn't suffer
> > from any interruptions for the next one or two weeks.
> >
> > Some remarks to the code:
> >
> > - We need to update the current timestamp `now` for decrementing the
> > passed time.
> > - `snmp_select_info` or's its FDs to rfds; thus it may be run repeatedly
> > to update `tvptr` and not run over any SNMP events.
> > - We are compiling without SNMP, thus this code is untested.
> > - If interrupted, we try again to receive FDs, but without waiting
> > (tvptr set to zero). Because of this, no large interruptions should
> > occur -- but we also won't skip over message handling when flooded with
> > signals.
> > - After 100 retries after being interrupted, we empty rfds and continue
> > to work, but print a warning. This is probably subject of discussion and
> > _much_ too large: we're currenlty dumping the number of interruptions,
> > and never had more than one at all before catching file descriptors (or
> > none).
> > - `int again` can and should be removed for live code, does not seem
> > relevant at all (but for our own debug purpose).
> >
> > Kind regards from Lake Constance, Germany,
> > Jens
> >
> >
> > Am 06.02.2016 um 13:00 schrieb Bron Gondwana:
> >> I could buy repeating the whole thing for EINTR but not for EAGAIN. Obviously everything is fine under non-heavy load no matter what.
> >>
> >> I'm pretty sure your patch is wrong, because it will process the fdset that the select was supposed to fill even if the select returned an error.
> >>
> >> You may have convinced me that my patch is wrong as well, and that we have to always check for the in_shutdown and sigquit magic in that tight loop in case it was a signal that needs to be responded to.
> >>
> >> Bron.
> >>
> >> On Sat, Feb 6, 2016, at 20:52, Jens Erat wrote:
> >>> Hi Bron,
> >>>
> >>> thank you for the fast reply.
> >>>
> >>>> One thing I would suggest is running something like nginx in front of
> >>> Cyrus, even if you only need it for authentication checks.
> >>>
> >>> We'll discuss this internally.
> >>>
> >>>> I believe this is the correct patch. If the select was interrupted
> >>> then it needs to be re-run, but there is no need to repeat all the other
> >>> work, so:
> >>>>
> >>>> - r = myselect(maxfd, &rfds, NULL, NULL, tvptr);
> >>>> - if (r == -1 && errno == EAGAIN) continue;
> >>>> - if (r == -1 && errno == EINTR) continue;
> >>>> + do {
> >>>> + r = myselect(maxfd, &rfds, NULL, NULL, tvptr);
> >>>> + } while (r == -1 && (errno == EAGAIN || errno == EINTR));
> >
>
> --
> Jens Erat
> Universität Konstanz
> Kommunikations-, Infomations-, Medienzentrum (KIM)
> Abteilung Basisdienste
> D-78457 Konstanz
> Mail: jens.erat at uni-konstanz.de
>
> Email had 1 attachment:
> + smime.p7s
> 7k (application/pkcs7-signature)
More information about the Cyrus-devel
mailing list