-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
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
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
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
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.
-------------------------------------------------------------------------------
-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
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\be n\bno\bon\bn-\b-p\bpr\bre\bee\bem\bmp\bpt\bti\biv\bve\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
The following text is from Patrik Rak and should be read together with the
postconf(5) manual that describes each configuration parameter in detail.
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.
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
============================================
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.
<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>
</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
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
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>
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-
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
<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).
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.
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>
</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>
</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>
$<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>
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>
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.
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.
</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>
</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>
</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>
</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>
</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>
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:
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>
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>.
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>
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>.
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>.
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>
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.
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
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.
.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
.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
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,
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)
$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
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
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
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)
.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
.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.
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
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;
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;
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;
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;
<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>
</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
# 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
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>
$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>
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.
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.
<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>
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
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
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
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++;
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);
/*
/* 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.
/* 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
#include <msg.h>
#include <mymalloc.h>
+#include <stringops.h>
/* Global library. */
/* 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);
/* 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;
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;
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++;
}
}
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++;
}
}
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;
* 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;
/*
* 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
* 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
/* 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.
* 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.
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)
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
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
/* 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
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];
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;
{
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[] = {
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,
};
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.
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
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)
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 */
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)
--- /dev/null
+/*++
+/* 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);
+}
#include <sys_defs.h>
#include <time.h>
-#include <math.h>
/* Utility library. */
#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)
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);
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. */
void qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
{
const char *myname = "qmgr_queue_throttle";
+ QMGR_TRANSPORT *transport = queue->transport;
double feedback;
/*
*/
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;
}
* 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)
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,
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);
* 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.