From: Wietse Venema Date: Thu, 29 Nov 2007 05:00:00 +0000 (-0500) Subject: postfix-2.5-20071129 X-Git-Tag: v2.5.0-RC1~23 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6a00ae1cd71f1e400938983a8d77fdc057d59a9d;p=thirdparty%2Fpostfix.git postfix-2.5-20071129 --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 96fd99799..2893e6b51 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -99,10 +99,10 @@ -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 @@ -172,6 +172,7 @@ -TPLPGSQL -TPOST_MAIL_STATE -TQMGR_ENTRY +-TQMGR_FEEDBACK -TQMGR_JOB -TQMGR_MESSAGE -TQMGR_PEER diff --git a/postfix/HISTORY b/postfix/HISTORY index 7a886a0b1..c07c98190 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -5215,7 +5215,7 @@ Apologies for any names omitted. 20010525 - Portability: gcc 2.6.3 does not have __attribute__ (Clive + Portability: gcc 2.6.3 does not have _attribute__ (Clive Jones, dgw.co.uk). File: util/sys_defs.h. Bugfix: the SMTP and LMTP clients claimed that a queue file @@ -12203,7 +12203,7 @@ Apologies for any names omitted. 20060516 - Portability: __float80 alignment, by Albert Chin. File: + Portability: _float80 alignment, by Albert Chin. File: util/sys_defs.h. Further testing of Milter support uncovered typos; a missing @@ -13829,16 +13829,11 @@ Apologies for any names omitted. late October 2007. To establish a baseline for further improvement, Wietse implemented a few simple mechanisms. - Configuration parameters: qmgr_concurrency_feedback_debug, - qmgr_negative_concurrency_feedback_hysteresis, - qmgr_negative_concurrency_feedback_style, - qmgr_positive_concurrency_feedback_hysteresis, - qmgr_positive_concurrency_feedback_style, qmgr_sacrifice_cohorts. - See postconf(5) for detailed information. Right now, the - defaults are compatible with older Postfix versions. After - further review the number of parameters will be consolidated - and the defaults will select the better algorithms. Files: - qmgr/qmgr_queue.c, qmgr/qmgr_deliver.c. + Configuration parameters for debugging, positive/negative + hysteresis, and positive/negative feedback. Some have since + been removed or renamed, so no point naming them here. + Files: global/mail_params.h, qmgr/qmgr_queue.c, + qmgr/qmgr_deliver.c. 20071121 @@ -13850,3 +13845,22 @@ Apologies for any names omitted. in sender-dependent relayhost maps. Parameter name: empty_address_relayhost_maps_lookup_key (default; <>). Keean Schupke. File: trivial-rewrite/resolve.c. + +20071127-9 + + Revision 2 of queue manager scheduler interface, allowing + feedback parameter settings with constants and variables + such as 1/8 or 1/concurrency. Some experimental parameters + were removed and others were renamed. The new names are: + default_concurrency_negative_feedback, + default_concurrency_positive_feedback, concurrency_feedback_debug, + default_concurrency_failed_cohort_limit. + + Also available are transport-specific overrides: + _initial_destination_concurrency, + _concurrency_negative_feedback, + _concurrency_positive_feedback, + _concurrency_failed_cohort_limit. + + Files: global/mail_params.h, qmgr/qmgr.c, qmgr/qmgr_transport.c, + qmgr/qmge_queue.c, qmgr/qmgr_feedback.c. diff --git a/postfix/README_FILES/SCHEDULER_README b/postfix/README_FILES/SCHEDULER_README index f94581196..e5f2f6e86 100644 --- a/postfix/README_FILES/SCHEDULER_README +++ b/postfix/README_FILES/SCHEDULER_README @@ -2,19 +2,367 @@ PPoossttffiixx QQuueeuuee SScchheedduulleerr ------------------------------------------------------------------------------- -WWhhaatt tthhiiss ffiillee iiss aabboouutt +OOvveerrvviieeww -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". -WWhhyy tthhee oolldd PPoossttffiixx qquueeuuee mmaannaaggeerr wwaass rreeppllaacceedd +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". + +CCoonnccuurrrreennccyy sscchheedduulliinngg + +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. + +SSuummmmaarryy ooff tthhee PPoossttffiixx 22..55 ccoonnccuurrrreennccyy ffeeeeddbbaacckk aallggoorriitthhmm + +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 +bbeeggiinnnniinngg 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. + +SSuummmmaarryy ooff tthhee PPoossttffiixx 22..55 ""ddeeaadd ddeessttiinnaattiioonn"" ddeetteeccttiioonn aallggoorriitthhmm + +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. + +PPsseeuuddooccooddee ffoorr tthhee PPoossttffiixx 22..55 ccoonnccuurrrreennccyy sscchheedduulleerr + +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 + +RReessuullttss ffoorr tthhee PPoossttffiixx 22..55 ccoonnccuurrrreennccyy ffeeeeddbbaacckk sscchheedduulleerr + +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. + + cclliieenntt sseerrvveerr ffeeeeddbbaacckk ccoonnnneeccttiioonn ppeerrcceennttaaggee cclliieenntt ttiimmeedd--oouutt iinn + lliimmiitt lliimmiitt ssttyyllee ccaacchhiinngg ddeeffeerrrreedd ccoonnccuurrrreennccyy ccoonnnneecctt// + aavveerraaggee//ssttddddeevv ggrreeeettiinngg + + ------------------------------------------------------------------------- + 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. + + cclliieenntt sseerrvveerr ffeeeeddbbaacckk ccoonnnneeccttiioonn ppeerrcceennttaaggee cclliieenntt ttiimmeedd--oouutt iinn + lliimmiitt lliimmiitt ssttyyllee ccaacchhiinngg ddeeffeerrrreedd ccoonnccuurrrreennccyy ccoonnnneecctt// + aavveerraaggee//ssttddddeevv ggrreeeettiinngg + + ------------------------------------------------------------------------- + 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. + + cclliieenntt sseerrvveerr ffeeeeddbbaacckk ccoonnnneeccttiioonn ppeerrcceennttaaggee cclliieenntt tthheeoorreettiiccaall + lliimmiitt lliimmiitt ssttyyllee ccaacchhiinngg ddeeffeerrrreedd ccoonnccuurrrreennccyy ddeeffeerr rraattee + aavveerraaggee//ssttddddeevv + + ------------------------------------------------------------------------- + 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. + +LLiimmiittaattiioonnss ooff lleessss--tthhaann--11 ffeeeeddbbaacckk + +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. + +PPrreeeemmppttiivvee sscchheedduulliinngg + +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)". + +WWhhyy tthhee nnoonn--pprreeeemmppttiivvee PPoossttffiixx qquueeuuee mmaannaaggeerr wwaass rreeppllaacceedd + +The non-preemptive Postfix scheduler had several limitations due to unfortunate +choices in its design. 1. Round-robin selection by destination for mail that is delivered via the same message delivery transport. The round-robin strategy was chosen with @@ -33,7 +381,7 @@ its design. scheduler allows mail with fewer recipients to slip past bulk mail in an elegant manner. -HHooww tthhee qquueeuuee mmaannaaggeerr sscchheedduulleerr wwoorrkkss +HHooww tthhee nnoonn--pprreeeemmppttiivvee qquueeuuee mmaannaaggeerr sscchheedduulleerr wwoorrkkss The following text is from Patrik Rak and should be read together with the postconf(5) manual that describes each configuration parameter in detail. diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index d6d5999ed..9bb5960e1 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -17,12 +17,12 @@ Incompatibility with Postfix 2.3 and earlier If you upgrade from Postfix 2.3 or earlier, read RELEASE_NOTES-2.4 before proceeding. -Major changes with Postfix snapshot 20071121 +Major changes with Postfix snapshot 20071129 ============================================ Revised queue manager with separate mechanisms for per-destination concurrency control and for dead destination detection. The -concurrency control supports non-integer feedback to allow for more +concurrency control supports less-than-1 feedback to allow for more gradual concurrency adjustments, and uses hysteresis to avoid rapid oscillations. A destination is declared "dead" after a configurable number of pseudo-cohorts(*) reports connection or handshake failure. @@ -33,27 +33,21 @@ number of pseudo-cohorts(*) reports connection or handshake failure. The drawbacks of the old +/-1 feedback scheduler are a) overshoot due to exponential delivery concurrency growth with each pseudo-cohort(*) (5-10-20...); b) throttling down to zero concurrency after a single -pseudo-cohort(*) failure. This was especially an issue with +pseudo-cohort(*) failure. The latter was especially an issue with low-concurrency channels where a single failure could be sufficient to mark a destination as "dead", and suspend further deliveries. -The new code is a laboratory model with a multitude of configuration -parameters, so that developers can experiment with different feedback -functions and hysteresis values. This is a baseline against which -further improvements will be measured: a) is the additional improvement -worth the additional complexity; b) is the design sound, i.e. free -from arbitrary constants and other tweaks that optimize for a narrow -range of application. - -New main.cf parameters: qmgr_concurrency_feedback_debug, -qmgr_negative_feedback_hysteresis, qmgr_negative_feedback_method, -qmgr_positive_feedback_hysteresis, qmgr_positive_feedback_method, -qmgr_sacrifice_cohorts. See postconf(5) for extensive descriptions. +New configuration parameters: concurrency_feedback_debug, +default_concurrency_positive_feedback, +default_concurrency_negative_feedback, +default_concurrency_failed_cohort_limit, as well as transport-specific +versions of the same. See postconf(5) for extensive descriptions, +and SCHEDULER_README for background information on why things work +the way they work. The default parameter settings are backwards compatible with older -Postfix versions. However, after a testing period, the number of -parameters will be consolidated, and the default settings will be -changed to take advantage of the "better" algorithm. +Postfix versions. This may change after better defaults are field +tested. Major changes with Postfix snapshot 20071111 ============================================ diff --git a/postfix/WISHLIST b/postfix/WISHLIST index 7592b261f..30c45f92d 100644 --- a/postfix/WISHLIST +++ b/postfix/WISHLIST @@ -1,7 +1,5 @@ Wish list: - document dict_get() as returning const char * - Make event_drain() a proper event loop; update the zero mask, and don't ignore a non-empty timer queue. diff --git a/postfix/html/SCHEDULER_README.html b/postfix/html/SCHEDULER_README.html index 25f56a51a..4f6e483b5 100644 --- a/postfix/html/SCHEDULER_README.html +++ b/postfix/html/SCHEDULER_README.html @@ -17,19 +17,519 @@
-

What this file is about

+

Overview

-

This is the beginning of documentation for the clever queue +

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".

+ +

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.

+ +

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".

    + +
+ +

Concurrency scheduling

+ +

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.

+ +

Summary of the Postfix 2.5 concurrency feedback algorithm

+ +

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 beginning 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.

+ +

Summary of the Postfix 2.5 "dead destination" detection algorithm

+ +

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. +

+ +

Pseudocode for the Postfix 2.5 concurrency scheduler

+ +

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
+
+ +

Results for the Postfix 2.5 concurrency feedback scheduler

+ +

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.

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    client
    limit
    server
    limit
    feedback
    +style
    connection
    caching
    percentage
    +deferred
    client concurrency
    average/stddev
    timed-out in
    connect/greeting

    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) no10.4 19.6 0.59 208 -
    20 5 1/sqrt(N) yes10.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.

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    client
    limit
    server
    limit
    feedback
    +style
    connection
    caching
    percentage
    +deferred
    client concurrency
    average/stddev
    timed-out in
    connect/greeting

    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) no1.21 19.9 0.23 4 238
    20 5 1/sqrt(N) yes1.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.

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    client
    limit
    server
    limit
    feedback
    +style
    connection
    caching
    percentage
    +deferred
    client concurrency
    average/stddev
    theoretical
    defer rate

    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) no24.5 5.28 0.45 1/4
    20 5 1/sqrt(N) yes24.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.

    + +

    Limitations of less-than-1 feedback

    + +

    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. +

    + +

    Preemptive scheduling

    + +

    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)".

    +manager, which is always called "qmgr(8)". The old queue manager +will for some time will be available under the name of "oqmgr(8)". +

    -

    Why the old Postfix queue manager was replaced

    +

    Why the non-preemptive Postfix queue manager was replaced

    -

    The old Postfix scheduler had several limitations due to -unfortunate choices in its design.

    +

    The non-preemptive Postfix scheduler had several limitations +due to unfortunate choices in its design.

      @@ -56,7 +556,7 @@ unfortunate choices in its design.

    -

    How the queue manager scheduler works

    +

    How the non-preemptive queue manager scheduler works

    The following text is from Patrik Rak and should be read together with the postconf(5) manual that describes each configuration diff --git a/postfix/html/master.5.html b/postfix/html/master.5.html index 5bddcf7c3..a527cc44f 100644 --- a/postfix/html/master.5.html +++ b/postfix/html/master.5.html @@ -19,9 +19,9 @@ MASTER(5) MASTER(5) process. The master.cf 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 max_use - clients, or after inactivity for max_idle or more units of - time. + cesses are short-lived and terminate voluntarily after + serving max_use clients, or after inactivity for max_idle + or more units of time. All daemons specified here must speak a Postfix-internal protocol. In order to execute non-Postfix software use the diff --git a/postfix/html/oqmgr.8.html b/postfix/html/oqmgr.8.html index f9db969c5..1c764bf70 100644 --- a/postfix/html/oqmgr.8.html +++ b/postfix/html/oqmgr.8.html @@ -220,7 +220,7 @@ OQMGR(8) OQMGR(8) The default maximal number of parallel deliveries to the same destination. - transport_destination_concurrency_limit + transport_destination_concurrency_limit Idem, for delivery via the named message transport. RECIPIENT SCHEDULING CONTROLS @@ -228,25 +228,27 @@ OQMGR(8) OQMGR(8) The default maximal number of recipients per mes- sage delivery. - transport_destination_recipient_limit + transport_destination_recipient_limit Idem, for delivery via the named message transport. OTHER RESOURCE AND RATE CONTROLS - minimal_backoff_time (version dependent) + minimal_backoff_time (300s) The minimal time between attempts to deliver a - deferred message. + deferred message; prior to Postfix 2.4 the default + value was 1000s. maximal_backoff_time (4000s) - The maximal time between attempts to deliver a + The maximal time between attempts to deliver a deferred message. maximal_queue_lifetime (5d) - The maximal time a message is queued before it is + The maximal time a message is queued before it is sent back as undeliverable. - queue_run_delay (version dependent) - The time between deferred queue scans by the queue - manager. + queue_run_delay (300s) + The time between deferred queue scans by the queue + manager; prior to Postfix 2.4 the default value was + 1000s. transport_retry_time (60s) The time between attempts by the Postfix queue man- diff --git a/postfix/html/pipe.8.html b/postfix/html/pipe.8.html index 7f578a15b..8b7e11dec 100644 --- a/postfix/html/pipe.8.html +++ b/postfix/html/pipe.8.html @@ -39,7 +39,7 @@ PIPE(8) PIPE(8) To prevent Postfix from sending multiple recipients per delivery request, specify - transport_destination_recipient_limit = 1 + transport_destination_recipient_limit = 1 in the Postfix main.cf file, where transport is the name in the first column of the Postfix master.cf entry for the @@ -107,7 +107,7 @@ PIPE(8) PIPE(8) O Prepend an "X-Original-To: recipient" mes- sage header with the recipient address as given to Postfix. Note: for this to work, - the transport_destination_recipient_limit + the transport_destination_recipient_limit must be 1 (see SINGLE-RECIPIENT DELIVERY above for details). @@ -409,19 +409,19 @@ PIPE(8) PIPE(8) In the text below, transport is the first field in a mas- ter.cf entry. - transport_destination_concurrency_limit ($default_destina- + transport_destination_concurrency_limit ($default_destina- tion_concurrency_limit) Limit the number of parallel deliveries to the same destination, for delivery via the named transport. The limit is enforced by the Postfix queue manager. - transport_destination_recipient_limit ($default_destina- + transport_destination_recipient_limit ($default_destina- tion_recipient_limit) Limit the number of recipients per message deliv- ery, for delivery via the named transport. The limit is enforced by the Postfix queue manager. - transport_time_limit ($command_time_limit) + transport_time_limit ($command_time_limit) Limit the time for delivery to external command, for delivery via the named transport. The limit is enforced by the pipe delivery agent. diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index eb47366d3..475022c8b 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -83,6 +83,19 @@ the sender. This feature is enabled with the transport_concurrency_failed_cohort_limit">transport_concurrency_failed_cohort_limit +(default: $default_concurrency_failed_cohort_limit)

    + +

    A transport-specific override for the +default_concurrency_failed_cohort_limit parameter value, where +transport is the master.cf name of the message delivery +transport.

    + +

    This feature is available in Postfix 2.5 and later.

    + +
    access_map_reject_code @@ -1362,6 +1375,17 @@ global ipc_timeout parameter as well.

    + + +
    concurrency_feedback_debug +(default: no)
    + +

    Make the queue manager's feedback algorithm verbose for performance +analysis purposes.

    + +

    This feature is available in Postfix 2.5 and later.

    + +
    config_directory @@ -1549,6 +1573,143 @@ Example: + + +
    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.

    + +

    A pseudo-cohort is the number of deliveries equal to a destination's +delivery concurrency.

    + +

    Use transport_concurrency_failed_cohort_limit to specify +a transport-specific override, where transport is the master.cf +name of the message delivery transport.

    + +

    This feature is available in Postfix 2.5. The default setting +is compatible with earlier Postfix versions.

    + + +
    + +
    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.

    + +

    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 +$transport_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. +

    + +

    Specify one of the following forms:

    + +
    + +
    number
    + +
    number / number
    + +
    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.
    + +
    number / concurrency
    + +
    Variable feedback of "number / (delivery concurrency)". +The number must be in the range 0..1 inclusive. With +number equal to "1", a destination's delivery concurrency +is decremented by 1 after each failed pseudo-cohort.
    + +
    number / sqrt_concurrency
    + +
    Variable feedback of "number / sqrt(delivery concurrency)". +The number must be in the range 0..1 inclusive. This setting +may be removed in a future version.
    + +
    + +

    A pseudo-cohort is the number of deliveries equal to a destination's +delivery concurrency.

    + +

    Use transport_concurrency_negative_feedback to specify +a transport-specific override, where transport is the master.cf +name of the message delivery transport.

    + +

    This feature is available in Postfix 2.5. The default setting +is compatible with earlier Postfix versions.

    + + +
    + +
    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.

    + +

    Specify one of the following forms:

    + +
    + +
    number
    + +
    number / number
    + +
    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.
    + +
    number / concurrency
    + +
    Variable feedback of "number / (delivery concurrency)". +The number must be in the range 0..1 inclusive. With +number equal to "1", a destination's delivery concurrency +is incremented by 1 after each successful pseudo-cohort.
    + +
    number / sqrt_concurrency
    + +
    Variable feedback of "number / sqrt(delivery concurrency)". +The number must be in the range 0..1 inclusive. This setting +may be removed in a future version.
    + +
    + +

    A pseudo-cohort is the number of deliveries equal to a destination's +delivery concurrency.

    + +

    Use transport_concurrency_positive_feedback to specify +a transport-specific override, where transport is the master.cf +name of the message delivery transport.

    + +

    This feature is available in Postfix 2.5 and later.

    + +
    default_database_type @@ -1897,6 +2058,8 @@ make sure the recipients are refilled in timely manner even when $default_recipient_refill_limit is too high for too slow deliveries.

    +

    This feature is available in Postfix 2.4 and later.

    + @@ -1911,6 +2074,8 @@ $default_recipient_refi lower than this when this limit is too high for too slow deliveries.

    +

    This feature is available in Postfix 2.4 and later.

    + @@ -3023,6 +3188,10 @@ to the same destination. This limit applies to delivery via
    pipe(8) and virtual(8) delivery agents.

    +

    Use transport_initial_destination_concurrency to specify +a transport-specific override, where transport is the master.cf +name of the message delivery transport (Postfix 2.5 and later).

    +

    Warning: with concurrency of 1, one bad message can be enough to block all mail to a site. @@ -4198,28 +4367,28 @@ $mynetworks. This setting will not prev address rewriting when mail from a remote client is forwarded by a neighboring system. -

    permit_sasl_authenticated
    +
    permit_sasl_authenticated
    Append the domain name in $myorigin or $mydomain when the client is successfully authenticated via the RFC 4954 (AUTH) protocol.
    -
    permit_tls_clientcerts
    +
    permit_tls_clientcerts
    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.
    -
    permit_tls_all_clientcerts
    +
    permit_tls_all_clientcerts
    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.
    -
    check_address_map type:table
    +
    check_address_map type:table
    -
    type:table
    +
    type:table
    Append the domain name in $myorigin or $mydomain when the client IP address matches the specified lookup table. @@ -5928,18 +6097,6 @@ This feature is available in Postfix 2.0 and later.

    -
    - -
    qmgr_concurrency_feedback_debug -(default: no)
    - -

    Make the queue manager's feedback algorithm verbose for performance -analysis purposes.

    - -

    This feature is temporarily available in Postfix 2.5; its final -form is likely to change.

    - -
    qmgr_fudge_factor @@ -5991,136 +6148,6 @@ parameter is 1.

    - - -
    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 beginning of a cycle of (hysteresis / feedback) steps. -At that same time, the destination's positive feedback hysteresis -cycle is reset to its beginning.

    - -

    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.

    - - -
    - -
    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.

    - -
    - -
    inverse_concurrency
    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.
    - -
    inverse_sqrt_concurrency
    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.
    - -
    fixed_1
    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.
    - -
    - -

    A pseudo-cohort is a number of deliveries equal to the destination's -delivery concurrency.

    - -

    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.

    - - -
    - -
    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 end of a cycle of (hysteresis / feedback) steps. At that -same time, the destination's negative feedback hysteresis cycle is -reset to its beginning.

    - -

    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.

    - - -
    - -
    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.

    - -
    - -
    inverse_concurrency
    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.
    - -
    inverse_sqrt_concurrency
    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.
    - -
    fixed_1
    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. -
    - -
    - -

    A pseudo-cohort is a number of deliveries equal to the destination's -delivery concurrency.

    - -

    This feature is temporarily available in Postfix 2.5. The default -setting is compatible with earlier Postfix versions.

    - - -
    - -
    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.

    - -

    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.

    - -
    qmqpd_authorized_clients @@ -12176,6 +12203,106 @@ This feature is available in Postfix 2.1 and later.

    + + +
    transport_concurrency_negative_feedback +(default: $default_concurrency_negative_feedback)
    + +

    A transport-specific override for the +default_concurrency_negative_feedback parameter value, where +transport is the master.cf name of the message delivery +transport.

    + +

    This feature is available in Postfix 2.5 and later.

    + + +
    + +
    transport_concurrency_positive_feedback +(default: $default_concurrency_positive_feedback)
    + +

    A transport-specific override for the +default_concurrency_positive_feedback parameter value, where +transport is the master.cf name of the message delivery +transport.

    + +

    This feature is available in Postfix 2.5 and later.

    + + +
    + +
    transport_delivery_slot_cost +(default: $default_delivery_slot_cost)
    + +

    A transport-specific override for the default_delivery_slot_cost +parameter value, where transport is the master.cf name of +the message delivery transport.

    + + +
    + +
    transport_delivery_slot_discount +(default: $default_delivery_slot_discount)
    + +

    A transport-specific override for the default_delivery_slot_discount +parameter value, where transport is the master.cf name of +the message delivery transport.

    + + +
    + +
    transport_delivery_slot_loan +(default: $default_delivery_slot_loan)
    + +

    A transport-specific override for the default_delivery_slot_loan +parameter value, where transport is the master.cf name of +the message delivery transport.

    + + +
    + +
    transport_destination_concurrency_limit +(default: $default_destination_concurrency_limit)
    + +

    A transport-specific override for the +default_destination_concurrency_limit parameter value, where +transport is the master.cf name of the message delivery +transport.

    + + +
    + +
    transport_destination_recipient_limit +(default: $default_destination_concurrency_limit)
    + +

    A transport-specific override for the +default_destination_recipient_limit parameter value, where +transport is the master.cf name of the message delivery +transport.

    + + +
    + +
    transport_extra_recipient_limit +(default: $default_extra_recipient_limit)
    + +

    A transport-specific override for the default_extra_recipient_limit +parameter value, where transport is the master.cf name of +the message delivery transport.

    + + +
    + +
    transport_initial_destination_concurrency +(default: $initial_destination_concurrency)
    + +

    A transport-specific override for the initial_destination_concurrency +parameter value, where transport is the master.cf name of +the message delivery transport.

    + +

    This feature is available in Postfix 2.5 and later.

    + +
    transport_maps @@ -12205,6 +12332,50 @@ Examples: + + +
    transport_minimum_delivery_slots +(default: $default_minimum_delivery_slots)
    + +

    A transport-specific override for the default_minimum_delivery_slots +parameter value, where transport is the master.cf name of +the message delivery transport.

    + + +
    + +
    transport_recipient_limit +(default: $default_recipient_limit)
    + +

    A transport-specific override for the default_recipient_limit +parameter value, where transport is the master.cf name of +the message delivery transport.

    + + +
    + +
    transport_recipient_refill_delay +(default: $default_recipient_refill_delay)
    + +

    A transport-specific override for the default_recipient_refill_delay +parameter value, where transport is the master.cf name of +the message delivery transport.

    + +

    This feature is available in Postfix 2.4 and later.

    + + +
    + +
    transport_recipient_refill_limit +(default: $default_recipient_refill_limit)
    + +

    A transport-specific override for the default_recipient_refill_limit +parameter value, where transport is the master.cf name of +the message delivery transport.

    + +

    This feature is available in Postfix 2.4 and later.

    + +
    transport_retry_time @@ -12221,6 +12392,16 @@ The default time unit is s (seconds).

    + + +
    transport_time_limit +(default: $command_time_limit)
    + +

    A transport-specific override for the command_time_limit parameter +value, where transport is the master.cf name of the message +delivery transport.

    + +
    trigger_timeout diff --git a/postfix/html/qmgr.8.html b/postfix/html/qmgr.8.html index 245cffdd4..87df9f99b 100644 --- a/postfix/html/qmgr.8.html +++ b/postfix/html/qmgr.8.html @@ -220,15 +220,15 @@ QMGR(8) QMGR(8) The default per-transport upper limit on the number of in-memory recipients. - transport_recipient_limit ($default_recipient_limit) + transport_recipient_limit ($default_recipient_limit) Idem, for delivery via the named message transport. default_extra_recipient_limit (1000) The default value for the extra per-transport limit imposed on the number of in-memory recipients. - transport_extra_recipient_limit ($default_extra_recipi- - ent_limit) + transport_extra_recipient_limit ($default_extra_recipi- + ent_limit) Idem, for delivery via the named message transport. Available in Postfix version 2.4 and later: @@ -237,16 +237,16 @@ QMGR(8) QMGR(8) The default per-transport limit on the number of recipients refilled at once. - transport_recipient_refill_limit ($default_recipi- - ent_refill_limit) + transport_recipient_refill_limit ($default_recipi- + ent_refill_limit) Idem, for delivery via the named message transport. default_recipient_refill_delay (5s) The default per-transport maximum delay between recipients refills. - transport_recipient_refill_delay ($default_recipi- - ent_refill_delay) + transport_recipient_refill_delay ($default_recipi- + ent_refill_delay) Idem, for delivery via the named message transport. DELIVERY CONCURRENCY CONTROLS @@ -258,48 +258,55 @@ QMGR(8) QMGR(8) The default maximal number of parallel deliveries to the same destination. - transport_destination_concurrency_limit ($default_destina- + transport_destination_concurrency_limit ($default_destina- tion_concurrency_limit) Idem, for delivery via the named message transport. - qmgr_concurrency_feedback_debug (no) - Make the queue manager's feedback algorithm verbose - for performance analysis purposes. + Available in Postfix version 2.5 and later: - qmgr_negative_concurrency_feedback_hysteresis (1) - The per-destination integer amount of negative con- - currency feedback that must accumulate between neg- - ative adjustments of a destination's delivery con- - currency. + transport_initial_destination_concurrency ($initial_desti- + nation_concurrency) + Initial concurrency for delivery via the named mes- + sage transport. - qmgr_negative_concurrency_feedback_style (fixed_1) - The per-destination amount of negative delivery - concurrency feedback, after a delivery completes - with a connection or handshake failure. + default_concurrency_failed_cohort_limit (1) + How many pseudo-cohorts must suffer connection or + handshake failure before a specific destination is + considered unavailable (and further delivery is + suspended). - qmgr_positive_concurrency_feedback_hysteresis (1) - The per-destination integer amount of positive con- - currency feedback that must accumulate before posi- - tive adjustments of a destination's delivery con- - currency. + transport_concurrency_failed_cohort_limit ($default_con- + currency_failed_cohort_limit) + Idem, for delivery via the named message transport. - qmgr_positive_concurrency_feedback_style (fixed_1) - The per-destination amount of positive delivery + default_concurrency_negative_feedback (1) + The per-destination amount of negative delivery concurrency feedback, after a delivery completes + with a connection or handshake failure. + + transport_concurrency_negative_feedback ($default_concur- + rency_negative_feedback) + Idem, for delivery via the named message transport. + + default_concurrency_positive_feedback (1) + The per-destination amount of positive delivery + concurrency feedback, after a delivery completes without connection or handshake failure. - qmgr_sacrificial_cohorts (1) - How many pseudo-cohorts must suffer connection or - handshake failure before a specific destination is - considered unavailable (and further delivery is - suspended). + transport_concurrency_positive_feedback ($default_concur- + rency_positive_feedback) + Idem, for delivery via the named message transport. + + concurrency_feedback_debug (no) + Make the queue manager's feedback algorithm verbose + for performance analysis purposes. RECIPIENT SCHEDULING CONTROLS default_destination_recipient_limit (50) The default maximal number of recipients per mes- sage delivery. - transport_destination_recipient_limit ($default_destina- + transport_destination_recipient_limit ($default_destina- tion_recipient_limit) Idem, for delivery via the named message transport. @@ -309,7 +316,7 @@ QMGR(8) QMGR(8) allowed to preempt delivery of one message with another. - transport_delivery_slot_cost ($default_delivery_slot_cost) + transport_delivery_slot_cost ($default_delivery_slot_cost) Idem, for delivery via the named message transport. default_minimum_delivery_slots (3) @@ -317,7 +324,7 @@ QMGR(8) QMGR(8) invoke the Postfix queue manager's scheduling algo- rithm at all. - transport_minimum_delivery_slots ($default_minimum_deliv- + transport_minimum_delivery_slots ($default_minimum_deliv- ery_slots) Idem, for delivery via the named message transport. @@ -325,7 +332,7 @@ QMGR(8) QMGR(8) The default value for transport-specific _deliv- ery_slot_discount settings. - transport_delivery_slot_discount ($default_deliv- + transport_delivery_slot_discount ($default_deliv- ery_slot_discount) Idem, for delivery via the named message transport. @@ -333,7 +340,7 @@ QMGR(8) QMGR(8) The default value for transport-specific _deliv- ery_slot_loan settings. - transport_delivery_slot_loan ($default_delivery_slot_loan) + transport_delivery_slot_loan ($default_delivery_slot_loan) Idem, for delivery via the named message transport. OTHER RESOURCE AND RATE CONTROLS diff --git a/postfix/html/spawn.8.html b/postfix/html/spawn.8.html index afd13628b..75c825a90 100644 --- a/postfix/html/spawn.8.html +++ b/postfix/html/spawn.8.html @@ -77,7 +77,7 @@ SPAWN(8) SPAWN(8) entry in the master.cf file. RESOURCE AND RATE CONTROL - transport_time_limit ($command_time_limit) + transport_time_limit ($command_time_limit) The amount of time the command is allowed to run before it is terminated. diff --git a/postfix/man/man5/master.5 b/postfix/man/man5/master.5 index 632a5af0f..7be2b2e8c 100644 --- a/postfix/man/man5/master.5 +++ b/postfix/man/man5/master.5 @@ -17,9 +17,9 @@ run in the background under control of the \fBmaster\fR(8) process. The master.cf configuration file defines how a client program connects to a service, and what daemon program runs when a service is requested. Most daemon -processes are short-lived and terminate after serving -\fBmax_use\fR clients, or after inactivity for \fBmax_idle\fR -or more units of time. +processes are short-lived and terminate voluntarily after +serving \fBmax_use\fR clients, or after inactivity for +\fBmax_idle\fR or more units of time. All daemons specified here must speak a Postfix-internal protocol. In order to execute non-Postfix software use the diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index bc75192dd..6936e4e74 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -63,6 +63,15 @@ operation of the mail system. The recipient of undeliverable mail that cannot be returned to the sender. This feature is enabled with the notify_classes parameter. +
    \fB\fItransport\fR_concurrency_failed_cohort_limit +(default: $default_concurrency_failed_cohort_limit)\fR
    +.PP +A transport-specific override for the +default_concurrency_failed_cohort_limit parameter value, where +\fItransport\fR is the master.cf name of the message delivery +transport. +.PP +This feature is available in Postfix 2.5 and later. .SH access_map_reject_code (default: 554) The numerical Postfix SMTP server response code when a client is rejected by an \fBaccess\fR(5) map restriction. @@ -749,6 +758,11 @@ delivery by the \fBpipe\fR(8) delivery agent. .PP Note: if you set this time limit to a large value you must update the global ipc_timeout parameter as well. +.SH concurrency_feedback_debug (default: no) +Make the queue manager's feedback algorithm verbose for performance +analysis purposes. +.PP +This feature is available in Postfix 2.5 and later. .SH config_directory (default: see "postconf -d" output) The default location of the Postfix main.cf and master.cf configuration files. This can be overruled via the following @@ -852,6 +866,102 @@ debugger_command = .fi .ad .ft R +.SH default_concurrency_failed_cohort_limit (default: 1) +How many pseudo-cohorts must suffer connection or handshake +failure before a specific destination is considered unavailable +(and further delivery is suspended). Specify zero to disable this +feature. A destination's pseudo-cohort failure count is reset each +time a delivery completes without connection or handshake failure +for that specific destination. +.PP +A pseudo-cohort is the number of deliveries equal to a destination's +delivery concurrency. +.PP +Use \fItransport\fR_concurrency_failed_cohort_limit to specify +a transport-specific override, where \fItransport\fR is the master.cf +name of the message delivery transport. +.PP +This feature is available in Postfix 2.5. The default setting +is compatible with earlier Postfix versions. +.SH default_concurrency_negative_feedback (default: 1) +The per-destination amount of negative delivery concurrency +feedback, after a delivery completes with a connection or handshake +failure. Feedback values are in range 0..1 inclusive. With negative +feedback, concurrency is decremented at the beginning of a sequence +of length 1/feedback. This is unlike positive feedback, where +concurrency is incremented at the end of a sequence of length +1/feedback. +.PP +As of Postfix version 2.5, negative feedback cannot reduce +delivery concurrency to zero. Instead, a destination is marked +dead (further delivery suspended) after the failed pseudo-cohort +count reaches $default_concurrency_failed_cohort_limit (or +$\fItransport\fR_concurrency_failed_cohort_limit). To make the +scheduler completely immune to connection or handshake failures, +specify a zero feedback value and a zero failed pseudo-cohort limit. +.PP +Specify one of the following forms: +.IP "\fB\fInumber\fR \fR" +.IP "\fB\fInumber\fR / \fInumber\fR \fR" +Constant feedback. The value must be in the range 0..1 inclusive. +The default setting of "1" is compatible with Postfix versions +before 2.5, where a destination's delivery concurrency is throttled +down to zero (and further delivery suspended) after a single failed +pseudo-cohort. +.IP "\fB\fInumber\fR / concurrency \fR" +Variable feedback of "\fInumber\fR / (delivery concurrency)". +The \fInumber\fR must be in the range 0..1 inclusive. With +\fInumber\fR equal to "1", a destination's delivery concurrency +is decremented by 1 after each failed pseudo-cohort. +.IP "\fB\fInumber\fR / sqrt_concurrency \fR" +Variable feedback of "\fInumber\fR / sqrt(delivery concurrency)". +The \fInumber\fR must be in the range 0..1 inclusive. This setting +may be removed in a future version. +.PP +A pseudo-cohort is the number of deliveries equal to a destination's +delivery concurrency. +.PP +Use \fItransport\fR_concurrency_negative_feedback to specify +a transport-specific override, where \fItransport\fR is the master.cf +name of the message delivery transport. +.PP +This feature is available in Postfix 2.5. The default setting +is compatible with earlier Postfix versions. +.SH default_concurrency_positive_feedback (default: 1) +The per-destination amount of positive delivery concurrency +feedback, after a delivery completes without connection or handshake +failure. Feedback values are in the range 0..1 inclusive. The +concurrency increases until it reaches the per-destination maximal +concurrency limit. With positive feedback, concurrency is incremented +at the end of a sequence with length 1/feedback. This is unlike +negative feedback, where concurrency is decremented at the start +of a sequence of length 1/feedback. +.PP +Specify one of the following forms: +.IP "\fB\fInumber\fR \fR" +.IP "\fB\fInumber\fR / \fInumber\fR \fR" +Constant feedback. The value must be in the range 0..1 +inclusive. The default setting of "1" is compatible with Postfix +versions before 2.5, where a destination's delivery concurrency +doubles after each successful pseudo-cohort. +.IP "\fB\fInumber\fR / concurrency \fR" +Variable feedback of "\fInumber\fR / (delivery concurrency)". +The \fInumber\fR must be in the range 0..1 inclusive. With +\fInumber\fR equal to "1", a destination's delivery concurrency +is incremented by 1 after each successful pseudo-cohort. +.IP "\fB\fInumber\fR / sqrt_concurrency \fR" +Variable feedback of "\fInumber\fR / sqrt(delivery concurrency)". +The \fInumber\fR must be in the range 0..1 inclusive. This setting +may be removed in a future version. +.PP +A pseudo-cohort is the number of deliveries equal to a destination's +delivery concurrency. +.PP +Use \fItransport\fR_concurrency_positive_feedback to specify +a transport-specific override, where \fItransport\fR is the master.cf +name of the message delivery transport. +.PP +This feature is available in Postfix 2.5 and later. .SH default_database_type (default: see "postconf -d" output) The default database type for use in \fBnewaliases\fR(1), \fBpostalias\fR(1) and \fBpostmap\fR(1) commands. On many UNIX systems the default type is @@ -1041,12 +1151,16 @@ When not all message recipients fit into the memory at once, keep loading more of them at least once every this many seconds. This is used to make sure the recipients are refilled in timely manner even when $default_recipient_refill_limit is too high for too slow deliveries. +.PP +This feature is available in Postfix 2.4 and later. .SH default_recipient_refill_limit (default: 100) The default per-transport limit on the number of recipients refilled at once. When not all message recipients fit into the memory at once, keep loading more of them in batches of at least this many at a time. See also $default_recipient_refill_delay, which may result in recipient batches lower than this when this limit is too high for too slow deliveries. +.PP +This feature is available in Postfix 2.4 and later. .SH default_transport (default: smtp) The default mail delivery transport and next-hop destination for destinations that do not match $mydestination, $inet_interfaces, @@ -1644,6 +1758,10 @@ The initial per-destination concurrency level for parallel delivery to the same destination. This limit applies to delivery via \fBsmtp\fR(8), and via the \fBpipe\fR(8) and \fBvirtual\fR(8) delivery agents. .PP +Use \fItransport\fR_initial_destination_concurrency to specify +a transport-specific override, where \fItransport\fR is the master.cf +name of the message delivery transport (Postfix 2.5 and later). +.PP Warning: with concurrency of 1, one bad message can be enough to block all mail to a site. .SH internal_mail_filter_classes (default: empty) @@ -2230,21 +2348,21 @@ client IP address matches any network or network address listed in $mynetworks. This setting will not prevent remote mail header address rewriting when mail from a remote client is forwarded by a neighboring system. -.IP "\fB permit_sasl_authenticated \fR" +.IP "\fBpermit_sasl_authenticated \fR" Append the domain name in $myorigin or $mydomain when the client is successfully authenticated via the RFC 4954 (AUTH) protocol. -.IP "\fB permit_tls_clientcerts \fR" +.IP "\fBpermit_tls_clientcerts \fR" Append the domain name in $myorigin or $mydomain when the client TLS certificate is successfully verified, and the client certificate fingerprint is listed in $relay_clientcerts. -.IP "\fB permit_tls_all_clientcerts \fR" +.IP "\fBpermit_tls_all_clientcerts \fR" Append the domain name in $myorigin or $mydomain when the client TLS certificate is successfully verified, regardless of whether it is listed on the server, and regardless of the certifying authority. -.IP "\fB check_address_map \fItype:table\fR \fR" -.IP "\fB \fItype:table\fR \fR" +.IP "\fBcheck_address_map \fItype:table\fR \fR" +.IP "\fB\fItype:table\fR \fR" Append the domain name in $myorigin or $mydomain when the client IP address matches the specified lookup table. The lookup result is ignored, and no subnet lookup is done. This @@ -3251,12 +3369,6 @@ clogging up the Postfix active queue. Specify 0 to disable. This feature is enabled with the helpful_warnings parameter. .PP This feature is available in Postfix 2.0 and later. -.SH qmgr_concurrency_feedback_debug (default: no) -Make the queue manager's feedback algorithm verbose for performance -analysis purposes. -.PP -This feature is temporarily available in Postfix 2.5; its final -form is likely to change. .SH qmgr_fudge_factor (default: 100) Obsolete feature: the percentage of delivery resources that a busy mail system will use up for delivery of a large mailing list @@ -3276,97 +3388,6 @@ takes priority over any other in-memory recipient limits (i.e., the global qmgr_message_recipient_limit and the per transport _recipient_limit) if necessary. The minimum value allowed for this parameter is 1. -.SH qmgr_negative_concurrency_feedback_hysteresis (default: 1) -The per-destination integer amount of negative concurrency -feedback that must accumulate between negative adjustments of a -destination's delivery concurrency. The concurrency adjustment is -equal in size to the negative hysteresis value, and is applied at -the \fBbeginning\fR of a cycle of (hysteresis / feedback) steps. -At that same time, the destination's positive feedback hysteresis -cycle is reset to its beginning. -.PP -This feature is temporarily available in Postfix 2.5; its final -form is likely to change. The default setting is compatible with -earlier Postfix versions. -.SH qmgr_negative_concurrency_feedback_style (default: fixed_1) -The per-destination amount of negative delivery concurrency -feedback, after a delivery completes with a connection or handshake -failure. -.IP "\fB inverse_concurrency \fR" -Variable feedback of -1 / (delivery concurrency). With this setting, and with -"qmgr_negative_concurrency_feedback_hysteresis = 1", the destination's -delivery concurrency is decremented by 1 after each failed -pseudo-cohort, and the destination is marked dead (further delivery -suspended) after the failed pseudo-cohort count reaches -$qmgr_sacrificial_cohorts. -.IP "\fB inverse_sqrt_concurrency \fR" -Variable feedback -of 1 / (square root of delivery concurrency). This is an intermediate -form between the other two. It lacks sound justification, and is a -candidate for removal. -.IP "\fB fixed_1 \fR" -Constant feedback of 1. This setting -is compatible with Postfix versions before 2.5, where a destination's -delivery concurrency is throttled down to zero (and further delivery -suspended) after a single failed pseudo-cohort. -.PP -A pseudo-cohort is a number of deliveries equal to the destination's -delivery concurrency. -.PP -This feature is temporarily available in Postfix 2.5; its final -form is likely to change. The default setting is compatible with -earlier Postfix versions. -.SH qmgr_positive_concurrency_feedback_hysteresis (default: 1) -The per-destination integer amount of positive concurrency -feedback that must accumulate before positive adjustments of a -destination's delivery concurrency. The concurrency adjustment is -equal in size to the positive hysteresis value, and is applied at -the \fBend\fR of a cycle of (hysteresis / feedback) steps. At that -same time, the destination's negative feedback hysteresis cycle is -reset to its beginning. -.PP -This feature is temporarily available in Postfix 2.5; its final -form is likely to change. The default setting is compatible with -earlier Postfix versions. -.SH qmgr_positive_concurrency_feedback_style (default: fixed_1) -The per-destination amount of positive delivery concurrency -feedback, after a delivery completes without connection or handshake -failure. -.IP "\fB inverse_concurrency \fR" -Variable feedback of -1 / (delivery concurrency). With this setting, and with -"qmgr_positive_concurrency_feedback_hysteresis = 1", the destination's -delivery concurrency is incremented by 1 after each successful -pseudo-cohort, until it reaches the per-destination maximal concurrency -limit. -.IP "\fB inverse_sqrt_concurrency \fR" -Variable feedback -of 1 / (square root of delivery concurrency). This is an intermediate -form between the other two. It lacks sound justification, and is a -candidate for removal. -.IP "\fB fixed_1 \fR" -Constant feedback of 1. This setting -is compatible with Postfix versions before 2.5, where a destination's -delivery concurrency is doubled after each successful pseudo-cohort, -until it reaches the per-destination maximal concurrency limit. -.PP -A pseudo-cohort is a number of deliveries equal to the destination's -delivery concurrency. -.PP -This feature is temporarily available in Postfix 2.5. The default -setting is compatible with earlier Postfix versions. -.SH qmgr_sacrificial_cohorts (default: 1) -How many pseudo-cohorts must suffer connection or handshake -failure before a specific destination is considered unavailable -(and further delivery is suspended). A pseudo-cohort is a number -of deliveries equal to a destination's concurrency. The pseudo-cohort -failure count is reset each time a delivery completes without -connection or handshake failure for that specific destination. -.PP -This feature is temporarily available in Postfix 2.5; its final -form is likely to change. The default setting is compatible with -earlier Postfix versions. .SH qmqpd_authorized_clients (default: empty) What clients are allowed to connect to the QMQP server port. .PP @@ -7394,6 +7415,52 @@ of mail deliveries and produces a mail delivery report when verbose delivery is requested with "\fBsendmail -v\fR". .PP This feature is available in Postfix 2.1 and later. +.SH transport_concurrency_negative_feedback (default: $default_concurrency_negative_feedback) +A transport-specific override for the +default_concurrency_negative_feedback parameter value, where +\fItransport\fR is the master.cf name of the message delivery +transport. +.PP +This feature is available in Postfix 2.5 and later. +.SH transport_concurrency_positive_feedback (default: $default_concurrency_positive_feedback) +A transport-specific override for the +default_concurrency_positive_feedback parameter value, where +\fItransport\fR is the master.cf name of the message delivery +transport. +.PP +This feature is available in Postfix 2.5 and later. +.SH transport_delivery_slot_cost (default: $default_delivery_slot_cost) +A transport-specific override for the default_delivery_slot_cost +parameter value, where \fItransport\fR is the master.cf name of +the message delivery transport. +.SH transport_delivery_slot_discount (default: $default_delivery_slot_discount) +A transport-specific override for the default_delivery_slot_discount +parameter value, where \fItransport\fR is the master.cf name of +the message delivery transport. +.SH transport_delivery_slot_loan (default: $default_delivery_slot_loan) +A transport-specific override for the default_delivery_slot_loan +parameter value, where \fItransport\fR is the master.cf name of +the message delivery transport. +.SH transport_destination_concurrency_limit (default: $default_destination_concurrency_limit) +A transport-specific override for the +default_destination_concurrency_limit parameter value, where +\fItransport\fR is the master.cf name of the message delivery +transport. +.SH transport_destination_recipient_limit (default: $default_destination_concurrency_limit) +A transport-specific override for the +default_destination_recipient_limit parameter value, where +\fItransport\fR is the master.cf name of the message delivery +transport. +.SH transport_extra_recipient_limit (default: $default_extra_recipient_limit) +A transport-specific override for the default_extra_recipient_limit +parameter value, where \fItransport\fR is the master.cf name of +the message delivery transport. +.SH transport_initial_destination_concurrency (default: $initial_destination_concurrency) +A transport-specific override for the initial_destination_concurrency +parameter value, where \fItransport\fR is the master.cf name of +the message delivery transport. +.PP +This feature is available in Postfix 2.5 and later. .SH transport_maps (default: empty) Optional lookup tables with mappings from recipient address to (message delivery transport, next-hop destination). See \fBtransport\fR(5) @@ -7416,12 +7483,36 @@ transport_maps = hash:/etc/postfix/transport .fi .ad .ft R +.SH transport_minimum_delivery_slots (default: $default_minimum_delivery_slots) +A transport-specific override for the default_minimum_delivery_slots +parameter value, where \fItransport\fR is the master.cf name of +the message delivery transport. +.SH transport_recipient_limit (default: $default_recipient_limit) +A transport-specific override for the default_recipient_limit +parameter value, where \fItransport\fR is the master.cf name of +the message delivery transport. +.SH transport_recipient_refill_delay (default: $default_recipient_refill_delay) +A transport-specific override for the default_recipient_refill_delay +parameter value, where \fItransport\fR is the master.cf name of +the message delivery transport. +.PP +This feature is available in Postfix 2.4 and later. +.SH transport_recipient_refill_limit (default: $default_recipient_refill_limit) +A transport-specific override for the default_recipient_refill_limit +parameter value, where \fItransport\fR is the master.cf name of +the message delivery transport. +.PP +This feature is available in Postfix 2.4 and later. .SH transport_retry_time (default: 60s) The time between attempts by the Postfix queue manager to contact a malfunctioning message delivery transport. .PP Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks). The default time unit is s (seconds). +.SH transport_time_limit (default: $command_time_limit) +A transport-specific override for the command_time_limit parameter +value, where \fItransport\fR is the master.cf name of the message +delivery transport. .SH trigger_timeout (default: 10s) The time limit for sending a trigger to a Postfix daemon (for example, the \fBpickup\fR(8) or \fBqmgr\fR(8) daemon). This time limit prevents diff --git a/postfix/man/man8/oqmgr.8 b/postfix/man/man8/oqmgr.8 index 8130f93dd..45210143d 100644 --- a/postfix/man/man8/oqmgr.8 +++ b/postfix/man/man8/oqmgr.8 @@ -226,15 +226,17 @@ Idem, for delivery via the named message \fItransport\fR. .nf .ad .fi -.IP "\fBminimal_backoff_time (version dependent)\fR" -The minimal time between attempts to deliver a deferred message. +.IP "\fBminimal_backoff_time (300s)\fR" +The minimal time between attempts to deliver a deferred message; +prior to Postfix 2.4 the default value was 1000s. .IP "\fBmaximal_backoff_time (4000s)\fR" The maximal time between attempts to deliver a deferred message. .IP "\fBmaximal_queue_lifetime (5d)\fR" The maximal time a message is queued before it is sent back as undeliverable. -.IP "\fBqueue_run_delay (version dependent)\fR" -The time between deferred queue scans by the queue manager. +.IP "\fBqueue_run_delay (300s)\fR" +The time between deferred queue scans by the queue manager; +prior to Postfix 2.4 the default value was 1000s. .IP "\fBtransport_retry_time (60s)\fR" The time between attempts by the Postfix queue manager to contact a malfunctioning message delivery transport. diff --git a/postfix/man/man8/qmgr.8 b/postfix/man/man8/qmgr.8 index 5e6f55dd4..dad2641c3 100644 --- a/postfix/man/man8/qmgr.8 +++ b/postfix/man/man8/qmgr.8 @@ -235,29 +235,32 @@ The default maximal number of parallel deliveries to the same destination. .IP "\fItransport\fB_destination_concurrency_limit ($default_destination_concurrency_limit)\fR" Idem, for delivery via the named message \fItransport\fR. -.IP "\fBqmgr_concurrency_feedback_debug (no)\fR" -Make the queue manager's feedback algorithm verbose for performance -analysis purposes. -.IP "\fBqmgr_negative_concurrency_feedback_hysteresis (1)\fR" -The per-destination integer amount of negative concurrency -feedback that must accumulate between negative adjustments of a -destination's delivery concurrency. -.IP "\fBqmgr_negative_concurrency_feedback_style (fixed_1)\fR" +.PP +Available in Postfix version 2.5 and later: +.IP "\fItransport\fB_initial_destination_concurrency ($initial_destination_concurrency)\fR" +Initial concurrency for delivery via the named message +\fItransport\fR. +.IP "\fBdefault_concurrency_failed_cohort_limit (1)\fR" +How many pseudo-cohorts must suffer connection or handshake +failure before a specific destination is considered unavailable +(and further delivery is suspended). +.IP "\fItransport\fB_concurrency_failed_cohort_limit ($default_concurrency_failed_cohort_limit)\fR" +Idem, for delivery via the named message \fItransport\fR. +.IP "\fBdefault_concurrency_negative_feedback (1)\fR" The per-destination amount of negative delivery concurrency feedback, after a delivery completes with a connection or handshake failure. -.IP "\fBqmgr_positive_concurrency_feedback_hysteresis (1)\fR" -The per-destination integer amount of positive concurrency -feedback that must accumulate before positive adjustments of a -destination's delivery concurrency. -.IP "\fBqmgr_positive_concurrency_feedback_style (fixed_1)\fR" +.IP "\fItransport\fB_concurrency_negative_feedback ($default_concurrency_negative_feedback)\fR" +Idem, for delivery via the named message \fItransport\fR. +.IP "\fBdefault_concurrency_positive_feedback (1)\fR" The per-destination amount of positive delivery concurrency feedback, after a delivery completes without connection or handshake failure. -.IP "\fBqmgr_sacrificial_cohorts (1)\fR" -How many pseudo-cohorts must suffer connection or handshake -failure before a specific destination is considered unavailable -(and further delivery is suspended). +.IP "\fItransport\fB_concurrency_positive_feedback ($default_concurrency_positive_feedback)\fR" +Idem, for delivery via the named message \fItransport\fR. +.IP "\fBconcurrency_feedback_debug (no)\fR" +Make the queue manager's feedback algorithm verbose for performance +analysis purposes. .SH "RECIPIENT SCHEDULING CONTROLS" .na .nf diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink index ea5f9d3f2..d96c3a028 100755 --- a/postfix/mantools/postlink +++ b/postfix/mantools/postlink @@ -131,15 +131,15 @@ while (<>) { s;\bdefault_deliv[-]*\n* *[]*ery_slot_cost\b;$&;g; s;\bdefault_deliv[-]*\n* *[]*ery_slot_discount\b;$&;g; s;\bdefault_deliv[-]*\n* *[]*ery_slot_loan\b;$&;g; - s;\bdefault_destina[-]*\n* *[]*tion_concurrency_limit\b;$&;g; + s;\bdefault_destina[-]*\n* *[]*tion_concur[-]*\n* *[]*rency_limit\b;$&;g; s;\bdefault_destina[-]*\n* *[]*tion_recip[-]*\n* *[]*ient_limit\b;$&;g; - s;\bdefault_extra_recip[-]*\n* *[]*ient_limit\b;$&;g; + s;\bdefault_extra_recipi[-]*\n* *[]*ent_limit\b;$&;g; s;\bdefault_minimum_deliv[-]*\n* *[]*ery_slots\b;$&;g; s;\bdefault_privs\b;$&;g; s;\bdefault_process_limit\b;$&;g; s;\bdefault_rbl_reply\b;$&;g; - s;\bdefault_recipient_refill_limit\b;$&;g; - s;\bdefault_recipient_refill_delay\b;$&;g; + s;\bdefault_recipi[-]*\n* *[]*ent_refill_limit\b;$&;g; + s;\bdefault_recipi[-]*\n* *[]*ent_refill_delay\b;$&;g; s;\bdefault_recip[-]*\n* *[]*ient_limit\b;$&;g; s;\bdefault_transport\b;$&;g; s;\bdefault_verp_delimiters\b;$&;g; @@ -194,7 +194,7 @@ while (<>) { s;\bin_flow_delay\b;$&;g; s;\binet_inter[-]*\n*[ ]*faces\b;$&;g; s;\binet_protocols\b;$&;g; - s;\binitial_destination_concurrency\b;$&;g; + s;\binitial_desti[-]*\n*[ ]*nation_concurrency\b;$&;g; s;\binvalid_hostname_reject_code\b;$&;g; s;\bipc_idle\b;$&;g; s;\bipc_timeout\b;$&;g; @@ -335,12 +335,10 @@ while (<>) { s;\bqmgr_message_recip[-]*\n* *[]*ient_minimum\b;$&;g; s;\bqmqpd_authorized_clients\b;$&;g; - s;\bqmgr_negative_concurrency_feedback_hysteresis\b;$&;g; - s;\bqmgr_negative_concurrency_feedback_style\b;$&;g; - s;\bqmgr_positive_concurrency_feedback_hysteresis\b;$&;g; - s;\bqmgr_positive_concurrency_feedback_style\b;$&;g; - s;\bqmgr_sacrificial_cohorts\b;$&;g; - s;\bqmgr_concurrency_feedback_debug\b;$&;g; + s;\bdefault_concur[-]*\n* *[]*rency_negative_feedback\b;$&;g; + s;\bdefault_concur[-]*\n* *[]*rency_positive_feedback\b;$&;g; + s;\bdefault_con[-]*\n* *[]*currency_failed_cohort_limit\b;$&;g; + s;\bconcurrency_feedback_debug\b;$&;g; s;\bqmqpd_error_delay\b;$&;g; s;\bqmqpd_timeout\b;$&;g; @@ -620,6 +618,25 @@ while (<>) { s;\bfrozen_delivered_to\b;$&;g; + # Transport-dependent magical parameters. + + s;(transport)()?(_concurrency_failed_cohort_limit)\b;$2$1$3;g; + s;(transport)()?(_concurrency_negative_feedback)\b;$2$1$3;g; + s;(transport)()?(_concurrency_positive_feedback)\b;$2$1$3;g; + s;(transport)()?(_delivery_slot_cost)\b;$2$1$3;g; + s;(transport)()?(_delivery_slot_discount)\b;$2$1$3;g; + s;(transport)()?(_delivery_slot_loan)\b;$2$1$3;g; + s;(transport)()?(_destination_concurrency_limit)\b;$2$1$3;g; + s;(transport)()?(_destination_recipient_limit)\b;$2$1$3;g; + s;(transport)()?(_extra_recipient_limit)\b;$2$1$3;g; + s;(transport)()?(_initial_destination_concurrency)\b;$2$1$3;g; + s;(transport)()?(_minimum_delivery_slots)\b;$2$1$3;g; + s;(transport)()?(_recipient_limit)\b;$2$1$3;g; + s;(transport)()?(_recipient_refill_delay)\b;$2$1$3;g; + s;(transport)()?(_recipient_refill_limit)\b;$2$1$3;g; + s;(transport)()?(_time_limit)\b;$2$1$3;g; + s;(transport)()?(delivery_slot_discount)\b;$2$1$3;g; + # Undo hyperlinks of manual pages with the same name as parameters. s/([^<]*)<\/a>\(/$1(/g; diff --git a/postfix/proto/SCHEDULER_README.html b/postfix/proto/SCHEDULER_README.html index c81be4857..8e8d96abc 100644 --- a/postfix/proto/SCHEDULER_README.html +++ b/postfix/proto/SCHEDULER_README.html @@ -17,19 +17,519 @@
    -

    What this file is about

    +

    Overview

    -

    This is the beginning of documentation for the clever queue +

    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".

    + +

    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.

    + +

    There are two major classes of mechanisms that control the +operation of the queue manager:

    + +
    + +

    Concurrency scheduling

    + +

    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.

    + +

    Summary of the Postfix 2.5 concurrency feedback algorithm

    + +

    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 beginning 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.

    + +

    Summary of the Postfix 2.5 "dead destination" detection algorithm

    + +

    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. +

    + +

    Pseudocode for the Postfix 2.5 concurrency scheduler

    + +

    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
    +
    + +

    Results for the Postfix 2.5 concurrency feedback scheduler

    + +

    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.

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    client
    limit
    server
    limit
    feedback
    +style
    connection
    caching
    percentage
    +deferred
    client concurrency
    average/stddev
    timed-out in
    connect/greeting

    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) no10.4 19.6 0.59 208 -
    20 5 1/sqrt(N) yes10.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.

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    client
    limit
    server
    limit
    feedback
    +style
    connection
    caching
    percentage
    +deferred
    client concurrency
    average/stddev
    timed-out in
    connect/greeting

    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) no1.21 19.9 0.23 4 238
    20 5 1/sqrt(N) yes1.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.

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    client
    limit
    server
    limit
    feedback
    +style
    connection
    caching
    percentage
    +deferred
    client concurrency
    average/stddev
    theoretical
    defer rate

    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) no24.5 5.28 0.45 1/4
    20 5 1/sqrt(N) yes24.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.

    + +

    Limitations of less-than-1 feedback

    + +

    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. +

    + +

    Preemptive scheduling

    + +

    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)".

    +manager, which is always called "qmgr(8)". The old queue manager +will for some time will be available under the name of "oqmgr(8)". +

    -

    Why the old Postfix queue manager was replaced

    +

    Why the non-preemptive Postfix queue manager was replaced

    -

    The old Postfix scheduler had several limitations due to -unfortunate choices in its design.

    +

    The non-preemptive Postfix scheduler had several limitations +due to unfortunate choices in its design.

      @@ -56,7 +556,7 @@ unfortunate choices in its design.

    -

    How the queue manager scheduler works

    +

    How the non-preemptive queue manager scheduler works

    The following text is from Patrik Rak and should be read together with the postconf(5) manual that describes each configuration diff --git a/postfix/proto/master b/postfix/proto/master index 57674b13d..5ee1c8079 100644 --- a/postfix/proto/master +++ b/postfix/proto/master @@ -13,9 +13,9 @@ # process. The master.cf configuration file defines how a # client program connects to a service, and what daemon # program runs when a service is requested. Most daemon -# processes are short-lived and terminate after serving -# \fBmax_use\fR clients, or after inactivity for \fBmax_idle\fR -# or more units of time. +# processes are short-lived and terminate voluntarily after +# serving \fBmax_use\fR clients, or after inactivity for +# \fBmax_idle\fR or more units of time. # # All daemons specified here must speak a Postfix-internal # protocol. In order to execute non-Postfix software use the diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index 8d7423299..845471922 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -1107,6 +1107,8 @@ $default_recipient_refill_delay, which may result in recipient batches lower than this when this limit is too high for too slow deliveries.

    +

    This feature is available in Postfix 2.4 and later.

    + %PARAM default_recipient_refill_delay 5s

    @@ -1117,6 +1119,8 @@ make sure the recipients are refilled in timely manner even when $default_recipient_refill_limit is too high for too slow deliveries.

    +

    This feature is available in Postfix 2.4 and later.

    + %PARAM default_transport smtp

    @@ -1803,6 +1807,10 @@ to the same destination. This limit applies to delivery via smtp(8), and via the pipe(8) and virtual(8) delivery agents.

    +

    Use transport_initial_destination_concurrency to specify +a transport-specific override, where transport is the master.cf +name of the message delivery transport (Postfix 2.5 and later).

    +

    Warning: with concurrency of 1, one bad message can be enough to block all mail to a site. @@ -8080,28 +8088,28 @@ $mynetworks. This setting will not prevent remote mail header address rewriting when mail from a remote client is forwarded by a neighboring system.

  • -
    permit_sasl_authenticated
    +
    permit_sasl_authenticated
    Append the domain name in $myorigin or $mydomain when the client is successfully authenticated via the RFC 4954 (AUTH) protocol.
    -
    permit_tls_clientcerts
    +
    permit_tls_clientcerts
    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.
    -
    permit_tls_all_clientcerts
    +
    permit_tls_all_clientcerts
    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.
    -
    check_address_map type:table
    +
    check_address_map type:table
    -
    type:table
    +
    type:table
    Append the domain name in $myorigin or $mydomain when the client IP address matches the specified lookup table. @@ -10684,120 +10692,241 @@ that change the delivery time or destination are not available.

    This feature is available in Postfix 2.5 and later.

    -%PARAM qmgr_concurrency_feedback_debug no +%PARAM concurrency_feedback_debug no

    Make the queue manager's feedback algorithm verbose for performance analysis purposes.

    -

    This feature is temporarily available in Postfix 2.5; its final -form is likely to change.

    +

    This feature is available in Postfix 2.5 and later.

    -%PARAM qmgr_sacrificial_cohorts 1 +%PARAM default_concurrency_failed_cohort_limit 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.

    +(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.

    -

    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.

    +

    A pseudo-cohort is the number of deliveries equal to a destination's +delivery concurrency.

    -%PARAM qmgr_negative_concurrency_feedback_hysteresis 1 +

    Use transport_concurrency_failed_cohort_limit to specify +a transport-specific override, where transport is the master.cf +name of the message delivery transport.

    -

    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 beginning of a cycle of (hysteresis / feedback) steps. -At that same time, the destination's positive feedback hysteresis -cycle is reset to its beginning.

    +

    This feature is available in Postfix 2.5. The default setting +is compatible with earlier Postfix versions.

    -

    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.

    +%PARAM default_concurrency_negative_feedback 1 -%PARAM qmgr_positive_concurrency_feedback_hysteresis 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.

    -

    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 end of a cycle of (hysteresis / feedback) steps. At that -same time, the destination's negative feedback hysteresis cycle is -reset to its beginning.

    +

    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 +$transport_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. +

    -

    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.

    +

    Specify one of the following forms:

    -%PARAM qmgr_negative_concurrency_feedback_style fixed_1 +
    -

    The per-destination amount of negative delivery concurrency -feedback, after a delivery completes with a connection or handshake -failure.

    +
    number
    -
    +
    number / number
    + +
    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.
    -
    inverse_concurrency
    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.
    +
    number / concurrency
    -
    inverse_sqrt_concurrency
    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.
    +
    Variable feedback of "number / (delivery concurrency)". +The number must be in the range 0..1 inclusive. With +number equal to "1", a destination's delivery concurrency +is decremented by 1 after each failed pseudo-cohort.
    -
    fixed_1
    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.
    +
    number / sqrt_concurrency
    + +
    Variable feedback of "number / sqrt(delivery concurrency)". +The number must be in the range 0..1 inclusive. This setting +may be removed in a future version.
    -

    A pseudo-cohort is a number of deliveries equal to the destination's +

    A pseudo-cohort is the number of deliveries equal to a destination's delivery concurrency.

    -

    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.

    +

    Use transport_concurrency_negative_feedback to specify +a transport-specific override, where transport is the master.cf +name of the message delivery transport.

    + +

    This feature is available in Postfix 2.5. The default setting +is compatible with earlier Postfix versions.

    -%PARAM qmgr_positive_concurrency_feedback_style fixed_1 +%PARAM default_concurrency_positive_feedback 1

    The per-destination amount of positive delivery concurrency feedback, after a delivery completes without connection or handshake -failure.

    +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.

    + +

    Specify one of the following forms:

    -
    inverse_concurrency
    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.
    - -
    inverse_sqrt_concurrency
    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.
    - -
    fixed_1
    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. -
    +
    number
    + +
    number / number
    + +
    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.
    + +
    number / concurrency
    + +
    Variable feedback of "number / (delivery concurrency)". +The number must be in the range 0..1 inclusive. With +number equal to "1", a destination's delivery concurrency +is incremented by 1 after each successful pseudo-cohort.
    + +
    number / sqrt_concurrency
    + +
    Variable feedback of "number / sqrt(delivery concurrency)". +The number must be in the range 0..1 inclusive. This setting +may be removed in a future version.
    -

    A pseudo-cohort is a number of deliveries equal to the destination's +

    A pseudo-cohort is the number of deliveries equal to a destination's delivery concurrency.

    -

    This feature is temporarily available in Postfix 2.5. The default -setting is compatible with earlier Postfix versions.

    +

    Use transport_concurrency_positive_feedback to specify +a transport-specific override, where transport is the master.cf +name of the message delivery transport.

    + +

    This feature is available in Postfix 2.5 and later.

    + +%PARAM transport_concurrency_failed_cohort_limit $default_concurrency_failed_cohort_limit + +

    A transport-specific override for the +default_concurrency_failed_cohort_limit parameter value, where +transport is the master.cf name of the message delivery +transport.

    + +

    This feature is available in Postfix 2.5 and later.

    + +%PARAM transport_concurrency_positive_feedback $default_concurrency_positive_feedback + +

    A transport-specific override for the +default_concurrency_positive_feedback parameter value, where +transport is the master.cf name of the message delivery +transport.

    + +

    This feature is available in Postfix 2.5 and later.

    + +%PARAM transport_concurrency_negative_feedback $default_concurrency_negative_feedback + +

    A transport-specific override for the +default_concurrency_negative_feedback parameter value, where +transport is the master.cf name of the message delivery +transport.

    + +

    This feature is available in Postfix 2.5 and later.

    + +%PARAM transport_initial_destination_concurrency $initial_destination_concurrency + +

    A transport-specific override for the initial_destination_concurrency +parameter value, where transport is the master.cf name of +the message delivery transport.

    + +

    This feature is available in Postfix 2.5 and later.

    + +%PARAM transport_destination_concurrency_limit $default_destination_concurrency_limit + +

    A transport-specific override for the +default_destination_concurrency_limit parameter value, where +transport is the master.cf name of the message delivery +transport.

    + +%PARAM transport_destination_recipient_limit $default_destination_concurrency_limit + +

    A transport-specific override for the +default_destination_recipient_limit parameter value, where +transport is the master.cf name of the message delivery +transport.

    + +%PARAM transport_time_limit $command_time_limit + +

    A transport-specific override for the command_time_limit parameter +value, where transport is the master.cf name of the message +delivery transport.

    + +%PARAM transport_delivery_slot_cost $default_delivery_slot_cost + +

    A transport-specific override for the default_delivery_slot_cost +parameter value, where transport is the master.cf name of +the message delivery transport.

    + +%PARAM transport_delivery_slot_loan $default_delivery_slot_loan + +

    A transport-specific override for the default_delivery_slot_loan +parameter value, where transport is the master.cf name of +the message delivery transport.

    + +%PARAM transport_delivery_slot_discount $default_delivery_slot_discount + +

    A transport-specific override for the default_delivery_slot_discount +parameter value, where transport is the master.cf name of +the message delivery transport.

    + +%PARAM transport_minimum_delivery_slots $default_minimum_delivery_slots + +

    A transport-specific override for the default_minimum_delivery_slots +parameter value, where transport is the master.cf name of +the message delivery transport.

    + +%PARAM transport_recipient_limit $default_recipient_limit + +

    A transport-specific override for the default_recipient_limit +parameter value, where transport is the master.cf name of +the message delivery transport.

    + +%PARAM transport_extra_recipient_limit $default_extra_recipient_limit + +

    A transport-specific override for the default_extra_recipient_limit +parameter value, where transport is the master.cf name of +the message delivery transport.

    + +%PARAM transport_recipient_refill_limit $default_recipient_refill_limit + +

    A transport-specific override for the default_recipient_refill_limit +parameter value, where transport is the master.cf name of +the message delivery transport.

    + +

    This feature is available in Postfix 2.4 and later.

    + +%PARAM transport_recipient_refill_delay $default_recipient_refill_delay + +

    A transport-specific override for the default_recipient_refill_delay +parameter value, where transport is the master.cf name of +the message delivery transport.

    + +

    This feature is available in Postfix 2.4 and later.

    diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in index d077f3dc6..e354ed69e 100644 --- a/postfix/src/global/Makefile.in +++ b/postfix/src/global/Makefile.in @@ -766,21 +766,6 @@ delivered_hdr.o: quote_822_local.h delivered_hdr.o: quote_flags.h delivered_hdr.o: rec_type.h delivered_hdr.o: record.h -header_body_checks.o: ../../include/argv.h -header_body_checks.o: ../../include/dict.h -header_body_checks.o: ../../include/msg.h -header_body_checks.o: ../../include/mymalloc.h -header_body_checks.o: ../../include/sys_defs.h -header_body_checks.o: ../../include/vbuf.h -header_body_checks.o: ../../include/vstream.h -header_body_checks.o: ../../include/vstring.h -header_body_checks.o: header_body_checks.c -header_body_checks.o: header_body_checks.h -header_body_checks.o: header_opts.h -header_body_checks.o: is_header.h -header_body_checks.o: maps.h -header_body_checks.o: mime_state.h -header_body_checks.o: rec_type.h dict_ldap.o: ../../include/argv.h dict_ldap.o: ../../include/binhash.h dict_ldap.o: ../../include/dict.h @@ -965,6 +950,23 @@ fold_addr.o: ../../include/vbuf.h fold_addr.o: ../../include/vstring.h fold_addr.o: fold_addr.c fold_addr.o: fold_addr.h +header_body_checks.o: ../../include/argv.h +header_body_checks.o: ../../include/dict.h +header_body_checks.o: ../../include/msg.h +header_body_checks.o: ../../include/mymalloc.h +header_body_checks.o: ../../include/sys_defs.h +header_body_checks.o: ../../include/vbuf.h +header_body_checks.o: ../../include/vstream.h +header_body_checks.o: ../../include/vstring.h +header_body_checks.o: cleanup_user.h +header_body_checks.o: dsn_util.h +header_body_checks.o: header_body_checks.c +header_body_checks.o: header_body_checks.h +header_body_checks.o: header_opts.h +header_body_checks.o: is_header.h +header_body_checks.o: maps.h +header_body_checks.o: mime_state.h +header_body_checks.o: rec_type.h header_opts.o: ../../include/htable.h header_opts.o: ../../include/msg.h header_opts.o: ../../include/stringops.h @@ -1125,7 +1127,10 @@ mail_conf_raw.o: mail_conf.h mail_conf_raw.o: mail_conf_raw.c mail_conf_str.o: ../../include/msg.h mail_conf_str.o: ../../include/mymalloc.h +mail_conf_str.o: ../../include/stringops.h mail_conf_str.o: ../../include/sys_defs.h +mail_conf_str.o: ../../include/vbuf.h +mail_conf_str.o: ../../include/vstring.h mail_conf_str.o: mail_conf.h mail_conf_str.o: mail_conf_str.c mail_conf_time.o: ../../include/argv.h diff --git a/postfix/src/global/header_body_checks.c b/postfix/src/global/header_body_checks.c index 1ead675dd..de8f58bc9 100644 --- a/postfix/src/global/header_body_checks.c +++ b/postfix/src/global/header_body_checks.c @@ -230,13 +230,9 @@ static char *hbc_action(void *context, HBC_CALL_BACKS *cb, char *ret; /* - * XXX We don't delegate action logging to the action call-back - * functions, because actions without call-back must be logged here - * anyway. This means that some actions must report back whether the - * action should be logged. This is admittedly a little clumsy. - * - * XXX We don't use a hash table for action lookup. Mail rarely triggers an - * action, and mail that triggers multiple actions is even rarer. + * XXX We don't use a hash table for action lookup. Mail rarely triggers + * an action, and mail that triggers multiple actions is even rarer. + * Setting up the hash table costs more than we would gain from using it. */ while (*cmd_args && ISSPACE(*cmd_args)) cmd_args++; diff --git a/postfix/src/global/mail_conf.h b/postfix/src/global/mail_conf.h index aa0e5d4aa..a8bff1b57 100644 --- a/postfix/src/global/mail_conf.h +++ b/postfix/src/global/mail_conf.h @@ -51,6 +51,7 @@ extern int get_mail_conf_bool(const char *, int); extern int get_mail_conf_time(const char *, const char *, int, int); extern char *get_mail_conf_raw(const char *, const char *, int, int); +extern char *get_mail_conf_str2(const char *, const char *, const char *, int, int); extern int get_mail_conf_int2(const char *, const char *, int, int, int); extern long get_mail_conf_long2(const char *, const char *, long, long, long); extern int get_mail_conf_time2(const char *, const char *, int, int, int, int); diff --git a/postfix/src/global/mail_conf_str.c b/postfix/src/global/mail_conf_str.c index bb734520d..1f8af32e3 100644 --- a/postfix/src/global/mail_conf_str.c +++ b/postfix/src/global/mail_conf_str.c @@ -27,6 +27,13 @@ /* /* void get_mail_conf_str_fn_table(table) /* CONFIG_STR_TABLE *table; +/* AUXILIARY FUNCTIONS +/* char *get_mail_conf_str2(name, suffix, defval, min, max) +/* const char *name; +/* const char *suffix; +/* const char *defval; +/* int min; +/* int max; /* DESCRIPTION /* This module implements support for string-valued global /* configuration parameters. @@ -49,6 +56,9 @@ /* get_mail_conf_str_table() and get_mail_conf_str_fn_table() read /* lists of variables, as directed by their table arguments. A table /* must be terminated by a null entry. +/* +/* get_mail_conf_str2() concatenates the two names and is otherwise +/* identical to get_mail_conf_str(). /* DIAGNOSTICS /* Fatal errors: bad string length. /* SEE ALSO @@ -74,6 +84,7 @@ #include #include +#include /* Global library. */ @@ -82,7 +93,7 @@ /* check_mail_conf_str - validate string length */ static void check_mail_conf_str(const char *name, const char *strval, - int min, int max) + int min, int max) { ssize_t len = strlen(strval); @@ -97,7 +108,7 @@ static void check_mail_conf_str(const char *name, const char *strval, /* get_mail_conf_str - evaluate string-valued configuration variable */ char *get_mail_conf_str(const char *name, const char *defval, - int min, int max) + int min, int max) { const char *strval; @@ -109,12 +120,31 @@ char *get_mail_conf_str(const char *name, const char *defval, return (mystrdup(strval)); } +/* get_mail_conf_str2 - evaluate string-valued configuration variable */ + +char *get_mail_conf_str2(const char *name1, const char *name2, + const char *defval, + int min, int max) +{ + const char *strval; + char *name; + + name = concatenate(name1, name2, (char *) 0); + if ((strval = mail_conf_lookup_eval(name)) == 0) { + strval = mail_conf_eval(defval); + mail_conf_update(name, strval); + } + check_mail_conf_str(name, strval, min, max); + myfree(name); + return (mystrdup(strval)); +} + /* get_mail_conf_str_fn - evaluate string-valued configuration variable */ typedef const char *(*stupid_indent_str) (void); char *get_mail_conf_str_fn(const char *name, stupid_indent_str defval, - int min, int max) + int min, int max) { const char *strval; @@ -141,7 +171,7 @@ void get_mail_conf_str_table(CONFIG_STR_TABLE *table) if (table->target[0]) myfree(table->target[0]); table->target[0] = get_mail_conf_str(table->name, table->defval, - table->min, table->max); + table->min, table->max); table++; } } @@ -154,7 +184,7 @@ void get_mail_conf_str_fn_table(CONFIG_STR_FN_TABLE *table) if (table->target[0]) myfree(table->target[0]); table->target[0] = get_mail_conf_str_fn(table->name, table->defval, - table->min, table->max); + table->min, table->max); table++; } } diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 6579ade12..ff38bb613 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -456,6 +456,7 @@ extern bool var_biff; extern char *var_allow_commands; #define VAR_COMMAND_MAXTIME "command_time_limit" +#define _MAXTIME "_time_limit" #define DEF_COMMAND_MAXTIME "1000s" extern int var_command_maxtime; @@ -731,6 +732,7 @@ extern int var_qmgr_fudge; * Queue manager: default destination concurrency levels. */ #define VAR_INIT_DEST_CON "initial_destination_concurrency" +#define _INIT_DEST_CON "_initial_destination_concurrency" #define DEF_INIT_DEST_CON 5 extern int var_init_dest_concurrency; @@ -2837,35 +2839,27 @@ extern char *var_smtp_body_chks; /* * Scheduler concurrency feedback algorithms. */ -#define VAR_QMGR_POS_FDBACK "qmgr_positive_concurrency_feedback_style" -#define DEF_QMGR_POS_FDBACK QMGR_FDBACK_NAME_FIXED_1 -extern char *var_qmgr_pos_feedback; +#define VAR_CONC_POS_FDBACK "default_concurrency_positive_feedback" +#define _CONC_POS_FDBACK "_concurrency_positive_feedback" +#define DEF_CONC_POS_FDBACK "1" +extern char *var_conc_pos_feedback; -#define VAR_QMGR_NEG_FDBACK "qmgr_negative_concurrency_feedback_style" -#define DEF_QMGR_NEG_FDBACK QMGR_FDBACK_NAME_FIXED_1 -extern char *var_qmgr_neg_feedback; +#define VAR_CONC_NEG_FDBACK "default_concurrency_negative_feedback" +#define _CONC_NEG_FDBACK "_concurrency_negative_feedback" +#define DEF_CONC_NEG_FDBACK "1" +extern char *var_conc_neg_feedback; -#define QMGR_FDBACK_NAME_FIXED_1 "fixed_1" -#define QMGR_FDBACK_NAME_INVERSE_1 "inverse_1" /* deprecated */ -#define QMGR_FDBACK_NAME_INVERSE_WIN "inverse_concurrency" -#define QMGR_FDBACK_NAME_INV_SQRT "inverse_sqrt" /* deprecated */ -#define QMGR_FDBACK_NAME_INV_SQRT_WIN "inverse_sqrt_concurrency" +#define CONC_FDBACK_NAME_WIN "concurrency" +#define CONC_FDBACK_NAME_SQRT_WIN "sqrt_concurrency" -#define VAR_QMGR_POS_HYST "qmgr_positive_concurrency_feedback_hysteresis" -#define DEF_QMGR_POS_HYST 1 -extern int var_qmgr_pos_hysteresis; +#define VAR_CONC_COHORT_LIM "default_concurrency_failed_cohort_limit" +#define _CONC_COHORT_LIM "_concurrency_failed_cohort_limit" +#define DEF_CONC_COHORT_LIM 1 +extern int var_conc_cohort_limit; -#define VAR_QMGR_NEG_HYST "qmgr_negative_concurrency_feedback_hysteresis" -#define DEF_QMGR_NEG_HYST 1 -extern int var_qmgr_neg_hysteresis; - -#define VAR_QMGR_SAC_COHORTS "qmgr_sacrificial_cohorts" -#define DEF_QMGR_SAC_COHORTS 1 -extern int var_qmgr_sac_cohorts; - -#define VAR_QMGR_FDBACK_DEBUG "qmgr_concurrency_feedback_debug" -#define DEF_QMGR_FDBACK_DEBUG 0 -extern bool var_qmgr_feedback_debug; +#define VAR_CONC_FDBACK_DEBUG "concurrency_feedback_debug" +#define DEF_CONC_FDBACK_DEBUG 0 +extern bool var_conc_feedback_debug; /* LICENSE /* .ad diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 62f2fc5e4..18f01dc9a 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20071122" +#define MAIL_RELEASE_DATE "20071129" #define MAIL_VERSION_NUMBER "2.5" #ifdef SNAPSHOT diff --git a/postfix/src/oqmgr/qmgr.c b/postfix/src/oqmgr/qmgr.c index 6b7b037a3..a49131572 100644 --- a/postfix/src/oqmgr/qmgr.c +++ b/postfix/src/oqmgr/qmgr.c @@ -192,15 +192,17 @@ /* 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. diff --git a/postfix/src/pipe/pipe.c b/postfix/src/pipe/pipe.c index e5c37b13f..e8fd4702e 100644 --- a/postfix/src/pipe/pipe.c +++ b/postfix/src/pipe/pipe.c @@ -789,7 +789,7 @@ static void get_service_params(PIPE_PARAMS *config, char *service) * Figure out the command time limit for this transport. */ config->time_limit = - get_mail_conf_time2(service, "_time_limit", var_command_maxtime, 's', 1, 0); + get_mail_conf_time2(service, _MAXTIME, var_command_maxtime, 's', 1, 0); /* * Give the poor tester a clue of what is going on. diff --git a/postfix/src/qmgr/Makefile.in b/postfix/src/qmgr/Makefile.in index ea62bdecc..cd2ddc2f7 100644 --- a/postfix/src/qmgr/Makefile.in +++ b/postfix/src/qmgr/Makefile.in @@ -2,11 +2,13 @@ SHELL = /bin/sh SRCS = qmgr.c qmgr_active.c qmgr_transport.c qmgr_queue.c qmgr_entry.c \ qmgr_message.c qmgr_deliver.c qmgr_move.c \ qmgr_job.c qmgr_peer.c \ - qmgr_defer.c qmgr_enable.c qmgr_scan.c qmgr_bounce.c qmgr_error.c + qmgr_defer.c qmgr_enable.c qmgr_scan.c qmgr_bounce.c qmgr_error.c \ + qmgr_feedback.c OBJS = qmgr.o qmgr_active.o qmgr_transport.o qmgr_queue.o qmgr_entry.o \ qmgr_message.o qmgr_deliver.o qmgr_move.o \ qmgr_job.o qmgr_peer.o \ - qmgr_defer.o qmgr_enable.o qmgr_scan.o qmgr_bounce.o qmgr_error.o + qmgr_defer.o qmgr_enable.o qmgr_scan.o qmgr_bounce.o qmgr_error.o \ + qmgr_feedback.o HDRS = qmgr.h TESTSRC = DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) @@ -209,6 +211,21 @@ qmgr_error.o: ../../include/vstream.h qmgr_error.o: ../../include/vstring.h qmgr_error.o: qmgr.h qmgr_error.o: qmgr_error.c +qmgr_feedback.o: ../../include/dsn.h +qmgr_feedback.o: ../../include/mail_conf.h +qmgr_feedback.o: ../../include/mail_params.h +qmgr_feedback.o: ../../include/msg.h +qmgr_feedback.o: ../../include/mymalloc.h +qmgr_feedback.o: ../../include/name_code.h +qmgr_feedback.o: ../../include/recipient_list.h +qmgr_feedback.o: ../../include/scan_dir.h +qmgr_feedback.o: ../../include/stringops.h +qmgr_feedback.o: ../../include/sys_defs.h +qmgr_feedback.o: ../../include/vbuf.h +qmgr_feedback.o: ../../include/vstream.h +qmgr_feedback.o: ../../include/vstring.h +qmgr_feedback.o: qmgr.h +qmgr_feedback.o: qmgr_feedback.c qmgr_job.o: ../../include/dsn.h qmgr_job.o: ../../include/htable.h qmgr_job.o: ../../include/msg.h @@ -284,13 +301,15 @@ qmgr_peer.o: ../../include/vbuf.h qmgr_peer.o: ../../include/vstream.h qmgr_peer.o: qmgr.h qmgr_peer.o: qmgr_peer.c +qmgr_queue.o: ../../include/attr.h qmgr_queue.o: ../../include/dsn.h qmgr_queue.o: ../../include/events.h qmgr_queue.o: ../../include/htable.h +qmgr_queue.o: ../../include/iostuff.h qmgr_queue.o: ../../include/mail_params.h +qmgr_queue.o: ../../include/mail_proto.h qmgr_queue.o: ../../include/msg.h qmgr_queue.o: ../../include/mymalloc.h -qmgr_queue.o: ../../include/name_code.h qmgr_queue.o: ../../include/recipient_list.h qmgr_queue.o: ../../include/scan_dir.h qmgr_queue.o: ../../include/sys_defs.h diff --git a/postfix/src/qmgr/qmgr.c b/postfix/src/qmgr/qmgr.c index c932d1c2a..d2b31325f 100644 --- a/postfix/src/qmgr/qmgr.c +++ b/postfix/src/qmgr/qmgr.c @@ -205,29 +205,32 @@ /* destination. /* .IP "\fItransport\fB_destination_concurrency_limit ($default_destination_concurrency_limit)\fR" /* Idem, for delivery via the named message \fItransport\fR. -/* .IP "\fBqmgr_concurrency_feedback_debug (no)\fR" -/* Make the queue manager's feedback algorithm verbose for performance -/* analysis purposes. -/* .IP "\fBqmgr_negative_concurrency_feedback_hysteresis (1)\fR" -/* The per-destination integer amount of negative concurrency -/* feedback that must accumulate between negative adjustments of a -/* destination's delivery concurrency. -/* .IP "\fBqmgr_negative_concurrency_feedback_style (fixed_1)\fR" +/* .PP +/* Available in Postfix version 2.5 and later: +/* .IP "\fItransport\fB_initial_destination_concurrency ($initial_destination_concurrency)\fR" +/* Initial concurrency for delivery via the named message +/* \fItransport\fR. +/* .IP "\fBdefault_concurrency_failed_cohort_limit (1)\fR" +/* How many pseudo-cohorts must suffer connection or handshake +/* failure before a specific destination is considered unavailable +/* (and further delivery is suspended). +/* .IP "\fItransport\fB_concurrency_failed_cohort_limit ($default_concurrency_failed_cohort_limit)\fR" +/* Idem, for delivery via the named message \fItransport\fR. +/* .IP "\fBdefault_concurrency_negative_feedback (1)\fR" /* The per-destination amount of negative delivery concurrency /* feedback, after a delivery completes with a connection or handshake /* failure. -/* .IP "\fBqmgr_positive_concurrency_feedback_hysteresis (1)\fR" -/* The per-destination integer amount of positive concurrency -/* feedback that must accumulate before positive adjustments of a -/* destination's delivery concurrency. -/* .IP "\fBqmgr_positive_concurrency_feedback_style (fixed_1)\fR" +/* .IP "\fItransport\fB_concurrency_negative_feedback ($default_concurrency_negative_feedback)\fR" +/* Idem, for delivery via the named message \fItransport\fR. +/* .IP "\fBdefault_concurrency_positive_feedback (1)\fR" /* The per-destination amount of positive delivery concurrency /* feedback, after a delivery completes without connection or handshake /* failure. -/* .IP "\fBqmgr_sacrificial_cohorts (1)\fR" -/* How many pseudo-cohorts must suffer connection or handshake -/* failure before a specific destination is considered unavailable -/* (and further delivery is suspended). +/* .IP "\fItransport\fB_concurrency_positive_feedback ($default_concurrency_positive_feedback)\fR" +/* Idem, for delivery via the named message \fItransport\fR. +/* .IP "\fBconcurrency_feedback_debug (no)\fR" +/* Make the queue manager's feedback algorithm verbose for performance +/* analysis purposes. /* RECIPIENT SCHEDULING CONTROLS /* .ad /* .fi @@ -415,12 +418,10 @@ int var_local_rcpt_lim; int var_proc_limit; bool var_verp_bounce_off; int var_qmgr_clog_warn_time; -char *var_qmgr_pos_feedback; -char *var_qmgr_neg_feedback; -int var_qmgr_pos_hysteresis; -int var_qmgr_neg_hysteresis; -int var_qmgr_sac_cohorts; -int var_qmgr_feedback_debug; +char *var_conc_pos_feedback; +char *var_conc_neg_feedback; +int var_conc_cohort_limit; +int var_conc_feedback_debug; static QMGR_SCAN *qmgr_scans[2]; @@ -645,11 +646,6 @@ static void qmgr_post_init(char *name, char **unused_argv) qmgr_scans[QMGR_SCAN_IDX_DEFERRED] = qmgr_scan_create(MAIL_QUEUE_DEFERRED); qmgr_scan_request(qmgr_scans[QMGR_SCAN_IDX_INCOMING], QMGR_SCAN_START); qmgr_deferred_run_event(0, (char *) 0); - - /* - * Scheduler initialization. - */ - qmgr_queue_feedback_init(); } MAIL_VERSION_STAMP_DECLARE; @@ -660,8 +656,8 @@ int main(int argc, char **argv) { static CONFIG_STR_TABLE str_table[] = { VAR_DEFER_XPORTS, DEF_DEFER_XPORTS, &var_defer_xports, 0, 0, - VAR_QMGR_POS_FDBACK, DEF_QMGR_POS_FDBACK, &var_qmgr_pos_feedback, 1, 0, - VAR_QMGR_NEG_FDBACK, DEF_QMGR_NEG_FDBACK, &var_qmgr_neg_feedback, 1, 0, + VAR_CONC_POS_FDBACK, DEF_CONC_POS_FDBACK, &var_conc_pos_feedback, 1, 0, + VAR_CONC_NEG_FDBACK, DEF_CONC_NEG_FDBACK, &var_conc_neg_feedback, 1, 0, 0, }; static CONFIG_TIME_TABLE time_table[] = { @@ -692,15 +688,13 @@ int main(int argc, char **argv) VAR_LOCAL_RCPT_LIMIT, DEF_LOCAL_RCPT_LIMIT, &var_local_rcpt_lim, 0, 0, VAR_LOCAL_CON_LIMIT, DEF_LOCAL_CON_LIMIT, &var_local_con_lim, 0, 0, VAR_PROC_LIMIT, DEF_PROC_LIMIT, &var_proc_limit, 1, 0, - VAR_QMGR_POS_HYST, DEF_QMGR_POS_HYST, &var_qmgr_pos_hysteresis, 1, 0, - VAR_QMGR_NEG_HYST, DEF_QMGR_NEG_HYST, &var_qmgr_neg_hysteresis, 1, 0, - VAR_QMGR_SAC_COHORTS, DEF_QMGR_SAC_COHORTS, &var_qmgr_sac_cohorts, 1, 0, + VAR_CONC_COHORT_LIM, DEF_CONC_COHORT_LIM, &var_conc_cohort_limit, 0, 0, 0, }; static CONFIG_BOOL_TABLE bool_table[] = { VAR_ALLOW_MIN_USER, DEF_ALLOW_MIN_USER, &var_allow_min_user, VAR_VERP_BOUNCE_OFF, DEF_VERP_BOUNCE_OFF, &var_verp_bounce_off, - VAR_QMGR_FDBACK_DEBUG, DEF_QMGR_FDBACK_DEBUG, &var_qmgr_feedback_debug, + VAR_CONC_FDBACK_DEBUG, DEF_CONC_FDBACK_DEBUG, &var_conc_feedback_debug, 0, }; diff --git a/postfix/src/qmgr/qmgr.h b/postfix/src/qmgr/qmgr.h index 7490e842e..3f6a3999e 100644 --- a/postfix/src/qmgr/qmgr.h +++ b/postfix/src/qmgr/qmgr.h @@ -42,6 +42,7 @@ typedef struct QMGR_ENTRY_LIST QMGR_ENTRY_LIST; typedef struct QMGR_JOB_LIST QMGR_JOB_LIST; typedef struct QMGR_PEER_LIST QMGR_PEER_LIST; typedef struct QMGR_SCAN QMGR_SCAN; +typedef struct QMGR_FEEDBACK QMGR_FEEDBACK; /* * Hairy macros to update doubly-linked lists. @@ -113,6 +114,40 @@ struct QMGR_TRANSPORT_LIST { extern struct HTABLE *qmgr_transport_byname; /* transport by name */ extern QMGR_TRANSPORT_LIST qmgr_transport_list; /* transports, round robin */ + /* + * Delivery agents provide feedback, as hints that Postfix should expend + * more or fewer resources on a specific destination domain. The main.cf + * file specifies how feedback affects delivery concurrency: add/subtract a + * constant, a ratio of constants, or a constant divided by the delivery + * concurrency; and it specifies how much feedback must accumulate between + * concurrency updates. + */ +struct QMGR_FEEDBACK { + int hysteresis; /* to pass, need to be this tall */ + double base; /* pre-computed from main.cf */ + int index; /* none, window, sqrt(window) */ +}; + +#define QMGR_FEEDBACK_IDX_NONE 0 /* no window dependence */ +#define QMGR_FEEDBACK_IDX_WIN 1 /* 1/window dependence */ +#define QMGR_FEEDBACK_IDX_SQRT_WIN 2 /* 1/sqrt(window) dependence */ + +#ifdef QMGR_FEEDBACK_IDX_SQRT_WIN +#include +#endif + +extern void qmgr_feedback_init(QMGR_FEEDBACK *, const char *, const char *, const char *, const char *); + +#ifndef QMGR_FEEDBACK_IDX_SQRT_WIN +#define QMGR_FEEDBACK_VAL(fb, win) \ + ((fb).index == QMGR_FEEDBACK_IDX_NONE ? (fb).base : (fb).base / (win)) +#else +#define QMGR_FEEDBACK_VAL(fb, win) \ + ((fb).index == QMGR_FEEDBACK_IDX_NONE ? (fb).base : \ + (fb).index == QMGR_FEEDBACK_IDX_WIN ? (fb).base / (win) : \ + (fb).base / sqrt(win)) +#endif + /* * Each transport (local, smtp-out, bounce) can have one queue per next hop * name. Queues are looked up by next hop name (when we have resolved a @@ -164,6 +199,9 @@ struct QMGR_TRANSPORT { int blocker_tag; /* for marking blocker jobs */ QMGR_TRANSPORT_LIST peers; /* linkage */ DSN *dsn; /* why unavailable */ + QMGR_FEEDBACK pos_feedback; /* positive feedback control */ + QMGR_FEEDBACK neg_feedback; /* negative feedback control */ + int fail_cohort_limit; /* flow shutdown control */ }; #define QMGR_TRANSPORT_STAT_DEAD (1<<1) @@ -198,8 +236,8 @@ struct QMGR_QUEUE { int todo_refcount; /* queue entries (todo list) */ int busy_refcount; /* queue entries (busy list) */ int window; /* slow open algorithm */ - double success; /* cumulative positive feedback */ - double failure; /* cumulative negative feedback */ + double success; /* accumulated positive feedback */ + double failure; /* accumulated negative feedback */ double fail_cohorts; /* pseudo-cohort failure count */ QMGR_TRANSPORT *transport; /* transport linkage */ QMGR_ENTRY_LIST todo; /* todo queue entries */ @@ -220,7 +258,6 @@ extern void qmgr_queue_done(QMGR_QUEUE *); extern void qmgr_queue_throttle(QMGR_QUEUE *, DSN *); extern void qmgr_queue_unthrottle(QMGR_QUEUE *); extern QMGR_QUEUE *qmgr_queue_find(QMGR_TRANSPORT *, const char *); -extern void qmgr_queue_feedback_init(void); #define QMGR_QUEUE_THROTTLED(q) ((q)->window <= 0) diff --git a/postfix/src/qmgr/qmgr_feedback.c b/postfix/src/qmgr/qmgr_feedback.c new file mode 100644 index 000000000..6d62a6ed2 --- /dev/null +++ b/postfix/src/qmgr/qmgr_feedback.c @@ -0,0 +1,177 @@ +/*++ +/* NAME +/* qmgr_feedback 3 +/* SUMMARY +/* delivery agent feedback management +/* SYNOPSIS +/* #include "qmgr.h" +/* +/* void qmgr_feedback_init(fbck_ctl, name_prefix, name_tail, +/* def_name, def_value) +/* QMGR_FEEDBACK *fbck_ctl; +/* const char *name_prefix; +/* const char *name_tail; +/* const char *def_name; +/* const char *def_value; +/* +/* double QMGR_FEEDBACK_VAL(fbck_ctl, concurrency) +/* QMGR_FEEDBACK *fbck_ctl; +/* const int concurrency; +/* DESCRIPTION +/* Upon completion of a delivery request, a delivery agent +/* provides a hint that the scheduler should dedicate fewer or +/* more resources to a specific destination. +/* +/* qmgr_feedback_init() looks up transport-dependent positive +/* or negative concurrency feedback control information from +/* main.cf, and converts it to internal form. +/* +/* QMGR_FEEDBACK_VAL() computes a concurrency adjustment based +/* on a preprocessed feedback control information and the +/* current concurrency window. This is an "unsafe" macro that +/* evaluates some arguments multiple times. +/* +/* Arguments: +/* .IP fbck_ctl +/* Pointer to QMGR_FEEDBACK structure where the result will +/* be stored. +/* .IP name_prefix +/* Mail delivery transport name, used as the initial portion +/* of a transport-dependent concurrency feedback parameter +/* name. +/* .IP name_tail +/* The second, and fixed, portion of a transport-dependent +/* concurrency feedback parameter. +/* .IP def_name +/* The name of a default feedback parameter. +/* .IP def_val +/* The value of the default feedback parameter. +/* .IP concurrency +/* Delivery concurrency for concurrency-dependent feedback calculation. +/* DIAGNOSTICS +/* Warning: configuration error or unreasonable input. The program +/* uses name_tail feedback instead. +/* Panic: consistency check failure. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include +#include /* INT_MAX */ +#include /* sscanf() */ +#include + +/* Utility library. */ + +#include +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* 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); +} diff --git a/postfix/src/qmgr/qmgr_queue.c b/postfix/src/qmgr/qmgr_queue.c index 08a5f1858..4e73c3b38 100644 --- a/postfix/src/qmgr/qmgr_queue.c +++ b/postfix/src/qmgr/qmgr_queue.c @@ -82,7 +82,6 @@ #include #include -#include /* Utility library. */ @@ -90,7 +89,6 @@ #include #include #include -#include /* Global library. */ @@ -104,81 +102,20 @@ int qmgr_queue_count; - /* - * Lookup tables for main.cf feedback method names. - */ -#define QMGR_FDBACK_CODE_BAD 0 -#define QMGR_FDBACK_CODE_FIXED_1 1 -#define QMGR_FDBACK_CODE_INVERSE_WIN 2 -#define QMGR_FDBACK_CODE_INVERSE_1 QMGR_FDBACK_CODE_INVERSE_WIN -#define QMGR_FDBACK_CODE_INV_SQRT_WIN 3 -#define QMGR_FDBACK_CODE_INV_SQRT QMGR_FDBACK_CODE_INV_SQRT_WIN - -NAME_CODE qmgr_feedback_map[] = { - QMGR_FDBACK_NAME_FIXED_1, QMGR_FDBACK_CODE_FIXED_1, - QMGR_FDBACK_NAME_INVERSE_WIN, QMGR_FDBACK_CODE_INVERSE_WIN, - QMGR_FDBACK_NAME_INVERSE_1, QMGR_FDBACK_CODE_INVERSE_1, - QMGR_FDBACK_NAME_INV_SQRT_WIN, QMGR_FDBACK_CODE_INV_SQRT_WIN, - QMGR_FDBACK_NAME_INV_SQRT, QMGR_FDBACK_CODE_INV_SQRT, - 0, QMGR_FDBACK_CODE_BAD, -}; -static int qmgr_pos_feedback_idx; -static int qmgr_neg_feedback_idx; - - /* - * Choosing the right feedback method at run-time. - */ -#define QMGR_FEEDBACK_VAL(idx, window) ( \ - (idx) == QMGR_FDBACK_CODE_INVERSE_1 ? (1.0 / (window)) : \ - (idx) == QMGR_FDBACK_CODE_FIXED_1 ? (1.0) : \ - (1.0 / sqrt(window)) \ - ) - #define QMGR_ERROR_OR_RETRY_QUEUE(queue) \ (strcmp(queue->transport->name, MAIL_SERVICE_RETRY) == 0 \ || strcmp(queue->transport->name, MAIL_SERVICE_ERROR) == 0) #define QMGR_LOG_FEEDBACK(feedback) \ - if (var_qmgr_feedback_debug && !QMGR_ERROR_OR_RETRY_QUEUE(queue)) \ + if (var_conc_feedback_debug && !QMGR_ERROR_OR_RETRY_QUEUE(queue)) \ msg_info("%s: feedback %g", myname, feedback); #define QMGR_LOG_WINDOW(queue) \ - if (var_qmgr_feedback_debug && !QMGR_ERROR_OR_RETRY_QUEUE(queue)) \ + if (var_conc_feedback_debug && !QMGR_ERROR_OR_RETRY_QUEUE(queue)) \ msg_info("%s: queue %s: limit %d window %d success %g failure %g fail_cohorts %g", \ myname, queue->name, queue->transport->dest_concurrency_limit, \ queue->window, queue->success, queue->failure, queue->fail_cohorts); -/* qmgr_queue_feedback_init - initialize feedback selection */ - -void qmgr_queue_feedback_init(void) -{ - - /* - * Positive and negative feedback method indices. - */ - qmgr_pos_feedback_idx = name_code(qmgr_feedback_map, NAME_CODE_FLAG_NONE, - var_qmgr_pos_feedback); - if (qmgr_pos_feedback_idx == QMGR_FDBACK_CODE_BAD) - msg_fatal("%s: bad feedback method: %s", - VAR_QMGR_POS_FDBACK, var_qmgr_pos_feedback); - if (var_qmgr_feedback_debug) - msg_info("positive feedback method %d, value at %d: %g", - qmgr_pos_feedback_idx, var_init_dest_concurrency, - QMGR_FEEDBACK_VAL(qmgr_pos_feedback_idx, - var_init_dest_concurrency)); - - qmgr_neg_feedback_idx = name_code(qmgr_feedback_map, NAME_CODE_FLAG_NONE, - var_qmgr_neg_feedback); - if (qmgr_neg_feedback_idx == QMGR_FDBACK_CODE_BAD) - msg_fatal("%s: bad feedback method: %s", - VAR_QMGR_NEG_FDBACK, var_qmgr_neg_feedback); - if (var_qmgr_feedback_debug) - msg_info("negative feedback method %d, value at %d: %g", - qmgr_neg_feedback_idx, var_init_dest_concurrency, - QMGR_FEEDBACK_VAL(qmgr_neg_feedback_idx, - var_init_dest_concurrency)); -} - /* qmgr_queue_unthrottle_wrapper - in case (char *) != (struct *) */ static void qmgr_queue_unthrottle_wrapper(int unused_event, char *context) @@ -202,7 +139,6 @@ void qmgr_queue_unthrottle(QMGR_QUEUE *queue) const char *myname = "qmgr_queue_unthrottle"; QMGR_TRANSPORT *transport = queue->transport; double feedback; - double multiplier; if (msg_verbose) msg_info("%s: queue %s", myname, queue->name); @@ -253,13 +189,13 @@ void qmgr_queue_unthrottle(QMGR_QUEUE *queue) if (transport->dest_concurrency_limit == 0 || transport->dest_concurrency_limit > queue->window) if (queue->window < queue->busy_refcount + transport->init_dest_concurrency) { - feedback = QMGR_FEEDBACK_VAL(qmgr_pos_feedback_idx, queue->window); + feedback = QMGR_FEEDBACK_VAL(transport->pos_feedback, queue->window); QMGR_LOG_FEEDBACK(feedback); queue->success += feedback; /* Prepare for overshoot (feedback > hysteresis, rounding error). */ - while (queue->success >= var_qmgr_pos_hysteresis) { - queue->window += var_qmgr_pos_hysteresis; - queue->success -= var_qmgr_pos_hysteresis; + while (queue->success + feedback / 2 >= transport->pos_feedback.hysteresis) { + queue->window += transport->pos_feedback.hysteresis; + queue->success -= transport->pos_feedback.hysteresis; queue->failure = 0; } /* Prepare for overshoot. */ @@ -275,6 +211,7 @@ void qmgr_queue_unthrottle(QMGR_QUEUE *queue) void qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn) { const char *myname = "qmgr_queue_throttle"; + QMGR_TRANSPORT *transport = queue->transport; double feedback; /* @@ -301,7 +238,8 @@ void qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn) */ if (queue->window > 0) { queue->fail_cohorts += 1.0 / queue->window; - if (queue->fail_cohorts >= var_qmgr_sac_cohorts) + if (transport->fail_cohort_limit > 0 + && queue->fail_cohorts >= transport->fail_cohort_limit) queue->window = 0; } @@ -315,14 +253,14 @@ void qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn) * negative feedback can cancel out positive feedback. */ if (queue->window > 0) { - feedback = QMGR_FEEDBACK_VAL(qmgr_neg_feedback_idx, queue->window); + feedback = QMGR_FEEDBACK_VAL(transport->neg_feedback, queue->window); QMGR_LOG_FEEDBACK(feedback); queue->failure -= feedback; /* Prepare for overshoot (feedback > hysteresis, rounding error). */ - while (queue->failure < 0) { - queue->window -= var_qmgr_neg_hysteresis; + while (queue->failure - feedback / 2 < 0) { + queue->window -= transport->neg_feedback.hysteresis; queue->success = 0; - queue->failure += var_qmgr_neg_hysteresis; + queue->failure += transport->neg_feedback.hysteresis; } /* Prepare for overshoot. */ if (queue->window < 1) diff --git a/postfix/src/qmgr/qmgr_transport.c b/postfix/src/qmgr/qmgr_transport.c index f539a1754..89fde326f 100644 --- a/postfix/src/qmgr/qmgr_transport.c +++ b/postfix/src/qmgr/qmgr_transport.c @@ -386,11 +386,12 @@ QMGR_TRANSPORT *qmgr_transport_create(const char *name) transport->recipient_limit = get_mail_conf_int2(name, _DEST_RCPT_LIMIT, var_dest_rcpt_limit, 0, 0); + transport->init_dest_concurrency = + get_mail_conf_int2(name, _INIT_DEST_CON, + var_init_dest_concurrency, 1, 0); - if (transport->dest_concurrency_limit == 0 - || transport->dest_concurrency_limit >= var_init_dest_concurrency) - transport->init_dest_concurrency = var_init_dest_concurrency; - else + if (transport->dest_concurrency_limit != 0 + && transport->dest_concurrency_limit < transport->init_dest_concurrency) transport->init_dest_concurrency = transport->dest_concurrency_limit; transport->slot_cost = get_mail_conf_int2(name, _DELIVERY_SLOT_COST, @@ -423,6 +424,13 @@ QMGR_TRANSPORT *qmgr_transport_create(const char *name) transport->candidate_cache_time = (time_t) 0; transport->blocker_tag = 1; transport->dsn = 0; + qmgr_feedback_init(&transport->pos_feedback, name, _CONC_POS_FDBACK, + VAR_CONC_POS_FDBACK, var_conc_pos_feedback); + qmgr_feedback_init(&transport->neg_feedback, name, _CONC_NEG_FDBACK, + VAR_CONC_NEG_FDBACK, var_conc_neg_feedback); + transport->fail_cohort_limit = + get_mail_conf_int2(name, _CONC_COHORT_LIM, + var_conc_cohort_limit, 0, 0); if (qmgr_transport_byname == 0) qmgr_transport_byname = htable_create(10); htable_enter(qmgr_transport_byname, name, (char *) transport); diff --git a/postfix/src/spawn/spawn.c b/postfix/src/spawn/spawn.c index 90fe7987b..3e0613871 100644 --- a/postfix/src/spawn/spawn.c +++ b/postfix/src/spawn/spawn.c @@ -198,7 +198,7 @@ static void get_service_attr(SPAWN_ATTR *attr, char *service, char **argv) * Figure out the command time limit for this transport. */ attr->time_limit = - get_mail_conf_time2(service, "_time_limit", var_command_maxtime, 's', 1, 0); + get_mail_conf_time2(service, _MAXTIME, var_command_maxtime, 's', 1, 0); /* * Iterate over the command-line attribute list.