]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.5-20071129
authorWietse Venema <wietse@porcupine.org>
Thu, 29 Nov 2007 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:33:29 +0000 (06:33 +0000)
35 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/README_FILES/SCHEDULER_README
postfix/RELEASE_NOTES
postfix/WISHLIST
postfix/html/SCHEDULER_README.html
postfix/html/master.5.html
postfix/html/oqmgr.8.html
postfix/html/pipe.8.html
postfix/html/postconf.5.html
postfix/html/qmgr.8.html
postfix/html/spawn.8.html
postfix/man/man5/master.5
postfix/man/man5/postconf.5
postfix/man/man8/oqmgr.8
postfix/man/man8/qmgr.8
postfix/mantools/postlink
postfix/proto/SCHEDULER_README.html
postfix/proto/master
postfix/proto/postconf.proto
postfix/src/global/Makefile.in
postfix/src/global/header_body_checks.c
postfix/src/global/mail_conf.h
postfix/src/global/mail_conf_str.c
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/oqmgr/qmgr.c
postfix/src/pipe/pipe.c
postfix/src/qmgr/Makefile.in
postfix/src/qmgr/qmgr.c
postfix/src/qmgr/qmgr.h
postfix/src/qmgr/qmgr_feedback.c [new file with mode: 0644]
postfix/src/qmgr/qmgr_queue.c
postfix/src/qmgr/qmgr_transport.c
postfix/src/spawn/spawn.c

index 96fd9979929befc4080e796972e3e505dc12b442..2893e6b51e533a8a770ce0d0011deadadca4b634 100644 (file)
 -TEXPAND_ATTR
 -TFILE
 -TFORWARD_INFO
--THBC_OUTPUT_CALL_BACKS
 -THBC_ACTION_CALL_BACKS
 -THBC_CHECKS
 -THBC_MAP_INFO
+-THBC_OUTPUT_CALL_BACKS
 -THBC_TEST_CONTEXT
 -THEADER_OPTS
 -THEADER_TOKEN
 -TPLPGSQL
 -TPOST_MAIL_STATE
 -TQMGR_ENTRY
+-TQMGR_FEEDBACK
 -TQMGR_JOB
 -TQMGR_MESSAGE
 -TQMGR_PEER
index 7a886a0b10313fa64878175a80e23d47507c6ef4..c07c9819003f42af340a24454a2982f1de7102f4 100644 (file)
@@ -5215,7 +5215,7 @@ Apologies for any names omitted.
 
 20010525
 
-       Portability: gcc 2.6.3 does not have __attribute__ (Clive
+       Portability: gcc 2.6.3 does not have _attribute__ (Clive
        Jones, dgw.co.uk). File: util/sys_defs.h.
 
        Bugfix: the SMTP and LMTP clients claimed that a queue file
@@ -12203,7 +12203,7 @@ Apologies for any names omitted.
 
 20060516
 
-       Portability: __float80 alignment, by Albert Chin.  File:
+       Portability: _float80 alignment, by Albert Chin.  File:
        util/sys_defs.h.
 
        Further testing of Milter support uncovered typos; a missing
@@ -13829,16 +13829,11 @@ Apologies for any names omitted.
        late October 2007. To establish a baseline for further
        improvement, Wietse implemented a few simple mechanisms.
 
-       Configuration parameters: qmgr_concurrency_feedback_debug,
-       qmgr_negative_concurrency_feedback_hysteresis,
-       qmgr_negative_concurrency_feedback_style,
-       qmgr_positive_concurrency_feedback_hysteresis,
-       qmgr_positive_concurrency_feedback_style, qmgr_sacrifice_cohorts.
-       See postconf(5) for detailed information. Right now, the
-       defaults are compatible with older Postfix versions.  After
-       further review the number of parameters will be consolidated
-       and the defaults will select the better algorithms.  Files:
-       qmgr/qmgr_queue.c, qmgr/qmgr_deliver.c.
+       Configuration parameters for debugging, positive/negative
+       hysteresis, and positive/negative feedback. Some have since
+       been removed or renamed, so no point naming them here.
+       Files: global/mail_params.h, qmgr/qmgr_queue.c,
+       qmgr/qmgr_deliver.c.
 
 20071121
 
@@ -13850,3 +13845,22 @@ Apologies for any names omitted.
        in sender-dependent relayhost maps. Parameter name:
        empty_address_relayhost_maps_lookup_key (default; <>).
        Keean Schupke. File: trivial-rewrite/resolve.c.
+
+20071127-9
+
+       Revision 2 of queue manager scheduler interface, allowing
+       feedback parameter settings with constants and variables
+       such as 1/8 or 1/concurrency.  Some experimental parameters
+       were removed and others were renamed. The new names are:
+       default_concurrency_negative_feedback,
+       default_concurrency_positive_feedback, concurrency_feedback_debug,
+       default_concurrency_failed_cohort_limit.
+
+       Also available are transport-specific overrides:
+       <transport>_initial_destination_concurrency,
+       <transport>_concurrency_negative_feedback,
+       <transport>_concurrency_positive_feedback,
+       <transport>_concurrency_failed_cohort_limit.
+
+       Files: global/mail_params.h, qmgr/qmgr.c, qmgr/qmgr_transport.c,
+       qmgr/qmge_queue.c, qmgr/qmgr_feedback.c.
index f94581196b1ed85829691c772e80877135374866..e5f2f6e869d734e4c394244995d1c754f73dacc8 100644 (file)
@@ -2,19 +2,367 @@ P\bPo\bos\bst\btf\bfi\bix\bx Q\bQu\bue\beu\bue\be S\bSc\bch\bhe\bed\bdu\bul\ble\ber\br
 
 -------------------------------------------------------------------------------
 
-W\bWh\bha\bat\bt t\bth\bhi\bis\bs f\bfi\bil\ble\be i\bis\bs a\bab\bbo\bou\but\bt
+O\bOv\bve\ber\brv\bvi\bie\bew\bw
 
-This is the beginning of documentation for the clever queue manager scheduling
-algorithm by Patrik Rak. For a long time, this code was made available under
-the name "nqmgr(8)" (new queue manager), as an optional module. As of Postfix
-2.1 this is the default queue manager, which is always called "qmgr(8)". The
-old queue manager will for some time will be available under the name of "oqmgr
-(8)".
+The queue manager is by far the most complex part of the Postfix mail system.
+It schedules delivery of new mail, retries failed deliveries at specific times,
+and removes mail from the queue after the last delivery attempt. Once started,
+the qmgr(8) process runs until "postfix reload" or "postfix stop".
 
-W\bWh\bhy\by t\bth\bhe\be o\bol\bld\bd P\bPo\bos\bst\btf\bfi\bix\bx q\bqu\bue\beu\bue\be m\bma\ban\bna\bag\bge\ber\br w\bwa\bas\bs r\bre\bep\bpl\bla\bac\bce\bed\bd
+As a persistent process, the queue manager has to meet strict requirements with
+respect to code correctness and robustness. Unlike non-persistent daemon
+processes, the queue manager cannot benefit from Postfix's process rejuvenation
+mechanism that limit the impact from resource leaks and other coding errors.
 
-The old Postfix scheduler had several limitations due to unfortunate choices in
-its design.
+There are two major classes of mechanisms that control the operation of the
+queue manager:
+
+  * Mechanisms concerned with the number of concurrent deliveries to a specific
+    destination, including decisions on when to suspend deliveries after
+    persistent failures. These are described under "Concurrency scheduling".
+
+  * Mechanisms concerned with the selection of what mail to deliver to a given
+    destination. These are described under "Preemptive scheduling".
+
+C\bCo\bon\bnc\bcu\bur\brr\bre\ben\bnc\bcy\by s\bsc\bch\bhe\bed\bdu\bul\bli\bin\bng\bg
+
+This section documents the Postfix 2.5 concurrency scheduler. Prior Postfix
+versions used a simple but robust algorithm where the per-destination delivery
+concurrency was decremented by 1 after a delivery suffered connection or
+handshake failure, and was incremented by 1 otherwise. Of course the
+concurrency was never allowed to exceed the maximum per-destination concurrency
+limit. And when a destination's concurrency level dropped to zero, the
+destination was declared "dead" and delivery was suspended.
+
+Drawbacks of the old +/-1 feedback concurrency scheduler are:
+
+  * Overshoot due to exponential delivery concurrency growth with each pseudo-
+    cohort(*). For example, with the default initial concurrency of 5,
+    concurrency would proceed over time as (5-10-20).
+
+  * Throttling down to zero concurrency after a single pseudo-cohort(*)
+    failure. This was especially an issue with low-concurrency channels where a
+    single failure could be sufficient to mark a destination as "dead", causing
+    the suspension of further deliveries to the affected destination.
+
+(*) A pseudo-cohort is a number of delivery requests equal to a destination's
+delivery concurrency.
+
+The revised concurrency scheduler has a highly modular structure. It uses
+separate mechanisms for per-destination concurrency control and for "dead
+destination" detection. The concurrency control in turn is built from two
+separate mechanisms: it supports less-than-1 feedback to allow for more gradual
+concurrency adjustments, and it uses feedback hysteresis to suppress
+concurrency oscillations. And instead of waiting for delivery concurrency to
+throttle down to zero, a destination is declared "dead" after a configurable
+number of pseudo-cohorts reports connection or handshake failure.
+
+S\bSu\bum\bmm\bma\bar\bry\by o\bof\bf t\bth\bhe\be P\bPo\bos\bst\btf\bfi\bix\bx 2\b2.\b.5\b5 c\bco\bon\bnc\bcu\bur\brr\bre\ben\bnc\bcy\by f\bfe\bee\bed\bdb\bba\bac\bck\bk a\bal\blg\bgo\bor\bri\bit\bth\bhm\bm
+
+We want to increment a destination's delivery concurrency after some (not
+necessarily consecutive) number of deliveries without connection or handshake
+failure. This is implemented with positive feedback g(N) where N is the
+destination's delivery concurrency. With g(N)=1 we get the old scheduler's
+exponential growth in time, while g(N)=1/N gives linear growth in time. Less-
+than-1 feedback and integer truncation naturally give us hysteresis, so that
+transitions to larger concurrency happen every 1/g(N) positive feedback events.
+
+We want to decrement a destination's delivery concurrency after some (not
+necessarily consecutive) number of deliveries suffer connection or handshake
+failure. This is implemented with negative feedback f(N) where N is the
+destination's delivery concurrency. With f(N)=1 we get the old scheduler's
+behavior where concurrency is throttled down dramatically after a single
+pseudo-cohort failure, while f(N)=1/N backs off more gently. Again, less-than-
+1 feedback and integer truncation naturally give us hysteresis, so that
+transitions to lower concurrency happen every 1/f(N) negative feedback events.
+
+However, with negative feedback we introduce a subtle twist. We "reverse" the
+hysteresis cycle so that the transition to lower concurrency happens at the
+b\bbe\beg\bgi\bin\bnn\bni\bin\bng\bg of a sequence of 1/f(N) negative feedback events. Otherwise, a
+correction for overload would be made too late. In the case of a concurrency-
+limited server, this makes the choice of f(N) relatively unimportant, as borne
+out by measurements.
+
+In summary, the main ingredients for the Postfix 2.5 concurrency feedback
+algorithm are a) the option of less-than-1 positive feedback to avoid
+overwhelming servers, b) the option of less-than-1 negative feedback to avoid
+or giving up too fast, c) feedback hysteresis to avoid rapid oscillation, and
+c) a "reverse" hysteresis cycle for negative feedback, so that it can correct
+for overload quickly.
+
+S\bSu\bum\bmm\bma\bar\bry\by o\bof\bf t\bth\bhe\be P\bPo\bos\bst\btf\bfi\bix\bx 2\b2.\b.5\b5 "\b"d\bde\bea\bad\bd d\bde\bes\bst\bti\bin\bna\bat\bti\bio\bon\bn"\b" d\bde\bet\bte\bec\bct\bti\bio\bon\bn a\bal\blg\bgo\bor\bri\bit\bth\bhm\bm
+
+We want to suspend deliveries to a specific destination after some number of
+deliveries suffers connection or handshake failure. The old scheduler declares
+a destination "dead" when negative (-1) feedback throttles the delivery
+concurrency down to zero. With less-than-1 feedback, this throttling down would
+obviously take too long. We therefore have to separate "dead destination"
+detection from concurrency feedback. This is implemented by introducing the
+concept of pseudo-cohort failure. The Postfix 2.5 concurrency scheduler
+declares a destination "dead" after a configurable number of pseudo-cohort
+failures. The old scheduler corresponds to the special case where the pseudo-
+cohort failure limit is equal to 1.
+
+P\bPs\bse\beu\bud\bdo\boc\bco\bod\bde\be f\bfo\bor\br t\bth\bhe\be P\bPo\bos\bst\btf\bfi\bix\bx 2\b2.\b.5\b5 c\bco\bon\bnc\bcu\bur\brr\bre\ben\bnc\bcy\by s\bsc\bch\bhe\bed\bdu\bul\ble\ber\br
+
+The pseudo code shows how the ideas behind new concurrency scheduler are
+implemented as of November 2007. The actual code can be found in the module
+qmgr/qmgr_queue.c.
+
+Types:
+       Each destination has one set of the following variables
+        int window
+        double success
+        double failure
+        double fail_cohorts
+
+Feedback functions:
+       N is concurrency; x, y are arbitrary numbers in [0..1] inclusive
+        positive feedback: g(N) = x/N | x/sqrt(N) | x
+        negative feedback: f(N) = y/N | y/sqrt(N) | y
+
+Initialization:
+        window = initial_concurrency
+        success = 0
+        failure = 0
+        fail_cohorts = 0
+
+After success:
+        fail_cohorts = 0
+        Be prepared for feedback > hysteresis, or rounding error
+        success += g(window)
+        while (success >= 1)    Hysteresis 1
+            window += 1         Hysteresis 1
+            failure = 0
+            success -= 1        Hysteresis 1
+        Be prepared for overshoot
+        if (window > concurrency limit)
+            window = concurrency limit
+
+Safety:
+        Don't apply positive feedback unless
+            window < busy_refcount + init_dest_concurrency
+        otherwise negative feedback effect could be delayed
+
+After failure:
+        if (window > 0)
+            fail_cohorts += 1.0 / window
+            if (fail_cohorts > cohort_failure_limit)
+                window = 0
+        if (window > 0)
+            Be prepared for feedback > hysteresis, rounding errors
+            failure -= f(window)
+            while (failure < 0)
+                window -= 1     Hysteresis 1
+                failure += 1    Hysteresis 1
+                success = 0
+            Be prepared for overshoot
+            if (window < 1)
+                window = 1
+
+R\bRe\bes\bsu\bul\blt\bts\bs f\bfo\bor\br t\bth\bhe\be P\bPo\bos\bst\btf\bfi\bix\bx 2\b2.\b.5\b5 c\bco\bon\bnc\bcu\bur\brr\bre\ben\bnc\bcy\by f\bfe\bee\bed\bdb\bba\bac\bck\bk s\bsc\bch\bhe\bed\bdu\bul\ble\ber\br
+
+Discussions about the concurrency scheduler redesign started early 2004, when
+the primary goal was to find alternatives that did not exhibit exponential
+growth or rapid concurrency throttling. No code was implemented until late
+2007, when the primary concern had shifted towards better handling of server
+concurrency limits. For this reason we measure how well the new scheduler does
+this job. The table below compares mail delivery performance of the old +/-
+1 feedback with other feedback functions, for different server concurrency
+enforcement methods. Measurements were done with a FreeBSD 6.2 client and with
+FreeBSD 6.2 and various Linux servers.
+
+Server configuration:
+
+  * The mail flow was slowed down with 1 second latency per recipient
+    ("smtpd_client_restrictions = sleep 1"). The purpose was to make results
+    less dependent on hardware details, by reducing the slow-downs by disk I/O,
+    logging I/O, and network I/O.
+  * Concurrency was limited by the server process limit ("default_process_limit
+    = 5", "smtpd_client_event_limit_exceptions = static:all"). Postfix was
+    stopped and started after changing the process limit, because the same
+    number is also used as the backlog argument to the listen(2) system call,
+    and "postfix reload" does not re-issue this call.
+  * Mail was discarded with "local_recipient_maps = static:all" and
+    "local_transport = discard". The discard action in header/body checks could
+    not be used as it fails to update the in_flow_delay counters.
+
+Client configuration:
+
+  * Queue file overhead was minimized by sending one message to a virtual alias
+    that expanded into 2000 different remote recipients. All recipients were
+    accounted for according to the maillog file. The
+    virtual_alias_expansion_limit setting was increased to avoid complaints
+    from the cleanup(8) server.
+  * The number of deliveries was maximized with
+    "smtp_destination_recipient_limit = 2". A smaller limit would cause Postfix
+    to schedule the concurrency per recipient instead of domain, which is not
+    what we want.
+  * Maximal concurrency was limited with "smtp_destination_concurrency_limit =
+    20", and initial_destination_concurrency was set to the same value.
+  * The positive and negative concurrency feedback hysteresis was 1.
+    Concurrency was incremented by 1 at the END of 1/feedback steps of positive
+    feedback, and was decremented by 1 at the START of 1/feedback steps of
+    negative feedback.
+  * The SMTP client used the default 30s SMTP connect timeout and 300s SMTP
+    greeting timeout.
+
+The first results are for a FreeBSD 6.2 server, where our artificially low
+listen(2) backlog results in a very short kernel queue for established
+connections. As the table shows, all deferred deliveries failed due to a 30s
+connection timeout, and none failed due to a server greeting timeout. This
+measurement simulates what happens when the server's connection queue is
+completely full under load, and the TCP engine drops new connections.
+
+    c\bcl\bli\bie\ben\bnt\bt s\bse\ber\brv\bve\ber\br f\bfe\bee\bed\bdb\bba\bac\bck\bk  c\bco\bon\bnn\bne\bec\bct\bti\bio\bon\bn p\bpe\ber\brc\bce\ben\bnt\bta\bag\bge\be c\bcl\bli\bie\ben\bnt\bt         t\bti\bim\bme\bed\bd-\b-o\bou\but\bt i\bin\bn
+    l\bli\bim\bmi\bit\bt  l\bli\bim\bmi\bit\bt  s\bst\bty\byl\ble\be     c\bca\bac\bch\bhi\bin\bng\bg    d\bde\bef\bfe\ber\brr\bre\bed\bd   c\bco\bon\bnc\bcu\bur\brr\bre\ben\bnc\bcy\by    c\bco\bon\bnn\bne\bec\bct\bt/\b/
+                                                  a\bav\bve\ber\bra\bag\bge\be/\b/s\bst\btd\bdd\bde\bev\bv g\bgr\bre\bee\bet\bti\bin\bng\bg
+
+    -------------------------------------------------------------------------
+       20     5       1/N         no        9.9   19.4    0.49   198      -
+
+       20     5       1/N        yes       10.3   19.4    0.49   206      -
+
+       20     5   1/sqrt(N)       no       10.4   19.6    0.59   208      -
+
+       20     5   1/sqrt(N)      yes       10.6   19.6    0.61   212      -
+
+       20     5         1         no       10.1   19.5    1.29   202      -
+
+       20     5         1        yes       10.8   19.3    1.57   216      -
+
+    -------------------------------------------------------------------------
+
+    A busy server with a completely full connection queue. N is the client
+    delivery concurrency. Failed deliveries time out after 30s without
+    completing the TCP handshake. See below for a discussion of results.
+
+The next table shows results for a Fedora Core 8 server (results for RedHat 7.3
+are identical). In this case, the listen(2) backlog argument has little if any
+effect on the kernel's established connection queue. As the table shows,
+practically all deferred deliveries fail after the 300s SMTP greeting timeout.
+As these timeouts were 10x longer than with the previous measurement, we
+increased the recipient count (and thus the running time) by a factor of 10 to
+keep the results comparable.
+
+    c\bcl\bli\bie\ben\bnt\bt s\bse\ber\brv\bve\ber\br f\bfe\bee\bed\bdb\bba\bac\bck\bk  c\bco\bon\bnn\bne\bec\bct\bti\bio\bon\bn p\bpe\ber\brc\bce\ben\bnt\bta\bag\bge\be c\bcl\bli\bie\ben\bnt\bt         t\bti\bim\bme\bed\bd-\b-o\bou\but\bt i\bin\bn
+    l\bli\bim\bmi\bit\bt  l\bli\bim\bmi\bit\bt  s\bst\bty\byl\ble\be     c\bca\bac\bch\bhi\bin\bng\bg    d\bde\bef\bfe\ber\brr\bre\bed\bd   c\bco\bon\bnc\bcu\bur\brr\bre\ben\bnc\bcy\by    c\bco\bon\bnn\bne\bec\bct\bt/\b/
+                                                  a\bav\bve\ber\bra\bag\bge\be/\b/s\bst\btd\bdd\bde\bev\bv g\bgr\bre\bee\bet\bti\bin\bng\bg
+
+    -------------------------------------------------------------------------
+       20     5       1/N         no       1.16   19.8    0.37   -      230
+
+       20     5       1/N        yes       1.36   19.8    0.36   -      272
+
+       20     5   1/sqrt(N)       no       1.21   19.9    0.23   4      238
+
+       20     5   1/sqrt(N)      yes       1.36   20.0    0.23   -      272
+
+       20     5         1         no       1.18   20.0    0.16   -      236
+
+       20     5         1        yes       1.39   20.0    0.16   -      278
+
+    -------------------------------------------------------------------------
+
+    A busy server with a non-full connection queue. N is the client delivery
+    concurrency. Failed deliveries complete at the TCP level, but time out
+    after 300s while waiting for the SMTP greeting. See below for a discussion
+    of results.
+
+The final concurrency limited result shows what happens when SMTP connections
+don't time out, but are rejected immediately with the Postfix server's
+smtpd_client_connection_count_limit feature. Similar results can be expected
+with concurrency limiting features built into other MTAs or firewalls. For this
+measurement we specified a server concurrency limit and a client initial
+destination concurrency of 5, and a server process limit of 10. The server was
+FreeBSD 6.2 but that does not matter here, because the "push back" is done
+entirely by the server's Postfix itself.
+
+    c\bcl\bli\bie\ben\bnt\bt s\bse\ber\brv\bve\ber\br f\bfe\bee\bed\bdb\bba\bac\bck\bk  c\bco\bon\bnn\bne\bec\bct\bti\bio\bon\bn p\bpe\ber\brc\bce\ben\bnt\bta\bag\bge\be c\bcl\bli\bie\ben\bnt\bt         t\bth\bhe\beo\bor\bre\bet\bti\bic\bca\bal\bl
+    l\bli\bim\bmi\bit\bt  l\bli\bim\bmi\bit\bt  s\bst\bty\byl\ble\be     c\bca\bac\bch\bhi\bin\bng\bg    d\bde\bef\bfe\ber\brr\bre\bed\bd   c\bco\bon\bnc\bcu\bur\brr\bre\ben\bnc\bcy\by    d\bde\bef\bfe\ber\br r\bra\bat\bte\be
+                                                  a\bav\bve\ber\bra\bag\bge\be/\b/s\bst\btd\bdd\bde\bev\bv
+
+    -------------------------------------------------------------------------
+       20     5       1/N         no       16.5   5.17    0.38         1/6
+
+       20     5       1/N        yes       16.5   5.17    0.38         1/6
+
+       20     5   1/sqrt(N)       no       24.5   5.28    0.45         1/4
+
+       20     5   1/sqrt(N)      yes       24.3   5.28    0.46         1/4
+
+       20     5         1         no       49.7   5.63    0.67         1/2
+
+       20     5         1        yes       49.7   5.68    0.70         1/2
+
+    -------------------------------------------------------------------------
+
+    A server with active per-client concurrency limiter that replies with 421
+    and disconnects. N is the client delivery concurrency. The theoretical mail
+    deferral rate is 1/(1+roundup(1/feedback)). This is always 1/2 with the
+    fixed +/-1 feedback; with the variable feedback variants, the defer rate
+    decreases with increasing concurrency. See below for a discussion of
+    results.
+
+The results are based on the first delivery runs only; they do not include any
+second etc. delivery attempts.
+
+The first two examples show that the feedback method matters little when
+concurrency is limited due to congestion. This is because the initial
+concurrency was already at the client's concurrency maximum, and because there
+was 10-100 times more positive than negative feedback. The contribution from
+SMTP connection caching was also minor for these two examples.
+
+In the last example, the old +/-1 feedback scheduler defers 50% of the mail
+when confronted with an active (anvil-style) server concurrency limit, where
+the server hangs up immediately with a 421 status (a TCP-level RST would have
+the same result). Less aggressive feedback mechanisms fare better here, and the
+concurrency-dependent feedback fares even better at higher concurrencies than
+shown here, but they have limitations as discussed in the next section.
+
+L\bLi\bim\bmi\bit\bta\bat\bti\bio\bon\bns\bs o\bof\bf l\ble\bes\bss\bs-\b-t\bth\bha\ban\bn-\b-1\b1 f\bfe\bee\bed\bdb\bba\bac\bck\bk
+
+The delivery concurrency scheduler with less-than-1 feedback solves a problem
+with servers that have active concurrency limiters, but this works well only
+because feedback is handled in a peculiar manner: positive feedback increments
+the concurrency by 1 at the end of a sequence of events of length 1/feedback,
+while negative feedback decrements concurrency by 1 at the beginning of such a
+sequence. This is how Postfix adjusts quickly for overshoot without causing
+lots of mail to be deferred. Without this difference in feedback treatment,
+less-than-1 feedback would defer 50% of the mail, and would be no better in
+this respect than the simple +/-1 feedback scheduler.
+
+Unfortunately, the same feature that corrects quickly for concurrency overshoot
+also makes the scheduler more sensitive for noisy negative feedback. The reason
+is that one lonely negative feedback event has the same effect as a complete
+sequence of length 1/feedback: in both cases delivery concurrency is dropped by
+1 immediately. For example, when multiple servers are placed behind a load
+balancer on a single IP address, and 1 out of K servers fails to complete the
+SMTP handshake, a scheduler with 1/N (N = concurrency) feedback will stop
+increasing its concurrency once it reaches roughly K. Even though the good
+servers behind the load balancer are perfectly capable of handling more mail,
+the 1/N feedback scheduler will linger around concurrency K.
+
+This problem with 1/N feedback gets worse as 1/N gets smaller. A workaround is
+to use fixed less-than-1 values for positive and negative feedback that limit
+the noise sensitivity, for example: positive feedback of 1/4 and negative
+feedback 1/10. Of course using fixed feedback means concurrency growth is
+moderated only for a limited range of concurrencies. Sites that deliver at per-
+destination concurrencies of 50 or more will require special configuration.
+
+P\bPr\bre\bee\bem\bmp\bpt\bti\biv\bve\be s\bsc\bch\bhe\bed\bdu\bul\bli\bin\bng\bg
+
+This is the beginning of documentation for a preemptive queue manager
+scheduling algorithm by Patrik Rak. For a long time, this code was made
+available under the name "nqmgr(8)" (new queue manager), as an optional module.
+As of Postfix 2.1 this is the default queue manager, which is always called
+"qmgr(8)". The old queue manager will for some time will be available under the
+name of "oqmgr(8)".
+
+W\bWh\bhy\by t\bth\bhe\be n\bno\bon\bn-\b-p\bpr\bre\bee\bem\bmp\bpt\bti\biv\bve\be P\bPo\bos\bst\btf\bfi\bix\bx q\bqu\bue\beu\bue\be m\bma\ban\bna\bag\bge\ber\br w\bwa\bas\bs r\bre\bep\bpl\bla\bac\bce\bed\bd
+
+The non-preemptive Postfix scheduler had several limitations due to unfortunate
+choices in its design.
 
  1. Round-robin selection by destination for mail that is delivered via the
     same message delivery transport. The round-robin strategy was chosen with
@@ -33,7 +381,7 @@ its design.
     scheduler allows mail with fewer recipients to slip past bulk mail in an
     elegant manner.
 
-H\bHo\bow\bw t\bth\bhe\be q\bqu\bue\beu\bue\be m\bma\ban\bna\bag\bge\ber\br s\bsc\bch\bhe\bed\bdu\bul\ble\ber\br w\bwo\bor\brk\bks\bs
+H\bHo\bow\bw t\bth\bhe\bn\bno\bon\bn-\b-p\bpr\bre\bee\bem\bmp\bpt\bti\biv\bve\bq\bqu\bue\beu\bue\be m\bma\ban\bna\bag\bge\ber\br s\bsc\bch\bhe\bed\bdu\bul\ble\ber\br w\bwo\bor\brk\bks\bs
 
 The following text is from Patrik Rak and should be read together with the
 postconf(5) manual that describes each configuration parameter in detail.
index d6d5999ed0c0635a6c36d18eabe568e81cab22f7..9bb5960e1ed3f06e15f576340d929409cb06c78e 100644 (file)
@@ -17,12 +17,12 @@ Incompatibility with Postfix 2.3 and earlier
 If you upgrade from Postfix 2.3 or earlier, read RELEASE_NOTES-2.4
 before proceeding.
 
-Major changes with Postfix snapshot 20071121
+Major changes with Postfix snapshot 20071129
 ============================================
 
 Revised queue manager with separate mechanisms for per-destination
 concurrency control and for dead destination detection.  The
-concurrency control supports non-integer feedback to allow for more
+concurrency control supports less-than-1 feedback to allow for more
 gradual concurrency adjustments, and uses hysteresis to avoid rapid
 oscillations.  A destination is declared "dead" after a configurable
 number of pseudo-cohorts(*) reports connection or handshake failure.
@@ -33,27 +33,21 @@ number of pseudo-cohorts(*) reports connection or handshake failure.
 The drawbacks of the old +/-1 feedback scheduler are a) overshoot
 due to exponential delivery concurrency growth with each pseudo-cohort(*)
 (5-10-20...); b) throttling down to zero concurrency after a single
-pseudo-cohort(*) failure. This was especially an issue with
+pseudo-cohort(*) failure. The latter was especially an issue with
 low-concurrency channels where a single failure could be sufficient
 to mark a destination as "dead", and suspend further deliveries.
 
-The new code is a laboratory model with a multitude of configuration
-parameters, so that developers can experiment with different feedback
-functions and hysteresis values.  This is a baseline against which
-further improvements will be measured: a) is the additional improvement
-worth the additional complexity; b) is the design sound, i.e. free
-from arbitrary constants and other tweaks that optimize for a narrow
-range of application.
-
-New main.cf parameters: qmgr_concurrency_feedback_debug,
-qmgr_negative_feedback_hysteresis, qmgr_negative_feedback_method,
-qmgr_positive_feedback_hysteresis, qmgr_positive_feedback_method,
-qmgr_sacrifice_cohorts.  See postconf(5) for extensive descriptions.
+New configuration parameters: concurrency_feedback_debug,
+default_concurrency_positive_feedback,
+default_concurrency_negative_feedback,
+default_concurrency_failed_cohort_limit, as well as transport-specific
+versions of the same.  See postconf(5) for extensive descriptions,
+and SCHEDULER_README for background information on why things work
+the way they work.
 
 The default parameter settings are backwards compatible with older
-Postfix versions.  However, after a testing period, the number of
-parameters will be consolidated, and the default settings will be
-changed to take advantage of the "better" algorithm.
+Postfix versions. This may change after better defaults are field
+tested.
 
 Major changes with Postfix snapshot 20071111
 ============================================
index 7592b261f66c0ace7ce378c143b0eda5c603ad30..30c45f92dcdce8d4901333767afe7bee57db00de 100644 (file)
@@ -1,7 +1,5 @@
 Wish list:
 
-       document dict_get() as returning const char *
-
        Make event_drain() a proper event loop; update the zero mask,
        and don't ignore a non-empty timer queue.
 
index 25f56a51aae9bfc85a2ca89a57e1b9899cf0dd65..4f6e483b51a4f1dea1f0befa4a487c5f02a90e75 100644 (file)
 
 <hr>
 
-<h2>What this file is about</h2>
+<h2> Overview </h2>
 
-<p> This is the beginning of documentation for the clever queue
+<p> The queue manager is by far the most complex part of the Postfix
+mail system. It schedules delivery of new mail, retries failed
+deliveries at specific times, and removes mail from the queue after
+the last delivery attempt. Once started, the <a href="qmgr.8.html">qmgr(8)</a> process runs
+until "postfix reload" or "postfix stop". </p>
+
+<p> As a persistent process, the queue manager has to meet strict
+requirements with respect to code correctness and robustness. Unlike
+non-persistent daemon processes, the queue manager cannot benefit
+from Postfix's process rejuvenation mechanism that limit the impact
+from resource leaks and other coding errors. </p>
+
+<p> There are two major classes of mechanisms that control the
+operation of the queue manager: </p>
+
+<ul>
+
+<li> <p> Mechanisms concerned with the number of concurrent deliveries
+to a specific destination, including decisions on when to suspend
+deliveries after persistent failures. These are described under "<a
+href="#concurrency">Concurrency scheduling</a>". </p>
+
+<li> <p> Mechanisms concerned with the selection of what mail to
+deliver to a given destination.  These are described under "<a
+href="#jobs">Preemptive scheduling</a>". </p>
+
+</ul>
+
+<h2> <a name="concurrency"> Concurrency scheduling </a> </h2>
+
+<p> This section documents the Postfix 2.5 concurrency scheduler.
+Prior Postfix versions used a simple but robust algorithm where the
+per-destination delivery concurrency was decremented by 1 after a
+delivery suffered connection or handshake failure, and was incremented
+by 1 otherwise.  Of course the concurrency was never allowed to
+exceed the maximum per-destination concurrency limit.  And when a
+destination's concurrency level dropped to zero, the destination
+was declared "dead" and delivery was suspended.  </p>
+
+<p> Drawbacks of the old +/-1 feedback concurrency scheduler are:
+<p>
+
+<ul>
+
+<li> <p> Overshoot due to exponential delivery concurrency growth
+with each pseudo-cohort(*). For example, with the default initial
+concurrency of 5, concurrency would proceed over time as (5-10-20).
+</p>
+
+<li> <p> Throttling down to zero concurrency after a single
+pseudo-cohort(*) failure. This was especially an issue with
+low-concurrency channels where a single failure could be sufficient
+to mark a destination as "dead", causing the suspension of further
+deliveries to the affected destination. </p>
+
+</ul>
+
+<p> (*) A pseudo-cohort is a number of delivery requests equal to
+a destination's delivery concurrency. </p>
+
+<p> The revised concurrency scheduler has a highly modular structure.
+It uses separate mechanisms for per-destination concurrency control
+and for "dead destination" detection.  The concurrency control in
+turn is built from two separate mechanisms: it supports less-than-1
+feedback to allow for more gradual concurrency adjustments, and it
+uses feedback hysteresis to suppress concurrency oscillations.  And
+instead of waiting for delivery concurrency to throttle down to
+zero, a destination is declared "dead" after a configurable number
+of pseudo-cohorts reports connection or handshake failure.  </p>
+
+<h2> Summary of the Postfix 2.5 concurrency feedback algorithm </h2>
+
+<p> We want to increment a destination's delivery concurrency after
+some (not necessarily consecutive) number of deliveries without
+connection or handshake failure.  This is implemented with positive
+feedback g(N) where N is the destination's delivery concurrency.
+With g(N)=1 we get the old scheduler's exponential growth in time,
+while g(N)=1/N gives linear growth in time.  Less-than-1 feedback
+and integer truncation naturally give us hysteresis, so that
+transitions to larger concurrency happen every 1/g(N) positive
+feedback events. </p>
+
+<p> We want to decrement a destination's delivery concurrency after
+some (not necessarily consecutive) number of deliveries suffer
+connection or handshake failure.  This is implemented with negative
+feedback f(N) where N is the destination's delivery concurrency.
+With f(N)=1 we get the old scheduler's behavior where concurrency
+is throttled down dramatically after a single pseudo-cohort failure,
+while f(N)=1/N backs off more gently.  Again, less-than-1 feedback
+and integer truncation naturally give us hysteresis, so that
+transitions to lower concurrency happen every 1/f(N) negative
+feedback events. </p>
+
+<p> However, with negative feedback we introduce a subtle twist.
+We "reverse" the hysteresis cycle so that the transition to lower
+concurrency happens at the <b>beginning</b> of a sequence of 1/f(N)
+negative feedback events.  Otherwise, a correction for overload
+would be made too late.  In the case of a concurrency-limited server,
+this makes the choice of f(N) relatively unimportant, as borne out
+by measurements.  </p>
+
+<p> In summary, the main ingredients for the Postfix 2.5 concurrency
+feedback algorithm are a) the option of less-than-1 positive feedback
+to avoid overwhelming servers, b) the option of less-than-1 negative
+feedback to avoid or giving up too fast, c) feedback hysteresis to
+avoid rapid oscillation, and c) a "reverse" hysteresis cycle for
+negative feedback, so that it can correct for overload quickly. </p>
+
+<h2> Summary of the Postfix 2.5 "dead destination" detection algorithm </h2>
+
+<p> We want to suspend deliveries to a specific destination after
+some number of deliveries suffers connection or handshake failure.
+The old scheduler declares a destination "dead" when negative (-1)
+feedback throttles the delivery concurrency down to zero. With
+less-than-1 feedback, this throttling down would obviously take too
+long.  We therefore have to separate "dead destination" detection
+from concurrency feedback.  This is implemented by introducing the
+concept of pseudo-cohort failure. The Postfix 2.5 concurrency
+scheduler declares a destination "dead" after a configurable number
+of pseudo-cohort failures. The old scheduler corresponds to the
+special case where the pseudo-cohort failure limit is equal to 1.
+</p>
+
+<h2> Pseudocode for the Postfix 2.5 concurrency scheduler </h2>
+
+<p> The pseudo code shows how the ideas behind new concurrency
+scheduler are implemented as of November 2007.  The actual code can
+be found in the module qmgr/qmgr_queue.c.  </p>
+
+<pre>
+Types:
+       Each destination has one set of the following variables
+        int window
+        double success
+        double failure
+        double fail_cohorts
+
+Feedback functions:
+       N is concurrency; x, y are arbitrary numbers in [0..1] inclusive
+        positive feedback: g(N) = x/N | x/sqrt(N) | x
+        negative feedback: f(N) = y/N | y/sqrt(N) | y
+
+Initialization:
+        window = initial_concurrency
+        success = 0
+        failure = 0
+        fail_cohorts = 0
+
+After success:
+        fail_cohorts = 0
+        Be prepared for feedback > hysteresis, or rounding error
+        success += g(window)
+        while (success >= 1)    Hysteresis 1
+            window += 1         Hysteresis 1
+            failure = 0
+            success -= 1        Hysteresis 1
+        Be prepared for overshoot
+        if (window > concurrency limit)
+            window = concurrency limit
+
+Safety:
+        Don't apply positive feedback unless
+            window < busy_refcount + init_dest_concurrency
+        otherwise negative feedback effect could be delayed
+
+After failure:
+        if (window > 0)
+            fail_cohorts += 1.0 / window
+            if (fail_cohorts > cohort_failure_limit)
+                window = 0
+        if (window > 0)
+            Be prepared for feedback > hysteresis, rounding errors
+            failure -= f(window)
+            while (failure < 0)
+                window -= 1     Hysteresis 1
+                failure += 1    Hysteresis 1
+                success = 0
+            Be prepared for overshoot
+            if (window < 1)
+                window = 1
+</pre>
+
+<h2> Results for the Postfix 2.5 concurrency feedback scheduler </h2>
+
+<p> Discussions about the concurrency scheduler redesign started
+early 2004, when the primary goal was to find alternatives that did
+not exhibit exponential growth or rapid concurrency throttling.  No
+code was implemented until late 2007, when the primary concern had
+shifted towards better handling of server concurrency limits. For
+this reason we measure how well the new scheduler does this
+job.  The table below compares mail delivery performance of the old
++/-1 feedback with other feedback functions, for different server
+concurrency enforcement methods.  Measurements were done with a
+FreeBSD 6.2 client and with FreeBSD 6.2 and various Linux servers.
+</p>
+
+<li> Server configuration:
+
+<ul> <li> The mail flow was slowed down with 1 second latency per
+recipient ("<a href="postconf.5.html#smtpd_client_restrictions">smtpd_client_restrictions</a> = sleep 1"). The purpose was
+to make results less dependent on hardware details, by reducing the
+slow-downs by disk I/O, logging I/O, and network I/O.
+
+<li> Concurrency was limited by the server process limit
+("<a href="postconf.5.html#default_process_limit">default_process_limit</a> = 5", "<a href="postconf.5.html#smtpd_client_event_limit_exceptions">smtpd_client_event_limit_exceptions</a>
+= static:all"). Postfix was stopped and started after changing the
+process limit, because the same number is also used as the backlog
+argument to the listen(2) system call, and "postfix reload" does
+not re-issue this call.
+
+<li> Mail was discarded with "<a href="postconf.5.html#local_recipient_maps">local_recipient_maps</a> = static:all" and
+"<a href="postconf.5.html#local_transport">local_transport</a> = discard". The discard action in header/body checks
+could not be used as it fails to update the <a href="postconf.5.html#in_flow_delay">in_flow_delay</a> counters.
+
+</ul>
+
+<li> Client configuration:
+
+<ul>
+
+<li> Queue file overhead was minimized by sending one message to a
+virtual alias that expanded into 2000 different remote recipients.
+All recipients were accounted for according to the maillog file.
+The <a href="postconf.5.html#virtual_alias_expansion_limit">virtual_alias_expansion_limit</a> setting was increased to avoid
+complaints from the <a href="cleanup.8.html">cleanup(8)</a> server.
+
+<li> The number of deliveries was maximized with
+"<a href="postconf.5.html#smtp_destination_recipient_limit">smtp_destination_recipient_limit</a> = 2". A smaller limit would cause
+Postfix to schedule the concurrency per recipient instead of domain,
+which is not what we want.
+
+<li> Maximal concurrency was limited with
+"<a href="postconf.5.html#smtp_destination_concurrency_limit">smtp_destination_concurrency_limit</a> = 20", and
+<a href="postconf.5.html#initial_destination_concurrency">initial_destination_concurrency</a> was set to the same value.
+
+<li> The positive and negative concurrency feedback hysteresis was
+1.  Concurrency was incremented by 1 at the END of 1/feedback steps
+of positive feedback, and was decremented by 1 at the START of
+1/feedback steps of negative feedback.
+
+<li> The SMTP client used the default 30s SMTP connect timeout and
+300s SMTP greeting timeout.
+
+</ul>
+
+<p> The first results are for a FreeBSD 6.2 server, where our
+artificially low listen(2) backlog results in a very short kernel
+queue for established connections. As the table shows, all deferred
+deliveries failed due to a 30s connection timeout, and none failed
+due to a server greeting timeout.  This measurement simulates what
+happens when the server's connection queue is completely full under
+load, and the TCP engine drops new connections.  </p>
+
+<blockquote>
+
+<table>
+
+<tr> <th>client<br> limit</th> <th>server<br> limit</th> <th>feedback<br>
+style</th> <th>connection<br> caching</th> <th>percentage<br>
+deferred</th> <th colspan="2">client concurrency<br> average/stddev</th>
+<th colspan=2>timed-out in<br> connect/greeting </th> </tr>
+
+<tr> <td align="center" colspan="9"> <hr> </td> </tr>
+
+<tr><td align="center">20</td> <td align="center">5</td> <td
+align="center">1/N</td> <td align="center">no</td> <td
+align="center">9.9</td> <td align="center">19.4</td> <td
+align="center">0.49</td> <td align="center">198</td> <td
+align="center">-</td> </tr>
+
+<tr><td align="center">20</td> <td align="center">5</td> <td
+align="center">1/N</td> <td align="center">yes</td> <td
+align="center">10.3</td> <td align="center">19.4</td> <td
+align="center">0.49</td> <td align="center">206</td> <td
+align="center">-</td> </tr>
+
+<tr><td align="center">20</td> <td align="center">5</td> <td
+align="center">1/sqrt(N)</td> <td align="center">no</td>
+<td align="center">10.4</td> <td align="center">19.6</td> <td
+align="center">0.59</td> <td align="center">208</td> <td
+align="center">-</td> </tr>
+
+<tr><td align="center">20</td> <td align="center">5</td> <td
+align="center">1/sqrt(N)</td> <td align="center">yes</td>
+<td align="center">10.6</td> <td align="center">19.6</td> <td
+align="center">0.61</td> <td align="center">212</td> <td
+align="center">-</td> </tr>
+
+<tr><td align="center">20</td> <td align="center">5</td> <td
+align="center">1</td> <td align="center">no</td> <td
+align="center">10.1</td> <td align="center">19.5</td> <td
+align="center">1.29</td> <td align="center">202</td> <td
+align="center">-</td> </tr>
+
+<tr><td align="center">20</td> <td align="center">5</td> <td
+align="center">1</td> <td align="center">yes</td> <td
+align="center">10.8</td> <td align="center">19.3</td> <td
+align="center">1.57</td> <td align="center">216</td> <td
+align="center">-</td> </tr>
+
+<tr> <td align="center" colspan="9"> <hr> </td> </tr>
+
+</table>
+
+<p> A busy server with a completely full connection queue.  N is
+the client delivery concurrency.  Failed deliveries time out after
+30s without completing the TCP handshake. See below for a discussion
+of results. </p>
+
+</blockquote>
+
+<p> The next table shows results for a Fedora Core 8 server (results
+for RedHat 7.3 are identical). In this case, the listen(2) backlog
+argument has little if any effect on the kernel's established
+connection queue.  As the table shows, practically all deferred
+deliveries fail after the 300s SMTP greeting timeout. As these
+timeouts were 10x longer than with the previous measurement, we
+increased the recipient count (and thus the running time) by a
+factor of 10 to keep the results comparable. </p>
+
+<blockquote>
+
+<table>
+
+<tr> <th>client<br> limit</th> <th>server<br> limit</th> <th>feedback<br>
+style</th> <th>connection<br> caching</th> <th>percentage<br>
+deferred</th> <th colspan="2">client concurrency<br> average/stddev</th>
+<th colspan=2>timed-out in<br> connect/greeting </th> </tr>
+
+<tr> <td align="center" colspan="9"> <hr> </td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1/N</td> <td align="center">no</td> <td
+align="center">1.16</td> <td align="center">19.8</td> <td
+align="center">0.37</td> <td align="center">-</td> <td
+align="center">230</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1/N</td> <td align="center">yes</td> <td
+align="center">1.36</td> <td align="center">19.8</td> <td
+align="center">0.36</td> <td align="center">-</td> <td
+align="center">272</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1/sqrt(N)</td> <td align="center">no</td>
+<td align="center">1.21</td> <td align="center">19.9</td> <td
+align="center">0.23</td> <td align="center">4</td> <td
+align="center">238</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1/sqrt(N)</td> <td align="center">yes</td>
+<td align="center">1.36</td> <td align="center">20.0</td> <td
+align="center">0.23</td> <td align="center">-</td> <td
+align="center">272</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1</td> <td align="center">no</td> <td
+align="center">1.18</td> <td align="center">20.0</td> <td
+align="center">0.16</td> <td align="center">-</td> <td
+align="center">236</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1</td> <td align="center">yes</td> <td
+align="center">1.39</td> <td align="center">20.0</td> <td
+align="center">0.16</td> <td align="center">-</td> <td
+align="center">278</td> </tr>
+
+<tr> <td align="center" colspan="9"> <hr> </td> </tr>
+
+</table>
+
+<p> A busy server with a non-full connection queue.  N is the client
+delivery concurrency. Failed deliveries complete at the TCP level,
+but time out after 300s while waiting for the SMTP greeting.  See
+below for a discussion of results.  </p>
+
+</blockquote>
+
+
+<p> The final concurrency limited result shows what happens when
+SMTP connections don't time out, but are rejected immediately with
+the Postfix server's <a href="postconf.5.html#smtpd_client_connection_count_limit">smtpd_client_connection_count_limit</a> feature.
+Similar results can be expected with concurrency limiting features
+built into other MTAs or firewalls.  For this measurement we specified
+a server concurrency limit and a client initial destination concurrency
+of 5, and a server process limit of 10. The server was FreeBSD 6.2
+but that does not matter here, because the "push back" is done
+entirely by the server's Postfix itself. </p>
+
+<blockquote>
+
+<table>
+
+<tr> <th>client<br> limit</th> <th>server<br> limit</th> <th>feedback<br>
+style</th> <th>connection<br> caching</th> <th>percentage<br>
+deferred</th> <th colspan="2">client concurrency<br> average/stddev</th>
+<th>theoretical<br>defer rate</th> </tr>
+
+<tr> <td align="center" colspan="9"> <hr> </td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1/N</td> <td align="center">no</td> <td
+align="center">16.5</td> <td align="center">5.17</td> <td
+align="center">0.38</td> <td align="center">1/6</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1/N</td> <td align="center">yes</td> <td
+align="center">16.5</td> <td align="center">5.17</td> <td
+align="center">0.38</td> <td align="center">1/6</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1/sqrt(N)</td> <td align="center">no</td>
+<td align="center">24.5</td> <td align="center">5.28</td> <td
+align="center">0.45</td> <td align="center">1/4</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1/sqrt(N)</td> <td align="center">yes</td>
+<td align="center">24.3</td> <td align="center">5.28</td> <td
+align="center">0.46</td> <td align="center">1/4</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1</td> <td align="center">no</td> <td
+align="center">49.7</td> <td align="center">5.63</td> <td
+align="center">0.67</td> <td align="center">1/2</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1</td> <td align="center">yes</td> <td
+align="center">49.7</td> <td align="center">5.68</td> <td
+align="center">0.70</td> <td align="center">1/2</td> </tr>
+
+<tr> <td align="center" colspan="9"> <hr> </td> </tr>
+
+</table>
+
+<p> A server with active per-client concurrency limiter that replies
+with 421 and disconnects.  N is the client delivery concurrency.
+The theoretical mail deferral rate is 1/(1+roundup(1/feedback)).
+This is always 1/2 with the fixed +/-1 feedback; with the variable
+feedback variants, the defer rate decreases with increasing
+concurrency. See below for a discussion of results. </p>
+
+</blockquote>
+
+<p> The results are based on the first delivery runs only; they do
+not include any second etc. delivery attempts.  
+
+<p> The first two examples show that the feedback method matters
+little when concurrency is limited due to congestion. This is because
+the initial concurrency was already at the client's concurrency
+maximum, and because there was 10-100 times more positive than
+negative feedback.  The contribution from SMTP connection caching
+was also minor for these two examples. </p>
+
+<p> In the last example, the old +/-1 feedback scheduler defers 50%
+of the mail when confronted with an active (anvil-style) server
+concurrency limit, where the server hangs up immediately with a 421
+status (a TCP-level RST would have the same result).  Less aggressive
+feedback mechanisms fare better here, and the concurrency-dependent
+feedback fares even better at higher concurrencies than shown here,
+but they have limitations as discussed in the next section.  </p>
+
+<h2> Limitations of less-than-1 feedback </h2>
+
+<p> The delivery concurrency scheduler with less-than-1 feedback
+solves a problem with servers that have active concurrency limiters,
+but this works well only because feedback is handled in a peculiar
+manner: positive feedback increments the concurrency by 1 at the
+end of a sequence of events of length 1/feedback, while negative
+feedback decrements concurrency by 1 at the beginning of such a
+sequence. This is how Postfix adjusts quickly for overshoot without
+causing lots of mail to be deferred.  Without this difference in
+feedback treatment, less-than-1 feedback would defer 50% of the
+mail, and would be no better in this respect than the simple +/-1
+feedback scheduler.  </p>
+
+<p> Unfortunately, the same feature that corrects quickly for
+concurrency overshoot also makes the scheduler more sensitive for
+noisy negative feedback.  The reason is that one lonely negative
+feedback event has the same effect as a complete sequence of length
+1/feedback: in both cases delivery concurrency is dropped by 1
+immediately.  For example, when multiple servers are placed behind
+a load balancer on a single IP address, and 1 out of K servers fails
+to complete the SMTP handshake, a scheduler with 1/N (N = concurrency)
+feedback will stop increasing its concurrency once it reaches roughly
+K.  Even though the good servers behind the load balancer are
+perfectly capable of handling more mail, the 1/N feedback scheduler
+will linger around concurrency K. </p>
+
+<p> This problem with 1/N feedback gets worse as 1/N gets smaller.
+A workaround is to use fixed less-than-1 values for positive and
+negative feedback that limit the noise sensitivity, for example:
+positive feedback of 1/4 and negative feedback 1/10.  Of course
+using fixed feedback means concurrency growth is moderated only for
+a limited range of concurrencies.  Sites that deliver at per-destination
+concurrencies of 50 or more will require special configuration.
+</p>
+
+<h2> <a name="jobs"> Preemptive scheduling </a> </h2>
+
+<p> This is the beginning of documentation for a preemptive queue
 manager scheduling algorithm by Patrik Rak. For a long time, this
 code was made available under the name "nqmgr(8)" (new queue manager),
 as an optional module. As of Postfix 2.1 this is the default queue
-manager, which is always called "<a href="qmgr.8.html">qmgr(8)</a>". The old queue manager will
-for some time will be available under the name of "<a href="qmgr.8.html">oqmgr(8)</a>". </p>
+manager, which is always called "<a href="qmgr.8.html">qmgr(8)</a>". The old queue manager
+will for some time will be available under the name of "<a href="qmgr.8.html">oqmgr(8)</a>".
+</p>
 
-<h2>Why the old Postfix queue manager was replaced</h2>
+<h3>Why the non-preemptive Postfix queue manager was replaced</h3>
 
-<p> The old Postfix scheduler had several limitations due to
-unfortunate choices in its design. </p>
+<p> The non-preemptive Postfix scheduler had several limitations
+due to unfortunate choices in its design. </p>
 
 <ol>
 
@@ -56,7 +556,7 @@ unfortunate choices in its design. </p>
 
 </ol>
 
-<h2>How the queue manager scheduler works </h2>
+<h3>How the non-preemptive queue manager scheduler works </h3>
 
 <p> The following text is from Patrik Rak and should be read together
 with the <a href="postconf.5.html">postconf(5)</a> manual that describes each configuration
index 5bddcf7c3e0d6a3705f0bd14106056b4a234f458..a527cc44f442e65109f4addedd2259eabb6748f7 100644 (file)
@@ -19,9 +19,9 @@ MASTER(5)                                                            MASTER(5)
        process.   The  <a href="master.5.html">master.cf</a> configuration file defines how a
        client program connects to a service, and what daemon pro-
        gram  runs  when a service is requested.  Most daemon pro-
-       cesses are short-lived and terminate after serving <b><a href="postconf.5.html#max_use">max_use</a></b>
-       clients, or after inactivity for <b><a href="postconf.5.html#max_idle">max_idle</a></b> or more units of
-       time.
+       cesses are short-lived  and  terminate  voluntarily  after
+       serving  <b><a href="postconf.5.html#max_use">max_use</a></b> clients, or after inactivity for <b><a href="postconf.5.html#max_idle">max_idle</a></b>
+       or more units of time.
 
        All daemons specified here must speak  a  Postfix-internal
        protocol. In order to execute non-Postfix software use the
index f9db969c5ad1f40cdab4a5e385e7a82ced0f05ae..1c764bf7062b9215a6ad94bcb64c65ba80c4b1cb 100644 (file)
@@ -220,7 +220,7 @@ OQMGR(8)                                                              OQMGR(8)
               The default maximal number of  parallel  deliveries
               to the same destination.
 
-       <i>transport</i><b>_destination_concurrency_limit</b>
+       <b><a href="postconf.5.html#transport_destination_concurrency_limit"><i>transport</i>_destination_concurrency_limit</a></b>
               Idem, for delivery via the named message <i>transport</i>.
 
 <b>RECIPIENT SCHEDULING CONTROLS</b>
@@ -228,25 +228,27 @@ OQMGR(8)                                                              OQMGR(8)
               The default maximal number of recipients  per  mes-
               sage delivery.
 
-       <i>transport</i><b>_destination_recipient_limit</b>
+       <b><a href="postconf.5.html#transport_destination_recipient_limit"><i>transport</i>_destination_recipient_limit</a></b>
               Idem, for delivery via the named message <i>transport</i>.
 
 <b>OTHER RESOURCE AND RATE CONTROLS</b>
-       <b><a href="postconf.5.html#minimal_backoff_time">minimal_backoff_time</a> (version dependent)</b>
+       <b><a href="postconf.5.html#minimal_backoff_time">minimal_backoff_time</a> (300s)</b>
               The minimal time  between  attempts  to  deliver  a
-              deferred message.
+              deferred  message; prior to Postfix 2.4 the default
+              value was 1000s.
 
        <b><a href="postconf.5.html#maximal_backoff_time">maximal_backoff_time</a> (4000s)</b>
-              The  maximal  time  between  attempts  to deliver a
+              The maximal time  between  attempts  to  deliver  a
               deferred message.
 
        <b><a href="postconf.5.html#maximal_queue_lifetime">maximal_queue_lifetime</a> (5d)</b>
-              The maximal time a message is queued before  it  is
+              The  maximal  time a message is queued before it is
               sent back as undeliverable.
 
-       <b><a href="postconf.5.html#queue_run_delay">queue_run_delay</a> (version dependent)</b>
-              The  time between <a href="QSHAPE_README.html#deferred_queue">deferred queue</a> scans by the queue
-              manager.
+       <b><a href="postconf.5.html#queue_run_delay">queue_run_delay</a> (300s)</b>
+              The time between <a href="QSHAPE_README.html#deferred_queue">deferred queue</a> scans by the  queue
+              manager; prior to Postfix 2.4 the default value was
+              1000s.
 
        <b><a href="postconf.5.html#transport_retry_time">transport_retry_time</a> (60s)</b>
               The time between attempts by the Postfix queue man-
index 7f578a15be88307e5697d66440381101faf99614..8b7e11dec6275d6c84fb7f437eb5665f10178e6f 100644 (file)
@@ -39,7 +39,7 @@ PIPE(8)                                                                PIPE(8)
        To  prevent  Postfix  from sending multiple recipients per
        delivery request, specify
 
-           <i>transport</i><b>_destination_recipient_limit = 1</b>
+           <b><a href="postconf.5.html#transport_destination_recipient_limit"><i>transport</i>_destination_recipient_limit</a> = 1</b>
 
        in the Postfix <a href="postconf.5.html"><b>main.cf</b></a> file, where <i>transport</i> is  the  name
        in the first column of the Postfix <a href="master.5.html"><b>master.cf</b></a> entry for the
@@ -107,7 +107,7 @@ PIPE(8)                                                                PIPE(8)
               <b>O</b>      Prepend  an  "<b>X-Original-To:</b> <i>recipient</i>" mes-
                      sage header with the  recipient  address  as
                      given  to  Postfix.  Note: for this to work,
-                     the    <i>transport</i><b>_destination_recipient_limit</b>
+                     the    <b><a href="postconf.5.html#transport_destination_recipient_limit"><i>transport</i>_destination_recipient_limit</a></b>
                      must  be  1  (see  SINGLE-RECIPIENT DELIVERY
                      above for details).
 
@@ -409,19 +409,19 @@ PIPE(8)                                                                PIPE(8)
        In  the text below, <i>transport</i> is the first field in a <b>mas-</b>
        <b>ter.cf</b> entry.
 
-       <i>transport</i><b>_destination_concurrency_limit ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destina</a>-</b>
+       <b><a href="postconf.5.html#transport_destination_concurrency_limit"><i>transport</i>_destination_concurrency_limit</a> ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destina</a>-</b>
        <b><a href="postconf.5.html#default_destination_concurrency_limit">tion_concurrency_limit</a>)</b>
               Limit the number of parallel deliveries to the same
               destination,  for delivery via the named <i>transport</i>.
               The limit is enforced by the Postfix queue manager.
 
-       <i>transport</i><b>_destination_recipient_limit   ($<a href="postconf.5.html#default_destination_recipient_limit">default_destina</a>-</b>
+       <b><a href="postconf.5.html#transport_destination_recipient_limit"><i>transport</i>_destination_recipient_limit</a>   ($<a href="postconf.5.html#default_destination_recipient_limit">default_destina</a>-</b>
        <b><a href="postconf.5.html#default_destination_recipient_limit">tion_recipient_limit</a>)</b>
               Limit  the  number of recipients per message deliv-
               ery, for delivery via  the  named  <i>transport</i>.   The
               limit is enforced by the Postfix queue manager.
 
-       <i>transport</i><b>_time_limit ($<a href="postconf.5.html#command_time_limit">command_time_limit</a>)</b>
+       <b><a href="postconf.5.html#transport_time_limit"><i>transport</i>_time_limit</a> ($<a href="postconf.5.html#command_time_limit">command_time_limit</a>)</b>
               Limit  the  time  for delivery to external command,
               for delivery via the named <i>transport</i>.  The limit is
               enforced by the pipe delivery agent.
index eb47366d37435b104d10170490d8a2508e05a640..475022c8b78bf46c5f6dd83cbf206910c60a7e12 100644 (file)
@@ -83,6 +83,19 @@ the sender.  This feature is enabled with the <a href="postconf.5.html#notify_cl
 parameter.  </p>
 
 
+</DD>
+
+<DT><b><a name="<i>transport</i>_concurrency_failed_cohort_limit"><i>transport</i>_concurrency_failed_cohort_limit</a>
+(default: $<a href="postconf.5.html#default_concurrency_failed_cohort_limit">default_concurrency_failed_cohort_limit</a>)</b></DT><DD>
+
+<p> A transport-specific override for the
+<a href="postconf.5.html#default_concurrency_failed_cohort_limit">default_concurrency_failed_cohort_limit</a> parameter value, where
+<i>transport</i> is the <a href="master.5.html">master.cf</a> name of the message delivery
+transport. </p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+
 </DD>
 
 <DT><b><a name="access_map_reject_code">access_map_reject_code</a>
@@ -1362,6 +1375,17 @@ global <a href="postconf.5.html#ipc_timeout">ipc_timeout</a> parameter as well.
 </p>
 
 
+</DD>
+
+<DT><b><a name="concurrency_feedback_debug">concurrency_feedback_debug</a>
+(default: no)</b></DT><DD>
+
+<p> Make the queue manager's feedback algorithm verbose for performance
+analysis purposes. </p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+
 </DD>
 
 <DT><b><a name="config_directory">config_directory</a>
@@ -1549,6 +1573,143 @@ Example:
 </pre>
 
 
+</DD>
+
+<DT><b><a name="default_concurrency_failed_cohort_limit">default_concurrency_failed_cohort_limit</a>
+(default: 1)</b></DT><DD>
+
+<p> How many pseudo-cohorts must suffer connection or handshake
+failure before a specific destination is considered unavailable
+(and further delivery is suspended). Specify zero to disable this
+feature. A destination's pseudo-cohort failure count is reset each
+time a delivery completes without connection or handshake failure
+for that specific destination. </p>
+
+<p> A pseudo-cohort is the number of deliveries equal to a destination's
+delivery concurrency. </p>
+
+<p> Use <a href="postconf.5.html#transport_concurrency_failed_cohort_limit"><i>transport</i>_concurrency_failed_cohort_limit</a> to specify
+a transport-specific override, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
+name of the message delivery transport. </p>
+
+<p> This feature is available in Postfix 2.5. The default setting
+is compatible with earlier Postfix versions. </p>
+
+
+</DD>
+
+<DT><b><a name="default_concurrency_negative_feedback">default_concurrency_negative_feedback</a>
+(default: 1)</b></DT><DD>
+
+<p> The per-destination amount of negative delivery concurrency
+feedback, after a delivery completes with a connection or handshake
+failure. Feedback values are in range 0..1 inclusive. With negative
+feedback, concurrency is decremented at the beginning of a sequence
+of length 1/feedback. This is unlike positive feedback, where
+concurrency is incremented at the end of a sequence of length
+1/feedback. </p>
+
+<p> As of Postfix version 2.5, negative feedback cannot reduce
+delivery concurrency to zero.  Instead, a destination is marked
+dead (further delivery suspended) after the failed pseudo-cohort
+count reaches $<a href="postconf.5.html#default_concurrency_failed_cohort_limit">default_concurrency_failed_cohort_limit</a> (or
+$<a href="postconf.5.html#transport_concurrency_failed_cohort_limit"><i>transport</i>_concurrency_failed_cohort_limit</a>). To make the
+scheduler completely immune to connection or handshake failures,
+specify a zero feedback value and a zero failed pseudo-cohort limit.
+</p>
+
+<p> Specify one of the following forms: </p>
+
+<dl>
+
+<dt> <b><i>number</i> </b> </dt>
+
+<dt> <b><i>number</i> / <i>number</i> </b> </dt>
+
+<dd> Constant feedback. The value must be in the range 0..1 inclusive.
+The default setting of "1" is compatible with Postfix versions
+before 2.5, where a destination's delivery concurrency is throttled
+down to zero (and further delivery suspended) after a single failed
+pseudo-cohort. </dd>
+
+<dt> <b><i>number</i> / concurrency </b> </dt>
+
+<dd> Variable feedback of "<i>number</i> / (delivery concurrency)".
+The <i>number</i> must be in the range 0..1 inclusive. With
+<i>number</i> equal to "1", a destination's delivery concurrency
+is decremented by 1 after each failed pseudo-cohort.  </dd>
+
+<dt> <b><i>number</i> / sqrt_concurrency </b> </dt>
+
+<dd> Variable feedback of "<i>number</i> / sqrt(delivery concurrency)".
+The <i>number</i> must be in the range 0..1 inclusive. This setting
+may be removed in a future version.  </dd>
+
+</dl>
+
+<p> A pseudo-cohort is the number of deliveries equal to a destination's
+delivery concurrency. </p>
+
+<p> Use <a href="postconf.5.html#transport_concurrency_positive_feedback"><i>transport</i>_concurrency_negative_feedback</a> to specify
+a transport-specific override, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
+name of the message delivery transport. </p>
+
+<p> This feature is available in Postfix 2.5. The default setting
+is compatible with earlier Postfix versions. </p>
+
+
+</DD>
+
+<DT><b><a name="default_concurrency_positive_feedback">default_concurrency_positive_feedback</a>
+(default: 1)</b></DT><DD>
+
+<p> The per-destination amount of positive delivery concurrency
+feedback, after a delivery completes without connection or handshake
+failure. Feedback values are in the range 0..1 inclusive.  The
+concurrency increases until it reaches the per-destination maximal
+concurrency limit. With positive feedback, concurrency is incremented
+at the end of a sequence with length 1/feedback. This is unlike
+negative feedback, where concurrency is decremented at the start
+of a sequence of length 1/feedback. </p>
+
+<p> Specify one of the following forms:  </p>
+
+<dl>
+
+<dt> <b><i>number</i> </b> </dt>
+
+<dt> <b><i>number</i> / <i>number</i> </b> </dt>
+
+<dd> Constant feedback.  The value must be in the range 0..1
+inclusive. The default setting of "1" is compatible with Postfix
+versions before 2.5, where a destination's delivery concurrency
+doubles after each successful pseudo-cohort.  </dd>
+
+<dt> <b><i>number</i> / concurrency </b> </dt>
+
+<dd> Variable feedback of "<i>number</i> / (delivery concurrency)".
+The <i>number</i> must be in the range 0..1 inclusive. With
+<i>number</i> equal to "1", a destination's delivery concurrency
+is incremented by 1 after each successful pseudo-cohort.  </dd>
+
+<dt> <b><i>number</i> / sqrt_concurrency </b> </dt>
+
+<dd> Variable feedback of "<i>number</i> / sqrt(delivery concurrency)".
+The <i>number</i> must be in the range 0..1 inclusive. This setting
+may be removed in a future version.  </dd>
+
+</dl>
+
+<p> A pseudo-cohort is the number of deliveries equal to a destination's
+delivery concurrency. </p>
+
+<p> Use <a href="postconf.5.html#transport_concurrency_positive_feedback"><i>transport</i>_concurrency_positive_feedback</a> to specify
+a transport-specific override, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
+name of the message delivery transport. </p>
+
+<p> This feature is available in Postfix 2.5 and later.  </p>
+
+
 </DD>
 
 <DT><b><a name="default_database_type">default_database_type</a>
@@ -1897,6 +2058,8 @@ make sure the recipients are refilled in timely manner even when
 $<a href="postconf.5.html#default_recipient_refill_limit">default_recipient_refill_limit</a> is too high for too slow deliveries.
 </p>
 
+<p> This feature is available in Postfix 2.4 and later. </p>
+
 
 </DD>
 
@@ -1911,6 +2074,8 @@ $<a href="postconf.5.html#default_recipient_refill_delay">default_recipient_refi
 lower than this when this limit is too high for too slow deliveries.
 </p>
 
+<p> This feature is available in Postfix 2.4 and later. </p>
+
 
 </DD>
 
@@ -3023,6 +3188,10 @@ to the same destination. This limit applies to delivery via <a href="smtp.8.html
 and via the <a href="pipe.8.html">pipe(8)</a> and <a href="virtual.8.html">virtual(8)</a> delivery agents.
 </p>
 
+<p> Use <a href="postconf.5.html#transport_initial_destination_concurrency"><i>transport</i>_initial_destination_concurrency</a> to specify
+a transport-specific override, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
+name of the message delivery transport (Postfix 2.5 and later). </p>
+
 <p>
 Warning: with concurrency of 1, one bad message can be enough to
 block all mail to a site.
@@ -4198,28 +4367,28 @@ $<a href="postconf.5.html#mynetworks">mynetworks</a>. This setting will not prev
 address rewriting when mail from a remote client is forwarded by
 a neighboring system.  </dd>
 
-<dt><b> <a href="postconf.5.html#permit_sasl_authenticated">permit_sasl_authenticated</a> </b></dt>
+<dt><b><a href="postconf.5.html#permit_sasl_authenticated">permit_sasl_authenticated</a> </b></dt>
 
 <dd> Append the domain name in $<a href="postconf.5.html#myorigin">myorigin</a> or $<a href="postconf.5.html#mydomain">mydomain</a> when the
 client is successfully authenticated via the <a href="http://tools.ietf.org/html/rfc4954">RFC 4954</a> (AUTH)
 protocol. </dd>
 
-<dt><b> <a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a> </b></dt>
+<dt><b><a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a> </b></dt>
 
 <dd> Append the domain name in $<a href="postconf.5.html#myorigin">myorigin</a> or $<a href="postconf.5.html#mydomain">mydomain</a> when the
 client TLS certificate is successfully verified, and the client
 certificate fingerprint is listed in $<a href="postconf.5.html#relay_clientcerts">relay_clientcerts</a>.  </dd>
 
-<dt><b> <a href="postconf.5.html#permit_tls_all_clientcerts">permit_tls_all_clientcerts</a> </b></dt>
+<dt><b><a href="postconf.5.html#permit_tls_all_clientcerts">permit_tls_all_clientcerts</a> </b></dt>
 
 <dd> Append the domain name in $<a href="postconf.5.html#myorigin">myorigin</a> or $<a href="postconf.5.html#mydomain">mydomain</a> when the
 client TLS certificate is successfully verified, regardless of
 whether it is listed on the server, and regardless of the certifying
 authority. </dd>
 
-<dt><b> <a name="check_address_map">check_address_map</a> <i><a href="DATABASE_README.html">type:table</a></i> </b></dt>
+<dt><b><a name="check_address_map">check_address_map</a> <i><a href="DATABASE_README.html">type:table</a></i> </b></dt>
 
-<dt><b> <i><a href="DATABASE_README.html">type:table</a></i> </b></dt>
+<dt><b><i><a href="DATABASE_README.html">type:table</a></i> </b></dt>
 
 <dd> Append the domain name in $<a href="postconf.5.html#myorigin">myorigin</a> or $<a href="postconf.5.html#mydomain">mydomain</a> when the
 client IP address matches the specified lookup table.
@@ -5928,18 +6097,6 @@ This feature is available in Postfix 2.0 and later.
 </p>
 
 
-</DD>
-
-<DT><b><a name="qmgr_concurrency_feedback_debug">qmgr_concurrency_feedback_debug</a>
-(default: no)</b></DT><DD>
-
-<p> Make the queue manager's feedback algorithm verbose for performance
-analysis purposes. </p>
-
-<p> This feature is temporarily available in Postfix 2.5; its final
-form is likely to change. </p>
-
-
 </DD>
 
 <DT><b><a name="qmgr_fudge_factor">qmgr_fudge_factor</a>
@@ -5991,136 +6148,6 @@ parameter is 1.
 </p>
 
 
-</DD>
-
-<DT><b><a name="qmgr_negative_concurrency_feedback_hysteresis">qmgr_negative_concurrency_feedback_hysteresis</a>
-(default: 1)</b></DT><DD>
-
-<p> The per-destination integer amount of negative concurrency
-feedback that must accumulate between negative adjustments of a
-destination's delivery concurrency.  The concurrency adjustment is
-equal in size to the negative hysteresis value, and is applied at
-the <b>beginning</b> of a cycle of (hysteresis / feedback) steps.
-At that same time, the destination's positive feedback hysteresis
-cycle is reset to its beginning.  </p>
-
-<p> This feature is temporarily available in Postfix 2.5; its final
-form is likely to change. The default setting is compatible with
-earlier Postfix versions. </p>
-
-
-</DD>
-
-<DT><b><a name="qmgr_negative_concurrency_feedback_style">qmgr_negative_concurrency_feedback_style</a>
-(default: fixed_1)</b></DT><DD>
-
-<p> The per-destination amount of negative delivery concurrency
-feedback, after a delivery completes with a connection or handshake
-failure.  </p>
-
-<dl>
-
-<dt> <b> inverse_concurrency </b> </dt> <dd> Variable feedback of
-1 / (delivery concurrency). With this setting, and with
-"<a href="postconf.5.html#qmgr_negative_concurrency_feedback_hysteresis">qmgr_negative_concurrency_feedback_hysteresis</a> = 1", the destination's
-delivery concurrency is decremented by 1 after each failed
-pseudo-cohort, and the destination is marked dead (further delivery
-suspended) after the failed pseudo-cohort count reaches
-$<a href="postconf.5.html#qmgr_sacrificial_cohorts">qmgr_sacrificial_cohorts</a>.  </dd>
-
-<dt> <b> inverse_sqrt_concurrency </b> </dt> <dd> Variable feedback
-of 1 / (square root of delivery concurrency). This is an intermediate
-form between the other two. It lacks sound justification, and is a
-candidate for removal. </dd>
-
-<dt> <b> fixed_1 </b> </dt> <dd> Constant feedback of 1. This setting
-is compatible with Postfix versions before 2.5, where a destination's
-delivery concurrency is throttled down to zero (and further delivery
-suspended) after a single failed pseudo-cohort.  </dd>
-
-</dl>
-
-<p> A pseudo-cohort is a number of deliveries equal to the destination's
-delivery concurrency. </p>
-
-<p> This feature is temporarily available in Postfix 2.5; its final
-form is likely to change. The default setting is compatible with
-earlier Postfix versions. </p>
-
-
-</DD>
-
-<DT><b><a name="qmgr_positive_concurrency_feedback_hysteresis">qmgr_positive_concurrency_feedback_hysteresis</a>
-(default: 1)</b></DT><DD>
-
-<p> The per-destination integer amount of positive concurrency
-feedback that must accumulate before positive adjustments of a
-destination's delivery concurrency.  The concurrency adjustment is
-equal in size to the positive hysteresis value, and is applied at
-the <b>end</b> of a cycle of (hysteresis / feedback) steps. At that
-same time, the destination's negative feedback hysteresis cycle is
-reset to its beginning. </p>
-
-<p> This feature is temporarily available in Postfix 2.5; its final
-form is likely to change. The default setting is compatible with
-earlier Postfix versions. </p>
-
-
-</DD>
-
-<DT><b><a name="qmgr_positive_concurrency_feedback_style">qmgr_positive_concurrency_feedback_style</a>
-(default: fixed_1)</b></DT><DD>
-
-<p> The per-destination amount of positive delivery concurrency
-feedback, after a delivery completes without connection or handshake
-failure.  </p>
-
-<dl>
-
-<dt> <b> inverse_concurrency </b> </dt> <dd> Variable feedback of
-1 / (delivery concurrency). With this setting, and with
-"<a href="postconf.5.html#qmgr_positive_concurrency_feedback_hysteresis">qmgr_positive_concurrency_feedback_hysteresis</a> = 1", the destination's
-delivery concurrency is incremented by 1 after each successful
-pseudo-cohort, until it reaches the per-destination maximal concurrency
-limit.  </dd>
-
-<dt> <b> inverse_sqrt_concurrency </b> </dt> <dd> Variable feedback
-of 1 / (square root of delivery concurrency). This is an intermediate
-form between the other two. It lacks sound justification, and is a
-candidate for removal. </dd>
-
-<dt> <b> fixed_1 </b> </dt> <dd> Constant feedback of 1. This setting
-is compatible with Postfix versions before 2.5, where a destination's
-delivery concurrency is doubled after each successful pseudo-cohort,
-until it reaches the per-destination maximal concurrency limit.
-</dd>
-
-</dl>
-
-<p> A pseudo-cohort is a number of deliveries equal to the destination's
-delivery concurrency. </p>
-
-<p> This feature is temporarily available in Postfix 2.5. The default
-setting is compatible with earlier Postfix versions. </p>
-
-
-</DD>
-
-<DT><b><a name="qmgr_sacrificial_cohorts">qmgr_sacrificial_cohorts</a>
-(default: 1)</b></DT><DD>
-
-<p> How many pseudo-cohorts must suffer connection or handshake
-failure before a specific destination is considered unavailable
-(and further delivery is suspended). A pseudo-cohort is a number
-of deliveries equal to a destination's concurrency. The pseudo-cohort
-failure count is reset each time a delivery completes without
-connection or handshake failure for that specific destination.  </p>
-
-<p> This feature is temporarily available in Postfix 2.5; its final
-form is likely to change. The default setting is compatible with
-earlier Postfix versions. </p>
-
-
 </DD>
 
 <DT><b><a name="qmqpd_authorized_clients">qmqpd_authorized_clients</a>
@@ -12176,6 +12203,106 @@ This feature is available in Postfix 2.1 and later.
 </p>
 
 
+</DD>
+
+<DT><b><a name="transport_concurrency_negative_feedback">transport_concurrency_negative_feedback</a>
+(default: $<a href="postconf.5.html#default_concurrency_negative_feedback">default_concurrency_negative_feedback</a>)</b></DT><DD>
+
+<p> A transport-specific override for the
+<a href="postconf.5.html#default_concurrency_negative_feedback">default_concurrency_negative_feedback</a> parameter value, where
+<i>transport</i> is the <a href="master.5.html">master.cf</a> name of the message delivery
+transport. </p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="transport_concurrency_positive_feedback">transport_concurrency_positive_feedback</a>
+(default: $<a href="postconf.5.html#default_concurrency_positive_feedback">default_concurrency_positive_feedback</a>)</b></DT><DD>
+
+<p> A transport-specific override for the
+<a href="postconf.5.html#default_concurrency_positive_feedback">default_concurrency_positive_feedback</a> parameter value, where
+<i>transport</i> is the <a href="master.5.html">master.cf</a> name of the message delivery
+transport. </p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="transport_delivery_slot_cost">transport_delivery_slot_cost</a>
+(default: $<a href="postconf.5.html#default_delivery_slot_cost">default_delivery_slot_cost</a>)</b></DT><DD>
+
+<p> A transport-specific override for the <a href="postconf.5.html#default_delivery_slot_cost">default_delivery_slot_cost</a>
+parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a> name of
+the message delivery transport. </p>
+
+
+</DD>
+
+<DT><b><a name="transport_delivery_slot_discount">transport_delivery_slot_discount</a>
+(default: $<a href="postconf.5.html#default_delivery_slot_discount">default_delivery_slot_discount</a>)</b></DT><DD>
+
+<p> A transport-specific override for the <a href="postconf.5.html#default_delivery_slot_discount">default_delivery_slot_discount</a>
+parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a> name of
+the message delivery transport. </p>
+
+
+</DD>
+
+<DT><b><a name="transport_delivery_slot_loan">transport_delivery_slot_loan</a>
+(default: $<a href="postconf.5.html#default_delivery_slot_loan">default_delivery_slot_loan</a>)</b></DT><DD>
+
+<p> A transport-specific override for the <a href="postconf.5.html#default_delivery_slot_loan">default_delivery_slot_loan</a>
+parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a> name of
+the message delivery transport. </p>
+
+
+</DD>
+
+<DT><b><a name="transport_destination_concurrency_limit">transport_destination_concurrency_limit</a>
+(default: $<a href="postconf.5.html#default_destination_concurrency_limit">default_destination_concurrency_limit</a>)</b></DT><DD>
+
+<p> A transport-specific override for the
+<a href="postconf.5.html#default_destination_concurrency_limit">default_destination_concurrency_limit</a> parameter value, where
+<i>transport</i> is the <a href="master.5.html">master.cf</a> name of the message delivery
+transport. </p>
+
+
+</DD>
+
+<DT><b><a name="transport_destination_recipient_limit">transport_destination_recipient_limit</a>
+(default: $<a href="postconf.5.html#default_destination_concurrency_limit">default_destination_concurrency_limit</a>)</b></DT><DD>
+
+<p> A transport-specific override for the
+<a href="postconf.5.html#default_destination_recipient_limit">default_destination_recipient_limit</a> parameter value, where
+<i>transport</i> is the <a href="master.5.html">master.cf</a> name of the message delivery
+transport. </p>
+
+
+</DD>
+
+<DT><b><a name="transport_extra_recipient_limit">transport_extra_recipient_limit</a>
+(default: $<a href="postconf.5.html#default_extra_recipient_limit">default_extra_recipient_limit</a>)</b></DT><DD>
+
+<p> A transport-specific override for the <a href="postconf.5.html#default_extra_recipient_limit">default_extra_recipient_limit</a>
+parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a> name of
+the message delivery transport. </p>
+
+
+</DD>
+
+<DT><b><a name="transport_initial_destination_concurrency">transport_initial_destination_concurrency</a>
+(default: $<a href="postconf.5.html#initial_destination_concurrency">initial_destination_concurrency</a>)</b></DT><DD>
+
+<p> A transport-specific override for the <a href="postconf.5.html#initial_destination_concurrency">initial_destination_concurrency</a>
+parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a> name of
+the message delivery transport. </p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+
 </DD>
 
 <DT><b><a name="transport_maps">transport_maps</a>
@@ -12205,6 +12332,50 @@ Examples:
 </pre>
 
 
+</DD>
+
+<DT><b><a name="transport_minimum_delivery_slots">transport_minimum_delivery_slots</a>
+(default: $<a href="postconf.5.html#default_minimum_delivery_slots">default_minimum_delivery_slots</a>)</b></DT><DD>
+
+<p> A transport-specific override for the <a href="postconf.5.html#default_minimum_delivery_slots">default_minimum_delivery_slots</a>
+parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a> name of
+the message delivery transport. </p>
+
+
+</DD>
+
+<DT><b><a name="transport_recipient_limit">transport_recipient_limit</a>
+(default: $<a href="postconf.5.html#default_recipient_limit">default_recipient_limit</a>)</b></DT><DD>
+
+<p> A transport-specific override for the <a href="postconf.5.html#default_recipient_limit">default_recipient_limit</a>
+parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a> name of
+the message delivery transport. </p>
+
+
+</DD>
+
+<DT><b><a name="transport_recipient_refill_delay">transport_recipient_refill_delay</a>
+(default: $<a href="postconf.5.html#default_recipient_refill_delay">default_recipient_refill_delay</a>)</b></DT><DD>
+
+<p> A transport-specific override for the <a href="postconf.5.html#default_recipient_refill_delay">default_recipient_refill_delay</a>
+parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a> name of
+the message delivery transport. </p>
+
+<p> This feature is available in Postfix 2.4 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="transport_recipient_refill_limit">transport_recipient_refill_limit</a>
+(default: $<a href="postconf.5.html#default_recipient_refill_limit">default_recipient_refill_limit</a>)</b></DT><DD>
+
+<p> A transport-specific override for the <a href="postconf.5.html#default_recipient_refill_limit">default_recipient_refill_limit</a>
+parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a> name of
+the message delivery transport. </p>
+
+<p> This feature is available in Postfix 2.4 and later. </p>
+
+
 </DD>
 
 <DT><b><a name="transport_retry_time">transport_retry_time</a>
@@ -12221,6 +12392,16 @@ The default time unit is s (seconds).
 </p>
 
 
+</DD>
+
+<DT><b><a name="transport_time_limit">transport_time_limit</a>
+(default: $<a href="postconf.5.html#command_time_limit">command_time_limit</a>)</b></DT><DD>
+
+<p> A transport-specific override for the <a href="postconf.5.html#command_time_limit">command_time_limit</a> parameter
+value, where <i>transport</i> is the <a href="master.5.html">master.cf</a> name of the message
+delivery transport. </p>
+
+
 </DD>
 
 <DT><b><a name="trigger_timeout">trigger_timeout</a>
index 245cffdd42ef5ae0e6aab3987f89a3a33fd2def8..87df9f99b1c11ea615fdeac55d12f476a9c1f905 100644 (file)
@@ -220,15 +220,15 @@ QMGR(8)                                                                QMGR(8)
               The default per-transport upper limit on the number
               of in-memory recipients.
 
-       <i>transport</i><b>_recipient_limit ($<a href="postconf.5.html#default_recipient_limit">default_recipient_limit</a>)</b>
+       <b><a href="postconf.5.html#transport_recipient_limit"><i>transport</i>_recipient_limit</a> ($<a href="postconf.5.html#default_recipient_limit">default_recipient_limit</a>)</b>
               Idem, for delivery via the named message <i>transport</i>.
 
        <b><a href="postconf.5.html#default_extra_recipient_limit">default_extra_recipient_limit</a> (1000)</b>
               The default value for the extra per-transport limit
               imposed on the number of in-memory recipients.
 
-       <i>transport</i><b>_extra_recipient_limit    ($default_extra_recipi-</b>
-       <b>ent_limit)</b>
+       <b><a href="postconf.5.html#transport_extra_recipient_limit"><i>transport</i>_extra_recipient_limit</a>    ($<a href="postconf.5.html#default_extra_recipient_limit">default_extra_recipi</a>-</b>
+       <b><a href="postconf.5.html#default_extra_recipient_limit">ent_limit</a>)</b>
               Idem, for delivery via the named message <i>transport</i>.
 
        Available in Postfix version 2.4 and later:
@@ -237,16 +237,16 @@ QMGR(8)                                                                QMGR(8)
               The  default  per-transport  limit on the number of
               recipients refilled at once.
 
-       <i>transport</i><b>_recipient_refill_limit         ($default_recipi-</b>
-       <b>ent_refill_limit)</b>
+       <b><a href="postconf.5.html#transport_recipient_refill_limit"><i>transport</i>_recipient_refill_limit</a>         ($<a href="postconf.5.html#default_recipient_refill_limit">default_recipi</a>-</b>
+       <b><a href="postconf.5.html#default_recipient_refill_limit">ent_refill_limit</a>)</b>
               Idem, for delivery via the named message <i>transport</i>.
 
        <b><a href="postconf.5.html#default_recipient_refill_delay">default_recipient_refill_delay</a> (5s)</b>
               The  default  per-transport  maximum  delay between
               recipients refills.
 
-       <i>transport</i><b>_recipient_refill_delay         ($default_recipi-</b>
-       <b>ent_refill_delay)</b>
+       <b><a href="postconf.5.html#transport_recipient_refill_delay"><i>transport</i>_recipient_refill_delay</a>         ($<a href="postconf.5.html#default_recipient_refill_delay">default_recipi</a>-</b>
+       <b><a href="postconf.5.html#default_recipient_refill_delay">ent_refill_delay</a>)</b>
               Idem, for delivery via the named message <i>transport</i>.
 
 <b>DELIVERY CONCURRENCY CONTROLS</b>
@@ -258,48 +258,55 @@ QMGR(8)                                                                QMGR(8)
               The default maximal number of  parallel  deliveries
               to the same destination.
 
-       <i>transport</i><b>_destination_concurrency_limit ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destina</a>-</b>
+       <b><a href="postconf.5.html#transport_destination_concurrency_limit"><i>transport</i>_destination_concurrency_limit</a> ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destina</a>-</b>
        <b><a href="postconf.5.html#default_destination_concurrency_limit">tion_concurrency_limit</a>)</b>
               Idem, for delivery via the named message <i>transport</i>.
 
-       <b><a href="postconf.5.html#qmgr_concurrency_feedback_debug">qmgr_concurrency_feedback_debug</a> (no)</b>
-              Make the queue manager's feedback algorithm verbose
-              for performance analysis purposes.
+       Available in Postfix version 2.5 and later:
 
-       <b><a href="postconf.5.html#qmgr_negative_concurrency_feedback_hysteresis">qmgr_negative_concurrency_feedback_hysteresis</a> (1)</b>
-              The per-destination integer amount of negative con-
-              currency feedback that must accumulate between neg-
-              ative  adjustments of a destination's delivery con-
-              currency.
+       <b><a href="postconf.5.html#transport_initial_destination_concurrency"><i>transport</i>_initial_destination_concurrency</a> ($<a href="postconf.5.html#initial_destination_concurrency">initial_desti</a>-</b>
+       <b><a href="postconf.5.html#initial_destination_concurrency">nation_concurrency</a>)</b>
+              Initial concurrency for delivery via the named mes-
+              sage <i>transport</i>.
 
-       <b><a href="postconf.5.html#qmgr_negative_concurrency_feedback_style">qmgr_negative_concurrency_feedback_style</a> (fixed_1)</b>
-              The per-destination  amount  of  negative  delivery
-              concurrency  feedback,  after  a delivery completes
-              with a connection or handshake failure.
+       <b><a href="postconf.5.html#default_concurrency_failed_cohort_limit">default_concurrency_failed_cohort_limit</a> (1)</b>
+              How  many  pseudo-cohorts must suffer connection or
+              handshake failure before a specific destination  is
+              considered  unavailable  (and  further  delivery is
+              suspended).
 
-       <b><a href="postconf.5.html#qmgr_positive_concurrency_feedback_hysteresis">qmgr_positive_concurrency_feedback_hysteresis</a> (1)</b>
-              The per-destination integer amount of positive con-
-              currency feedback that must accumulate before posi-
-              tive adjustments of a destination's  delivery  con-
-              currency.
+       <b><a href="postconf.5.html#transport_concurrency_failed_cohort_limit"><i>transport</i>_concurrency_failed_cohort_limit</a>   ($<a href="postconf.5.html#default_concurrency_failed_cohort_limit">default_con</a>-</b>
+       <b><a href="postconf.5.html#default_concurrency_failed_cohort_limit">currency_failed_cohort_limit</a>)</b>
+              Idem, for delivery via the named message <i>transport</i>.
 
-       <b><a href="postconf.5.html#qmgr_positive_concurrency_feedback_style">qmgr_positive_concurrency_feedback_style</a> (fixed_1)</b>
-              The  per-destination  amount  of  positive delivery
+       <b><a href="postconf.5.html#default_concurrency_negative_feedback">default_concurrency_negative_feedback</a> (1)</b>
+              The  per-destination  amount  of  negative delivery
               concurrency feedback, after  a  delivery  completes
+              with a connection or handshake failure.
+
+       <b><a href="postconf.5.html#transport_concurrency_positive_feedback"><i>transport</i>_concurrency_negative_feedback</a>  ($<a href="postconf.5.html#default_concurrency_negative_feedback">default_concur</a>-</b>
+       <b><a href="postconf.5.html#default_concurrency_negative_feedback">rency_negative_feedback</a>)</b>
+              Idem, for delivery via the named message <i>transport</i>.
+
+       <b><a href="postconf.5.html#default_concurrency_positive_feedback">default_concurrency_positive_feedback</a> (1)</b>
+              The per-destination  amount  of  positive  delivery
+              concurrency  feedback,  after  a delivery completes
               without connection or handshake failure.
 
-       <b><a href="postconf.5.html#qmgr_sacrificial_cohorts">qmgr_sacrificial_cohorts</a> (1)</b>
-              How  many  pseudo-cohorts must suffer connection or
-              handshake failure before a specific destination  is
-              considered  unavailable  (and  further  delivery is
-              suspended).
+       <b><a href="postconf.5.html#transport_concurrency_positive_feedback"><i>transport</i>_concurrency_positive_feedback</a>  ($<a href="postconf.5.html#default_concurrency_positive_feedback">default_concur</a>-</b>
+       <b><a href="postconf.5.html#default_concurrency_positive_feedback">rency_positive_feedback</a>)</b>
+              Idem, for delivery via the named message <i>transport</i>.
+
+       <b><a href="postconf.5.html#concurrency_feedback_debug">concurrency_feedback_debug</a> (no)</b>
+              Make the queue manager's feedback algorithm verbose
+              for performance analysis purposes.
 
 <b>RECIPIENT SCHEDULING CONTROLS</b>
        <b><a href="postconf.5.html#default_destination_recipient_limit">default_destination_recipient_limit</a> (50)</b>
               The default maximal number of recipients  per  mes-
               sage delivery.
 
-       <i>transport</i><b>_destination_recipient_limit   ($<a href="postconf.5.html#default_destination_recipient_limit">default_destina</a>-</b>
+       <b><a href="postconf.5.html#transport_destination_recipient_limit"><i>transport</i>_destination_recipient_limit</a>   ($<a href="postconf.5.html#default_destination_recipient_limit">default_destina</a>-</b>
        <b><a href="postconf.5.html#default_destination_recipient_limit">tion_recipient_limit</a>)</b>
               Idem, for delivery via the named message <i>transport</i>.
 
@@ -309,7 +316,7 @@ QMGR(8)                                                                QMGR(8)
               allowed  to  preempt  delivery  of one message with
               another.
 
-       <i>transport</i><b>_delivery_slot_cost ($<a href="postconf.5.html#default_delivery_slot_cost">default_delivery_slot_cost</a>)</b>
+       <b><a href="postconf.5.html#transport_delivery_slot_cost"><i>transport</i>_delivery_slot_cost</a> ($<a href="postconf.5.html#default_delivery_slot_cost">default_delivery_slot_cost</a>)</b>
               Idem, for delivery via the named message <i>transport</i>.
 
        <b><a href="postconf.5.html#default_minimum_delivery_slots">default_minimum_delivery_slots</a> (3)</b>
@@ -317,7 +324,7 @@ QMGR(8)                                                                QMGR(8)
               invoke the Postfix queue manager's scheduling algo-
               rithm at all.
 
-       <i>transport</i><b>_minimum_delivery_slots  ($<a href="postconf.5.html#default_minimum_delivery_slots">default_minimum_deliv</a>-</b>
+       <b><a href="postconf.5.html#transport_minimum_delivery_slots"><i>transport</i>_minimum_delivery_slots</a>  ($<a href="postconf.5.html#default_minimum_delivery_slots">default_minimum_deliv</a>-</b>
        <b><a href="postconf.5.html#default_minimum_delivery_slots">ery_slots</a>)</b>
               Idem, for delivery via the named message <i>transport</i>.
 
@@ -325,7 +332,7 @@ QMGR(8)                                                                QMGR(8)
               The default value  for  transport-specific  _deliv-
               ery_slot_discount settings.
 
-       <i>transport</i><b>_delivery_slot_discount          ($<a href="postconf.5.html#default_delivery_slot_discount">default_deliv</a>-</b>
+       <b><a href="postconf.5.html#transport_delivery_slot_discount"><i>transport</i>_delivery_slot_discount</a>          ($<a href="postconf.5.html#default_delivery_slot_discount">default_deliv</a>-</b>
        <b><a href="postconf.5.html#default_delivery_slot_discount">ery_slot_discount</a>)</b>
               Idem, for delivery via the named message <i>transport</i>.
 
@@ -333,7 +340,7 @@ QMGR(8)                                                                QMGR(8)
               The default value  for  transport-specific  _deliv-
               ery_slot_loan settings.
 
-       <i>transport</i><b>_delivery_slot_loan ($<a href="postconf.5.html#default_delivery_slot_loan">default_delivery_slot_loan</a>)</b>
+       <b><a href="postconf.5.html#transport_delivery_slot_loan"><i>transport</i>_delivery_slot_loan</a> ($<a href="postconf.5.html#default_delivery_slot_loan">default_delivery_slot_loan</a>)</b>
               Idem, for delivery via the named message <i>transport</i>.
 
 <b>OTHER RESOURCE AND RATE CONTROLS</b>
index afd13628bd65cf8a0b66eb18fe2794439ac69d29..75c825a902def41ece585049950a652c5422e17d 100644 (file)
@@ -77,7 +77,7 @@ SPAWN(8)                                                              SPAWN(8)
        entry in the <a href="master.5.html"><b>master.cf</b></a> file.
 
 <b>RESOURCE AND RATE CONTROL</b>
-       <i>transport</i><b>_time_limit ($<a href="postconf.5.html#command_time_limit">command_time_limit</a>)</b>
+       <b><a href="postconf.5.html#transport_time_limit"><i>transport</i>_time_limit</a> ($<a href="postconf.5.html#command_time_limit">command_time_limit</a>)</b>
               The  amount  of  time the command is allowed to run
               before it is terminated.
 
index 632a5af0feec06fe344b414d5fa0ab9f52234a88..7be2b2e8c72b5e7ae62caf1b808d440d5b29ba1a 100644 (file)
@@ -17,9 +17,9 @@ run in the background under control of the \fBmaster\fR(8)
 process.  The master.cf configuration file defines how a
 client program connects to a service, and what daemon
 program runs when a service is requested.  Most daemon
-processes are short-lived and terminate after serving
-\fBmax_use\fR clients, or after inactivity for \fBmax_idle\fR
-or more units of time.
+processes are short-lived and terminate voluntarily after
+serving \fBmax_use\fR clients, or after inactivity for
+\fBmax_idle\fR or more units of time.
 
 All daemons specified here must speak a Postfix-internal
 protocol. In order to execute non-Postfix software use the
index bc75192dd4834ea4fe20ffca5a5b9c216ec14a79..6936e4e742d6fbeff2b37b369223a38f934d6e7e 100644 (file)
@@ -63,6 +63,15 @@ operation of the mail system.
 The recipient of undeliverable mail that cannot be returned to
 the sender.  This feature is enabled with the notify_classes
 parameter.
+<DT>\fB\fItransport\fR_concurrency_failed_cohort_limit
+(default: $default_concurrency_failed_cohort_limit)\fR</DT><DD>
+.PP
+A transport-specific override for the
+default_concurrency_failed_cohort_limit parameter value, where
+\fItransport\fR is the master.cf name of the message delivery
+transport.
+.PP
+This feature is available in Postfix 2.5 and later.
 .SH access_map_reject_code (default: 554)
 The numerical Postfix SMTP server response code when a client
 is rejected by an \fBaccess\fR(5) map restriction.
@@ -749,6 +758,11 @@ delivery by the \fBpipe\fR(8) delivery agent.
 .PP
 Note: if you set this time limit to a large value you must update the
 global ipc_timeout parameter as well.
+.SH concurrency_feedback_debug (default: no)
+Make the queue manager's feedback algorithm verbose for performance
+analysis purposes.
+.PP
+This feature is available in Postfix 2.5 and later.
 .SH config_directory (default: see "postconf -d" output)
 The default location of the Postfix main.cf and master.cf
 configuration files. This can be overruled via the following
@@ -852,6 +866,102 @@ debugger_command =
 .fi
 .ad
 .ft R
+.SH default_concurrency_failed_cohort_limit (default: 1)
+How many pseudo-cohorts must suffer connection or handshake
+failure before a specific destination is considered unavailable
+(and further delivery is suspended). Specify zero to disable this
+feature. A destination's pseudo-cohort failure count is reset each
+time a delivery completes without connection or handshake failure
+for that specific destination.
+.PP
+A pseudo-cohort is the number of deliveries equal to a destination's
+delivery concurrency.
+.PP
+Use \fItransport\fR_concurrency_failed_cohort_limit to specify
+a transport-specific override, where \fItransport\fR is the master.cf
+name of the message delivery transport.
+.PP
+This feature is available in Postfix 2.5. The default setting
+is compatible with earlier Postfix versions.
+.SH default_concurrency_negative_feedback (default: 1)
+The per-destination amount of negative delivery concurrency
+feedback, after a delivery completes with a connection or handshake
+failure. Feedback values are in range 0..1 inclusive. With negative
+feedback, concurrency is decremented at the beginning of a sequence
+of length 1/feedback. This is unlike positive feedback, where
+concurrency is incremented at the end of a sequence of length
+1/feedback.
+.PP
+As of Postfix version 2.5, negative feedback cannot reduce
+delivery concurrency to zero.  Instead, a destination is marked
+dead (further delivery suspended) after the failed pseudo-cohort
+count reaches $default_concurrency_failed_cohort_limit (or
+$\fItransport\fR_concurrency_failed_cohort_limit). To make the
+scheduler completely immune to connection or handshake failures,
+specify a zero feedback value and a zero failed pseudo-cohort limit.
+.PP
+Specify one of the following forms:
+.IP "\fB\fInumber\fR \fR"
+.IP "\fB\fInumber\fR / \fInumber\fR \fR"
+Constant feedback. The value must be in the range 0..1 inclusive.
+The default setting of "1" is compatible with Postfix versions
+before 2.5, where a destination's delivery concurrency is throttled
+down to zero (and further delivery suspended) after a single failed
+pseudo-cohort.
+.IP "\fB\fInumber\fR / concurrency \fR"
+Variable feedback of "\fInumber\fR / (delivery concurrency)".
+The \fInumber\fR must be in the range 0..1 inclusive. With
+\fInumber\fR equal to "1", a destination's delivery concurrency
+is decremented by 1 after each failed pseudo-cohort.
+.IP "\fB\fInumber\fR / sqrt_concurrency \fR"
+Variable feedback of "\fInumber\fR / sqrt(delivery concurrency)".
+The \fInumber\fR must be in the range 0..1 inclusive. This setting
+may be removed in a future version.
+.PP
+A pseudo-cohort is the number of deliveries equal to a destination's
+delivery concurrency.
+.PP
+Use \fItransport\fR_concurrency_negative_feedback to specify
+a transport-specific override, where \fItransport\fR is the master.cf
+name of the message delivery transport.
+.PP
+This feature is available in Postfix 2.5. The default setting
+is compatible with earlier Postfix versions.
+.SH default_concurrency_positive_feedback (default: 1)
+The per-destination amount of positive delivery concurrency
+feedback, after a delivery completes without connection or handshake
+failure. Feedback values are in the range 0..1 inclusive.  The
+concurrency increases until it reaches the per-destination maximal
+concurrency limit. With positive feedback, concurrency is incremented
+at the end of a sequence with length 1/feedback. This is unlike
+negative feedback, where concurrency is decremented at the start
+of a sequence of length 1/feedback.
+.PP
+Specify one of the following forms:
+.IP "\fB\fInumber\fR \fR"
+.IP "\fB\fInumber\fR / \fInumber\fR \fR"
+Constant feedback.  The value must be in the range 0..1
+inclusive. The default setting of "1" is compatible with Postfix
+versions before 2.5, where a destination's delivery concurrency
+doubles after each successful pseudo-cohort.
+.IP "\fB\fInumber\fR / concurrency \fR"
+Variable feedback of "\fInumber\fR / (delivery concurrency)".
+The \fInumber\fR must be in the range 0..1 inclusive. With
+\fInumber\fR equal to "1", a destination's delivery concurrency
+is incremented by 1 after each successful pseudo-cohort.
+.IP "\fB\fInumber\fR / sqrt_concurrency \fR"
+Variable feedback of "\fInumber\fR / sqrt(delivery concurrency)".
+The \fInumber\fR must be in the range 0..1 inclusive. This setting
+may be removed in a future version.
+.PP
+A pseudo-cohort is the number of deliveries equal to a destination's
+delivery concurrency.
+.PP
+Use \fItransport\fR_concurrency_positive_feedback to specify
+a transport-specific override, where \fItransport\fR is the master.cf
+name of the message delivery transport.
+.PP
+This feature is available in Postfix 2.5 and later.
 .SH default_database_type (default: see "postconf -d" output)
 The default database type for use in \fBnewaliases\fR(1), \fBpostalias\fR(1)
 and \fBpostmap\fR(1) commands. On many UNIX systems the default type is
@@ -1041,12 +1151,16 @@ When not all message recipients fit into the memory at once, keep loading
 more of them at least once every this many seconds.  This is used to
 make sure the recipients are refilled in timely manner even when
 $default_recipient_refill_limit is too high for too slow deliveries.
+.PP
+This feature is available in Postfix 2.4 and later.
 .SH default_recipient_refill_limit (default: 100)
 The default per-transport limit on the number of recipients refilled at
 once.  When not all message recipients fit into the memory at once, keep
 loading more of them in batches of at least this many at a time.  See also
 $default_recipient_refill_delay, which may result in recipient batches
 lower than this when this limit is too high for too slow deliveries.
+.PP
+This feature is available in Postfix 2.4 and later.
 .SH default_transport (default: smtp)
 The default mail delivery transport and next-hop destination for
 destinations that do not match $mydestination, $inet_interfaces,
@@ -1644,6 +1758,10 @@ The initial per-destination concurrency level for parallel delivery
 to the same destination. This limit applies to delivery via \fBsmtp\fR(8),
 and via the \fBpipe\fR(8) and \fBvirtual\fR(8) delivery agents.
 .PP
+Use \fItransport\fR_initial_destination_concurrency to specify
+a transport-specific override, where \fItransport\fR is the master.cf
+name of the message delivery transport (Postfix 2.5 and later).
+.PP
 Warning: with concurrency of 1, one bad message can be enough to
 block all mail to a site.
 .SH internal_mail_filter_classes (default: empty)
@@ -2230,21 +2348,21 @@ client IP address matches any network or network address listed in
 $mynetworks. This setting will not prevent remote mail header
 address rewriting when mail from a remote client is forwarded by
 a neighboring system.
-.IP "\fB permit_sasl_authenticated \fR"
+.IP "\fBpermit_sasl_authenticated \fR"
 Append the domain name in $myorigin or $mydomain when the
 client is successfully authenticated via the RFC 4954 (AUTH)
 protocol.
-.IP "\fB permit_tls_clientcerts \fR"
+.IP "\fBpermit_tls_clientcerts \fR"
 Append the domain name in $myorigin or $mydomain when the
 client TLS certificate is successfully verified, and the client
 certificate fingerprint is listed in $relay_clientcerts.
-.IP "\fB permit_tls_all_clientcerts \fR"
+.IP "\fBpermit_tls_all_clientcerts \fR"
 Append the domain name in $myorigin or $mydomain when the
 client TLS certificate is successfully verified, regardless of
 whether it is listed on the server, and regardless of the certifying
 authority.
-.IP "\fB check_address_map \fItype:table\fR \fR"
-.IP "\fB \fItype:table\fR \fR"
+.IP "\fBcheck_address_map \fItype:table\fR \fR"
+.IP "\fB\fItype:table\fR \fR"
 Append the domain name in $myorigin or $mydomain when the
 client IP address matches the specified lookup table.
 The lookup result is ignored, and no subnet lookup is done. This
@@ -3251,12 +3369,6 @@ clogging up the Postfix active queue. Specify 0 to disable.
 This feature is enabled with the helpful_warnings parameter.
 .PP
 This feature is available in Postfix 2.0 and later.
-.SH qmgr_concurrency_feedback_debug (default: no)
-Make the queue manager's feedback algorithm verbose for performance
-analysis purposes.
-.PP
-This feature is temporarily available in Postfix 2.5; its final
-form is likely to change.
 .SH qmgr_fudge_factor (default: 100)
 Obsolete feature: the percentage of delivery resources that a busy
 mail system will use up for delivery of a large mailing  list
@@ -3276,97 +3388,6 @@ takes priority over any other in-memory recipient limits (i.e.,
 the global qmgr_message_recipient_limit and the per transport
 _recipient_limit) if necessary. The minimum value allowed for this
 parameter is 1.
-.SH qmgr_negative_concurrency_feedback_hysteresis (default: 1)
-The per-destination integer amount of negative concurrency
-feedback that must accumulate between negative adjustments of a
-destination's delivery concurrency.  The concurrency adjustment is
-equal in size to the negative hysteresis value, and is applied at
-the \fBbeginning\fR of a cycle of (hysteresis / feedback) steps.
-At that same time, the destination's positive feedback hysteresis
-cycle is reset to its beginning.
-.PP
-This feature is temporarily available in Postfix 2.5; its final
-form is likely to change. The default setting is compatible with
-earlier Postfix versions.
-.SH qmgr_negative_concurrency_feedback_style (default: fixed_1)
-The per-destination amount of negative delivery concurrency
-feedback, after a delivery completes with a connection or handshake
-failure.
-.IP "\fB inverse_concurrency \fR"
-Variable feedback of
-1 / (delivery concurrency). With this setting, and with
-"qmgr_negative_concurrency_feedback_hysteresis = 1", the destination's
-delivery concurrency is decremented by 1 after each failed
-pseudo-cohort, and the destination is marked dead (further delivery
-suspended) after the failed pseudo-cohort count reaches
-$qmgr_sacrificial_cohorts.
-.IP "\fB inverse_sqrt_concurrency \fR"
-Variable feedback
-of 1 / (square root of delivery concurrency). This is an intermediate
-form between the other two. It lacks sound justification, and is a
-candidate for removal.
-.IP "\fB fixed_1 \fR"
-Constant feedback of 1. This setting
-is compatible with Postfix versions before 2.5, where a destination's
-delivery concurrency is throttled down to zero (and further delivery
-suspended) after a single failed pseudo-cohort.
-.PP
-A pseudo-cohort is a number of deliveries equal to the destination's
-delivery concurrency.
-.PP
-This feature is temporarily available in Postfix 2.5; its final
-form is likely to change. The default setting is compatible with
-earlier Postfix versions.
-.SH qmgr_positive_concurrency_feedback_hysteresis (default: 1)
-The per-destination integer amount of positive concurrency
-feedback that must accumulate before positive adjustments of a
-destination's delivery concurrency.  The concurrency adjustment is
-equal in size to the positive hysteresis value, and is applied at
-the \fBend\fR of a cycle of (hysteresis / feedback) steps. At that
-same time, the destination's negative feedback hysteresis cycle is
-reset to its beginning.
-.PP
-This feature is temporarily available in Postfix 2.5; its final
-form is likely to change. The default setting is compatible with
-earlier Postfix versions.
-.SH qmgr_positive_concurrency_feedback_style (default: fixed_1)
-The per-destination amount of positive delivery concurrency
-feedback, after a delivery completes without connection or handshake
-failure.
-.IP "\fB inverse_concurrency \fR"
-Variable feedback of
-1 / (delivery concurrency). With this setting, and with
-"qmgr_positive_concurrency_feedback_hysteresis = 1", the destination's
-delivery concurrency is incremented by 1 after each successful
-pseudo-cohort, until it reaches the per-destination maximal concurrency
-limit.
-.IP "\fB inverse_sqrt_concurrency \fR"
-Variable feedback
-of 1 / (square root of delivery concurrency). This is an intermediate
-form between the other two. It lacks sound justification, and is a
-candidate for removal.
-.IP "\fB fixed_1 \fR"
-Constant feedback of 1. This setting
-is compatible with Postfix versions before 2.5, where a destination's
-delivery concurrency is doubled after each successful pseudo-cohort,
-until it reaches the per-destination maximal concurrency limit.
-.PP
-A pseudo-cohort is a number of deliveries equal to the destination's
-delivery concurrency.
-.PP
-This feature is temporarily available in Postfix 2.5. The default
-setting is compatible with earlier Postfix versions.
-.SH qmgr_sacrificial_cohorts (default: 1)
-How many pseudo-cohorts must suffer connection or handshake
-failure before a specific destination is considered unavailable
-(and further delivery is suspended). A pseudo-cohort is a number
-of deliveries equal to a destination's concurrency. The pseudo-cohort
-failure count is reset each time a delivery completes without
-connection or handshake failure for that specific destination.
-.PP
-This feature is temporarily available in Postfix 2.5; its final
-form is likely to change. The default setting is compatible with
-earlier Postfix versions.
 .SH qmqpd_authorized_clients (default: empty)
 What clients are allowed to connect to the QMQP server port.
 .PP
@@ -7394,6 +7415,52 @@ of mail deliveries and produces a mail delivery report when verbose
 delivery is requested with "\fBsendmail -v\fR".
 .PP
 This feature is available in Postfix 2.1 and later.
+.SH transport_concurrency_negative_feedback (default: $default_concurrency_negative_feedback)
+A transport-specific override for the
+default_concurrency_negative_feedback parameter value, where
+\fItransport\fR is the master.cf name of the message delivery
+transport.
+.PP
+This feature is available in Postfix 2.5 and later.
+.SH transport_concurrency_positive_feedback (default: $default_concurrency_positive_feedback)
+A transport-specific override for the
+default_concurrency_positive_feedback parameter value, where
+\fItransport\fR is the master.cf name of the message delivery
+transport.
+.PP
+This feature is available in Postfix 2.5 and later.
+.SH transport_delivery_slot_cost (default: $default_delivery_slot_cost)
+A transport-specific override for the default_delivery_slot_cost
+parameter value, where \fItransport\fR is the master.cf name of
+the message delivery transport.
+.SH transport_delivery_slot_discount (default: $default_delivery_slot_discount)
+A transport-specific override for the default_delivery_slot_discount
+parameter value, where \fItransport\fR is the master.cf name of
+the message delivery transport.
+.SH transport_delivery_slot_loan (default: $default_delivery_slot_loan)
+A transport-specific override for the default_delivery_slot_loan
+parameter value, where \fItransport\fR is the master.cf name of
+the message delivery transport.
+.SH transport_destination_concurrency_limit (default: $default_destination_concurrency_limit)
+A transport-specific override for the
+default_destination_concurrency_limit parameter value, where
+\fItransport\fR is the master.cf name of the message delivery
+transport.
+.SH transport_destination_recipient_limit (default: $default_destination_concurrency_limit)
+A transport-specific override for the
+default_destination_recipient_limit parameter value, where
+\fItransport\fR is the master.cf name of the message delivery
+transport.
+.SH transport_extra_recipient_limit (default: $default_extra_recipient_limit)
+A transport-specific override for the default_extra_recipient_limit
+parameter value, where \fItransport\fR is the master.cf name of
+the message delivery transport.
+.SH transport_initial_destination_concurrency (default: $initial_destination_concurrency)
+A transport-specific override for the initial_destination_concurrency
+parameter value, where \fItransport\fR is the master.cf name of
+the message delivery transport.
+.PP
+This feature is available in Postfix 2.5 and later.
 .SH transport_maps (default: empty)
 Optional lookup tables with mappings from recipient address to
 (message delivery transport, next-hop destination).  See \fBtransport\fR(5)
@@ -7416,12 +7483,36 @@ transport_maps = hash:/etc/postfix/transport
 .fi
 .ad
 .ft R
+.SH transport_minimum_delivery_slots (default: $default_minimum_delivery_slots)
+A transport-specific override for the default_minimum_delivery_slots
+parameter value, where \fItransport\fR is the master.cf name of
+the message delivery transport.
+.SH transport_recipient_limit (default: $default_recipient_limit)
+A transport-specific override for the default_recipient_limit
+parameter value, where \fItransport\fR is the master.cf name of
+the message delivery transport.
+.SH transport_recipient_refill_delay (default: $default_recipient_refill_delay)
+A transport-specific override for the default_recipient_refill_delay
+parameter value, where \fItransport\fR is the master.cf name of
+the message delivery transport.
+.PP
+This feature is available in Postfix 2.4 and later.
+.SH transport_recipient_refill_limit (default: $default_recipient_refill_limit)
+A transport-specific override for the default_recipient_refill_limit
+parameter value, where \fItransport\fR is the master.cf name of
+the message delivery transport.
+.PP
+This feature is available in Postfix 2.4 and later.
 .SH transport_retry_time (default: 60s)
 The time between attempts by the Postfix queue manager to contact
 a malfunctioning message delivery transport.
 .PP
 Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
 The default time unit is s (seconds).
+.SH transport_time_limit (default: $command_time_limit)
+A transport-specific override for the command_time_limit parameter
+value, where \fItransport\fR is the master.cf name of the message
+delivery transport.
 .SH trigger_timeout (default: 10s)
 The time limit for sending a trigger to a Postfix daemon (for
 example, the \fBpickup\fR(8) or \fBqmgr\fR(8) daemon). This time limit prevents
index 8130f93dd4763068760a15ea6f2d3fc4d529a860..45210143d3a2897dbc2e872b9183b41ef3b7fde5 100644 (file)
@@ -226,15 +226,17 @@ Idem, for delivery via the named message \fItransport\fR.
 .nf
 .ad
 .fi
-.IP "\fBminimal_backoff_time (version dependent)\fR"
-The minimal time between attempts to deliver a deferred message.
+.IP "\fBminimal_backoff_time (300s)\fR"
+The minimal time between attempts to deliver a deferred message;
+prior to Postfix 2.4 the default value was 1000s.
 .IP "\fBmaximal_backoff_time (4000s)\fR"
 The maximal time between attempts to deliver a deferred message.
 .IP "\fBmaximal_queue_lifetime (5d)\fR"
 The maximal time a message is queued before it is sent back as
 undeliverable.
-.IP "\fBqueue_run_delay (version dependent)\fR"
-The time between deferred queue scans by the queue manager.
+.IP "\fBqueue_run_delay (300s)\fR"
+The time between deferred queue scans by the queue manager;
+prior to Postfix 2.4 the default value was 1000s.
 .IP "\fBtransport_retry_time (60s)\fR"
 The time between attempts by the Postfix queue manager to contact
 a malfunctioning message delivery transport.
index 5e6f55dd42c75141c4ad5d9a3e9336014a3bb24b..dad2641c36d1e42f3c21f4683f2c5e37d4322d40 100644 (file)
@@ -235,29 +235,32 @@ The default maximal number of parallel deliveries to the same
 destination.
 .IP "\fItransport\fB_destination_concurrency_limit ($default_destination_concurrency_limit)\fR"
 Idem, for delivery via the named message \fItransport\fR.
-.IP "\fBqmgr_concurrency_feedback_debug (no)\fR"
-Make the queue manager's feedback algorithm verbose for performance
-analysis purposes.
-.IP "\fBqmgr_negative_concurrency_feedback_hysteresis (1)\fR"
-The per-destination integer amount of negative concurrency
-feedback that must accumulate between negative adjustments of a
-destination's delivery concurrency.
-.IP "\fBqmgr_negative_concurrency_feedback_style (fixed_1)\fR"
+.PP
+Available in Postfix version 2.5 and later:
+.IP "\fItransport\fB_initial_destination_concurrency ($initial_destination_concurrency)\fR"
+Initial concurrency for delivery via the named message
+\fItransport\fR.
+.IP "\fBdefault_concurrency_failed_cohort_limit (1)\fR"
+How many pseudo-cohorts must suffer connection or handshake
+failure before a specific destination is considered unavailable
+(and further delivery is suspended).
+.IP "\fItransport\fB_concurrency_failed_cohort_limit ($default_concurrency_failed_cohort_limit)\fR"
+Idem, for delivery via the named message \fItransport\fR.
+.IP "\fBdefault_concurrency_negative_feedback (1)\fR"
 The per-destination amount of negative delivery concurrency
 feedback, after a delivery completes with a connection or handshake
 failure.
-.IP "\fBqmgr_positive_concurrency_feedback_hysteresis (1)\fR"
-The per-destination integer amount of positive concurrency
-feedback that must accumulate before positive adjustments of a
-destination's delivery concurrency.
-.IP "\fBqmgr_positive_concurrency_feedback_style (fixed_1)\fR"
+.IP "\fItransport\fB_concurrency_negative_feedback ($default_concurrency_negative_feedback)\fR"
+Idem, for delivery via the named message \fItransport\fR.
+.IP "\fBdefault_concurrency_positive_feedback (1)\fR"
 The per-destination amount of positive delivery concurrency
 feedback, after a delivery completes without connection or handshake
 failure.
-.IP "\fBqmgr_sacrificial_cohorts (1)\fR"
-How many pseudo-cohorts must suffer connection or handshake
-failure before a specific destination is considered unavailable
-(and further delivery is suspended).
+.IP "\fItransport\fB_concurrency_positive_feedback ($default_concurrency_positive_feedback)\fR"
+Idem, for delivery via the named message \fItransport\fR.
+.IP "\fBconcurrency_feedback_debug (no)\fR"
+Make the queue manager's feedback algorithm verbose for performance
+analysis purposes.
 .SH "RECIPIENT SCHEDULING CONTROLS"
 .na
 .nf
index ea5f9d3f20cc90ba9d4f8594957a8de9009cce85..d96c3a02842662b78837cbf381a29bf268dbc695 100755 (executable)
@@ -131,15 +131,15 @@ while (<>) {
     s;\bdefault_deliv[-</Bb>]*\n* *[<Bb>]*ery_slot_cost\b;<a href="postconf.5.html#default_delivery_slot_cost">$&</a>;g;
     s;\bdefault_deliv[-</Bb>]*\n* *[<Bb>]*ery_slot_discount\b;<a href="postconf.5.html#default_delivery_slot_discount">$&</a>;g;
     s;\bdefault_deliv[-</Bb>]*\n* *[<Bb>]*ery_slot_loan\b;<a href="postconf.5.html#default_delivery_slot_loan">$&</a>;g;
-    s;\bdefault_destina[-</Bb>]*\n* *[<Bb>]*tion_concurrency_limit\b;<a href="postconf.5.html#default_destination_concurrency_limit">$&</a>;g;
+    s;\bdefault_destina[-</Bb>]*\n* *[<Bb>]*tion_concur[-</Bb>]*\n* *[<Bb>]*rency_limit\b;<a href="postconf.5.html#default_destination_concurrency_limit">$&</a>;g;
     s;\bdefault_destina[-</Bb>]*\n* *[<Bb>]*tion_recip[-</bB>]*\n* *[<bB>]*ient_limit\b;<a href="postconf.5.html#default_destination_recipient_limit">$&</a>;g;
-    s;\bdefault_extra_recip[-</bB>]*\n* *[<bB>]*ient_limit\b;<a href="postconf.5.html#default_extra_recipient_limit">$&</a>;g;
+    s;\bdefault_extra_recipi[-</bB>]*\n* *[<bB>]*ent_limit\b;<a href="postconf.5.html#default_extra_recipient_limit">$&</a>;g;
     s;\bdefault_minimum_deliv[-</Bb>]*\n* *[<Bb>]*ery_slots\b;<a href="postconf.5.html#default_minimum_delivery_slots">$&</a>;g;
     s;\bdefault_privs\b;<a href="postconf.5.html#default_privs">$&</a>;g;
     s;\bdefault_process_limit\b;<a href="postconf.5.html#default_process_limit">$&</a>;g;
     s;\bdefault_rbl_reply\b;<a href="postconf.5.html#default_rbl_reply">$&</a>;g;
-    s;\bdefault_recipient_refill_limit\b;<a href="postconf.5.html#default_recipient_refill_limit">$&</a>;g;
-    s;\bdefault_recipient_refill_delay\b;<a href="postconf.5.html#default_recipient_refill_delay">$&</a>;g;
+    s;\bdefault_recipi[-</bB>]*\n* *[<bB>]*ent_refill_limit\b;<a href="postconf.5.html#default_recipient_refill_limit">$&</a>;g;
+    s;\bdefault_recipi[-</bB>]*\n* *[<bB>]*ent_refill_delay\b;<a href="postconf.5.html#default_recipient_refill_delay">$&</a>;g;
     s;\bdefault_recip[-</bB>]*\n* *[<bB>]*ient_limit\b;<a href="postconf.5.html#default_recipient_limit">$&</a>;g;
     s;\bdefault_transport\b;<a href="postconf.5.html#default_transport">$&</a>;g;
     s;\bdefault_verp_delimiters\b;<a href="postconf.5.html#default_verp_delimiters">$&</a>;g;
@@ -194,7 +194,7 @@ while (<>) {
     s;\bin_flow_delay\b;<a href="postconf.5.html#in_flow_delay">$&</a>;g;
     s;\binet_inter[-</bB>]*\n*[ <bB>]*faces\b;<a href="postconf.5.html#inet_interfaces">$&</a>;g;
     s;\binet_protocols\b;<a href="postconf.5.html#inet_protocols">$&</a>;g;
-    s;\binitial_destination_concurrency\b;<a href="postconf.5.html#initial_destination_concurrency">$&</a>;g;
+    s;\binitial_desti[-</bB>]*\n*[ <bB>]*nation_concurrency\b;<a href="postconf.5.html#initial_destination_concurrency">$&</a>;g;
     s;\binvalid_hostname_reject_code\b;<a href="postconf.5.html#invalid_hostname_reject_code">$&</a>;g;
     s;\bipc_idle\b;<a href="postconf.5.html#ipc_idle">$&</a>;g;
     s;\bipc_timeout\b;<a href="postconf.5.html#ipc_timeout">$&</a>;g;
@@ -335,12 +335,10 @@ while (<>) {
     s;\bqmgr_message_recip[-</bB>]*\n* *[<bB>]*ient_minimum\b;<a href="postconf.5.html#qmgr_message_recipient_minimum">$&</a>;g;
     s;\bqmqpd_authorized_clients\b;<a href="postconf.5.html#qmqpd_authorized_clients">$&</a>;g;
 
-    s;\bqmgr_negative_concurrency_feedback_hysteresis\b;<a href="postconf.5.html#qmgr_negative_concurrency_feedback_hysteresis">$&</a>;g;
-    s;\bqmgr_negative_concurrency_feedback_style\b;<a href="postconf.5.html#qmgr_negative_concurrency_feedback_style">$&</a>;g;
-    s;\bqmgr_positive_concurrency_feedback_hysteresis\b;<a href="postconf.5.html#qmgr_positive_concurrency_feedback_hysteresis">$&</a>;g;
-    s;\bqmgr_positive_concurrency_feedback_style\b;<a href="postconf.5.html#qmgr_positive_concurrency_feedback_style">$&</a>;g;
-    s;\bqmgr_sacrificial_cohorts\b;<a href="postconf.5.html#qmgr_sacrificial_cohorts">$&</a>;g;
-    s;\bqmgr_concurrency_feedback_debug\b;<a href="postconf.5.html#qmgr_concurrency_feedback_debug">$&</a>;g;
+    s;\bdefault_concur[-</Bb>]*\n* *[<Bb>]*rency_negative_feedback\b;<a href="postconf.5.html#default_concurrency_negative_feedback">$&</a>;g;
+    s;\bdefault_concur[-</Bb>]*\n* *[<Bb>]*rency_positive_feedback\b;<a href="postconf.5.html#default_concurrency_positive_feedback">$&</a>;g;
+    s;\bdefault_con[-</Bb>]*\n* *[<Bb>]*currency_failed_cohort_limit\b;<a href="postconf.5.html#default_concurrency_failed_cohort_limit">$&</a>;g;
+    s;\bconcurrency_feedback_debug\b;<a href="postconf.5.html#concurrency_feedback_debug">$&</a>;g;
 
     s;\bqmqpd_error_delay\b;<a href="postconf.5.html#qmqpd_error_delay">$&</a>;g;
     s;\bqmqpd_timeout\b;<a href="postconf.5.html#qmqpd_timeout">$&</a>;g;
@@ -620,6 +618,25 @@ while (<>) {
  
     s;\bfrozen_delivered_to\b;<a href="postconf.5.html#frozen_delivered_to">$&</a>;g;
 
+    # Transport-dependent magical parameters.
+
+    s;(<i>transport</i>)(<b>)?(_concurrency_failed_cohort_limit)\b;$2<a href="postconf.5.html#transport_concurrency_failed_cohort_limit">$1$3</a>;g;
+    s;(<i>transport</i>)(<b>)?(_concurrency_negative_feedback)\b;$2<a href="postconf.5.html#transport_concurrency_positive_feedback">$1$3</a>;g;
+    s;(<i>transport</i>)(<b>)?(_concurrency_positive_feedback)\b;$2<a href="postconf.5.html#transport_concurrency_positive_feedback">$1$3</a>;g;
+    s;(<i>transport</i>)(<b>)?(_delivery_slot_cost)\b;$2<a href="postconf.5.html#transport_delivery_slot_cost">$1$3</a>;g;
+    s;(<i>transport</i>)(<b>)?(_delivery_slot_discount)\b;$2<a href="postconf.5.html#transport_delivery_slot_discount">$1$3</a>;g;
+    s;(<i>transport</i>)(<b>)?(_delivery_slot_loan)\b;$2<a href="postconf.5.html#transport_delivery_slot_loan">$1$3</a>;g;
+    s;(<i>transport</i>)(<b>)?(_destination_concurrency_limit)\b;$2<a href="postconf.5.html#transport_destination_concurrency_limit">$1$3</a>;g;
+    s;(<i>transport</i>)(<b>)?(_destination_recipient_limit)\b;$2<a href="postconf.5.html#transport_destination_recipient_limit">$1$3</a>;g;
+    s;(<i>transport</i>)(<b>)?(_extra_recipient_limit)\b;$2<a href="postconf.5.html#transport_extra_recipient_limit">$1$3</a>;g;
+    s;(<i>transport</i>)(<b>)?(_initial_destination_concurrency)\b;$2<a href="postconf.5.html#transport_initial_destination_concurrency">$1$3</a>;g;
+    s;(<i>transport</i>)(<b>)?(_minimum_delivery_slots)\b;$2<a href="postconf.5.html#transport_minimum_delivery_slots">$1$3</a>;g;
+    s;(<i>transport</i>)(<b>)?(_recipient_limit)\b;$2<a href="postconf.5.html#transport_recipient_limit">$1$3</a>;g;
+    s;(<i>transport</i>)(<b>)?(_recipient_refill_delay)\b;$2<a href="postconf.5.html#transport_recipient_refill_delay">$1$3</a>;g;
+    s;(<i>transport</i>)(<b>)?(_recipient_refill_limit)\b;$2<a href="postconf.5.html#transport_recipient_refill_limit">$1$3</a>;g;
+    s;(<i>transport</i>)(<b>)?(_time_limit)\b;$2<a href="postconf.5.html#transport_time_limit">$1$3</a>;g;
+    s;(<i>transport</i>)(<b>)?(delivery_slot_discount)\b;$2<a href="postconf.5.html#transportdelivery_slot_discount">$1$3</a>;g;
+
     # Undo hyperlinks of manual pages with the same name as parameters.
 
     s/<a href="[^"]*">([^<]*)<\/a>\(/$1(/g;
index c81be485795289d2eee29e662de8a6c5265d9353..8e8d96abc8b7bc4444ee817a2e8019917a885ec2 100644 (file)
 
 <hr>
 
-<h2>What this file is about</h2>
+<h2> Overview </h2>
 
-<p> This is the beginning of documentation for the clever queue
+<p> The queue manager is by far the most complex part of the Postfix
+mail system. It schedules delivery of new mail, retries failed
+deliveries at specific times, and removes mail from the queue after
+the last delivery attempt. Once started, the qmgr(8) process runs
+until "postfix reload" or "postfix stop". </p>
+
+<p> As a persistent process, the queue manager has to meet strict
+requirements with respect to code correctness and robustness. Unlike
+non-persistent daemon processes, the queue manager cannot benefit
+from Postfix's process rejuvenation mechanism that limit the impact
+from resource leaks and other coding errors. </p>
+
+<p> There are two major classes of mechanisms that control the
+operation of the queue manager: </p>
+
+<ul>
+
+<li> <p> Mechanisms concerned with the number of concurrent deliveries
+to a specific destination, including decisions on when to suspend
+deliveries after persistent failures. These are described under "<a
+href="#concurrency">Concurrency scheduling</a>". </p>
+
+<li> <p> Mechanisms concerned with the selection of what mail to
+deliver to a given destination.  These are described under "<a
+href="#jobs">Preemptive scheduling</a>". </p>
+
+</ul>
+
+<h2> <a name="concurrency"> Concurrency scheduling </a> </h2>
+
+<p> This section documents the Postfix 2.5 concurrency scheduler.
+Prior Postfix versions used a simple but robust algorithm where the
+per-destination delivery concurrency was decremented by 1 after a
+delivery suffered connection or handshake failure, and was incremented
+by 1 otherwise.  Of course the concurrency was never allowed to
+exceed the maximum per-destination concurrency limit.  And when a
+destination's concurrency level dropped to zero, the destination
+was declared "dead" and delivery was suspended.  </p>
+
+<p> Drawbacks of the old +/-1 feedback concurrency scheduler are:
+<p>
+
+<ul>
+
+<li> <p> Overshoot due to exponential delivery concurrency growth
+with each pseudo-cohort(*). For example, with the default initial
+concurrency of 5, concurrency would proceed over time as (5-10-20).
+</p>
+
+<li> <p> Throttling down to zero concurrency after a single
+pseudo-cohort(*) failure. This was especially an issue with
+low-concurrency channels where a single failure could be sufficient
+to mark a destination as "dead", causing the suspension of further
+deliveries to the affected destination. </p>
+
+</ul>
+
+<p> (*) A pseudo-cohort is a number of delivery requests equal to
+a destination's delivery concurrency. </p>
+
+<p> The revised concurrency scheduler has a highly modular structure.
+It uses separate mechanisms for per-destination concurrency control
+and for "dead destination" detection.  The concurrency control in
+turn is built from two separate mechanisms: it supports less-than-1
+feedback to allow for more gradual concurrency adjustments, and it
+uses feedback hysteresis to suppress concurrency oscillations.  And
+instead of waiting for delivery concurrency to throttle down to
+zero, a destination is declared "dead" after a configurable number
+of pseudo-cohorts reports connection or handshake failure.  </p>
+
+<h2> Summary of the Postfix 2.5 concurrency feedback algorithm </h2>
+
+<p> We want to increment a destination's delivery concurrency after
+some (not necessarily consecutive) number of deliveries without
+connection or handshake failure.  This is implemented with positive
+feedback g(N) where N is the destination's delivery concurrency.
+With g(N)=1 we get the old scheduler's exponential growth in time,
+while g(N)=1/N gives linear growth in time.  Less-than-1 feedback
+and integer truncation naturally give us hysteresis, so that
+transitions to larger concurrency happen every 1/g(N) positive
+feedback events. </p>
+
+<p> We want to decrement a destination's delivery concurrency after
+some (not necessarily consecutive) number of deliveries suffer
+connection or handshake failure.  This is implemented with negative
+feedback f(N) where N is the destination's delivery concurrency.
+With f(N)=1 we get the old scheduler's behavior where concurrency
+is throttled down dramatically after a single pseudo-cohort failure,
+while f(N)=1/N backs off more gently.  Again, less-than-1 feedback
+and integer truncation naturally give us hysteresis, so that
+transitions to lower concurrency happen every 1/f(N) negative
+feedback events. </p>
+
+<p> However, with negative feedback we introduce a subtle twist.
+We "reverse" the hysteresis cycle so that the transition to lower
+concurrency happens at the <b>beginning</b> of a sequence of 1/f(N)
+negative feedback events.  Otherwise, a correction for overload
+would be made too late.  In the case of a concurrency-limited server,
+this makes the choice of f(N) relatively unimportant, as borne out
+by measurements.  </p>
+
+<p> In summary, the main ingredients for the Postfix 2.5 concurrency
+feedback algorithm are a) the option of less-than-1 positive feedback
+to avoid overwhelming servers, b) the option of less-than-1 negative
+feedback to avoid or giving up too fast, c) feedback hysteresis to
+avoid rapid oscillation, and c) a "reverse" hysteresis cycle for
+negative feedback, so that it can correct for overload quickly. </p>
+
+<h2> Summary of the Postfix 2.5 "dead destination" detection algorithm </h2>
+
+<p> We want to suspend deliveries to a specific destination after
+some number of deliveries suffers connection or handshake failure.
+The old scheduler declares a destination "dead" when negative (-1)
+feedback throttles the delivery concurrency down to zero. With
+less-than-1 feedback, this throttling down would obviously take too
+long.  We therefore have to separate "dead destination" detection
+from concurrency feedback.  This is implemented by introducing the
+concept of pseudo-cohort failure. The Postfix 2.5 concurrency
+scheduler declares a destination "dead" after a configurable number
+of pseudo-cohort failures. The old scheduler corresponds to the
+special case where the pseudo-cohort failure limit is equal to 1.
+</p>
+
+<h2> Pseudocode for the Postfix 2.5 concurrency scheduler </h2>
+
+<p> The pseudo code shows how the ideas behind new concurrency
+scheduler are implemented as of November 2007.  The actual code can
+be found in the module qmgr/qmgr_queue.c.  </p>
+
+<pre>
+Types:
+       Each destination has one set of the following variables
+        int window
+        double success
+        double failure
+        double fail_cohorts
+
+Feedback functions:
+       N is concurrency; x, y are arbitrary numbers in [0..1] inclusive
+        positive feedback: g(N) = x/N | x/sqrt(N) | x
+        negative feedback: f(N) = y/N | y/sqrt(N) | y
+
+Initialization:
+        window = initial_concurrency
+        success = 0
+        failure = 0
+        fail_cohorts = 0
+
+After success:
+        fail_cohorts = 0
+        Be prepared for feedback > hysteresis, or rounding error
+        success += g(window)
+        while (success >= 1)    Hysteresis 1
+            window += 1         Hysteresis 1
+            failure = 0
+            success -= 1        Hysteresis 1
+        Be prepared for overshoot
+        if (window > concurrency limit)
+            window = concurrency limit
+
+Safety:
+        Don't apply positive feedback unless
+            window < busy_refcount + init_dest_concurrency
+        otherwise negative feedback effect could be delayed
+
+After failure:
+        if (window > 0)
+            fail_cohorts += 1.0 / window
+            if (fail_cohorts > cohort_failure_limit)
+                window = 0
+        if (window > 0)
+            Be prepared for feedback > hysteresis, rounding errors
+            failure -= f(window)
+            while (failure < 0)
+                window -= 1     Hysteresis 1
+                failure += 1    Hysteresis 1
+                success = 0
+            Be prepared for overshoot
+            if (window < 1)
+                window = 1
+</pre>
+
+<h2> Results for the Postfix 2.5 concurrency feedback scheduler </h2>
+
+<p> Discussions about the concurrency scheduler redesign started
+early 2004, when the primary goal was to find alternatives that did
+not exhibit exponential growth or rapid concurrency throttling.  No
+code was implemented until late 2007, when the primary concern had
+shifted towards better handling of server concurrency limits. For
+this reason we measure how well the new scheduler does this
+job.  The table below compares mail delivery performance of the old
++/-1 feedback with other feedback functions, for different server
+concurrency enforcement methods.  Measurements were done with a
+FreeBSD 6.2 client and with FreeBSD 6.2 and various Linux servers.
+</p>
+
+<li> Server configuration:
+
+<ul> <li> The mail flow was slowed down with 1 second latency per
+recipient ("smtpd_client_restrictions = sleep 1"). The purpose was
+to make results less dependent on hardware details, by reducing the
+slow-downs by disk I/O, logging I/O, and network I/O.
+
+<li> Concurrency was limited by the server process limit
+("default_process_limit = 5", "smtpd_client_event_limit_exceptions
+= static:all"). Postfix was stopped and started after changing the
+process limit, because the same number is also used as the backlog
+argument to the listen(2) system call, and "postfix reload" does
+not re-issue this call.
+
+<li> Mail was discarded with "local_recipient_maps = static:all" and
+"local_transport = discard". The discard action in header/body checks
+could not be used as it fails to update the in_flow_delay counters.
+
+</ul>
+
+<li> Client configuration:
+
+<ul>
+
+<li> Queue file overhead was minimized by sending one message to a
+virtual alias that expanded into 2000 different remote recipients.
+All recipients were accounted for according to the maillog file.
+The virtual_alias_expansion_limit setting was increased to avoid
+complaints from the cleanup(8) server.
+
+<li> The number of deliveries was maximized with
+"smtp_destination_recipient_limit = 2". A smaller limit would cause
+Postfix to schedule the concurrency per recipient instead of domain,
+which is not what we want.
+
+<li> Maximal concurrency was limited with
+"smtp_destination_concurrency_limit = 20", and
+initial_destination_concurrency was set to the same value.
+
+<li> The positive and negative concurrency feedback hysteresis was
+1.  Concurrency was incremented by 1 at the END of 1/feedback steps
+of positive feedback, and was decremented by 1 at the START of
+1/feedback steps of negative feedback.
+
+<li> The SMTP client used the default 30s SMTP connect timeout and
+300s SMTP greeting timeout.
+
+</ul>
+
+<p> The first results are for a FreeBSD 6.2 server, where our
+artificially low listen(2) backlog results in a very short kernel
+queue for established connections. As the table shows, all deferred
+deliveries failed due to a 30s connection timeout, and none failed
+due to a server greeting timeout.  This measurement simulates what
+happens when the server's connection queue is completely full under
+load, and the TCP engine drops new connections.  </p>
+
+<blockquote>
+
+<table>
+
+<tr> <th>client<br> limit</th> <th>server<br> limit</th> <th>feedback<br>
+style</th> <th>connection<br> caching</th> <th>percentage<br>
+deferred</th> <th colspan="2">client concurrency<br> average/stddev</th>
+<th colspan=2>timed-out in<br> connect/greeting </th> </tr>
+
+<tr> <td align="center" colspan="9"> <hr> </td> </tr>
+
+<tr><td align="center">20</td> <td align="center">5</td> <td
+align="center">1/N</td> <td align="center">no</td> <td
+align="center">9.9</td> <td align="center">19.4</td> <td
+align="center">0.49</td> <td align="center">198</td> <td
+align="center">-</td> </tr>
+
+<tr><td align="center">20</td> <td align="center">5</td> <td
+align="center">1/N</td> <td align="center">yes</td> <td
+align="center">10.3</td> <td align="center">19.4</td> <td
+align="center">0.49</td> <td align="center">206</td> <td
+align="center">-</td> </tr>
+
+<tr><td align="center">20</td> <td align="center">5</td> <td
+align="center">1/sqrt(N)</td> <td align="center">no</td>
+<td align="center">10.4</td> <td align="center">19.6</td> <td
+align="center">0.59</td> <td align="center">208</td> <td
+align="center">-</td> </tr>
+
+<tr><td align="center">20</td> <td align="center">5</td> <td
+align="center">1/sqrt(N)</td> <td align="center">yes</td>
+<td align="center">10.6</td> <td align="center">19.6</td> <td
+align="center">0.61</td> <td align="center">212</td> <td
+align="center">-</td> </tr>
+
+<tr><td align="center">20</td> <td align="center">5</td> <td
+align="center">1</td> <td align="center">no</td> <td
+align="center">10.1</td> <td align="center">19.5</td> <td
+align="center">1.29</td> <td align="center">202</td> <td
+align="center">-</td> </tr>
+
+<tr><td align="center">20</td> <td align="center">5</td> <td
+align="center">1</td> <td align="center">yes</td> <td
+align="center">10.8</td> <td align="center">19.3</td> <td
+align="center">1.57</td> <td align="center">216</td> <td
+align="center">-</td> </tr>
+
+<tr> <td align="center" colspan="9"> <hr> </td> </tr>
+
+</table>
+
+<p> A busy server with a completely full connection queue.  N is
+the client delivery concurrency.  Failed deliveries time out after
+30s without completing the TCP handshake. See below for a discussion
+of results. </p>
+
+</blockquote>
+
+<p> The next table shows results for a Fedora Core 8 server (results
+for RedHat 7.3 are identical). In this case, the listen(2) backlog
+argument has little if any effect on the kernel's established
+connection queue.  As the table shows, practically all deferred
+deliveries fail after the 300s SMTP greeting timeout. As these
+timeouts were 10x longer than with the previous measurement, we
+increased the recipient count (and thus the running time) by a
+factor of 10 to keep the results comparable. </p>
+
+<blockquote>
+
+<table>
+
+<tr> <th>client<br> limit</th> <th>server<br> limit</th> <th>feedback<br>
+style</th> <th>connection<br> caching</th> <th>percentage<br>
+deferred</th> <th colspan="2">client concurrency<br> average/stddev</th>
+<th colspan=2>timed-out in<br> connect/greeting </th> </tr>
+
+<tr> <td align="center" colspan="9"> <hr> </td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1/N</td> <td align="center">no</td> <td
+align="center">1.16</td> <td align="center">19.8</td> <td
+align="center">0.37</td> <td align="center">-</td> <td
+align="center">230</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1/N</td> <td align="center">yes</td> <td
+align="center">1.36</td> <td align="center">19.8</td> <td
+align="center">0.36</td> <td align="center">-</td> <td
+align="center">272</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1/sqrt(N)</td> <td align="center">no</td>
+<td align="center">1.21</td> <td align="center">19.9</td> <td
+align="center">0.23</td> <td align="center">4</td> <td
+align="center">238</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1/sqrt(N)</td> <td align="center">yes</td>
+<td align="center">1.36</td> <td align="center">20.0</td> <td
+align="center">0.23</td> <td align="center">-</td> <td
+align="center">272</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1</td> <td align="center">no</td> <td
+align="center">1.18</td> <td align="center">20.0</td> <td
+align="center">0.16</td> <td align="center">-</td> <td
+align="center">236</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1</td> <td align="center">yes</td> <td
+align="center">1.39</td> <td align="center">20.0</td> <td
+align="center">0.16</td> <td align="center">-</td> <td
+align="center">278</td> </tr>
+
+<tr> <td align="center" colspan="9"> <hr> </td> </tr>
+
+</table>
+
+<p> A busy server with a non-full connection queue.  N is the client
+delivery concurrency. Failed deliveries complete at the TCP level,
+but time out after 300s while waiting for the SMTP greeting.  See
+below for a discussion of results.  </p>
+
+</blockquote>
+
+
+<p> The final concurrency limited result shows what happens when
+SMTP connections don't time out, but are rejected immediately with
+the Postfix server's smtpd_client_connection_count_limit feature.
+Similar results can be expected with concurrency limiting features
+built into other MTAs or firewalls.  For this measurement we specified
+a server concurrency limit and a client initial destination concurrency
+of 5, and a server process limit of 10. The server was FreeBSD 6.2
+but that does not matter here, because the "push back" is done
+entirely by the server's Postfix itself. </p>
+
+<blockquote>
+
+<table>
+
+<tr> <th>client<br> limit</th> <th>server<br> limit</th> <th>feedback<br>
+style</th> <th>connection<br> caching</th> <th>percentage<br>
+deferred</th> <th colspan="2">client concurrency<br> average/stddev</th>
+<th>theoretical<br>defer rate</th> </tr>
+
+<tr> <td align="center" colspan="9"> <hr> </td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1/N</td> <td align="center">no</td> <td
+align="center">16.5</td> <td align="center">5.17</td> <td
+align="center">0.38</td> <td align="center">1/6</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1/N</td> <td align="center">yes</td> <td
+align="center">16.5</td> <td align="center">5.17</td> <td
+align="center">0.38</td> <td align="center">1/6</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1/sqrt(N)</td> <td align="center">no</td>
+<td align="center">24.5</td> <td align="center">5.28</td> <td
+align="center">0.45</td> <td align="center">1/4</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1/sqrt(N)</td> <td align="center">yes</td>
+<td align="center">24.3</td> <td align="center">5.28</td> <td
+align="center">0.46</td> <td align="center">1/4</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1</td> <td align="center">no</td> <td
+align="center">49.7</td> <td align="center">5.63</td> <td
+align="center">0.67</td> <td align="center">1/2</td> </tr>
+
+<tr> <td align="center">20</td> <td align="center">5</td> <td
+align="center">1</td> <td align="center">yes</td> <td
+align="center">49.7</td> <td align="center">5.68</td> <td
+align="center">0.70</td> <td align="center">1/2</td> </tr>
+
+<tr> <td align="center" colspan="9"> <hr> </td> </tr>
+
+</table>
+
+<p> A server with active per-client concurrency limiter that replies
+with 421 and disconnects.  N is the client delivery concurrency.
+The theoretical mail deferral rate is 1/(1+roundup(1/feedback)).
+This is always 1/2 with the fixed +/-1 feedback; with the variable
+feedback variants, the defer rate decreases with increasing
+concurrency. See below for a discussion of results. </p>
+
+</blockquote>
+
+<p> The results are based on the first delivery runs only; they do
+not include any second etc. delivery attempts.  
+
+<p> The first two examples show that the feedback method matters
+little when concurrency is limited due to congestion. This is because
+the initial concurrency was already at the client's concurrency
+maximum, and because there was 10-100 times more positive than
+negative feedback.  The contribution from SMTP connection caching
+was also minor for these two examples. </p>
+
+<p> In the last example, the old +/-1 feedback scheduler defers 50%
+of the mail when confronted with an active (anvil-style) server
+concurrency limit, where the server hangs up immediately with a 421
+status (a TCP-level RST would have the same result).  Less aggressive
+feedback mechanisms fare better here, and the concurrency-dependent
+feedback fares even better at higher concurrencies than shown here,
+but they have limitations as discussed in the next section.  </p>
+
+<h2> Limitations of less-than-1 feedback </h2>
+
+<p> The delivery concurrency scheduler with less-than-1 feedback
+solves a problem with servers that have active concurrency limiters,
+but this works well only because feedback is handled in a peculiar
+manner: positive feedback increments the concurrency by 1 at the
+end of a sequence of events of length 1/feedback, while negative
+feedback decrements concurrency by 1 at the beginning of such a
+sequence. This is how Postfix adjusts quickly for overshoot without
+causing lots of mail to be deferred.  Without this difference in
+feedback treatment, less-than-1 feedback would defer 50% of the
+mail, and would be no better in this respect than the simple +/-1
+feedback scheduler.  </p>
+
+<p> Unfortunately, the same feature that corrects quickly for
+concurrency overshoot also makes the scheduler more sensitive for
+noisy negative feedback.  The reason is that one lonely negative
+feedback event has the same effect as a complete sequence of length
+1/feedback: in both cases delivery concurrency is dropped by 1
+immediately.  For example, when multiple servers are placed behind
+a load balancer on a single IP address, and 1 out of K servers fails
+to complete the SMTP handshake, a scheduler with 1/N (N = concurrency)
+feedback will stop increasing its concurrency once it reaches roughly
+K.  Even though the good servers behind the load balancer are
+perfectly capable of handling more mail, the 1/N feedback scheduler
+will linger around concurrency K. </p>
+
+<p> This problem with 1/N feedback gets worse as 1/N gets smaller.
+A workaround is to use fixed less-than-1 values for positive and
+negative feedback that limit the noise sensitivity, for example:
+positive feedback of 1/4 and negative feedback 1/10.  Of course
+using fixed feedback means concurrency growth is moderated only for
+a limited range of concurrencies.  Sites that deliver at per-destination
+concurrencies of 50 or more will require special configuration.
+</p>
+
+<h2> <a name="jobs"> Preemptive scheduling </a> </h2>
+
+<p> This is the beginning of documentation for a preemptive queue
 manager scheduling algorithm by Patrik Rak. For a long time, this
 code was made available under the name "nqmgr(8)" (new queue manager),
 as an optional module. As of Postfix 2.1 this is the default queue
-manager, which is always called "qmgr(8)". The old queue manager will
-for some time will be available under the name of "oqmgr(8)". </p>
+manager, which is always called "qmgr(8)". The old queue manager
+will for some time will be available under the name of "oqmgr(8)".
+</p>
 
-<h2>Why the old Postfix queue manager was replaced</h2>
+<h3>Why the non-preemptive Postfix queue manager was replaced</h3>
 
-<p> The old Postfix scheduler had several limitations due to
-unfortunate choices in its design. </p>
+<p> The non-preemptive Postfix scheduler had several limitations
+due to unfortunate choices in its design. </p>
 
 <ol>
 
@@ -56,7 +556,7 @@ unfortunate choices in its design. </p>
 
 </ol>
 
-<h2>How the queue manager scheduler works </h2>
+<h3>How the non-preemptive queue manager scheduler works </h3>
 
 <p> The following text is from Patrik Rak and should be read together
 with the postconf(5) manual that describes each configuration
index 57674b13d28ec1d4cdf0d286a69b4879c475da2d..5ee1c8079f5041c7d96b16daa6e8052252701db7 100644 (file)
@@ -13,9 +13,9 @@
 #      process.  The master.cf configuration file defines how a
 #      client program connects to a service, and what daemon
 #      program runs when a service is requested.  Most daemon
-#      processes are short-lived and terminate after serving
-#      \fBmax_use\fR clients, or after inactivity for \fBmax_idle\fR
-#      or more units of time.
+#      processes are short-lived and terminate voluntarily after
+#      serving \fBmax_use\fR clients, or after inactivity for
+#      \fBmax_idle\fR or more units of time.
 #
 #      All daemons specified here must speak a Postfix-internal
 #      protocol. In order to execute non-Postfix software use the
index 8d7423299ea1a796f3d149d1386489e5b26c15d6..8454719223542eeca8ab0b5ad870ac3d5fe68dc5 100644 (file)
@@ -1107,6 +1107,8 @@ $default_recipient_refill_delay, which may result in recipient batches
 lower than this when this limit is too high for too slow deliveries.
 </p>
 
+<p> This feature is available in Postfix 2.4 and later. </p>
+
 %PARAM default_recipient_refill_delay 5s
 
 <p>
@@ -1117,6 +1119,8 @@ make sure the recipients are refilled in timely manner even when
 $default_recipient_refill_limit is too high for too slow deliveries.
 </p>
 
+<p> This feature is available in Postfix 2.4 and later. </p>
+
 %PARAM default_transport smtp
 
 <p>
@@ -1803,6 +1807,10 @@ to the same destination. This limit applies to delivery via smtp(8),
 and via the pipe(8) and virtual(8) delivery agents.
 </p>
 
+<p> Use <i>transport</i>_initial_destination_concurrency to specify
+a transport-specific override, where <i>transport</i> is the master.cf
+name of the message delivery transport (Postfix 2.5 and later). </p>
+
 <p>
 Warning: with concurrency of 1, one bad message can be enough to
 block all mail to a site.
@@ -8080,28 +8088,28 @@ $mynetworks. This setting will not prevent remote mail header
 address rewriting when mail from a remote client is forwarded by
 a neighboring system.  </dd>
 
-<dt><b> permit_sasl_authenticated </b></dt>
+<dt><b>permit_sasl_authenticated </b></dt>
 
 <dd> Append the domain name in $myorigin or $mydomain when the
 client is successfully authenticated via the RFC 4954 (AUTH)
 protocol. </dd>
 
-<dt><b> permit_tls_clientcerts </b></dt>
+<dt><b>permit_tls_clientcerts </b></dt>
 
 <dd> Append the domain name in $myorigin or $mydomain when the
 client TLS certificate is successfully verified, and the client
 certificate fingerprint is listed in $relay_clientcerts.  </dd>
 
-<dt><b> permit_tls_all_clientcerts </b></dt>
+<dt><b>permit_tls_all_clientcerts </b></dt>
 
 <dd> Append the domain name in $myorigin or $mydomain when the
 client TLS certificate is successfully verified, regardless of
 whether it is listed on the server, and regardless of the certifying
 authority. </dd>
 
-<dt><b> <a name="check_address_map">check_address_map</a> <i><a href="DATABASE_README.html">type:table</a></i> </b></dt>
+<dt><b><a name="check_address_map">check_address_map</a> <i><a href="DATABASE_README.html">type:table</a></i> </b></dt>
 
-<dt><b> <i><a href="DATABASE_README.html">type:table</a></i> </b></dt>
+<dt><b><i><a href="DATABASE_README.html">type:table</a></i> </b></dt>
 
 <dd> Append the domain name in $myorigin or $mydomain when the
 client IP address matches the specified lookup table.
@@ -10684,120 +10692,241 @@ that change the delivery time or destination are not available.
 
 <p> This feature is available in Postfix 2.5 and later. </p>
 
-%PARAM qmgr_concurrency_feedback_debug no
+%PARAM concurrency_feedback_debug no
 
 <p> Make the queue manager's feedback algorithm verbose for performance
 analysis purposes. </p>
 
-<p> This feature is temporarily available in Postfix 2.5; its final
-form is likely to change. </p>
+<p> This feature is available in Postfix 2.5 and later. </p>
 
-%PARAM qmgr_sacrificial_cohorts 1
+%PARAM default_concurrency_failed_cohort_limit 1
 
 <p> How many pseudo-cohorts must suffer connection or handshake
 failure before a specific destination is considered unavailable
-(and further delivery is suspended). A pseudo-cohort is a number
-of deliveries equal to a destination's concurrency. The pseudo-cohort
-failure count is reset each time a delivery completes without
-connection or handshake failure for that specific destination.  </p>
+(and further delivery is suspended). Specify zero to disable this
+feature. A destination's pseudo-cohort failure count is reset each
+time a delivery completes without connection or handshake failure
+for that specific destination. </p>
 
-<p> This feature is temporarily available in Postfix 2.5; its final
-form is likely to change. The default setting is compatible with
-earlier Postfix versions. </p>
+<p> A pseudo-cohort is the number of deliveries equal to a destination's
+delivery concurrency. </p>
 
-%PARAM qmgr_negative_concurrency_feedback_hysteresis 1
+<p> Use <i>transport</i>_concurrency_failed_cohort_limit to specify
+a transport-specific override, where <i>transport</i> is the master.cf
+name of the message delivery transport. </p>
 
-<p> The per-destination integer amount of negative concurrency
-feedback that must accumulate between negative adjustments of a
-destination's delivery concurrency.  The concurrency adjustment is
-equal in size to the negative hysteresis value, and is applied at
-the <b>beginning</b> of a cycle of (hysteresis / feedback) steps.
-At that same time, the destination's positive feedback hysteresis
-cycle is reset to its beginning.  </p>
+<p> This feature is available in Postfix 2.5. The default setting
+is compatible with earlier Postfix versions. </p>
 
-<p> This feature is temporarily available in Postfix 2.5; its final
-form is likely to change. The default setting is compatible with
-earlier Postfix versions. </p>
+%PARAM default_concurrency_negative_feedback 1
 
-%PARAM qmgr_positive_concurrency_feedback_hysteresis 1
+<p> The per-destination amount of negative delivery concurrency
+feedback, after a delivery completes with a connection or handshake
+failure. Feedback values are in range 0..1 inclusive. With negative
+feedback, concurrency is decremented at the beginning of a sequence
+of length 1/feedback. This is unlike positive feedback, where
+concurrency is incremented at the end of a sequence of length
+1/feedback. </p>
 
-<p> The per-destination integer amount of positive concurrency
-feedback that must accumulate before positive adjustments of a
-destination's delivery concurrency.  The concurrency adjustment is
-equal in size to the positive hysteresis value, and is applied at
-the <b>end</b> of a cycle of (hysteresis / feedback) steps. At that
-same time, the destination's negative feedback hysteresis cycle is
-reset to its beginning. </p>
+<p> As of Postfix version 2.5, negative feedback cannot reduce
+delivery concurrency to zero.  Instead, a destination is marked
+dead (further delivery suspended) after the failed pseudo-cohort
+count reaches $default_concurrency_failed_cohort_limit (or
+$<i>transport</i>_concurrency_failed_cohort_limit). To make the
+scheduler completely immune to connection or handshake failures,
+specify a zero feedback value and a zero failed pseudo-cohort limit.
+</p>
 
-<p> This feature is temporarily available in Postfix 2.5; its final
-form is likely to change. The default setting is compatible with
-earlier Postfix versions. </p>
+<p> Specify one of the following forms: </p>
 
-%PARAM qmgr_negative_concurrency_feedback_style fixed_1
+<dl>
 
-<p> The per-destination amount of negative delivery concurrency
-feedback, after a delivery completes with a connection or handshake
-failure.  </p>
+<dt> <b><i>number</i> </b> </dt>
 
-<dl>
+<dt> <b><i>number</i> / <i>number</i> </b> </dt>
+
+<dd> Constant feedback. The value must be in the range 0..1 inclusive.
+The default setting of "1" is compatible with Postfix versions
+before 2.5, where a destination's delivery concurrency is throttled
+down to zero (and further delivery suspended) after a single failed
+pseudo-cohort. </dd>
 
-<dt> <b> inverse_concurrency </b> </dt> <dd> Variable feedback of
-1 / (delivery concurrency). With this setting, and with
-"qmgr_negative_concurrency_feedback_hysteresis = 1", the destination's
-delivery concurrency is decremented by 1 after each failed
-pseudo-cohort, and the destination is marked dead (further delivery
-suspended) after the failed pseudo-cohort count reaches
-$qmgr_sacrificial_cohorts.  </dd>
+<dt> <b><i>number</i> / concurrency </b> </dt>
 
-<dt> <b> inverse_sqrt_concurrency </b> </dt> <dd> Variable feedback
-of 1 / (square root of delivery concurrency). This is an intermediate
-form between the other two. It lacks sound justification, and is a
-candidate for removal. </dd>
+<dd> Variable feedback of "<i>number</i> / (delivery concurrency)".
+The <i>number</i> must be in the range 0..1 inclusive. With
+<i>number</i> equal to "1", a destination's delivery concurrency
+is decremented by 1 after each failed pseudo-cohort.  </dd>
 
-<dt> <b> fixed_1 </b> </dt> <dd> Constant feedback of 1. This setting
-is compatible with Postfix versions before 2.5, where a destination's
-delivery concurrency is throttled down to zero (and further delivery
-suspended) after a single failed pseudo-cohort.  </dd>
+<dt> <b><i>number</i> / sqrt_concurrency </b> </dt>
+
+<dd> Variable feedback of "<i>number</i> / sqrt(delivery concurrency)".
+The <i>number</i> must be in the range 0..1 inclusive. This setting
+may be removed in a future version.  </dd>
 
 </dl>
 
-<p> A pseudo-cohort is a number of deliveries equal to the destination's
+<p> A pseudo-cohort is the number of deliveries equal to a destination's
 delivery concurrency. </p>
 
-<p> This feature is temporarily available in Postfix 2.5; its final
-form is likely to change. The default setting is compatible with
-earlier Postfix versions. </p>
+<p> Use <i>transport</i>_concurrency_negative_feedback to specify
+a transport-specific override, where <i>transport</i> is the master.cf
+name of the message delivery transport. </p>
+
+<p> This feature is available in Postfix 2.5. The default setting
+is compatible with earlier Postfix versions. </p>
 
-%PARAM qmgr_positive_concurrency_feedback_style fixed_1
+%PARAM default_concurrency_positive_feedback 1
 
 <p> The per-destination amount of positive delivery concurrency
 feedback, after a delivery completes without connection or handshake
-failure.  </p>
+failure. Feedback values are in the range 0..1 inclusive.  The
+concurrency increases until it reaches the per-destination maximal
+concurrency limit. With positive feedback, concurrency is incremented
+at the end of a sequence with length 1/feedback. This is unlike
+negative feedback, where concurrency is decremented at the start
+of a sequence of length 1/feedback. </p>
+
+<p> Specify one of the following forms:  </p>
 
 <dl>
 
-<dt> <b> inverse_concurrency </b> </dt> <dd> Variable feedback of
-1 / (delivery concurrency). With this setting, and with
-"qmgr_positive_concurrency_feedback_hysteresis = 1", the destination's
-delivery concurrency is incremented by 1 after each successful
-pseudo-cohort, until it reaches the per-destination maximal concurrency
-limit.  </dd>
-
-<dt> <b> inverse_sqrt_concurrency </b> </dt> <dd> Variable feedback
-of 1 / (square root of delivery concurrency). This is an intermediate
-form between the other two. It lacks sound justification, and is a
-candidate for removal. </dd>
-
-<dt> <b> fixed_1 </b> </dt> <dd> Constant feedback of 1. This setting
-is compatible with Postfix versions before 2.5, where a destination's
-delivery concurrency is doubled after each successful pseudo-cohort,
-until it reaches the per-destination maximal concurrency limit.
-</dd>
+<dt> <b><i>number</i> </b> </dt>
+
+<dt> <b><i>number</i> / <i>number</i> </b> </dt>
+
+<dd> Constant feedback.  The value must be in the range 0..1
+inclusive. The default setting of "1" is compatible with Postfix
+versions before 2.5, where a destination's delivery concurrency
+doubles after each successful pseudo-cohort.  </dd>
+
+<dt> <b><i>number</i> / concurrency </b> </dt>
+
+<dd> Variable feedback of "<i>number</i> / (delivery concurrency)".
+The <i>number</i> must be in the range 0..1 inclusive. With
+<i>number</i> equal to "1", a destination's delivery concurrency
+is incremented by 1 after each successful pseudo-cohort.  </dd>
+
+<dt> <b><i>number</i> / sqrt_concurrency </b> </dt>
+
+<dd> Variable feedback of "<i>number</i> / sqrt(delivery concurrency)".
+The <i>number</i> must be in the range 0..1 inclusive. This setting
+may be removed in a future version.  </dd>
 
 </dl>
 
-<p> A pseudo-cohort is a number of deliveries equal to the destination's
+<p> A pseudo-cohort is the number of deliveries equal to a destination's
 delivery concurrency. </p>
 
-<p> This feature is temporarily available in Postfix 2.5. The default
-setting is compatible with earlier Postfix versions. </p>
+<p> Use <i>transport</i>_concurrency_positive_feedback to specify
+a transport-specific override, where <i>transport</i> is the master.cf
+name of the message delivery transport. </p>
+
+<p> This feature is available in Postfix 2.5 and later.  </p>
+
+%PARAM <i>transport</i>_concurrency_failed_cohort_limit $default_concurrency_failed_cohort_limit
+
+<p> A transport-specific override for the
+default_concurrency_failed_cohort_limit parameter value, where
+<i>transport</i> is the master.cf name of the message delivery
+transport. </p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+%PARAM transport_concurrency_positive_feedback $default_concurrency_positive_feedback
+
+<p> A transport-specific override for the
+default_concurrency_positive_feedback parameter value, where
+<i>transport</i> is the master.cf name of the message delivery
+transport. </p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+%PARAM transport_concurrency_negative_feedback $default_concurrency_negative_feedback
+
+<p> A transport-specific override for the
+default_concurrency_negative_feedback parameter value, where
+<i>transport</i> is the master.cf name of the message delivery
+transport. </p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+%PARAM transport_initial_destination_concurrency $initial_destination_concurrency
+
+<p> A transport-specific override for the initial_destination_concurrency
+parameter value, where <i>transport</i> is the master.cf name of
+the message delivery transport. </p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+%PARAM transport_destination_concurrency_limit $default_destination_concurrency_limit
+
+<p> A transport-specific override for the
+default_destination_concurrency_limit parameter value, where
+<i>transport</i> is the master.cf name of the message delivery
+transport. </p>
+
+%PARAM transport_destination_recipient_limit $default_destination_concurrency_limit
+
+<p> A transport-specific override for the
+default_destination_recipient_limit parameter value, where
+<i>transport</i> is the master.cf name of the message delivery
+transport. </p>
+
+%PARAM transport_time_limit $command_time_limit
+
+<p> A transport-specific override for the command_time_limit parameter
+value, where <i>transport</i> is the master.cf name of the message
+delivery transport. </p>
+
+%PARAM transport_delivery_slot_cost $default_delivery_slot_cost
+
+<p> A transport-specific override for the default_delivery_slot_cost
+parameter value, where <i>transport</i> is the master.cf name of
+the message delivery transport. </p>
+
+%PARAM transport_delivery_slot_loan $default_delivery_slot_loan
+
+<p> A transport-specific override for the default_delivery_slot_loan
+parameter value, where <i>transport</i> is the master.cf name of  
+the message delivery transport. </p>
+
+%PARAM transport_delivery_slot_discount $default_delivery_slot_discount
+
+<p> A transport-specific override for the default_delivery_slot_discount
+parameter value, where <i>transport</i> is the master.cf name of
+the message delivery transport. </p>
+
+%PARAM transport_minimum_delivery_slots $default_minimum_delivery_slots
+
+<p> A transport-specific override for the default_minimum_delivery_slots
+parameter value, where <i>transport</i> is the master.cf name of
+the message delivery transport. </p>
+
+%PARAM transport_recipient_limit $default_recipient_limit
+
+<p> A transport-specific override for the default_recipient_limit
+parameter value, where <i>transport</i> is the master.cf name of
+the message delivery transport. </p>
+
+%PARAM transport_extra_recipient_limit $default_extra_recipient_limit
+
+<p> A transport-specific override for the default_extra_recipient_limit
+parameter value, where <i>transport</i> is the master.cf name of
+the message delivery transport. </p>
+
+%PARAM transport_recipient_refill_limit $default_recipient_refill_limit
+
+<p> A transport-specific override for the default_recipient_refill_limit
+parameter value, where <i>transport</i> is the master.cf name of
+the message delivery transport. </p>
+
+<p> This feature is available in Postfix 2.4 and later. </p>
+
+%PARAM transport_recipient_refill_delay $default_recipient_refill_delay
+
+<p> A transport-specific override for the default_recipient_refill_delay
+parameter value, where <i>transport</i> is the master.cf name of
+the message delivery transport. </p>
+
+<p> This feature is available in Postfix 2.4 and later. </p>
index d077f3dc63fdc0e2cedae25147e7f958a1d9ec39..e354ed69e767b789db2d6cd6cfb10b677e398dbc 100644 (file)
@@ -766,21 +766,6 @@ delivered_hdr.o: quote_822_local.h
 delivered_hdr.o: quote_flags.h
 delivered_hdr.o: rec_type.h
 delivered_hdr.o: record.h
-header_body_checks.o: ../../include/argv.h
-header_body_checks.o: ../../include/dict.h
-header_body_checks.o: ../../include/msg.h
-header_body_checks.o: ../../include/mymalloc.h
-header_body_checks.o: ../../include/sys_defs.h
-header_body_checks.o: ../../include/vbuf.h
-header_body_checks.o: ../../include/vstream.h
-header_body_checks.o: ../../include/vstring.h
-header_body_checks.o: header_body_checks.c
-header_body_checks.o: header_body_checks.h
-header_body_checks.o: header_opts.h
-header_body_checks.o: is_header.h
-header_body_checks.o: maps.h
-header_body_checks.o: mime_state.h
-header_body_checks.o: rec_type.h
 dict_ldap.o: ../../include/argv.h
 dict_ldap.o: ../../include/binhash.h
 dict_ldap.o: ../../include/dict.h
@@ -965,6 +950,23 @@ fold_addr.o: ../../include/vbuf.h
 fold_addr.o: ../../include/vstring.h
 fold_addr.o: fold_addr.c
 fold_addr.o: fold_addr.h
+header_body_checks.o: ../../include/argv.h
+header_body_checks.o: ../../include/dict.h
+header_body_checks.o: ../../include/msg.h
+header_body_checks.o: ../../include/mymalloc.h
+header_body_checks.o: ../../include/sys_defs.h
+header_body_checks.o: ../../include/vbuf.h
+header_body_checks.o: ../../include/vstream.h
+header_body_checks.o: ../../include/vstring.h
+header_body_checks.o: cleanup_user.h
+header_body_checks.o: dsn_util.h
+header_body_checks.o: header_body_checks.c
+header_body_checks.o: header_body_checks.h
+header_body_checks.o: header_opts.h
+header_body_checks.o: is_header.h
+header_body_checks.o: maps.h
+header_body_checks.o: mime_state.h
+header_body_checks.o: rec_type.h
 header_opts.o: ../../include/htable.h
 header_opts.o: ../../include/msg.h
 header_opts.o: ../../include/stringops.h
@@ -1125,7 +1127,10 @@ mail_conf_raw.o: mail_conf.h
 mail_conf_raw.o: mail_conf_raw.c
 mail_conf_str.o: ../../include/msg.h
 mail_conf_str.o: ../../include/mymalloc.h
+mail_conf_str.o: ../../include/stringops.h
 mail_conf_str.o: ../../include/sys_defs.h
+mail_conf_str.o: ../../include/vbuf.h
+mail_conf_str.o: ../../include/vstring.h
 mail_conf_str.o: mail_conf.h
 mail_conf_str.o: mail_conf_str.c
 mail_conf_time.o: ../../include/argv.h
index 1ead675dd528bbdf3eddcff480f951ec621f3568..de8f58bc97bd6b68d248cc89bdcca2903992ae3a 100644 (file)
@@ -230,13 +230,9 @@ static char *hbc_action(void *context, HBC_CALL_BACKS *cb,
     char   *ret;
 
     /*
-     * XXX We don't delegate action logging to the action call-back
-     * functions, because actions without call-back must be logged here
-     * anyway. This means that some actions must report back whether the
-     * action should be logged. This is admittedly a little clumsy.
-     * 
-     * XXX We don't use a hash table for action lookup. Mail rarely triggers an
-     * action, and mail that triggers multiple actions is even rarer.
+     * XXX We don't use a hash table for action lookup. Mail rarely triggers
+     * an action, and mail that triggers multiple actions is even rarer.
+     * Setting up the hash table costs more than we would gain from using it.
      */
     while (*cmd_args && ISSPACE(*cmd_args))
        cmd_args++;
index aa0e5d4aa8ca9937165803a73fc3cf625e622fc9..a8bff1b57fb0ffcaef9a67fee26a83edbc5e5376 100644 (file)
@@ -51,6 +51,7 @@ extern int get_mail_conf_bool(const char *, int);
 extern int get_mail_conf_time(const char *, const char *, int, int);
 extern char *get_mail_conf_raw(const char *, const char *, int, int);
 
+extern char *get_mail_conf_str2(const char *, const char *, const char *, int, int);
 extern int get_mail_conf_int2(const char *, const char *, int, int, int);
 extern long get_mail_conf_long2(const char *, const char *, long, long, long);
 extern int get_mail_conf_time2(const char *, const char *, int, int, int, int);
index bb734520d15ac98cd69deb956f736433f7023e29..1f8af32e3e4cca71d2a8c5c44017974153c8a860 100644 (file)
 /*
 /*     void    get_mail_conf_str_fn_table(table)
 /*     CONFIG_STR_TABLE *table;
+/* AUXILIARY FUNCTIONS
+/*     char    *get_mail_conf_str2(name, suffix, defval, min, max)
+/*     const char *name;
+/*     const char *suffix;
+/*     const char *defval;
+/*     int     min;
+/*     int     max;
 /* DESCRIPTION
 /*     This module implements support for string-valued global
 /*     configuration parameters.
@@ -49,6 +56,9 @@
 /*     get_mail_conf_str_table() and get_mail_conf_str_fn_table() read
 /*     lists of variables, as directed by their table arguments. A table
 /*     must be terminated by a null entry.
+/*
+/*     get_mail_conf_str2() concatenates the two names and is otherwise
+/*     identical to get_mail_conf_str().
 /* DIAGNOSTICS
 /*     Fatal errors: bad string length.
 /* SEE ALSO
@@ -74,6 +84,7 @@
 
 #include <msg.h>
 #include <mymalloc.h>
+#include <stringops.h>
 
 /* Global library. */
 
@@ -82,7 +93,7 @@
 /* check_mail_conf_str - validate string length */
 
 static void check_mail_conf_str(const char *name, const char *strval,
-                                    int min, int max)
+                                       int min, int max)
 {
     ssize_t len = strlen(strval);
 
@@ -97,7 +108,7 @@ static void check_mail_conf_str(const char *name, const char *strval,
 /* get_mail_conf_str - evaluate string-valued configuration variable */
 
 char   *get_mail_conf_str(const char *name, const char *defval,
-                              int min, int max)
+                                 int min, int max)
 {
     const char *strval;
 
@@ -109,12 +120,31 @@ char   *get_mail_conf_str(const char *name, const char *defval,
     return (mystrdup(strval));
 }
 
+/* get_mail_conf_str2 - evaluate string-valued configuration variable */
+
+char   *get_mail_conf_str2(const char *name1, const char *name2,
+                                  const char *defval,
+                                  int min, int max)
+{
+    const char *strval;
+    char   *name;
+
+    name = concatenate(name1, name2, (char *) 0);
+    if ((strval = mail_conf_lookup_eval(name)) == 0) {
+       strval = mail_conf_eval(defval);
+       mail_conf_update(name, strval);
+    }
+    check_mail_conf_str(name, strval, min, max);
+    myfree(name);
+    return (mystrdup(strval));
+}
+
 /* get_mail_conf_str_fn - evaluate string-valued configuration variable */
 
 typedef const char *(*stupid_indent_str) (void);
 
 char   *get_mail_conf_str_fn(const char *name, stupid_indent_str defval,
-                                 int min, int max)
+                                    int min, int max)
 {
     const char *strval;
 
@@ -141,7 +171,7 @@ void    get_mail_conf_str_table(CONFIG_STR_TABLE *table)
        if (table->target[0])
            myfree(table->target[0]);
        table->target[0] = get_mail_conf_str(table->name, table->defval,
-                                         table->min, table->max);
+                                            table->min, table->max);
        table++;
     }
 }
@@ -154,7 +184,7 @@ void    get_mail_conf_str_fn_table(CONFIG_STR_FN_TABLE *table)
        if (table->target[0])
            myfree(table->target[0]);
        table->target[0] = get_mail_conf_str_fn(table->name, table->defval,
-                                            table->min, table->max);
+                                               table->min, table->max);
        table++;
     }
 }
index 6579ade1215e3fcf640d8ace86de575ee80cd646..ff38bb613298146908754d7020f473e6dcfdd246 100644 (file)
@@ -456,6 +456,7 @@ extern bool var_biff;
 extern char *var_allow_commands;
 
 #define VAR_COMMAND_MAXTIME    "command_time_limit"
+#define _MAXTIME               "_time_limit"
 #define DEF_COMMAND_MAXTIME    "1000s"
 extern int var_command_maxtime;
 
@@ -731,6 +732,7 @@ extern int var_qmgr_fudge;
   * Queue manager: default destination concurrency levels.
   */
 #define VAR_INIT_DEST_CON      "initial_destination_concurrency"
+#define _INIT_DEST_CON         "_initial_destination_concurrency"
 #define DEF_INIT_DEST_CON      5
 extern int var_init_dest_concurrency;
 
@@ -2837,35 +2839,27 @@ extern char *var_smtp_body_chks;
  /*
   * Scheduler concurrency feedback algorithms.
   */
-#define VAR_QMGR_POS_FDBACK    "qmgr_positive_concurrency_feedback_style"
-#define DEF_QMGR_POS_FDBACK    QMGR_FDBACK_NAME_FIXED_1
-extern char *var_qmgr_pos_feedback;
+#define VAR_CONC_POS_FDBACK    "default_concurrency_positive_feedback"
+#define _CONC_POS_FDBACK       "_concurrency_positive_feedback"
+#define DEF_CONC_POS_FDBACK    "1"
+extern char *var_conc_pos_feedback;
 
-#define VAR_QMGR_NEG_FDBACK    "qmgr_negative_concurrency_feedback_style"
-#define DEF_QMGR_NEG_FDBACK    QMGR_FDBACK_NAME_FIXED_1
-extern char *var_qmgr_neg_feedback;
+#define VAR_CONC_NEG_FDBACK    "default_concurrency_negative_feedback"
+#define _CONC_NEG_FDBACK       "_concurrency_negative_feedback"
+#define DEF_CONC_NEG_FDBACK    "1"
+extern char *var_conc_neg_feedback;
 
-#define QMGR_FDBACK_NAME_FIXED_1       "fixed_1"
-#define QMGR_FDBACK_NAME_INVERSE_1     "inverse_1"     /* deprecated */
-#define QMGR_FDBACK_NAME_INVERSE_WIN   "inverse_concurrency"
-#define QMGR_FDBACK_NAME_INV_SQRT      "inverse_sqrt"  /* deprecated */
-#define QMGR_FDBACK_NAME_INV_SQRT_WIN  "inverse_sqrt_concurrency"
+#define CONC_FDBACK_NAME_WIN   "concurrency"
+#define CONC_FDBACK_NAME_SQRT_WIN "sqrt_concurrency"
 
-#define VAR_QMGR_POS_HYST      "qmgr_positive_concurrency_feedback_hysteresis"
-#define DEF_QMGR_POS_HYST      1
-extern int var_qmgr_pos_hysteresis;
+#define VAR_CONC_COHORT_LIM    "default_concurrency_failed_cohort_limit"
+#define _CONC_COHORT_LIM       "_concurrency_failed_cohort_limit"
+#define DEF_CONC_COHORT_LIM    1
+extern int var_conc_cohort_limit;
 
-#define VAR_QMGR_NEG_HYST      "qmgr_negative_concurrency_feedback_hysteresis"
-#define DEF_QMGR_NEG_HYST      1
-extern int var_qmgr_neg_hysteresis;
-
-#define VAR_QMGR_SAC_COHORTS   "qmgr_sacrificial_cohorts"
-#define DEF_QMGR_SAC_COHORTS   1
-extern int var_qmgr_sac_cohorts;
-
-#define VAR_QMGR_FDBACK_DEBUG  "qmgr_concurrency_feedback_debug"
-#define DEF_QMGR_FDBACK_DEBUG  0
-extern bool var_qmgr_feedback_debug;
+#define VAR_CONC_FDBACK_DEBUG  "concurrency_feedback_debug"
+#define DEF_CONC_FDBACK_DEBUG  0
+extern bool var_conc_feedback_debug;
 
 /* LICENSE
 /* .ad
index 62f2fc5e4920b909c5c46cd7ef51545149089ef7..18f01dc9a0ddfca5fb088cc7f8201874cb26eb2a 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20071122"
+#define MAIL_RELEASE_DATE      "20071129"
 #define MAIL_VERSION_NUMBER    "2.5"
 
 #ifdef SNAPSHOT
index 6b7b037a380fd1253edeb94f581b5d1caf265c40..a49131572cd09a28374172a3e351cf6dfce836ec 100644 (file)
 /* OTHER RESOURCE AND RATE CONTROLS
 /* .ad
 /* .fi
-/* .IP "\fBminimal_backoff_time (version dependent)\fR"
-/*     The minimal time between attempts to deliver a deferred message.
+/* .IP "\fBminimal_backoff_time (300s)\fR"
+/*     The minimal time between attempts to deliver a deferred message;
+/*     prior to Postfix 2.4 the default value was 1000s.
 /* .IP "\fBmaximal_backoff_time (4000s)\fR"
 /*     The maximal time between attempts to deliver a deferred message.
 /* .IP "\fBmaximal_queue_lifetime (5d)\fR"
 /*     The maximal time a message is queued before it is sent back as
 /*     undeliverable.
-/* .IP "\fBqueue_run_delay (version dependent)\fR"
-/*     The time between deferred queue scans by the queue manager.
+/* .IP "\fBqueue_run_delay (300s)\fR"
+/*     The time between deferred queue scans by the queue manager;
+/*     prior to Postfix 2.4 the default value was 1000s.
 /* .IP "\fBtransport_retry_time (60s)\fR"
 /*     The time between attempts by the Postfix queue manager to contact
 /*     a malfunctioning message delivery transport.
index e5c37b13f6a448dc7d91d62318c4e753f79d7ab5..e8fd4702eefddf45128b654ef02959fd9a9b1d8a 100644 (file)
@@ -789,7 +789,7 @@ static void get_service_params(PIPE_PARAMS *config, char *service)
      * Figure out the command time limit for this transport.
      */
     config->time_limit =
-       get_mail_conf_time2(service, "_time_limit", var_command_maxtime, 's', 1, 0);
+       get_mail_conf_time2(service, _MAXTIME, var_command_maxtime, 's', 1, 0);
 
     /*
      * Give the poor tester a clue of what is going on.
index ea62bdecc3e276b545c3dcfe9dfc59b81772b63b..cd2ddc2f7da907161ff531ca960ae87a88cad625 100644 (file)
@@ -2,11 +2,13 @@ SHELL = /bin/sh
 SRCS   = qmgr.c qmgr_active.c qmgr_transport.c qmgr_queue.c qmgr_entry.c \
        qmgr_message.c qmgr_deliver.c qmgr_move.c \
        qmgr_job.c qmgr_peer.c \
-       qmgr_defer.c qmgr_enable.c qmgr_scan.c qmgr_bounce.c qmgr_error.c
+       qmgr_defer.c qmgr_enable.c qmgr_scan.c qmgr_bounce.c qmgr_error.c \
+       qmgr_feedback.c
 OBJS   = qmgr.o qmgr_active.o qmgr_transport.o qmgr_queue.o qmgr_entry.o \
        qmgr_message.o qmgr_deliver.o qmgr_move.o \
        qmgr_job.o qmgr_peer.o \
-       qmgr_defer.o qmgr_enable.o qmgr_scan.o qmgr_bounce.o qmgr_error.o
+       qmgr_defer.o qmgr_enable.o qmgr_scan.o qmgr_bounce.o qmgr_error.o \
+       qmgr_feedback.o
 HDRS   = qmgr.h
 TESTSRC        =
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
@@ -209,6 +211,21 @@ qmgr_error.o: ../../include/vstream.h
 qmgr_error.o: ../../include/vstring.h
 qmgr_error.o: qmgr.h
 qmgr_error.o: qmgr_error.c
+qmgr_feedback.o: ../../include/dsn.h
+qmgr_feedback.o: ../../include/mail_conf.h
+qmgr_feedback.o: ../../include/mail_params.h
+qmgr_feedback.o: ../../include/msg.h
+qmgr_feedback.o: ../../include/mymalloc.h
+qmgr_feedback.o: ../../include/name_code.h
+qmgr_feedback.o: ../../include/recipient_list.h
+qmgr_feedback.o: ../../include/scan_dir.h
+qmgr_feedback.o: ../../include/stringops.h
+qmgr_feedback.o: ../../include/sys_defs.h
+qmgr_feedback.o: ../../include/vbuf.h
+qmgr_feedback.o: ../../include/vstream.h
+qmgr_feedback.o: ../../include/vstring.h
+qmgr_feedback.o: qmgr.h
+qmgr_feedback.o: qmgr_feedback.c
 qmgr_job.o: ../../include/dsn.h
 qmgr_job.o: ../../include/htable.h
 qmgr_job.o: ../../include/msg.h
@@ -284,13 +301,15 @@ qmgr_peer.o: ../../include/vbuf.h
 qmgr_peer.o: ../../include/vstream.h
 qmgr_peer.o: qmgr.h
 qmgr_peer.o: qmgr_peer.c
+qmgr_queue.o: ../../include/attr.h
 qmgr_queue.o: ../../include/dsn.h
 qmgr_queue.o: ../../include/events.h
 qmgr_queue.o: ../../include/htable.h
+qmgr_queue.o: ../../include/iostuff.h
 qmgr_queue.o: ../../include/mail_params.h
+qmgr_queue.o: ../../include/mail_proto.h
 qmgr_queue.o: ../../include/msg.h
 qmgr_queue.o: ../../include/mymalloc.h
-qmgr_queue.o: ../../include/name_code.h
 qmgr_queue.o: ../../include/recipient_list.h
 qmgr_queue.o: ../../include/scan_dir.h
 qmgr_queue.o: ../../include/sys_defs.h
index c932d1c2ae974c6607b327b265b3500301b671cd..d2b31325f00162ee2c5e842a0e4bd56a2456d782 100644 (file)
 /*     destination.
 /* .IP "\fItransport\fB_destination_concurrency_limit ($default_destination_concurrency_limit)\fR"
 /*     Idem, for delivery via the named message \fItransport\fR.
-/* .IP "\fBqmgr_concurrency_feedback_debug (no)\fR"
-/*     Make the queue manager's feedback algorithm verbose for performance
-/*     analysis purposes.
-/* .IP "\fBqmgr_negative_concurrency_feedback_hysteresis (1)\fR"
-/*     The per-destination integer amount of negative concurrency
-/*     feedback that must accumulate between negative adjustments of a
-/*     destination's delivery concurrency.
-/* .IP "\fBqmgr_negative_concurrency_feedback_style (fixed_1)\fR"
+/* .PP
+/*     Available in Postfix version 2.5 and later:
+/* .IP "\fItransport\fB_initial_destination_concurrency ($initial_destination_concurrency)\fR"
+/*     Initial concurrency for delivery via the named message
+/*     \fItransport\fR.
+/* .IP "\fBdefault_concurrency_failed_cohort_limit (1)\fR"
+/*     How many pseudo-cohorts must suffer connection or handshake
+/*     failure before a specific destination is considered unavailable
+/*     (and further delivery is suspended).
+/* .IP "\fItransport\fB_concurrency_failed_cohort_limit ($default_concurrency_failed_cohort_limit)\fR"
+/*     Idem, for delivery via the named message \fItransport\fR.
+/* .IP "\fBdefault_concurrency_negative_feedback (1)\fR"
 /*     The per-destination amount of negative delivery concurrency
 /*     feedback, after a delivery completes with a connection or handshake
 /*     failure.
-/* .IP "\fBqmgr_positive_concurrency_feedback_hysteresis (1)\fR"
-/*     The per-destination integer amount of positive concurrency
-/*     feedback that must accumulate before positive adjustments of a
-/*     destination's delivery concurrency.
-/* .IP "\fBqmgr_positive_concurrency_feedback_style (fixed_1)\fR"
+/* .IP "\fItransport\fB_concurrency_negative_feedback ($default_concurrency_negative_feedback)\fR"
+/*     Idem, for delivery via the named message \fItransport\fR.
+/* .IP "\fBdefault_concurrency_positive_feedback (1)\fR"
 /*     The per-destination amount of positive delivery concurrency
 /*     feedback, after a delivery completes without connection or handshake
 /*     failure.
-/* .IP "\fBqmgr_sacrificial_cohorts (1)\fR"
-/*     How many pseudo-cohorts must suffer connection or handshake
-/*     failure before a specific destination is considered unavailable
-/*     (and further delivery is suspended).
+/* .IP "\fItransport\fB_concurrency_positive_feedback ($default_concurrency_positive_feedback)\fR"
+/*     Idem, for delivery via the named message \fItransport\fR.
+/* .IP "\fBconcurrency_feedback_debug (no)\fR"
+/*     Make the queue manager's feedback algorithm verbose for performance
+/*     analysis purposes.
 /* RECIPIENT SCHEDULING CONTROLS
 /* .ad
 /* .fi
@@ -415,12 +418,10 @@ int     var_local_rcpt_lim;
 int     var_proc_limit;
 bool    var_verp_bounce_off;
 int     var_qmgr_clog_warn_time;
-char   *var_qmgr_pos_feedback;
-char   *var_qmgr_neg_feedback;
-int     var_qmgr_pos_hysteresis;
-int     var_qmgr_neg_hysteresis;
-int     var_qmgr_sac_cohorts;
-int     var_qmgr_feedback_debug;
+char   *var_conc_pos_feedback;
+char   *var_conc_neg_feedback;
+int     var_conc_cohort_limit;
+int     var_conc_feedback_debug;
 
 static QMGR_SCAN *qmgr_scans[2];
 
@@ -645,11 +646,6 @@ static void qmgr_post_init(char *name, char **unused_argv)
     qmgr_scans[QMGR_SCAN_IDX_DEFERRED] = qmgr_scan_create(MAIL_QUEUE_DEFERRED);
     qmgr_scan_request(qmgr_scans[QMGR_SCAN_IDX_INCOMING], QMGR_SCAN_START);
     qmgr_deferred_run_event(0, (char *) 0);
-
-    /*
-     * Scheduler initialization.
-     */
-    qmgr_queue_feedback_init();
 }
 
 MAIL_VERSION_STAMP_DECLARE;
@@ -660,8 +656,8 @@ int     main(int argc, char **argv)
 {
     static CONFIG_STR_TABLE str_table[] = {
        VAR_DEFER_XPORTS, DEF_DEFER_XPORTS, &var_defer_xports, 0, 0,
-       VAR_QMGR_POS_FDBACK, DEF_QMGR_POS_FDBACK, &var_qmgr_pos_feedback, 1, 0,
-       VAR_QMGR_NEG_FDBACK, DEF_QMGR_NEG_FDBACK, &var_qmgr_neg_feedback, 1, 0,
+       VAR_CONC_POS_FDBACK, DEF_CONC_POS_FDBACK, &var_conc_pos_feedback, 1, 0,
+       VAR_CONC_NEG_FDBACK, DEF_CONC_NEG_FDBACK, &var_conc_neg_feedback, 1, 0,
        0,
     };
     static CONFIG_TIME_TABLE time_table[] = {
@@ -692,15 +688,13 @@ int     main(int argc, char **argv)
        VAR_LOCAL_RCPT_LIMIT, DEF_LOCAL_RCPT_LIMIT, &var_local_rcpt_lim, 0, 0,
        VAR_LOCAL_CON_LIMIT, DEF_LOCAL_CON_LIMIT, &var_local_con_lim, 0, 0,
        VAR_PROC_LIMIT, DEF_PROC_LIMIT, &var_proc_limit, 1, 0,
-       VAR_QMGR_POS_HYST, DEF_QMGR_POS_HYST, &var_qmgr_pos_hysteresis, 1, 0,
-       VAR_QMGR_NEG_HYST, DEF_QMGR_NEG_HYST, &var_qmgr_neg_hysteresis, 1, 0,
-       VAR_QMGR_SAC_COHORTS, DEF_QMGR_SAC_COHORTS, &var_qmgr_sac_cohorts, 1, 0,
+       VAR_CONC_COHORT_LIM, DEF_CONC_COHORT_LIM, &var_conc_cohort_limit, 0, 0,
        0,
     };
     static CONFIG_BOOL_TABLE bool_table[] = {
        VAR_ALLOW_MIN_USER, DEF_ALLOW_MIN_USER, &var_allow_min_user,
        VAR_VERP_BOUNCE_OFF, DEF_VERP_BOUNCE_OFF, &var_verp_bounce_off,
-       VAR_QMGR_FDBACK_DEBUG, DEF_QMGR_FDBACK_DEBUG, &var_qmgr_feedback_debug,
+       VAR_CONC_FDBACK_DEBUG, DEF_CONC_FDBACK_DEBUG, &var_conc_feedback_debug,
        0,
     };
 
index 7490e842e7718114bbb10833ee93b0d0812c9aec..3f6a3999e83b220504814ec90a23523d536a7ec1 100644 (file)
@@ -42,6 +42,7 @@ typedef struct QMGR_ENTRY_LIST QMGR_ENTRY_LIST;
 typedef struct QMGR_JOB_LIST QMGR_JOB_LIST;
 typedef struct QMGR_PEER_LIST QMGR_PEER_LIST;
 typedef struct QMGR_SCAN QMGR_SCAN;
+typedef struct QMGR_FEEDBACK QMGR_FEEDBACK;
 
  /*
   * Hairy macros to update doubly-linked lists.
@@ -113,6 +114,40 @@ struct QMGR_TRANSPORT_LIST {
 extern struct HTABLE *qmgr_transport_byname;   /* transport by name */
 extern QMGR_TRANSPORT_LIST qmgr_transport_list;        /* transports, round robin */
 
+ /*
+  * Delivery agents provide feedback, as hints that Postfix should expend
+  * more or fewer resources on a specific destination domain. The main.cf
+  * file specifies how feedback affects delivery concurrency: add/subtract a
+  * constant, a ratio of constants, or a constant divided by the delivery
+  * concurrency; and it specifies how much feedback must accumulate between
+  * concurrency updates.
+  */
+struct QMGR_FEEDBACK {
+    int     hysteresis;                        /* to pass, need to be this tall */
+    double  base;                      /* pre-computed from main.cf */
+    int     index;                     /* none, window, sqrt(window) */
+};
+
+#define QMGR_FEEDBACK_IDX_NONE         0       /* no window dependence */
+#define QMGR_FEEDBACK_IDX_WIN          1       /* 1/window dependence */
+#define QMGR_FEEDBACK_IDX_SQRT_WIN     2       /* 1/sqrt(window) dependence */
+
+#ifdef QMGR_FEEDBACK_IDX_SQRT_WIN
+#include <math.h>
+#endif
+
+extern void qmgr_feedback_init(QMGR_FEEDBACK *, const char *, const char *, const char *, const char *);
+
+#ifndef QMGR_FEEDBACK_IDX_SQRT_WIN
+#define QMGR_FEEDBACK_VAL(fb, win) \
+    ((fb).index == QMGR_FEEDBACK_IDX_NONE ? (fb).base : (fb).base / (win))
+#else
+#define QMGR_FEEDBACK_VAL(fb, win) \
+    ((fb).index == QMGR_FEEDBACK_IDX_NONE ? (fb).base : \
+    (fb).index == QMGR_FEEDBACK_IDX_WIN ? (fb).base / (win) : \
+    (fb).base / sqrt(win))
+#endif
+
  /*
   * Each transport (local, smtp-out, bounce) can have one queue per next hop
   * name. Queues are looked up by next hop name (when we have resolved a
@@ -164,6 +199,9 @@ struct QMGR_TRANSPORT {
     int     blocker_tag;               /* for marking blocker jobs */
     QMGR_TRANSPORT_LIST peers;         /* linkage */
     DSN    *dsn;                       /* why unavailable */
+    QMGR_FEEDBACK pos_feedback;                /* positive feedback control */
+    QMGR_FEEDBACK neg_feedback;                /* negative feedback control */
+    int     fail_cohort_limit;         /* flow shutdown control */
 };
 
 #define QMGR_TRANSPORT_STAT_DEAD       (1<<1)
@@ -198,8 +236,8 @@ struct QMGR_QUEUE {
     int     todo_refcount;             /* queue entries (todo list) */
     int     busy_refcount;             /* queue entries (busy list) */
     int     window;                    /* slow open algorithm */
-    double  success;                   /* cumulative positive feedback */
-    double  failure;                   /* cumulative negative feedback */
+    double  success;                   /* accumulated positive feedback */
+    double  failure;                   /* accumulated negative feedback */
     double  fail_cohorts;              /* pseudo-cohort failure count */
     QMGR_TRANSPORT *transport;         /* transport linkage */
     QMGR_ENTRY_LIST todo;              /* todo queue entries */
@@ -220,7 +258,6 @@ extern void qmgr_queue_done(QMGR_QUEUE *);
 extern void qmgr_queue_throttle(QMGR_QUEUE *, DSN *);
 extern void qmgr_queue_unthrottle(QMGR_QUEUE *);
 extern QMGR_QUEUE *qmgr_queue_find(QMGR_TRANSPORT *, const char *);
-extern void qmgr_queue_feedback_init(void);
 
 #define QMGR_QUEUE_THROTTLED(q) ((q)->window <= 0)
 
diff --git a/postfix/src/qmgr/qmgr_feedback.c b/postfix/src/qmgr/qmgr_feedback.c
new file mode 100644 (file)
index 0000000..6d62a6e
--- /dev/null
@@ -0,0 +1,177 @@
+/*++
+/* NAME
+/*     qmgr_feedback 3
+/* SUMMARY
+/*     delivery agent feedback management
+/* SYNOPSIS
+/*     #include "qmgr.h"
+/*
+/*     void    qmgr_feedback_init(fbck_ctl, name_prefix, name_tail,
+/*                                     def_name, def_value)
+/*     QMGR_FEEDBACK *fbck_ctl;
+/*     const char *name_prefix;
+/*     const char *name_tail;
+/*     const char *def_name;
+/*     const char *def_value;
+/*
+/*     double  QMGR_FEEDBACK_VAL(fbck_ctl, concurrency)
+/*     QMGR_FEEDBACK *fbck_ctl;
+/*     const int concurrency;
+/* DESCRIPTION
+/*     Upon completion of a delivery request, a delivery agent
+/*     provides a hint that the scheduler should dedicate fewer or
+/*     more resources to a specific destination.
+/*
+/*     qmgr_feedback_init() looks up transport-dependent positive
+/*     or negative concurrency feedback control information from
+/*     main.cf, and converts it to internal form.
+/*
+/*     QMGR_FEEDBACK_VAL() computes a concurrency adjustment based
+/*     on a preprocessed feedback control information and the
+/*     current concurrency window. This is an "unsafe" macro that
+/*     evaluates some arguments multiple times.
+/*
+/*     Arguments:
+/* .IP fbck_ctl
+/*     Pointer to QMGR_FEEDBACK structure where the result will
+/*     be stored.
+/* .IP name_prefix
+/*     Mail delivery transport name, used as the initial portion
+/*     of a transport-dependent concurrency feedback parameter
+/*     name.
+/* .IP name_tail
+/*     The second, and fixed, portion of a transport-dependent
+/*     concurrency feedback parameter.
+/* .IP def_name
+/*     The name of a default feedback parameter.
+/* .IP def_val
+/*     The value of the default feedback parameter.
+/* .IP concurrency
+/*     Delivery concurrency for concurrency-dependent feedback calculation.
+/* DIAGNOSTICS
+/*     Warning: configuration error or unreasonable input. The program
+/*     uses name_tail feedback instead.
+/*     Panic: consistency check failure.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdlib.h>
+#include <limits.h>                    /* INT_MAX */
+#include <stdio.h>                     /* sscanf() */
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <name_code.h>
+#include <stringops.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_conf.h>
+
+/* Application-specific. */
+
+#include "qmgr.h"
+
+ /*
+  * Lookup tables for main.cf feedback method names.
+  */
+NAME_CODE qmgr_feedback_map[] = {
+    CONC_FDBACK_NAME_WIN, QMGR_FEEDBACK_IDX_WIN,
+#ifdef QMGR_FEEDBACK_IDX_SQRT_WIN
+    CONC_FDBACK_NAME_SQRT_WIN, QMGR_FEEDBACK_IDX_SQRT_WIN,
+#endif
+    0, QMGR_FEEDBACK_IDX_NONE,
+};
+
+/* qmgr_feedback_init - initialize feedback control */
+
+void    qmgr_feedback_init(QMGR_FEEDBACK *fb,
+                                  const char *name_prefix,
+                                  const char *name_tail,
+                                  const char *def_name,
+                                  const char *def_val)
+{
+    double  enum_val;
+    char    denom_str[30 + 1];
+    double  denom_val;
+    char    slash;
+    char    junk;
+    char   *fbck_name;
+    char   *fbck_val;
+
+    /*
+     * Look up the transport-dependent feedback value.
+     */
+    fbck_name = concatenate(name_prefix, name_tail, (char *) 0);
+    fbck_val = get_mail_conf_str(fbck_name, def_val, 1, 0);
+
+    /*
+     * We allow users to express feedback as 1/8, as a more user-friendly
+     * alternative to 0.125 (or worse, having users specify the number of
+     * events in a feedback hysteresis cycle).
+     * 
+     * We use some sscanf() fu to parse the value into numerator and optional
+     * "/" followed by denominator. We're doing this only a few times during
+     * the process life time, so we strive for convenience instead of speed.
+     */
+#define INCLUSIVE_BOUNDS(val, low, high) ((val) >= (low) && (val) <= (high))
+
+    fb->hysteresis = 1;                                /* legacy */
+    fb->base = -1;                             /* assume error */
+
+    switch (sscanf(fbck_val, "%lf %1[/] %30s%c",
+                  &enum_val, &slash, denom_str, &junk)) {
+    case 1:
+       fb->index = QMGR_FEEDBACK_IDX_NONE;
+       fb->base = enum_val;
+       break;
+    case 3:
+       if ((fb->index = name_code(qmgr_feedback_map, NAME_CODE_FLAG_NONE,
+                                  denom_str)) != QMGR_FEEDBACK_IDX_NONE) {
+           fb->base = enum_val;
+       } else if (INCLUSIVE_BOUNDS(enum_val, 0, INT_MAX)
+                  && sscanf(denom_str, "%lf%c", &denom_val, &junk) == 1
+                  && INCLUSIVE_BOUNDS(denom_val, 1.0 / INT_MAX, INT_MAX)) {
+           fb->base = enum_val / denom_val;
+       }
+       break;
+    }
+
+    /*
+     * Sanity check. If input is bad, we just warn and use a reasonable
+     * default.
+     */
+    if (!INCLUSIVE_BOUNDS(fb->base, 0, 1)) {
+       msg_warn("%s: ignoring malformed or unreasonable feedback: %s",
+                strcmp(fbck_val, def_val) ? fbck_name : def_name, fbck_val);
+       fb->index = QMGR_FEEDBACK_IDX_NONE;
+       fb->base = 1;
+    }
+
+    /*
+     * Performance debugging/analysis.
+     */
+    if (var_conc_feedback_debug)
+       msg_info("%s: %s feedback type %d value at %d: %g",
+                name_prefix, strcmp(fbck_val, def_val) ?
+                fbck_name : def_name, fb->index, var_init_dest_concurrency,
+                QMGR_FEEDBACK_VAL(*fb, var_init_dest_concurrency));
+
+    myfree(fbck_name);
+    myfree(fbck_val);
+}
index 08a5f1858472bf53eebf4c30fd7556cddf727059..4e73c3b384ec89090a1c1486b7fa31646c699a6c 100644 (file)
@@ -82,7 +82,6 @@
 
 #include <sys_defs.h>
 #include <time.h>
-#include <math.h>
 
 /* Utility library. */
 
@@ -90,7 +89,6 @@
 #include <mymalloc.h>
 #include <events.h>
 #include <htable.h>
-#include <name_code.h>
 
 /* Global library. */
 
 
 int     qmgr_queue_count;
 
- /*
-  * Lookup tables for main.cf feedback method names.
-  */
-#define QMGR_FDBACK_CODE_BAD           0
-#define QMGR_FDBACK_CODE_FIXED_1       1
-#define QMGR_FDBACK_CODE_INVERSE_WIN   2
-#define QMGR_FDBACK_CODE_INVERSE_1     QMGR_FDBACK_CODE_INVERSE_WIN
-#define QMGR_FDBACK_CODE_INV_SQRT_WIN  3
-#define QMGR_FDBACK_CODE_INV_SQRT      QMGR_FDBACK_CODE_INV_SQRT_WIN
-
-NAME_CODE qmgr_feedback_map[] = {
-    QMGR_FDBACK_NAME_FIXED_1, QMGR_FDBACK_CODE_FIXED_1,
-    QMGR_FDBACK_NAME_INVERSE_WIN, QMGR_FDBACK_CODE_INVERSE_WIN,
-    QMGR_FDBACK_NAME_INVERSE_1, QMGR_FDBACK_CODE_INVERSE_1,
-    QMGR_FDBACK_NAME_INV_SQRT_WIN, QMGR_FDBACK_CODE_INV_SQRT_WIN,
-    QMGR_FDBACK_NAME_INV_SQRT, QMGR_FDBACK_CODE_INV_SQRT,
-    0, QMGR_FDBACK_CODE_BAD,
-};
-static int qmgr_pos_feedback_idx;
-static int qmgr_neg_feedback_idx;
-
- /*
-  * Choosing the right feedback method at run-time.
-  */
-#define QMGR_FEEDBACK_VAL(idx, window) ( \
-       (idx) == QMGR_FDBACK_CODE_INVERSE_1 ? (1.0 / (window)) : \
-       (idx) == QMGR_FDBACK_CODE_FIXED_1 ? (1.0) : \
-       (1.0 / sqrt(window)) \
-    )
-
 #define QMGR_ERROR_OR_RETRY_QUEUE(queue) \
        (strcmp(queue->transport->name, MAIL_SERVICE_RETRY) == 0 \
            || strcmp(queue->transport->name, MAIL_SERVICE_ERROR) == 0)
 
 #define QMGR_LOG_FEEDBACK(feedback) \
-       if (var_qmgr_feedback_debug && !QMGR_ERROR_OR_RETRY_QUEUE(queue)) \
+       if (var_conc_feedback_debug && !QMGR_ERROR_OR_RETRY_QUEUE(queue)) \
            msg_info("%s: feedback %g", myname, feedback);
 
 #define QMGR_LOG_WINDOW(queue) \
-       if (var_qmgr_feedback_debug && !QMGR_ERROR_OR_RETRY_QUEUE(queue)) \
+       if (var_conc_feedback_debug && !QMGR_ERROR_OR_RETRY_QUEUE(queue)) \
            msg_info("%s: queue %s: limit %d window %d success %g failure %g fail_cohorts %g", \
                    myname, queue->name, queue->transport->dest_concurrency_limit, \
                    queue->window, queue->success, queue->failure, queue->fail_cohorts);
 
-/* qmgr_queue_feedback_init - initialize feedback selection */
-
-void    qmgr_queue_feedback_init(void)
-{
-
-    /*
-     * Positive and negative feedback method indices.
-     */
-    qmgr_pos_feedback_idx = name_code(qmgr_feedback_map, NAME_CODE_FLAG_NONE,
-                                     var_qmgr_pos_feedback);
-    if (qmgr_pos_feedback_idx == QMGR_FDBACK_CODE_BAD)
-       msg_fatal("%s: bad feedback method: %s",
-                 VAR_QMGR_POS_FDBACK, var_qmgr_pos_feedback);
-    if (var_qmgr_feedback_debug)
-       msg_info("positive feedback method %d, value at %d: %g",
-                qmgr_pos_feedback_idx, var_init_dest_concurrency,
-                QMGR_FEEDBACK_VAL(qmgr_pos_feedback_idx,
-                                  var_init_dest_concurrency));
-
-    qmgr_neg_feedback_idx = name_code(qmgr_feedback_map, NAME_CODE_FLAG_NONE,
-                                     var_qmgr_neg_feedback);
-    if (qmgr_neg_feedback_idx == QMGR_FDBACK_CODE_BAD)
-       msg_fatal("%s: bad feedback method: %s",
-                 VAR_QMGR_NEG_FDBACK, var_qmgr_neg_feedback);
-    if (var_qmgr_feedback_debug)
-       msg_info("negative feedback method %d, value at %d: %g",
-                qmgr_neg_feedback_idx, var_init_dest_concurrency,
-                QMGR_FEEDBACK_VAL(qmgr_neg_feedback_idx,
-                                  var_init_dest_concurrency));
-}
-
 /* qmgr_queue_unthrottle_wrapper - in case (char *) != (struct *) */
 
 static void qmgr_queue_unthrottle_wrapper(int unused_event, char *context)
@@ -202,7 +139,6 @@ void    qmgr_queue_unthrottle(QMGR_QUEUE *queue)
     const char *myname = "qmgr_queue_unthrottle";
     QMGR_TRANSPORT *transport = queue->transport;
     double  feedback;
-    double  multiplier;
 
     if (msg_verbose)
        msg_info("%s: queue %s", myname, queue->name);
@@ -253,13 +189,13 @@ void    qmgr_queue_unthrottle(QMGR_QUEUE *queue)
     if (transport->dest_concurrency_limit == 0
        || transport->dest_concurrency_limit > queue->window)
        if (queue->window < queue->busy_refcount + transport->init_dest_concurrency) {
-           feedback = QMGR_FEEDBACK_VAL(qmgr_pos_feedback_idx, queue->window);
+           feedback = QMGR_FEEDBACK_VAL(transport->pos_feedback, queue->window);
            QMGR_LOG_FEEDBACK(feedback);
            queue->success += feedback;
            /* Prepare for overshoot (feedback > hysteresis, rounding error). */
-           while (queue->success >= var_qmgr_pos_hysteresis) {
-               queue->window += var_qmgr_pos_hysteresis;
-               queue->success -= var_qmgr_pos_hysteresis;
+           while (queue->success + feedback / 2 >= transport->pos_feedback.hysteresis) {
+               queue->window += transport->pos_feedback.hysteresis;
+               queue->success -= transport->pos_feedback.hysteresis;
                queue->failure = 0;
            }
            /* Prepare for overshoot. */
@@ -275,6 +211,7 @@ void    qmgr_queue_unthrottle(QMGR_QUEUE *queue)
 void    qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
 {
     const char *myname = "qmgr_queue_throttle";
+    QMGR_TRANSPORT *transport = queue->transport;
     double  feedback;
 
     /*
@@ -301,7 +238,8 @@ void    qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
      */
     if (queue->window > 0) {
        queue->fail_cohorts += 1.0 / queue->window;
-       if (queue->fail_cohorts >= var_qmgr_sac_cohorts)
+       if (transport->fail_cohort_limit > 0
+           && queue->fail_cohorts >= transport->fail_cohort_limit)
            queue->window = 0;
     }
 
@@ -315,14 +253,14 @@ void    qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
      * negative feedback can cancel out positive feedback.
      */
     if (queue->window > 0) {
-       feedback = QMGR_FEEDBACK_VAL(qmgr_neg_feedback_idx, queue->window);
+       feedback = QMGR_FEEDBACK_VAL(transport->neg_feedback, queue->window);
        QMGR_LOG_FEEDBACK(feedback);
        queue->failure -= feedback;
        /* Prepare for overshoot (feedback > hysteresis, rounding error). */
-       while (queue->failure < 0) {
-           queue->window -= var_qmgr_neg_hysteresis;
+       while (queue->failure - feedback / 2 < 0) {
+           queue->window -= transport->neg_feedback.hysteresis;
            queue->success = 0;
-           queue->failure += var_qmgr_neg_hysteresis;
+           queue->failure += transport->neg_feedback.hysteresis;
        }
        /* Prepare for overshoot. */
        if (queue->window < 1)
index f539a1754b9f2e89daa46e818562861620bc22a9..89fde326ff788cd0a622d4d903291817dabd0ca7 100644 (file)
@@ -386,11 +386,12 @@ QMGR_TRANSPORT *qmgr_transport_create(const char *name)
     transport->recipient_limit =
        get_mail_conf_int2(name, _DEST_RCPT_LIMIT,
                           var_dest_rcpt_limit, 0, 0);
+    transport->init_dest_concurrency =
+       get_mail_conf_int2(name, _INIT_DEST_CON,
+                          var_init_dest_concurrency, 1, 0);
 
-    if (transport->dest_concurrency_limit == 0
-       || transport->dest_concurrency_limit >= var_init_dest_concurrency)
-       transport->init_dest_concurrency = var_init_dest_concurrency;
-    else
+    if (transport->dest_concurrency_limit != 0
+    && transport->dest_concurrency_limit < transport->init_dest_concurrency)
        transport->init_dest_concurrency = transport->dest_concurrency_limit;
 
     transport->slot_cost = get_mail_conf_int2(name, _DELIVERY_SLOT_COST,
@@ -423,6 +424,13 @@ QMGR_TRANSPORT *qmgr_transport_create(const char *name)
     transport->candidate_cache_time = (time_t) 0;
     transport->blocker_tag = 1;
     transport->dsn = 0;
+    qmgr_feedback_init(&transport->pos_feedback, name, _CONC_POS_FDBACK,
+                      VAR_CONC_POS_FDBACK, var_conc_pos_feedback);
+    qmgr_feedback_init(&transport->neg_feedback, name, _CONC_NEG_FDBACK,
+                      VAR_CONC_NEG_FDBACK, var_conc_neg_feedback);
+    transport->fail_cohort_limit =
+       get_mail_conf_int2(name, _CONC_COHORT_LIM,
+                          var_conc_cohort_limit, 0, 0);
     if (qmgr_transport_byname == 0)
        qmgr_transport_byname = htable_create(10);
     htable_enter(qmgr_transport_byname, name, (char *) transport);
index 90fe7987b8c8b8efaaf5aca46b92b10b03d009dd..3e06138710f0ba56e34583b824fd5a8366e4259e 100644 (file)
@@ -198,7 +198,7 @@ static void get_service_attr(SPAWN_ATTR *attr, char *service, char **argv)
      * Figure out the command time limit for this transport.
      */
     attr->time_limit =
-       get_mail_conf_time2(service, "_time_limit", var_command_maxtime, 's', 1, 0);
+       get_mail_conf_time2(service, _MAXTIME, var_command_maxtime, 's', 1, 0);
 
     /*
      * Iterate over the command-line attribute list.