dgc / software / HAPPYMAIL for Qpopper
dgc / software / HAPPYMAIL for Qpopper


Background
In the University of Chicago's central mail services group, we use Qualcomm's Qpopper [1] POP3 server (along with UW-IMAP) to provide mail to approximately 25,000 clients. We find that there's always a subset of users who persistently, almost perniciously, will fetch their mail as often as possible, whether it's needed now or not -- and, to make things worse, have their mail clients set to "leave mail on server". With Qpopper particularly, this makes performance drag unbearably: with several hundred users popping regularly and frequently from inboxes stuffed with 25 to 100 MB of messages, there's a lot of unneeded spool I/O that degrades the system for all those other customers using it more responsibly.

We began by enabling server mode in qpopper, but that wasn't enough -- some people were simple checking mail much, much too often. So we decided that we needed more serious rate limiting, but qpopper didn't have it.


Implementation
The HAPPYMAIL patch [2] provides this. It allocates a 256K shared memory segment — essentially, an array of 2^16 32-bit integers — that is known to each instance of popper. Each time a user authenticates successfully, the user's UID and the current time are associated into the shared memory segment. When the user next authenticates, the current time is compared to the timestamp in the shared memory. If the user hasn't waited "long enough", he is rejected with an error message such as:

-ERR [AUTH] Please wait at least 15 minutes between checks

The shared memory segment is good because it avoids filesystem access, and there's never any contention for it between popper instances because each popper only looks at the 4 bytes assigned to the UID it's serving. Failed authentications don't count against you — the check/store doesn't occur until you've successfully authenticated, but before any mailboxes are copied or locked.


Configuration
The check-wait period is not only configurable; it can be dynamically calculated based on the size of the authenticating user's inbox. There are four parameters that go into this computation. All are configurable either in the popper.conf file or on the popper command line.

happymail-base The mininum number of seconds a user must wait between mail checks, regardless of how much mail he has.
happymail-free How many bytes of mail a user may have (in the inbox) before additional wait time is required.
happymail-rate-seconds The number of seconds added to a user's wait time for each <happymail-rate-bytes> bytes of mail above <happymail-free>.
happymail-rate-bytes The number of bytes above <happymail-free> that will trigger an additional <happymail-rate-seconds> of wait time.

So, suppose I've set these parameters in popper.conf:


set happymail-base=300			# 5 minutes
set happymail-free=10485760		# 10 MB
set happymail-rate-seconds=60		# 1 minute
set happymail-rate-bytes=5242880	# 5 MB

In this example, no user will be allowed to check more often than once each 5 minutes (happymail-base); each user may have 10 MB (happymail-free) of mail before additional time is levied; and above that 10 MB, users will be required to wait an additional minute (happymail-rate-seconds) per 5 megabytes (happymail-rate-bytes) in the inbox. So a person with 44 MB of mail would wait 11 minutes between mail checks — that's 5m + (1m * ( (44mb-10mb) / 5mb )).

If you just want to make everyone wait 15 minutes between checks, regardless of how much mail they have, you'd just set happymail-base to 900, and the others to zero.


Inquiry and Adjustment
This system has worked well — it immediately relieved a lot of pressure on our heavily-laden mail server. But we anticipated that there would be a lot of calls to our support center over this, as well as an occasional need to allow someone an immediate mail check even though the timer would otherwise not allow it. So I wrote a supplementary program called HAPPYTOOL [3] . HAPPYTOOL lets you inquire the last mail check time of:

Any users found can have their timers reset, so that they are immediately able to check mail again.

For more information on this, see the HAPPYTOOL README file [4] .


Miscellany

  • After applying the patch, you need to regenerate the configure script with autoconf, and run configure with the --enable-happymail option. So, a typical installation for me looks like:
    shell$
    gzip -dc < qpopper4.0.5.tar.gz | tar xf -
    cd qpopper4.0.5
    patch -p1 < /path/to/qpopper4.0.5-uchi-happymail-pl8.patch
    autoconf
    ./configure --my-usual-options --enable-happymail
    make
    

  • There's one other feature in the HAPPYMAIL code that's not particularly related to rate limiting, but that we added as a cattle prod to performance. The block size used for client mailbox copies is system-dependent, and can be small enough to be rather slow. If you have plenty of memory, though, there's no particular reason to keep it small. So, with HAPPYMAIL, you can set happymail-bufsiz in the popper.conf to configure a larger buffer size for maildrop copies.

  • I have a segment of a popper.conf [5] that illustrates and documents all five new parameters.

  • [March 2003] We've now been using the HAPPYMAIL code without change in qpopper-4.0.3, qpopper-4.0.4, and qpopper-4.0.5 for about 10 months, since May, 2002. It's kept our goose from cooking several times now: because the popper.conf can be tweaked on the fly, we can adjust the penalty rate as we need to, according to the current condition of mail service.

  • [pl7: 23 May 2003] With help from Tim Meader, I've uncovered some latent problems in command-line parsing of the -h option and in the interaction of the HAPPYMAIL checks with a certain other popper setting. Patchlevel 7 fixes these, improves the notational support in the popper config file (you can now use units in the config file as well as the command line; see the example popper.conf), and adds HAPPYMAIL version and configuration information to the CAPA output. There might be a couple of other minor bugfixes/tweaks, but nothing of operational significance.

  • [pl8: 23 May 2003] Updated the documentation in the popper.conf.happymail file. Added happymail-max setting and associated notation. Fixed dumb bug with (happymail-free) checking that made the whole thing basically not work, but fortunately only existed in pl7 for a few hours. (Grr.) Improved CAPA's X-HAPPYMAIL notation. Made happymail-bufsiz take units notation, or assume small integers to be kilobytes (as previously documented). This lasted us a year without patching; I really hope I'm done now.


    Links

      1. Qpopper: The Qualcomm POP3 daemon.
      2. HAPPYMAIL patch: HAPPYMAIL patchlevel 8 [pl6] [pl7] .
      3. HAPPYTOOL: A query/reset utility for HAPPYMAIL.
      4. The HAPPYTOOL README file.
      5. Popper.conf: Qpopper configuration parameters relevant to HAPPYMAIL.

    $Id: index.html4,v 1.3 2003/10/16 19:34:52 dgc Exp $
    Mail: dgc@uchicago.edu