mirror of
https://github.com/vdukhovni/postfix
synced 2025-08-29 13:18:12 +00:00
postfix-2.5-20071202
This commit is contained in:
parent
7d89860429
commit
93d0a26a92
@ -13863,5 +13863,18 @@ Apologies for any names omitted.
|
||||
<transport>_destination_concurrency_positive_feedback,
|
||||
<transport>_destination_concurrency_failed_cohort_limit.
|
||||
|
||||
Files: global/mail_params.h, qmgr/qmgr.c, qmgr/qmgr_transport.c,
|
||||
qmgr/qmge_queue.c, qmgr/qmgr_feedback.c, postconf/auto.awk.
|
||||
Files: global/mail_params.h, *qmgr/qmgr.c, *qmgr/qmgr_transport.c,
|
||||
*qmgr/qmgr_queue.c, *qmgr/qmgr_feedback.c, postconf/auto.awk.
|
||||
|
||||
20071202
|
||||
|
||||
Feature: output rate control. For example, specify
|
||||
"smtp_delivery_rate_delay = 5m" to insert a five-minute
|
||||
delay between deliveries. This was an opportunity to define
|
||||
the mutually exclusive states that a queue can have, and
|
||||
to detect invalid transitions. This will make adding new
|
||||
features code easier. Files: *qmgr/qmgr_transport.c,
|
||||
*qmgr/qmgr_queue.c, *qmgr/qmgr_entry.c.
|
||||
|
||||
Bugfix: don't update the back-to-back delivery time stamp
|
||||
while deferring mail. File: *qmgr/qmgr_entry.c.
|
||||
|
@ -351,14 +351,14 @@ next section.
|
||||
|
||||
LLiimmiittaattiioonnss ooff lleessss--tthhaann--11 ppeerr ddeelliivveerryy ffeeeeddbbaacckk
|
||||
|
||||
The delivery concurrency scheduler with less-than-1 concurrency feedback per
|
||||
delivery solves a problem with servers that have active concurrency limiters.
|
||||
This works only because feedback is handled in a peculiar manner: positive
|
||||
feedback will increment the concurrency by 1 at the eenndd of a sequence of events
|
||||
of length 1/feedback, while negative feedback will decrement concurrency by 1
|
||||
at the bbeeggiinnnniinngg 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 per delivery would defer 50% of the
|
||||
The scheduler with less-than-1 concurrency feedback per delivery solves a
|
||||
problem with servers that have active concurrency limiters. This works only
|
||||
because feedback is handled in a peculiar manner: positive feedback will
|
||||
increment the concurrency by 1 at the eenndd of a sequence of events of length 1/
|
||||
feedback, while negative feedback will decrement concurrency by 1 at the
|
||||
bbeeggiinnnniinngg 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 per delivery would defer 50% of the
|
||||
mail, and would be no better in this respect than the old +/-1 feedback per
|
||||
delivery.
|
||||
|
||||
@ -374,10 +374,10 @@ once it reaches a concurrency level of about K, even though the good servers
|
||||
behind the load balancer are perfectly capable of handling more traffic.
|
||||
|
||||
This noise problem gets worse as the amount of positive feedback per delivery
|
||||
gets smaller. A compromise is to avoid concurrency-dependent positive feedback,
|
||||
and to use fixed less-than-1 feedback values instead. For example, to tolerate
|
||||
1 of 4 bad servers in the above load balancer scenario, use positive feedback
|
||||
of 1/4 per "good" delivery (no connect or handshake error), and use an equal or
|
||||
gets smaller. A compromise is to use fixed less-than-1 positive feedback values
|
||||
instead of concurrency-dependent positive feedback. For example, to tolerate 1
|
||||
of 4 bad servers in the above load balancer scenario, use positive feedback of
|
||||
1/4 per "good" delivery (no connect or handshake error), and use an equal or
|
||||
smaller amount of negative feedback per "bad" delivery. The downside of using
|
||||
concurrency-independent feedback is that some of the old +/-1 feedback problems
|
||||
will return at large concurrencies. Sites that deliver at non-trivial per-
|
||||
|
@ -17,6 +17,14 @@ 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 20071202
|
||||
============================================
|
||||
|
||||
Output rate control in the queue manager. For example, specify
|
||||
"smtp_delivery_rate_delay = 5m", to pause five minutes between
|
||||
message deliveries. More information in the postconf(5) manual
|
||||
under "default_delivery_rate_delay".
|
||||
|
||||
Major changes with Postfix snapshot 20071130
|
||||
============================================
|
||||
|
||||
|
@ -554,7 +554,7 @@ the next section. </p>
|
||||
|
||||
<h3> <a name="concurrency_limitations"> Limitations of less-than-1 per delivery feedback </a> </h3>
|
||||
|
||||
<p> The delivery concurrency scheduler with less-than-1 concurrency
|
||||
<p> The scheduler with less-than-1 concurrency
|
||||
feedback per delivery solves a problem with servers that have active
|
||||
concurrency limiters. This works only because feedback is handled
|
||||
in a peculiar manner: positive feedback will increment the concurrency
|
||||
@ -580,9 +580,9 @@ level of about K, even though the good servers behind the load
|
||||
balancer are perfectly capable of handling more traffic. </p>
|
||||
|
||||
<p> This noise problem gets worse as the amount of positive feedback
|
||||
per delivery gets smaller. A compromise is to avoid concurrency-dependent
|
||||
positive feedback, and to use fixed less-than-1 feedback values
|
||||
instead. For example, to tolerate 1 of 4 bad servers in the above
|
||||
per delivery gets smaller. A compromise is to use fixed less-than-1
|
||||
positive feedback values instead of concurrency-dependent positive
|
||||
feedback. For example, to tolerate 1 of 4 bad servers in the above
|
||||
load balancer scenario, use positive feedback of 1/4 per "good"
|
||||
delivery (no connect or handshake error), and use an equal or smaller
|
||||
amount of negative feedback per "bad" delivery. The downside of
|
||||
|
@ -242,18 +242,18 @@ OQMGR(8) OQMGR(8)
|
||||
Idem, for delivery via the named message <i>transport</i>.
|
||||
|
||||
<b><a href="postconf.5.html#default_destination_concurrency_negative_feedback">default_destination_concurrency_negative_feedback</a> (1)</b>
|
||||
The per-destination amount of negative delivery
|
||||
concurrency feedback, after a delivery completes
|
||||
with a connection or handshake failure.
|
||||
The per-destination amount of delivery concurrency
|
||||
negative feedback, after a delivery completes with
|
||||
a connection or handshake failure.
|
||||
|
||||
<b><a href="postconf.5.html#transport_destination_concurrency_positive_feedback"><i>transport</i>_destination_concurrency_negative_feedback</a></b>
|
||||
<b>($<a href="postconf.5.html#default_destination_concurrency_negative_feedback">default_destination_concurrency_negative_feedback</a>)</b>
|
||||
Idem, for delivery via the named message <i>transport</i>.
|
||||
|
||||
<b><a href="postconf.5.html#default_destination_concurrency_positive_feedback">default_destination_concurrency_positive_feedback</a> (1)</b>
|
||||
The per-destination amount of positive delivery
|
||||
concurrency feedback, after a delivery completes
|
||||
without connection or handshake failure.
|
||||
The per-destination amount of delivery concurrency
|
||||
positive feedback, after a delivery completes with-
|
||||
out connection or handshake failure.
|
||||
|
||||
<b><a href="postconf.5.html#transport_destination_concurrency_positive_feedback"><i>transport</i>_destination_concurrency_positive_feedback</a></b>
|
||||
<b>($<a href="postconf.5.html#default_destination_concurrency_positive_feedback">default_destination_concurrency_positive_feedback</a>)</b>
|
||||
@ -301,6 +301,18 @@ OQMGR(8) OQMGR(8)
|
||||
The maximal time a bounce message is queued before
|
||||
it is considered undeliverable.
|
||||
|
||||
Available in Postfix version 2.5 and later:
|
||||
|
||||
<b><a href="postconf.5.html#default_delivery_rate_delay">default_delivery_rate_delay</a> (0s)</b>
|
||||
The default amount of delay that is inserted
|
||||
between individual deliveries to the same destina-
|
||||
tion; with per-destination recipient limit > 1, a
|
||||
destination is a domain, otherwise it is a recipi-
|
||||
ent.
|
||||
|
||||
<b><a href="postconf.5.html#transport_delivery_rate_delay"><i>transport</i>_delivery_rate_delay</a> $<a href="postconf.5.html#default_delivery_rate_delay">default_delivery_rate_delay</a></b>
|
||||
Idem, for delivery via the named message <i>transport</i>.
|
||||
|
||||
<b>MISCELLANEOUS CONTROLS</b>
|
||||
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
|
||||
The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
|
||||
|
@ -1584,6 +1584,35 @@ Examples:
|
||||
</pre>
|
||||
|
||||
|
||||
</DD>
|
||||
|
||||
<DT><b><a name="default_delivery_rate_delay">default_delivery_rate_delay</a>
|
||||
(default: 0s)</b></DT><DD>
|
||||
|
||||
<p> The default amount of delay that is inserted between individual
|
||||
deliveries to the same destination; with per-destination recipient
|
||||
limit > 1, a destination is a domain, otherwise it is a recipient.
|
||||
</p>
|
||||
|
||||
<p> To enable the delay, specify a non-zero time value (an integral
|
||||
value plus an optional one-letter suffix that specifies the time
|
||||
unit). </p>
|
||||
|
||||
<p> Time units: s (seconds), m (minutes), h (hours), d (days), w
|
||||
(weeks). The default time unit is s (seconds). </p>
|
||||
|
||||
<p> NOTE: the delay is enforced by the queue manager. The delay
|
||||
timer state does not survive "postfix reload" or "postfix stop".
|
||||
</p>
|
||||
|
||||
<p> Use <a href="postconf.5.html#transport_delivery_rate_delay"><i>transport</i>_delivery_rate_delay</a> to specify a
|
||||
transport-specific override, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
<p> This feature is available in Postfix 2.5 and later. </p>
|
||||
|
||||
|
||||
</DD>
|
||||
|
||||
<DT><b><a name="default_delivery_slot_cost">default_delivery_slot_cost</a>
|
||||
@ -1623,6 +1652,11 @@ message response times while making sure the mailing-list deliveries
|
||||
are not extended by more than 20-25 percent even in the worst case.
|
||||
</p>
|
||||
|
||||
<p> Use <a href="postconf.5.html#transport_delivery_slot_cost"><i>transport</i>_delivery_slot_cost</a> to specify a
|
||||
transport-specific override, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Examples:
|
||||
</p>
|
||||
@ -1653,6 +1687,11 @@ Note that the full amount will still have to be accumulated before
|
||||
another preemption can take place later.
|
||||
</p>
|
||||
|
||||
<p> Use <a href="postconf.5.html#transport_delivery_slot_discount"><a href="postconf.5.html#transport_delivery_slot_discount"><i>transport</i>_delivery_slot_discount</a></a> to specify a
|
||||
transport-specific override, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
|
||||
</DD>
|
||||
|
||||
@ -1674,6 +1713,11 @@ Note that the full amount will still have to be accumulated before
|
||||
another preemption can take place later.
|
||||
</p>
|
||||
|
||||
<p> Use <a href="postconf.5.html#transport_delivery_slot_loan"><i>transport</i>_delivery_slot_loan</a> to specify a
|
||||
transport-specific override, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
|
||||
</DD>
|
||||
|
||||
@ -1707,6 +1751,13 @@ is compatible with earlier Postfix versions. </p>
|
||||
The default maximal number of parallel deliveries to the same
|
||||
destination. This is the default limit for delivery via the <a href="lmtp.8.html">lmtp(8)</a>,
|
||||
<a href="pipe.8.html">pipe(8)</a>, <a href="smtp.8.html">smtp(8)</a> and <a href="virtual.8.html">virtual(8)</a> delivery agents.
|
||||
With per-destination recipient limit > 1, a destination is a domain,
|
||||
otherwise it is a recipient.
|
||||
</p>
|
||||
|
||||
<p> Use <a href="postconf.5.html#transport_destination_concurrency_limit"><i>transport</i>_destination_concurrency_limit</a> to specify a
|
||||
transport-specific override, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
|
||||
@ -1840,6 +1891,11 @@ This is the default limit for delivery via the <a href="lmtp.8.html">lmtp(8)</a>
|
||||
the corresponding per-destination concurrency limit from concurrency
|
||||
per domain into concurrency per recipient. </p>
|
||||
|
||||
<p> Use <a href="postconf.5.html#transport_destination_recipient_limit"><i>transport</i>_destination_recipient_limit</a> to specify a
|
||||
transport-specific override, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
|
||||
</DD>
|
||||
|
||||
@ -1855,6 +1911,11 @@ recipients slots for the chosen message in order to avoid performance
|
||||
degradation.
|
||||
</p>
|
||||
|
||||
<p> Use <a href="postconf.5.html#transport_extra_recipient_limit"><i>transport</i>_extra_recipient_limit</a> to specify a
|
||||
transport-specific override, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
|
||||
</DD>
|
||||
|
||||
@ -1868,6 +1929,11 @@ which would never accumulate at least this many delivery slots
|
||||
(subject to slot cost parameter as well) are never preempted.
|
||||
</p>
|
||||
|
||||
<p> Use <a href="postconf.5.html#transport_minimum_delivery_slots"><i>transport</i>_minimum_delivery_slots</a> to specify a
|
||||
transport-specific override, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
|
||||
</DD>
|
||||
|
||||
@ -2034,6 +2100,11 @@ to the respective transports. See also <a href="postconf.5.html#default_extra_r
|
||||
and <a href="postconf.5.html#qmgr_message_recipient_minimum">qmgr_message_recipient_minimum</a>.
|
||||
</p>
|
||||
|
||||
<p> Use <a href="postconf.5.html#transport_recipient_limit"><i>transport</i>_recipient_limit</a> to specify a
|
||||
transport-specific override, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
|
||||
</DD>
|
||||
|
||||
@ -2048,6 +2119,11 @@ make sure the recipients are refilled in timely manner even when
|
||||
$<a href="postconf.5.html#default_recipient_refill_limit">default_recipient_refill_limit</a> is too high for too slow deliveries.
|
||||
</p>
|
||||
|
||||
<p> Use <a href="postconf.5.html#transport_recipient_refill_delay"><i>transport</i>_recipient_refill_delay</a> to specify a
|
||||
transport-specific override, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
<p> This feature is available in Postfix 2.4 and later. </p>
|
||||
|
||||
|
||||
@ -2064,6 +2140,11 @@ $<a href="postconf.5.html#default_recipient_refill_delay">default_recipient_refi
|
||||
lower than this when this limit is too high for too slow deliveries.
|
||||
</p>
|
||||
|
||||
<p> Use <a href="postconf.5.html#transport_recipient_refill_limit"><i>transport</i>_recipient_refill_limit</a> to specify a
|
||||
transport-specific override, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
<p> This feature is available in Postfix 2.4 and later. </p>
|
||||
|
||||
|
||||
@ -3187,6 +3268,8 @@ Examples:
|
||||
The initial per-destination concurrency level for parallel delivery
|
||||
to the same destination. This limit applies to delivery via <a href="smtp.8.html">smtp(8)</a>,
|
||||
and via the <a href="pipe.8.html">pipe(8)</a> and <a href="virtual.8.html">virtual(8)</a> delivery agents.
|
||||
With per-destination recipient limit > 1, a destination is a domain,
|
||||
otherwise it is a recipient.
|
||||
</p>
|
||||
|
||||
<p> Use <a href="postconf.5.html#transport_initial_destination_concurrency"><i>transport</i>_initial_destination_concurrency</a> to specify
|
||||
@ -12204,6 +12287,18 @@ This feature is available in Postfix 2.1 and later.
|
||||
</p>
|
||||
|
||||
|
||||
</DD>
|
||||
|
||||
<DT><b><a name="transport_delivery_rate_delay">transport_delivery_rate_delay</a>
|
||||
(default: $<a href="postconf.5.html#default_delivery_rate_delay">default_delivery_rate_delay</a>)</b></DT><DD>
|
||||
|
||||
<p> A transport-specific override for the <a href="postconf.5.html#default_recipient_refill_delay">default_recipient_refill_delay</a>
|
||||
parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a> name of
|
||||
the message delivery transport. </p>
|
||||
|
||||
<p> This feature is available in Postfix 2.5 and later. </p>
|
||||
|
||||
|
||||
</DD>
|
||||
|
||||
<DT><b><a name="transport_delivery_slot_cost">transport_delivery_slot_cost</a>
|
||||
|
@ -280,18 +280,18 @@ QMGR(8) QMGR(8)
|
||||
Idem, for delivery via the named message <i>transport</i>.
|
||||
|
||||
<b><a href="postconf.5.html#default_destination_concurrency_negative_feedback">default_destination_concurrency_negative_feedback</a> (1)</b>
|
||||
The per-destination amount of negative delivery
|
||||
concurrency feedback, after a delivery completes
|
||||
with a connection or handshake failure.
|
||||
The per-destination amount of delivery concurrency
|
||||
negative feedback, after a delivery completes with
|
||||
a connection or handshake failure.
|
||||
|
||||
<b><a href="postconf.5.html#transport_destination_concurrency_positive_feedback"><i>transport</i>_destination_concurrency_negative_feedback</a></b>
|
||||
<b>($<a href="postconf.5.html#default_destination_concurrency_negative_feedback">default_destination_concurrency_negative_feedback</a>)</b>
|
||||
Idem, for delivery via the named message <i>transport</i>.
|
||||
|
||||
<b><a href="postconf.5.html#default_destination_concurrency_positive_feedback">default_destination_concurrency_positive_feedback</a> (1)</b>
|
||||
The per-destination amount of positive delivery
|
||||
concurrency feedback, after a delivery completes
|
||||
without connection or handshake failure.
|
||||
The per-destination amount of delivery concurrency
|
||||
positive feedback, after a delivery completes with-
|
||||
out connection or handshake failure.
|
||||
|
||||
<b><a href="postconf.5.html#transport_destination_concurrency_positive_feedback"><i>transport</i>_destination_concurrency_positive_feedback</a></b>
|
||||
<b>($<a href="postconf.5.html#default_destination_concurrency_positive_feedback">default_destination_concurrency_positive_feedback</a>)</b>
|
||||
@ -332,7 +332,7 @@ QMGR(8) QMGR(8)
|
||||
The default value for transport-specific _deliv-
|
||||
ery_slot_discount settings.
|
||||
|
||||
<b><a href="postconf.5.html#transport_delivery_slot_discount"><i>transport</i>_delivery_slot_discount</a> ($<a href="postconf.5.html#default_delivery_slot_discount">default_deliv</a>-</b>
|
||||
<b><a href="postconf.5.html#transport_delivery_slot_discount"><a href="postconf.5.html#transport_delivery_slot_discount"><i>transport</i>_delivery_slot_discount</a></a> ($<a href="postconf.5.html#default_delivery_slot_discount">default_deliv</a>-</b>
|
||||
<b><a href="postconf.5.html#default_delivery_slot_discount">ery_slot_discount</a>)</b>
|
||||
Idem, for delivery via the named message <i>transport</i>.
|
||||
|
||||
@ -373,6 +373,18 @@ QMGR(8) QMGR(8)
|
||||
The maximal time a bounce message is queued before
|
||||
it is considered undeliverable.
|
||||
|
||||
Available in Postfix version 2.5 and later:
|
||||
|
||||
<b><a href="postconf.5.html#default_delivery_rate_delay">default_delivery_rate_delay</a> (0s)</b>
|
||||
The default amount of delay that is inserted
|
||||
between individual deliveries to the same destina-
|
||||
tion; with per-destination recipient limit > 1, a
|
||||
destination is a domain, otherwise it is a recipi-
|
||||
ent.
|
||||
|
||||
<b><a href="postconf.5.html#transport_delivery_rate_delay"><i>transport</i>_delivery_rate_delay</a> $<a href="postconf.5.html#default_delivery_rate_delay">default_delivery_rate_delay</a></b>
|
||||
Idem, for delivery via the named message <i>transport</i>.
|
||||
|
||||
<b>MISCELLANEOUS CONTROLS</b>
|
||||
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
|
||||
The default location of the Postfix <a href="postconf.5.html">main.cf</a> and
|
||||
|
@ -877,6 +877,26 @@ default_database_type = dbm
|
||||
.fi
|
||||
.ad
|
||||
.ft R
|
||||
.SH default_delivery_rate_delay (default: 0s)
|
||||
The default amount of delay that is inserted between individual
|
||||
deliveries to the same destination; with per-destination recipient
|
||||
limit > 1, a destination is a domain, otherwise it is a recipient.
|
||||
.PP
|
||||
To enable the delay, specify a non-zero time value (an integral
|
||||
value plus an optional one-letter suffix that specifies the time
|
||||
unit).
|
||||
.PP
|
||||
Time units: s (seconds), m (minutes), h (hours), d (days), w
|
||||
(weeks). The default time unit is s (seconds).
|
||||
.PP
|
||||
NOTE: the delay is enforced by the queue manager. The delay
|
||||
timer state does not survive "postfix reload" or "postfix stop".
|
||||
.PP
|
||||
Use \fItransport\fR_delivery_rate_delay 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_delivery_slot_cost (default: 5)
|
||||
How often the Postfix queue manager's scheduler is allowed to
|
||||
preempt delivery of one message with another.
|
||||
@ -904,6 +924,10 @@ disabled. The default value of 5 turns out to provide reasonable
|
||||
message response times while making sure the mailing-list deliveries
|
||||
are not extended by more than 20-25 percent even in the worst case.
|
||||
.PP
|
||||
Use \fItransport\fR_delivery_slot_cost to specify a
|
||||
transport-specific override, where \fItransport\fR is the master.cf
|
||||
name of the message delivery transport.
|
||||
.PP
|
||||
Examples:
|
||||
.PP
|
||||
.nf
|
||||
@ -925,6 +949,10 @@ transport_delivery_slot_discount percent of the required amount
|
||||
plus transport_delivery_slot_loan still remains to be accumulated.
|
||||
Note that the full amount will still have to be accumulated before
|
||||
another preemption can take place later.
|
||||
.PP
|
||||
Use \fItransport\fR_delivery_slot_discount to specify a
|
||||
transport-specific override, where \fItransport\fR is the master.cf
|
||||
name of the message delivery transport.
|
||||
.SH default_delivery_slot_loan (default: 3)
|
||||
The default value for transport-specific _delivery_slot_loan
|
||||
settings.
|
||||
@ -936,6 +964,10 @@ transport_delivery_slot_discount percent of the required amount
|
||||
plus transport_delivery_slot_loan still remains to be accumulated.
|
||||
Note that the full amount will still have to be accumulated before
|
||||
another preemption can take place later.
|
||||
.PP
|
||||
Use \fItransport\fR_delivery_slot_loan to specify a
|
||||
transport-specific override, where \fItransport\fR is the master.cf
|
||||
name of the message delivery transport.
|
||||
.SH default_destination_concurrency_failed_cohort_limit (default: 1)
|
||||
How many pseudo-cohorts must suffer connection or handshake
|
||||
failure before a specific destination is considered unavailable
|
||||
@ -957,6 +989,12 @@ is compatible with earlier Postfix versions.
|
||||
The default maximal number of parallel deliveries to the same
|
||||
destination. This is the default limit for delivery via the \fBlmtp\fR(8),
|
||||
\fBpipe\fR(8), \fBsmtp\fR(8) and \fBvirtual\fR(8) delivery agents.
|
||||
With per-destination recipient limit > 1, a destination is a domain,
|
||||
otherwise it is a recipient.
|
||||
.PP
|
||||
Use \fItransport\fR_destination_concurrency_limit to specify a
|
||||
transport-specific override, where \fItransport\fR is the master.cf
|
||||
name of the message delivery transport.
|
||||
.SH default_destination_concurrency_negative_feedback (default: 1)
|
||||
The per-destination amount of delivery concurrency negative
|
||||
feedback, after a delivery completes with a connection or handshake
|
||||
@ -1046,6 +1084,10 @@ This is the default limit for delivery via the \fBlmtp\fR(8), \fBpipe\fR(8),
|
||||
Setting this parameter to a value of 1 changes the meaning of
|
||||
the corresponding per-destination concurrency limit from concurrency
|
||||
per domain into concurrency per recipient.
|
||||
.PP
|
||||
Use \fItransport\fR_destination_recipient_limit to specify a
|
||||
transport-specific override, where \fItransport\fR is the master.cf
|
||||
name of the message delivery transport.
|
||||
.SH default_extra_recipient_limit (default: 1000)
|
||||
The default value for the extra per-transport limit imposed on the
|
||||
number of in-memory recipients. This extra recipient space is
|
||||
@ -1053,11 +1095,19 @@ reserved for the cases when the Postfix queue manager's scheduler
|
||||
preempts one message with another and suddenly needs some extra
|
||||
recipients slots for the chosen message in order to avoid performance
|
||||
degradation.
|
||||
.PP
|
||||
Use \fItransport\fR_extra_recipient_limit to specify a
|
||||
transport-specific override, where \fItransport\fR is the master.cf
|
||||
name of the message delivery transport.
|
||||
.SH default_minimum_delivery_slots (default: 3)
|
||||
How many recipients a message must have in order to invoke the
|
||||
Postfix queue manager's scheduling algorithm at all. Messages
|
||||
which would never accumulate at least this many delivery slots
|
||||
(subject to slot cost parameter as well) are never preempted.
|
||||
.PP
|
||||
Use \fItransport\fR_minimum_delivery_slots to specify a
|
||||
transport-specific override, where \fItransport\fR is the master.cf
|
||||
name of the message delivery transport.
|
||||
.SH default_privs (default: nobody)
|
||||
The default rights used by the \fBlocal\fR(8) delivery agent for delivery
|
||||
to external file or command. These rights are used when delivery
|
||||
@ -1142,6 +1192,10 @@ recipients. These limits take priority over the global
|
||||
qmgr_message_recipient_limit after the message has been assigned
|
||||
to the respective transports. See also default_extra_recipient_limit
|
||||
and qmgr_message_recipient_minimum.
|
||||
.PP
|
||||
Use \fItransport\fR_recipient_limit to specify a
|
||||
transport-specific override, where \fItransport\fR is the master.cf
|
||||
name of the message delivery transport.
|
||||
.SH default_recipient_refill_delay (default: 5s)
|
||||
The default per-transport maximum delay between recipients refills.
|
||||
When not all message recipients fit into the memory at once, keep loading
|
||||
@ -1149,6 +1203,10 @@ 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
|
||||
Use \fItransport\fR_recipient_refill_delay 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.4 and later.
|
||||
.SH default_recipient_refill_limit (default: 100)
|
||||
The default per-transport limit on the number of recipients refilled at
|
||||
@ -1157,6 +1215,10 @@ 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
|
||||
Use \fItransport\fR_recipient_refill_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.4 and later.
|
||||
.SH default_transport (default: smtp)
|
||||
The default mail delivery transport and next-hop destination for
|
||||
@ -1759,6 +1821,8 @@ inet_protocols = ipv4, ipv6
|
||||
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.
|
||||
With per-destination recipient limit > 1, a destination is a domain,
|
||||
otherwise it is a recipient.
|
||||
.PP
|
||||
Use \fItransport\fR_initial_destination_concurrency to specify
|
||||
a transport-specific override, where \fItransport\fR is the master.cf
|
||||
@ -7417,6 +7481,12 @@ 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_delivery_rate_delay (default: $default_delivery_rate_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.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
|
||||
|
@ -224,13 +224,13 @@ failure before a specific destination is considered unavailable
|
||||
.IP "\fItransport\fB_destination_concurrency_failed_cohort_limit ($default_destination_concurrency_failed_cohort_limit)\fR"
|
||||
Idem, for delivery via the named message \fItransport\fR.
|
||||
.IP "\fBdefault_destination_concurrency_negative_feedback (1)\fR"
|
||||
The per-destination amount of negative delivery concurrency
|
||||
The per-destination amount of delivery concurrency negative
|
||||
feedback, after a delivery completes with a connection or handshake
|
||||
failure.
|
||||
.IP "\fItransport\fB_destination_concurrency_negative_feedback ($default_destination_concurrency_negative_feedback)\fR"
|
||||
Idem, for delivery via the named message \fItransport\fR.
|
||||
.IP "\fBdefault_destination_concurrency_positive_feedback (1)\fR"
|
||||
The per-destination amount of positive delivery concurrency
|
||||
The per-destination amount of delivery concurrency positive
|
||||
feedback, after a delivery completes without connection or handshake
|
||||
failure.
|
||||
.IP "\fItransport\fB_destination_concurrency_positive_feedback ($default_destination_concurrency_positive_feedback)\fR"
|
||||
@ -271,6 +271,14 @@ Available in Postfix version 2.1 and later:
|
||||
.IP "\fBbounce_queue_lifetime (5d)\fR"
|
||||
The maximal time a bounce message is queued before it is considered
|
||||
undeliverable.
|
||||
.PP
|
||||
Available in Postfix version 2.5 and later:
|
||||
.IP "\fBdefault_delivery_rate_delay (0s)\fR"
|
||||
The default amount of delay that is inserted between individual
|
||||
deliveries to the same destination; with per-destination recipient
|
||||
limit > 1, a destination is a domain, otherwise it is a recipient.
|
||||
.IP "\fItransport\fB_delivery_rate_delay $default_delivery_rate_delay
|
||||
Idem, for delivery via the named message \fItransport\fR.
|
||||
.SH MISCELLANEOUS CONTROLS
|
||||
.ad
|
||||
.fi
|
||||
|
@ -247,13 +247,13 @@ failure before a specific destination is considered unavailable
|
||||
.IP "\fItransport\fB_destination_concurrency_failed_cohort_limit ($default_destination_concurrency_failed_cohort_limit)\fR"
|
||||
Idem, for delivery via the named message \fItransport\fR.
|
||||
.IP "\fBdefault_destination_concurrency_negative_feedback (1)\fR"
|
||||
The per-destination amount of negative delivery concurrency
|
||||
The per-destination amount of delivery concurrency negative
|
||||
feedback, after a delivery completes with a connection or handshake
|
||||
failure.
|
||||
.IP "\fItransport\fB_destination_concurrency_negative_feedback ($default_destination_concurrency_negative_feedback)\fR"
|
||||
Idem, for delivery via the named message \fItransport\fR.
|
||||
.IP "\fBdefault_destination_concurrency_positive_feedback (1)\fR"
|
||||
The per-destination amount of positive delivery concurrency
|
||||
The per-destination amount of delivery concurrency positive
|
||||
feedback, after a delivery completes without connection or handshake
|
||||
failure.
|
||||
.IP "\fItransport\fB_destination_concurrency_positive_feedback ($default_destination_concurrency_positive_feedback)\fR"
|
||||
@ -319,6 +319,14 @@ Available in Postfix version 2.1 and later:
|
||||
.IP "\fBbounce_queue_lifetime (5d)\fR"
|
||||
The maximal time a bounce message is queued before it is considered
|
||||
undeliverable.
|
||||
.PP
|
||||
Available in Postfix version 2.5 and later:
|
||||
.IP "\fBdefault_delivery_rate_delay (0s)\fR"
|
||||
The default amount of delay that is inserted between individual
|
||||
deliveries to the same destination; with per-destination recipient
|
||||
limit > 1, a destination is a domain, otherwise it is a recipient.
|
||||
.IP "\fItransport\fB_delivery_rate_delay $default_delivery_rate_delay
|
||||
Idem, for delivery via the named message \fItransport\fR.
|
||||
.SH "MISCELLANEOUS CONTROLS"
|
||||
.na
|
||||
.nf
|
||||
|
@ -339,6 +339,7 @@ while (<>) {
|
||||
s;\bdefault_destination_concur[-</Bb>]*\n* *[<Bb>]*rency_positive_feedback\b;<a href="postconf.5.html#default_destination_concurrency_positive_feedback">$&</a>;g;
|
||||
s;\bdefault_destination_con[-</Bb>]*\n* *[<Bb>]*currency_failed_cohort_limit\b;<a href="postconf.5.html#default_destination_concurrency_failed_cohort_limit">$&</a>;g;
|
||||
s;\bdestination_concurrency_feedback_debug\b;<a href="postconf.5.html#destination_concurrency_feedback_debug">$&</a>;g;
|
||||
s;\bdefault_delivery_rate_delay\b;<a href="postconf.5.html#default_delivery_rate_delay">$&</a>;g;
|
||||
|
||||
s;\bqmqpd_error_delay\b;<a href="postconf.5.html#qmqpd_error_delay">$&</a>;g;
|
||||
s;\bqmqpd_timeout\b;<a href="postconf.5.html#qmqpd_timeout">$&</a>;g;
|
||||
@ -635,7 +636,8 @@ while (<>) {
|
||||
s;(<i>transport</i>)(<b>)?(_recipient_refill_delay)\b;$2<a href="postconf.5.html#transport_recipient_refill_delay">$1$3</a>;g;
|
||||
s;(<i>transport</i>)(<b>)?(_recipient_refill_limit)\b;$2<a href="postconf.5.html#transport_recipient_refill_limit">$1$3</a>;g;
|
||||
s;(<i>transport</i>)(<b>)?(_time_limit)\b;$2<a href="postconf.5.html#transport_time_limit">$1$3</a>;g;
|
||||
s;(<i>transport</i>)(<b>)?(delivery_slot_discount)\b;$2<a href="postconf.5.html#transportdelivery_slot_discount">$1$3</a>;g;
|
||||
s;(<i>transport</i>)(<b>)?(_delivery_slot_discount)\b;$2<a href="postconf.5.html#transport_delivery_slot_discount">$1$3</a>;g;
|
||||
s;(<i>transport</i>)(<b>)?(_delivery_rate_delay)\b;$2<a href="postconf.5.html#transport_delivery_rate_delay">$1$3</a>;g;
|
||||
|
||||
# Undo hyperlinks of manual pages with the same name as parameters.
|
||||
|
||||
|
@ -554,7 +554,7 @@ the next section. </p>
|
||||
|
||||
<h3> <a name="concurrency_limitations"> Limitations of less-than-1 per delivery feedback </a> </h3>
|
||||
|
||||
<p> The delivery concurrency scheduler with less-than-1 concurrency
|
||||
<p> The scheduler with less-than-1 concurrency
|
||||
feedback per delivery solves a problem with servers that have active
|
||||
concurrency limiters. This works only because feedback is handled
|
||||
in a peculiar manner: positive feedback will increment the concurrency
|
||||
@ -580,9 +580,9 @@ level of about K, even though the good servers behind the load
|
||||
balancer are perfectly capable of handling more traffic. </p>
|
||||
|
||||
<p> This noise problem gets worse as the amount of positive feedback
|
||||
per delivery gets smaller. A compromise is to avoid concurrency-dependent
|
||||
positive feedback, and to use fixed less-than-1 feedback values
|
||||
instead. For example, to tolerate 1 of 4 bad servers in the above
|
||||
per delivery gets smaller. A compromise is to use fixed less-than-1
|
||||
positive feedback values instead of concurrency-dependent positive
|
||||
feedback. For example, to tolerate 1 of 4 bad servers in the above
|
||||
load balancer scenario, use positive feedback of 1/4 per "good"
|
||||
delivery (no connect or handshake error), and use an equal or smaller
|
||||
amount of negative feedback per "bad" delivery. The downside of
|
||||
|
@ -885,6 +885,11 @@ message response times while making sure the mailing-list deliveries
|
||||
are not extended by more than 20-25 percent even in the worst case.
|
||||
</p>
|
||||
|
||||
<p> Use <i>transport</i>_delivery_slot_cost to specify a
|
||||
transport-specific override, where <i>transport</i> is the master.cf
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Examples:
|
||||
</p>
|
||||
@ -900,6 +905,13 @@ default_delivery_slot_cost = 2
|
||||
The default maximal number of parallel deliveries to the same
|
||||
destination. This is the default limit for delivery via the lmtp(8),
|
||||
pipe(8), smtp(8) and virtual(8) delivery agents.
|
||||
With per-destination recipient limit > 1, a destination is a domain,
|
||||
otherwise it is a recipient.
|
||||
</p>
|
||||
|
||||
<p> Use <i>transport</i>_destination_concurrency_limit to specify a
|
||||
transport-specific override, where <i>transport</i> is the master.cf
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
%PARAM default_destination_recipient_limit 50
|
||||
@ -914,6 +926,11 @@ smtp(8) and virtual(8) delivery agents.
|
||||
the corresponding per-destination concurrency limit from concurrency
|
||||
per domain into concurrency per recipient. </p>
|
||||
|
||||
<p> Use <i>transport</i>_destination_recipient_limit to specify a
|
||||
transport-specific override, where <i>transport</i> is the master.cf
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
%PARAM default_extra_recipient_limit 1000
|
||||
|
||||
<p>
|
||||
@ -925,6 +942,11 @@ recipients slots for the chosen message in order to avoid performance
|
||||
degradation.
|
||||
</p>
|
||||
|
||||
<p> Use <i>transport</i>_extra_recipient_limit to specify a
|
||||
transport-specific override, where <i>transport</i> is the master.cf
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
%PARAM default_minimum_delivery_slots 3
|
||||
|
||||
<p>
|
||||
@ -934,6 +956,11 @@ which would never accumulate at least this many delivery slots
|
||||
(subject to slot cost parameter as well) are never preempted.
|
||||
</p>
|
||||
|
||||
<p> Use <i>transport</i>_minimum_delivery_slots to specify a
|
||||
transport-specific override, where <i>transport</i> is the master.cf
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
%PARAM default_privs nobody
|
||||
|
||||
<p>
|
||||
@ -1097,6 +1124,11 @@ to the respective transports. See also default_extra_recipient_limit
|
||||
and qmgr_message_recipient_minimum.
|
||||
</p>
|
||||
|
||||
<p> Use <i>transport</i>_recipient_limit to specify a
|
||||
transport-specific override, where <i>transport</i> is the master.cf
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
%PARAM default_recipient_refill_limit 100
|
||||
|
||||
<p>
|
||||
@ -1107,6 +1139,11 @@ $default_recipient_refill_delay, which may result in recipient batches
|
||||
lower than this when this limit is too high for too slow deliveries.
|
||||
</p>
|
||||
|
||||
<p> Use <i>transport</i>_recipient_refill_limit to specify a
|
||||
transport-specific override, where <i>transport</i> is the master.cf
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
<p> This feature is available in Postfix 2.4 and later. </p>
|
||||
|
||||
%PARAM default_recipient_refill_delay 5s
|
||||
@ -1119,6 +1156,11 @@ make sure the recipients are refilled in timely manner even when
|
||||
$default_recipient_refill_limit is too high for too slow deliveries.
|
||||
</p>
|
||||
|
||||
<p> Use <i>transport</i>_recipient_refill_delay to specify a
|
||||
transport-specific override, where <i>transport</i> is the master.cf
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
<p> This feature is available in Postfix 2.4 and later. </p>
|
||||
|
||||
%PARAM default_transport smtp
|
||||
@ -1805,6 +1847,8 @@ inet_protocols = ipv4, ipv6
|
||||
The initial per-destination concurrency level for parallel delivery
|
||||
to the same destination. This limit applies to delivery via smtp(8),
|
||||
and via the pipe(8) and virtual(8) delivery agents.
|
||||
With per-destination recipient limit > 1, a destination is a domain,
|
||||
otherwise it is a recipient.
|
||||
</p>
|
||||
|
||||
<p> Use <i>transport</i>_initial_destination_concurrency to specify
|
||||
@ -6566,6 +6610,11 @@ Note that the full amount will still have to be accumulated before
|
||||
another preemption can take place later.
|
||||
</p>
|
||||
|
||||
<p> Use <i>transport</i>_delivery_slot_discount to specify a
|
||||
transport-specific override, where <i>transport</i> is the master.cf
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
%PARAM default_delivery_slot_loan 3
|
||||
|
||||
<p>
|
||||
@ -6583,6 +6632,11 @@ Note that the full amount will still have to be accumulated before
|
||||
another preemption can take place later.
|
||||
</p>
|
||||
|
||||
<p> Use <i>transport</i>_delivery_slot_loan to specify a
|
||||
transport-specific override, where <i>transport</i> is the master.cf
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
%CLASS verp VERP Support
|
||||
|
||||
<p>
|
||||
@ -10931,3 +10985,36 @@ parameter value, where <i>transport</i> is the master.cf name of
|
||||
the message delivery transport. </p>
|
||||
|
||||
<p> This feature is available in Postfix 2.4 and later. </p>
|
||||
|
||||
%PARAM default_delivery_rate_delay 0s
|
||||
|
||||
<p> The default amount of delay that is inserted between individual
|
||||
deliveries to the same destination; with per-destination recipient
|
||||
limit > 1, a destination is a domain, otherwise it is a recipient.
|
||||
</p>
|
||||
|
||||
<p> To enable the delay, specify a non-zero time value (an integral
|
||||
value plus an optional one-letter suffix that specifies the time
|
||||
unit). </p>
|
||||
|
||||
<p> Time units: s (seconds), m (minutes), h (hours), d (days), w
|
||||
(weeks). The default time unit is s (seconds). </p>
|
||||
|
||||
<p> NOTE: the delay is enforced by the queue manager. The delay
|
||||
timer state does not survive "postfix reload" or "postfix stop".
|
||||
</p>
|
||||
|
||||
<p> Use <i>transport</i>_delivery_rate_delay to specify a
|
||||
transport-specific override, where <i>transport</i> is the master.cf
|
||||
name of the message delivery transport.
|
||||
</p>
|
||||
|
||||
<p> This feature is available in Postfix 2.5 and later. </p>
|
||||
|
||||
%PARAM transport_delivery_rate_delay $default_delivery_rate_delay
|
||||
|
||||
<p> A transport-specific override for the default_recipient_refill_delay
|
||||
parameter value, where <i>transport</i> is the master.cf name of
|
||||
the message delivery transport. </p>
|
||||
|
||||
<p> This feature is available in Postfix 2.5 and later. </p>
|
||||
|
@ -2861,6 +2861,11 @@ extern int var_conc_cohort_limit;
|
||||
#define DEF_CONC_FDBACK_DEBUG 0
|
||||
extern bool var_conc_feedback_debug;
|
||||
|
||||
#define VAR_DEST_RATE_DELAY "default_delivery_rate_delay"
|
||||
#define _DEST_RATE_DELAY "_delivery_rate_delay"
|
||||
#define DEF_DEST_RATE_DELAY "0s"
|
||||
extern int var_dest_rate_delay;
|
||||
|
||||
/* LICENSE
|
||||
/* .ad
|
||||
/* .fi
|
||||
|
@ -194,13 +194,13 @@
|
||||
/* .IP "\fItransport\fB_destination_concurrency_failed_cohort_limit ($default_destination_concurrency_failed_cohort_limit)\fR"
|
||||
/* Idem, for delivery via the named message \fItransport\fR.
|
||||
/* .IP "\fBdefault_destination_concurrency_negative_feedback (1)\fR"
|
||||
/* The per-destination amount of negative delivery concurrency
|
||||
/* The per-destination amount of delivery concurrency negative
|
||||
/* feedback, after a delivery completes with a connection or handshake
|
||||
/* failure.
|
||||
/* .IP "\fItransport\fB_destination_concurrency_negative_feedback ($default_destination_concurrency_negative_feedback)\fR"
|
||||
/* Idem, for delivery via the named message \fItransport\fR.
|
||||
/* .IP "\fBdefault_destination_concurrency_positive_feedback (1)\fR"
|
||||
/* The per-destination amount of positive delivery concurrency
|
||||
/* The per-destination amount of delivery concurrency positive
|
||||
/* feedback, after a delivery completes without connection or handshake
|
||||
/* failure.
|
||||
/* .IP "\fItransport\fB_destination_concurrency_positive_feedback ($default_destination_concurrency_positive_feedback)\fR"
|
||||
@ -237,6 +237,14 @@
|
||||
/* .IP "\fBbounce_queue_lifetime (5d)\fR"
|
||||
/* The maximal time a bounce message is queued before it is considered
|
||||
/* undeliverable.
|
||||
/* .PP
|
||||
/* Available in Postfix version 2.5 and later:
|
||||
/* .IP "\fBdefault_delivery_rate_delay (0s)\fR"
|
||||
/* The default amount of delay that is inserted between individual
|
||||
/* deliveries to the same destination; with per-destination recipient
|
||||
/* limit > 1, a destination is a domain, otherwise it is a recipient.
|
||||
/* .IP "\fItransport\fB_delivery_rate_delay $default_delivery_rate_delay
|
||||
/* Idem, for delivery via the named message \fItransport\fR.
|
||||
/* .SH MISCELLANEOUS CONTROLS
|
||||
/* .ad
|
||||
/* .fi
|
||||
@ -362,6 +370,7 @@ char *var_conc_pos_feedback;
|
||||
char *var_conc_neg_feedback;
|
||||
int var_conc_cohort_limit;
|
||||
int var_conc_feedback_debug;
|
||||
int var_dest_rate_delay;
|
||||
|
||||
static QMGR_SCAN *qmgr_scans[2];
|
||||
|
||||
@ -601,6 +610,7 @@ int main(int argc, char **argv)
|
||||
VAR_DSN_QUEUE_TIME, DEF_DSN_QUEUE_TIME, &var_dsn_queue_time, 0, 8640000,
|
||||
VAR_XPORT_RETRY_TIME, DEF_XPORT_RETRY_TIME, &var_transport_retry_time, 1, 0,
|
||||
VAR_QMGR_CLOG_WARN_TIME, DEF_QMGR_CLOG_WARN_TIME, &var_qmgr_clog_warn_time, 0, 0,
|
||||
VAR_DEST_RATE_DELAY, DEF_DEST_RATE_DELAY, &var_dest_rate_delay, 0, 0,
|
||||
0,
|
||||
};
|
||||
static CONFIG_INT_TABLE int_table[] = {
|
||||
|
@ -161,6 +161,7 @@ struct QMGR_TRANSPORT {
|
||||
QMGR_FEEDBACK pos_feedback; /* positive feedback control */
|
||||
QMGR_FEEDBACK neg_feedback; /* negative feedback control */
|
||||
int fail_cohort_limit; /* flow shutdown control */
|
||||
int rate_delay; /* suspend per delivery */
|
||||
};
|
||||
|
||||
#define QMGR_TRANSPORT_STAT_DEAD (1<<1)
|
||||
@ -219,8 +220,36 @@ 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_suspend(QMGR_QUEUE *, int);
|
||||
|
||||
#define QMGR_QUEUE_THROTTLED(q) ((q)->window <= 0)
|
||||
/*
|
||||
* Exclusive queue states. Originally there were only two: "throttled" and
|
||||
* "not throttled". It was natural to encode these in the queue window size.
|
||||
* After 10 years it's not practical to rip out all the working code and
|
||||
* change representations, so we just clean up the names a little.
|
||||
*
|
||||
* Note: only the "ready" state can reach every state (including itself);
|
||||
* non-ready states can reach only the "ready" state. Other transitions are
|
||||
* forbidden, because they would result in dangling event handlers.
|
||||
*/
|
||||
#define QMGR_QUEUE_STAT_THROTTLED 0 /* back-off timer */
|
||||
#define QMGR_QUEUE_STAT_SUSPENDED -1 /* voluntary delay timer */
|
||||
#define QMGR_QUEUE_STAT_SAVED -2 /* delayed cleanup timer */
|
||||
#define QMGR_QUEUE_STAT_BAD -3 /* can't happen */
|
||||
|
||||
#define QMGR_QUEUE_READY(q) ((q)->window > 0)
|
||||
#define QMGR_QUEUE_THROTTLED(q) ((q)->window == QMGR_QUEUE_STAT_THROTTLED)
|
||||
#define QMGR_QUEUE_SUSPENDED(q) ((q)->window == QMGR_QUEUE_STAT_SUSPENDED)
|
||||
#define QMGR_QUEUE_SAVED(q) ((q)->window == QMGR_QUEUE_STAT_SAVED)
|
||||
#define QMGR_QUEUE_BAD(q) ((q)->window <= QMGR_QUEUE_STAT_BAD)
|
||||
|
||||
#define QMGR_QUEUE_STATUS(q) ( \
|
||||
QMGR_QUEUE_READY(q) ? "ready" : \
|
||||
QMGR_QUEUE_THROTTLED(q) ? "throttled" : \
|
||||
QMGR_QUEUE_SUSPENDED(q) ? "suspended" : \
|
||||
QMGR_QUEUE_SAVED(q) ? "saved" : \
|
||||
"invalid queue status" \
|
||||
)
|
||||
|
||||
/*
|
||||
* Structure of one next-hop queue entry. In order to save some copying
|
||||
|
@ -312,9 +312,9 @@ static void qmgr_deliver_update(int unused_event, char *context)
|
||||
if (VSTRING_LEN(dsb->reason) == 0)
|
||||
vstring_strcpy(dsb->reason, "unknown error");
|
||||
vstring_prepend(dsb->reason, SUSPENDED, sizeof(SUSPENDED) - 1);
|
||||
if (queue->window > 0) {
|
||||
if (QMGR_QUEUE_READY(queue)) {
|
||||
qmgr_queue_throttle(queue, DSN_FROM_DSN_BUF(dsb));
|
||||
if (queue->window == 0)
|
||||
if (QMGR_QUEUE_THROTTLED(queue))
|
||||
qmgr_defer_todo(queue, &dsb->dsn);
|
||||
}
|
||||
}
|
||||
|
@ -97,11 +97,11 @@ void qmgr_enable_transport(QMGR_TRANSPORT *transport)
|
||||
|
||||
void qmgr_enable_queue(QMGR_QUEUE *queue)
|
||||
{
|
||||
if (queue->window == 0) {
|
||||
if (QMGR_QUEUE_THROTTLED(queue)) {
|
||||
if (msg_verbose)
|
||||
msg_info("enable site %s/%s", queue->transport->name, queue->name);
|
||||
qmgr_queue_unthrottle(queue);
|
||||
}
|
||||
if (queue->todo.next == 0 && queue->busy.next == 0)
|
||||
if (QMGR_QUEUE_READY(queue) && queue->todo.next == 0 && queue->busy.next == 0)
|
||||
qmgr_queue_done(queue);
|
||||
}
|
||||
|
@ -212,14 +212,16 @@ void qmgr_entry_move_todo(QMGR_QUEUE *dst, QMGR_ENTRY *entry)
|
||||
|
||||
void qmgr_entry_done(QMGR_ENTRY *entry, int which)
|
||||
{
|
||||
const char *myname = "qmgr_entry_done";
|
||||
QMGR_QUEUE *queue = entry->queue;
|
||||
QMGR_MESSAGE *message = entry->message;
|
||||
QMGR_TRANSPORT *transport = queue->transport;
|
||||
|
||||
/*
|
||||
* Take this entry off the in-core queue.
|
||||
*/
|
||||
if (entry->stream != 0)
|
||||
msg_panic("qmgr_entry_done: file is open");
|
||||
msg_panic("%s: file is open", myname);
|
||||
if (which == QMGR_QUEUE_BUSY) {
|
||||
QMGR_LIST_UNLINK(queue->busy, QMGR_ENTRY *, entry);
|
||||
queue->busy_refcount--;
|
||||
@ -227,7 +229,7 @@ void qmgr_entry_done(QMGR_ENTRY *entry, int which)
|
||||
QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry);
|
||||
queue->todo_refcount--;
|
||||
} else {
|
||||
msg_panic("qmgr_entry_done: bad queue spec: %d", which);
|
||||
msg_panic("%s: bad queue spec: %d", myname, which);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -242,8 +244,22 @@ void qmgr_entry_done(QMGR_ENTRY *entry, int which)
|
||||
/*
|
||||
* Maintain back-to-back delivery status.
|
||||
*/
|
||||
if (which == QMGR_QUEUE_BUSY)
|
||||
queue->last_done = event_time();
|
||||
|
||||
/*
|
||||
* Suspend a rate-limited queue, so that mail trickles out.
|
||||
*/
|
||||
if (which == QMGR_QUEUE_BUSY && transport->rate_delay > 0) {
|
||||
if (queue->window > 1)
|
||||
msg_panic("%s: queue %s/%s: window %d > 1 on rate-limited service",
|
||||
myname, transport->name, queue->name, queue->window);
|
||||
if (QMGR_QUEUE_THROTTLED(queue)) /* XXX */
|
||||
qmgr_queue_unthrottle(queue);
|
||||
if (QMGR_QUEUE_READY(queue))
|
||||
qmgr_queue_suspend(queue, transport->rate_delay);
|
||||
}
|
||||
|
||||
/*
|
||||
* When the in-core queue for this site is empty and when this site is
|
||||
* not dead, discard the in-core queue. When this site is dead, but the
|
||||
@ -253,9 +269,9 @@ void qmgr_entry_done(QMGR_ENTRY *entry, int which)
|
||||
* See also: qmgr_entry_move_todo().
|
||||
*/
|
||||
if (queue->todo.next == 0 && queue->busy.next == 0) {
|
||||
if (queue->window == 0 && qmgr_queue_count > 2 * var_qmgr_rcpt_limit)
|
||||
if (QMGR_QUEUE_THROTTLED(queue) && qmgr_queue_count > 2 * var_qmgr_rcpt_limit)
|
||||
qmgr_queue_unthrottle(queue);
|
||||
if (queue->window > 0)
|
||||
if (QMGR_QUEUE_READY(queue))
|
||||
qmgr_queue_done(queue);
|
||||
}
|
||||
|
||||
@ -293,7 +309,7 @@ QMGR_ENTRY *qmgr_entry_create(QMGR_QUEUE *queue, QMGR_MESSAGE *message)
|
||||
/*
|
||||
* Sanity check.
|
||||
*/
|
||||
if (queue->window == 0)
|
||||
if (QMGR_QUEUE_THROTTLED(queue))
|
||||
msg_panic("qmgr_entry_create: dead queue: %s", queue->name);
|
||||
|
||||
/*
|
||||
|
@ -29,6 +29,10 @@
|
||||
/*
|
||||
/* void qmgr_queue_unthrottle(queue)
|
||||
/* QMGR_QUEUE *queue;
|
||||
/*
|
||||
/* void qmgr_queue_suspend(queue, delay)
|
||||
/* QMGR_QUEUE *queue;
|
||||
/* int delay;
|
||||
/* DESCRIPTION
|
||||
/* These routines add/delete/manipulate per-destination queues.
|
||||
/* Each queue corresponds to a specific transport and destination.
|
||||
@ -67,8 +71,11 @@
|
||||
/* provided that it does not exceed the destination concurrency
|
||||
/* limit specified for the transport. This routine implements
|
||||
/* "slow open" mode, and eliminates the "thundering herd" problem.
|
||||
/*
|
||||
/* qmgr_queue_suspend() suspends delivery for this destination
|
||||
/* briefly.
|
||||
/* DIAGNOSTICS
|
||||
/* None
|
||||
/* Panic: consistency check failure.
|
||||
/* LICENSE
|
||||
/* .ad
|
||||
/* .fi
|
||||
@ -118,6 +125,53 @@ int qmgr_queue_count;
|
||||
myname, queue->name, queue->transport->dest_concurrency_limit, \
|
||||
queue->window, queue->success, queue->failure, queue->fail_cohorts);
|
||||
|
||||
/* qmgr_queue_resume - resume delivery to destination */
|
||||
|
||||
static void qmgr_queue_resume(int event, char *context)
|
||||
{
|
||||
QMGR_QUEUE *queue = (QMGR_QUEUE *) context;
|
||||
const char *myname = "qmgr_queue_resume";
|
||||
|
||||
/*
|
||||
* Sanity checks.
|
||||
*/
|
||||
if (!QMGR_QUEUE_SUSPENDED(queue))
|
||||
msg_panic("%s: bad queue status: %s", myname, QMGR_QUEUE_STATUS(queue));
|
||||
|
||||
/*
|
||||
* We can't simply force delivery on this queue: the transport's pending
|
||||
* count may already be maxed out, and there may be other constraints
|
||||
* that definitely should be none of our business. The best we can do is
|
||||
* to play by the same rules as everyone else: trigger *some* delivery
|
||||
* via qmgr_active_drain() and let round-robin selection work for us.
|
||||
*/
|
||||
queue->window = 1;
|
||||
if (queue->todo_refcount > 0)
|
||||
qmgr_active_drain();
|
||||
}
|
||||
|
||||
/* qmgr_queue_suspend - briefly suspend a destination */
|
||||
|
||||
void qmgr_queue_suspend(QMGR_QUEUE *queue, int delay)
|
||||
{
|
||||
const char *myname = "qmgr_queue_suspend";
|
||||
|
||||
/*
|
||||
* Sanity checks.
|
||||
*/
|
||||
if (!QMGR_QUEUE_READY(queue))
|
||||
msg_panic("%s: bad queue status: %s", myname, QMGR_QUEUE_STATUS(queue));
|
||||
if (queue->busy_refcount > 0)
|
||||
msg_panic("%s: queue is busy", myname);
|
||||
|
||||
/*
|
||||
* Set the queue status to "suspended". No-one is supposed to remove a
|
||||
* queue in suspended state.
|
||||
*/
|
||||
queue->window = QMGR_QUEUE_STAT_SUSPENDED;
|
||||
event_request_timer(qmgr_queue_resume, (char *) queue, delay);
|
||||
}
|
||||
|
||||
/* qmgr_queue_unthrottle_wrapper - in case (char *) != (struct *) */
|
||||
|
||||
static void qmgr_queue_unthrottle_wrapper(int unused_event, char *context)
|
||||
@ -130,7 +184,7 @@ static void qmgr_queue_unthrottle_wrapper(int unused_event, char *context)
|
||||
* this in-core queue when it is empty and when this site is not dead.
|
||||
*/
|
||||
qmgr_queue_unthrottle(queue);
|
||||
if (queue->window > 0 && queue->todo.next == 0 && queue->busy.next == 0)
|
||||
if (QMGR_QUEUE_READY(queue) && queue->todo.next == 0 && queue->busy.next == 0)
|
||||
qmgr_queue_done(queue);
|
||||
}
|
||||
|
||||
@ -145,6 +199,12 @@ void qmgr_queue_unthrottle(QMGR_QUEUE *queue)
|
||||
if (msg_verbose)
|
||||
msg_info("%s: queue %s", myname, queue->name);
|
||||
|
||||
/*
|
||||
* Sanity checks.
|
||||
*/
|
||||
if (!QMGR_QUEUE_THROTTLED(queue) && !QMGR_QUEUE_READY(queue))
|
||||
msg_panic("%s: bad queue status: %s", myname, QMGR_QUEUE_STATUS(queue));
|
||||
|
||||
/*
|
||||
* Don't restart the negative feedback hysteresis cycle with every
|
||||
* positive feedback. Restart it only when we make a positive concurrency
|
||||
@ -157,7 +217,7 @@ void qmgr_queue_unthrottle(QMGR_QUEUE *queue)
|
||||
/*
|
||||
* Special case when this site was dead.
|
||||
*/
|
||||
if (queue->window == 0) {
|
||||
if (QMGR_QUEUE_THROTTLED(queue)) {
|
||||
event_cancel_timer(qmgr_queue_unthrottle_wrapper, (char *) queue);
|
||||
if (queue->dsn == 0)
|
||||
msg_panic("%s: queue %s: window 0 status 0", myname, queue->name);
|
||||
@ -219,6 +279,8 @@ void qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
|
||||
/*
|
||||
* Sanity checks.
|
||||
*/
|
||||
if (!QMGR_QUEUE_READY(queue))
|
||||
msg_panic("%s: bad queue status: %s", myname, QMGR_QUEUE_STATUS(queue));
|
||||
if (queue->dsn)
|
||||
msg_panic("%s: queue %s: spurious reason %s",
|
||||
myname, queue->name, queue->dsn->reason);
|
||||
@ -238,7 +300,7 @@ void qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
|
||||
* This queue is declared dead after a configurable number of
|
||||
* pseudo-cohort failures.
|
||||
*/
|
||||
if (queue->window > 0) {
|
||||
if (QMGR_QUEUE_READY(queue)) {
|
||||
queue->fail_cohorts += 1.0 / queue->window;
|
||||
if (transport->fail_cohort_limit > 0
|
||||
&& queue->fail_cohorts >= transport->fail_cohort_limit)
|
||||
@ -254,7 +316,7 @@ void qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
|
||||
* Even after reaching 1, we maintain the negative hysteresis cycle so that
|
||||
* negative feedback can cancel out positive feedback.
|
||||
*/
|
||||
if (queue->window > 0) {
|
||||
if (QMGR_QUEUE_READY(queue)) {
|
||||
feedback = QMGR_FEEDBACK_VAL(transport->neg_feedback, queue->window);
|
||||
QMGR_LOG_FEEDBACK(feedback);
|
||||
queue->failure -= feedback;
|
||||
@ -272,7 +334,7 @@ void qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
|
||||
/*
|
||||
* Special case for a site that just was declared dead.
|
||||
*/
|
||||
if (queue->window == 0) {
|
||||
if (QMGR_QUEUE_THROTTLED(queue)) {
|
||||
queue->dsn = DSN_COPY(dsn);
|
||||
event_request_timer(qmgr_queue_unthrottle_wrapper,
|
||||
(char *) queue, var_min_backoff_time);
|
||||
@ -318,8 +380,8 @@ void qmgr_queue_done(QMGR_QUEUE *queue)
|
||||
queue->busy_refcount + queue->todo_refcount);
|
||||
if (queue->todo.next || queue->busy.next)
|
||||
msg_panic("%s: queue not empty: %s", myname, queue->name);
|
||||
if (queue->window <= 0)
|
||||
msg_panic("%s: window %d", myname, queue->window);
|
||||
if (!QMGR_QUEUE_READY(queue))
|
||||
msg_panic("%s: bad queue status: %s", myname, QMGR_QUEUE_STATUS(queue));
|
||||
if (queue->dsn)
|
||||
msg_panic("%s: queue %s: spurious reason %s",
|
||||
myname, queue->name, queue->dsn->reason);
|
||||
|
@ -384,7 +384,12 @@ QMGR_TRANSPORT *qmgr_transport_create(const char *name)
|
||||
transport->init_dest_concurrency =
|
||||
get_mail_conf_int2(name, _INIT_DEST_CON,
|
||||
var_init_dest_concurrency, 1, 0);
|
||||
transport->rate_delay = get_mail_conf_time2(name, _DEST_RATE_DELAY,
|
||||
var_dest_rate_delay,
|
||||
's', 0, 0);
|
||||
|
||||
if (transport->rate_delay > 0)
|
||||
transport->dest_concurrency_limit = 1;
|
||||
if (transport->dest_concurrency_limit != 0
|
||||
&& transport->dest_concurrency_limit < transport->init_dest_concurrency)
|
||||
transport->init_dest_concurrency = transport->dest_concurrency_limit;
|
||||
|
@ -8,6 +8,7 @@ BEGIN {
|
||||
vars["destination_concurrency_positive_feedback"] = "default_destination_concurrency_positive_feedback"
|
||||
vars["destination_recipient_limit"] = "default_destination_recipient_limit"
|
||||
vars["initial_destination_concurrency"] = "initial_destination_concurrency"
|
||||
vars["delivery_rate_delay"] = "default_delivery_rate_delay"
|
||||
|
||||
# auto_table.h
|
||||
|
||||
|
@ -217,13 +217,13 @@
|
||||
/* .IP "\fItransport\fB_destination_concurrency_failed_cohort_limit ($default_destination_concurrency_failed_cohort_limit)\fR"
|
||||
/* Idem, for delivery via the named message \fItransport\fR.
|
||||
/* .IP "\fBdefault_destination_concurrency_negative_feedback (1)\fR"
|
||||
/* The per-destination amount of negative delivery concurrency
|
||||
/* The per-destination amount of delivery concurrency negative
|
||||
/* feedback, after a delivery completes with a connection or handshake
|
||||
/* failure.
|
||||
/* .IP "\fItransport\fB_destination_concurrency_negative_feedback ($default_destination_concurrency_negative_feedback)\fR"
|
||||
/* Idem, for delivery via the named message \fItransport\fR.
|
||||
/* .IP "\fBdefault_destination_concurrency_positive_feedback (1)\fR"
|
||||
/* The per-destination amount of positive delivery concurrency
|
||||
/* The per-destination amount of delivery concurrency positive
|
||||
/* feedback, after a delivery completes without connection or handshake
|
||||
/* failure.
|
||||
/* .IP "\fItransport\fB_destination_concurrency_positive_feedback ($default_destination_concurrency_positive_feedback)\fR"
|
||||
@ -283,6 +283,14 @@
|
||||
/* .IP "\fBbounce_queue_lifetime (5d)\fR"
|
||||
/* The maximal time a bounce message is queued before it is considered
|
||||
/* undeliverable.
|
||||
/* .PP
|
||||
/* Available in Postfix version 2.5 and later:
|
||||
/* .IP "\fBdefault_delivery_rate_delay (0s)\fR"
|
||||
/* The default amount of delay that is inserted between individual
|
||||
/* deliveries to the same destination; with per-destination recipient
|
||||
/* limit > 1, a destination is a domain, otherwise it is a recipient.
|
||||
/* .IP "\fItransport\fB_delivery_rate_delay $default_delivery_rate_delay
|
||||
/* Idem, for delivery via the named message \fItransport\fR.
|
||||
/* MISCELLANEOUS CONTROLS
|
||||
/* .ad
|
||||
/* .fi
|
||||
@ -422,6 +430,7 @@ char *var_conc_pos_feedback;
|
||||
char *var_conc_neg_feedback;
|
||||
int var_conc_cohort_limit;
|
||||
int var_conc_feedback_debug;
|
||||
int var_dest_rate_delay;
|
||||
|
||||
static QMGR_SCAN *qmgr_scans[2];
|
||||
|
||||
@ -669,6 +678,7 @@ int main(int argc, char **argv)
|
||||
VAR_XPORT_RETRY_TIME, DEF_XPORT_RETRY_TIME, &var_transport_retry_time, 1, 0,
|
||||
VAR_QMGR_CLOG_WARN_TIME, DEF_QMGR_CLOG_WARN_TIME, &var_qmgr_clog_warn_time, 0, 0,
|
||||
VAR_XPORT_REFILL_DELAY, DEF_XPORT_REFILL_DELAY, &var_xport_refill_delay, 1, 0,
|
||||
VAR_DEST_RATE_DELAY, DEF_DEST_RATE_DELAY, &var_dest_rate_delay, 0, 0,
|
||||
0,
|
||||
};
|
||||
static CONFIG_INT_TABLE int_table[] = {
|
||||
|
@ -202,6 +202,7 @@ struct QMGR_TRANSPORT {
|
||||
QMGR_FEEDBACK pos_feedback; /* positive feedback control */
|
||||
QMGR_FEEDBACK neg_feedback; /* negative feedback control */
|
||||
int fail_cohort_limit; /* flow shutdown control */
|
||||
int rate_delay; /* suspend per delivery */
|
||||
};
|
||||
|
||||
#define QMGR_TRANSPORT_STAT_DEAD (1<<1)
|
||||
@ -258,8 +259,36 @@ 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_suspend(QMGR_QUEUE *, int);
|
||||
|
||||
#define QMGR_QUEUE_THROTTLED(q) ((q)->window <= 0)
|
||||
/*
|
||||
* Exclusive queue states. Originally there were only two: "throttled" and
|
||||
* "not throttled". It was natural to encode these in the queue window size.
|
||||
* After 10 years it's not practical to rip out all the working code and
|
||||
* change representations, so we just clean up the names a little.
|
||||
*
|
||||
* Note: only the "ready" state can reach every state (including itself);
|
||||
* non-ready states can reach only the "ready" state. Other transitions are
|
||||
* forbidden, because they would result in dangling event handlers.
|
||||
*/
|
||||
#define QMGR_QUEUE_STAT_THROTTLED 0 /* back-off timer */
|
||||
#define QMGR_QUEUE_STAT_SUSPENDED -1 /* voluntary delay timer */
|
||||
#define QMGR_QUEUE_STAT_SAVED -2 /* delayed cleanup timer */
|
||||
#define QMGR_QUEUE_STAT_BAD -3 /* can't happen */
|
||||
|
||||
#define QMGR_QUEUE_READY(q) ((q)->window > 0)
|
||||
#define QMGR_QUEUE_THROTTLED(q) ((q)->window == QMGR_QUEUE_STAT_THROTTLED)
|
||||
#define QMGR_QUEUE_SUSPENDED(q) ((q)->window == QMGR_QUEUE_STAT_SUSPENDED)
|
||||
#define QMGR_QUEUE_SAVED(q) ((q)->window == QMGR_QUEUE_STAT_SAVED)
|
||||
#define QMGR_QUEUE_BAD(q) ((q)->window <= QMGR_QUEUE_STAT_BAD)
|
||||
|
||||
#define QMGR_QUEUE_STATUS(q) ( \
|
||||
QMGR_QUEUE_READY(q) ? "ready" : \
|
||||
QMGR_QUEUE_THROTTLED(q) ? "throttled" : \
|
||||
QMGR_QUEUE_SUSPENDED(q) ? "suspended" : \
|
||||
QMGR_QUEUE_SAVED(q) ? "saved" : \
|
||||
"invalid queue status" \
|
||||
)
|
||||
|
||||
/*
|
||||
* Structure of one next-hop queue entry. In order to save some copying
|
||||
|
@ -317,9 +317,9 @@ static void qmgr_deliver_update(int unused_event, char *context)
|
||||
if (VSTRING_LEN(dsb->reason) == 0)
|
||||
vstring_strcpy(dsb->reason, "unknown error");
|
||||
vstring_prepend(dsb->reason, SUSPENDED, sizeof(SUSPENDED) - 1);
|
||||
if (queue->window > 0) {
|
||||
if (QMGR_QUEUE_READY(queue)) {
|
||||
qmgr_queue_throttle(queue, DSN_FROM_DSN_BUF(dsb));
|
||||
if (queue->window == 0)
|
||||
if (QMGR_QUEUE_THROTTLED(queue))
|
||||
qmgr_defer_todo(queue, &dsb->dsn);
|
||||
}
|
||||
}
|
||||
|
@ -97,11 +97,11 @@ void qmgr_enable_transport(QMGR_TRANSPORT *transport)
|
||||
|
||||
void qmgr_enable_queue(QMGR_QUEUE *queue)
|
||||
{
|
||||
if (queue->window == 0) {
|
||||
if (QMGR_QUEUE_THROTTLED(queue)) {
|
||||
if (msg_verbose)
|
||||
msg_info("enable site %s/%s", queue->transport->name, queue->name);
|
||||
qmgr_queue_unthrottle(queue);
|
||||
}
|
||||
if (queue->todo.next == 0 && queue->busy.next == 0)
|
||||
if (QMGR_QUEUE_READY(queue) && queue->todo.next == 0 && queue->busy.next == 0)
|
||||
qmgr_queue_done(queue);
|
||||
}
|
||||
|
@ -186,10 +186,10 @@ void qmgr_entry_unselect(QMGR_ENTRY *entry)
|
||||
QMGR_QUEUE *queue = entry->queue;
|
||||
|
||||
/*
|
||||
* Move the entry back to the todo lists. In case of the peer list,
|
||||
* put it back to the beginning, so the select()/unselect() does
|
||||
* not reorder entries. We use this in qmgr_message_assign()
|
||||
* to put recipients into existing entries when possible.
|
||||
* Move the entry back to the todo lists. In case of the peer list, put
|
||||
* it back to the beginning, so the select()/unselect() does not reorder
|
||||
* entries. We use this in qmgr_message_assign() to put recipients into
|
||||
* existing entries when possible.
|
||||
*/
|
||||
QMGR_LIST_UNLINK(queue->busy, QMGR_ENTRY *, entry, queue_peers);
|
||||
queue->busy_refcount--;
|
||||
@ -249,6 +249,7 @@ void qmgr_entry_move_todo(QMGR_QUEUE *dst_queue, QMGR_ENTRY *entry)
|
||||
|
||||
void qmgr_entry_done(QMGR_ENTRY *entry, int which)
|
||||
{
|
||||
const char *myname = "qmgr_entry_done";
|
||||
QMGR_QUEUE *queue = entry->queue;
|
||||
QMGR_MESSAGE *message = entry->message;
|
||||
QMGR_PEER *peer = entry->peer;
|
||||
@ -259,7 +260,7 @@ void qmgr_entry_done(QMGR_ENTRY *entry, int which)
|
||||
* Take this entry off the in-core queue.
|
||||
*/
|
||||
if (entry->stream != 0)
|
||||
msg_panic("qmgr_entry_done: file is open");
|
||||
msg_panic("%s: file is open", myname);
|
||||
if (which == QMGR_QUEUE_BUSY) {
|
||||
QMGR_LIST_UNLINK(queue->busy, QMGR_ENTRY *, entry, queue_peers);
|
||||
queue->busy_refcount--;
|
||||
@ -269,7 +270,7 @@ void qmgr_entry_done(QMGR_ENTRY *entry, int which)
|
||||
QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry, queue_peers);
|
||||
queue->todo_refcount--;
|
||||
} else {
|
||||
msg_panic("qmgr_entry_done: bad queue spec: %d", which);
|
||||
msg_panic("%s: bad queue spec: %d", myname, which);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -319,7 +320,7 @@ void qmgr_entry_done(QMGR_ENTRY *entry, int which)
|
||||
transport->job_current = transport->job_list.next;
|
||||
transport->candidate_cache_current = 0;
|
||||
}
|
||||
if (queue->window > queue->busy_refcount || queue->window == 0)
|
||||
if (queue->window > queue->busy_refcount || QMGR_QUEUE_THROTTLED(queue))
|
||||
queue->blocker_tag = 0;
|
||||
}
|
||||
|
||||
@ -334,18 +335,32 @@ void qmgr_entry_done(QMGR_ENTRY *entry, int which)
|
||||
/*
|
||||
* Maintain back-to-back delivery status.
|
||||
*/
|
||||
if (which == QMGR_QUEUE_BUSY)
|
||||
queue->last_done = event_time();
|
||||
|
||||
/*
|
||||
* Suspend a rate-limited queue, so that mail trickles out.
|
||||
*/
|
||||
if (which == QMGR_QUEUE_BUSY && transport->rate_delay > 0) {
|
||||
if (queue->window > 1)
|
||||
msg_panic("%s: queue %s/%s: window %d > 1 on rate-limited service",
|
||||
myname, transport->name, queue->name, queue->window);
|
||||
if (QMGR_QUEUE_THROTTLED(queue)) /* XXX */
|
||||
qmgr_queue_unthrottle(queue);
|
||||
if (QMGR_QUEUE_READY(queue))
|
||||
qmgr_queue_suspend(queue, transport->rate_delay);
|
||||
}
|
||||
|
||||
/*
|
||||
* When the in-core queue for this site is empty and when this site is
|
||||
* not dead, discard the in-core queue. When this site is dead, but the
|
||||
* number of in-core queues exceeds some threshold, get rid of this
|
||||
* in-core queue anyway, in order to avoid running out of memory.
|
||||
* not dead or suspended, discard the in-core queue. When this site is
|
||||
* dead, but the number of in-core queues exceeds some threshold, get rid
|
||||
* of this in-core queue anyway, in order to avoid running out of memory.
|
||||
*/
|
||||
if (queue->todo.next == 0 && queue->busy.next == 0) {
|
||||
if (queue->window == 0 && qmgr_queue_count > 2 * var_qmgr_rcpt_limit)
|
||||
if (QMGR_QUEUE_THROTTLED(queue) && qmgr_queue_count > 2 * var_qmgr_rcpt_limit)
|
||||
qmgr_queue_unthrottle(queue);
|
||||
if (queue->window > 0)
|
||||
if (QMGR_QUEUE_READY(queue))
|
||||
qmgr_queue_done(queue);
|
||||
}
|
||||
|
||||
@ -368,7 +383,7 @@ QMGR_ENTRY *qmgr_entry_create(QMGR_PEER *peer, QMGR_MESSAGE *message)
|
||||
/*
|
||||
* Sanity check.
|
||||
*/
|
||||
if (queue->window == 0)
|
||||
if (QMGR_QUEUE_THROTTLED(queue))
|
||||
msg_panic("qmgr_entry_create: dead queue: %s", queue->name);
|
||||
|
||||
/*
|
||||
|
@ -26,6 +26,10 @@
|
||||
/*
|
||||
/* void qmgr_queue_unthrottle(queue)
|
||||
/* QMGR_QUEUE *queue;
|
||||
/*
|
||||
/* void qmgr_queue_suspend(queue, delay)
|
||||
/* QMGR_QUEUE *queue;
|
||||
/* int delay;
|
||||
/* DESCRIPTION
|
||||
/* These routines add/delete/manipulate per-destination queues.
|
||||
/* Each queue corresponds to a specific transport and destination.
|
||||
@ -60,6 +64,9 @@
|
||||
/* provided that it does not exceed the destination concurrency
|
||||
/* limit specified for the transport. This routine implements
|
||||
/* "slow open" mode, and eliminates the "thundering herd" problem.
|
||||
/*
|
||||
/* qmgr_queue_suspend() suspends delivery for this destination
|
||||
/* briefly.
|
||||
/* DIAGNOSTICS
|
||||
/* Panic: consistency check failure.
|
||||
/* LICENSE
|
||||
@ -120,6 +127,53 @@ int qmgr_queue_count;
|
||||
myname, queue->name, queue->transport->dest_concurrency_limit, \
|
||||
queue->window, queue->success, queue->failure, queue->fail_cohorts);
|
||||
|
||||
/* qmgr_queue_resume - resume delivery to destination */
|
||||
|
||||
static void qmgr_queue_resume(int event, char *context)
|
||||
{
|
||||
QMGR_QUEUE *queue = (QMGR_QUEUE *) context;
|
||||
const char *myname = "qmgr_queue_resume";
|
||||
|
||||
/*
|
||||
* Sanity checks.
|
||||
*/
|
||||
if (!QMGR_QUEUE_SUSPENDED(queue))
|
||||
msg_panic("%s: bad queue status: %s", myname, QMGR_QUEUE_STATUS(queue));
|
||||
|
||||
/*
|
||||
* We can't simply force delivery on this queue: the transport's pending
|
||||
* count may already be maxed out, and there may be other constraints
|
||||
* that definitely should be none of our business. The best we can do is
|
||||
* to play by the same rules as everyone else: trigger *some* delivery
|
||||
* via qmgr_active_drain() and let round-robin selection work for us.
|
||||
*/
|
||||
queue->window = 1;
|
||||
if (queue->todo_refcount > 0)
|
||||
qmgr_active_drain();
|
||||
}
|
||||
|
||||
/* qmgr_queue_suspend - briefly suspend a destination */
|
||||
|
||||
void qmgr_queue_suspend(QMGR_QUEUE *queue, int delay)
|
||||
{
|
||||
const char *myname = "qmgr_queue_suspend";
|
||||
|
||||
/*
|
||||
* Sanity checks.
|
||||
*/
|
||||
if (!QMGR_QUEUE_READY(queue))
|
||||
msg_panic("%s: bad queue status: %s", myname, QMGR_QUEUE_STATUS(queue));
|
||||
if (queue->busy_refcount > 0)
|
||||
msg_panic("%s: queue is busy", myname);
|
||||
|
||||
/*
|
||||
* Set the queue status to "suspended". No-one is supposed to remove a
|
||||
* queue in suspended state.
|
||||
*/
|
||||
queue->window = QMGR_QUEUE_STAT_SUSPENDED;
|
||||
event_request_timer(qmgr_queue_resume, (char *) queue, delay);
|
||||
}
|
||||
|
||||
/* qmgr_queue_unthrottle_wrapper - in case (char *) != (struct *) */
|
||||
|
||||
static void qmgr_queue_unthrottle_wrapper(int unused_event, char *context)
|
||||
@ -132,7 +186,7 @@ static void qmgr_queue_unthrottle_wrapper(int unused_event, char *context)
|
||||
* this in-core queue when it is empty and when this site is not dead.
|
||||
*/
|
||||
qmgr_queue_unthrottle(queue);
|
||||
if (queue->window > 0 && queue->todo.next == 0 && queue->busy.next == 0)
|
||||
if (QMGR_QUEUE_READY(queue) && queue->todo.next == 0 && queue->busy.next == 0)
|
||||
qmgr_queue_done(queue);
|
||||
}
|
||||
|
||||
@ -147,6 +201,12 @@ void qmgr_queue_unthrottle(QMGR_QUEUE *queue)
|
||||
if (msg_verbose)
|
||||
msg_info("%s: queue %s", myname, queue->name);
|
||||
|
||||
/*
|
||||
* Sanity checks.
|
||||
*/
|
||||
if (!QMGR_QUEUE_READY(queue) && !QMGR_QUEUE_THROTTLED(queue))
|
||||
msg_panic("%s: bad queue status: %s", myname, QMGR_QUEUE_STATUS(queue));
|
||||
|
||||
/*
|
||||
* Don't restart the negative feedback hysteresis cycle with every
|
||||
* positive feedback. Restart it only when we make a positive concurrency
|
||||
@ -159,7 +219,7 @@ void qmgr_queue_unthrottle(QMGR_QUEUE *queue)
|
||||
/*
|
||||
* Special case when this site was dead.
|
||||
*/
|
||||
if (queue->window == 0) {
|
||||
if (QMGR_QUEUE_THROTTLED(queue)) {
|
||||
event_cancel_timer(qmgr_queue_unthrottle_wrapper, (char *) queue);
|
||||
if (queue->dsn == 0)
|
||||
msg_panic("%s: queue %s: window 0 status 0", myname, queue->name);
|
||||
@ -221,6 +281,8 @@ void qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
|
||||
/*
|
||||
* Sanity checks.
|
||||
*/
|
||||
if (!QMGR_QUEUE_READY(queue))
|
||||
msg_panic("%s: bad queue status: %s", myname, QMGR_QUEUE_STATUS(queue));
|
||||
if (queue->dsn)
|
||||
msg_panic("%s: queue %s: spurious reason %s",
|
||||
myname, queue->name, queue->dsn->reason);
|
||||
@ -240,7 +302,7 @@ void qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
|
||||
* This queue is declared dead after a configurable number of
|
||||
* pseudo-cohort failures.
|
||||
*/
|
||||
if (queue->window > 0) {
|
||||
if (QMGR_QUEUE_READY(queue)) {
|
||||
queue->fail_cohorts += 1.0 / queue->window;
|
||||
if (transport->fail_cohort_limit > 0
|
||||
&& queue->fail_cohorts >= transport->fail_cohort_limit)
|
||||
@ -256,7 +318,7 @@ void qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
|
||||
* Even after reaching 1, we maintain the negative hysteresis cycle so that
|
||||
* negative feedback can cancel out positive feedback.
|
||||
*/
|
||||
if (queue->window > 0) {
|
||||
if (QMGR_QUEUE_READY(queue)) {
|
||||
feedback = QMGR_FEEDBACK_VAL(transport->neg_feedback, queue->window);
|
||||
QMGR_LOG_FEEDBACK(feedback);
|
||||
queue->failure -= feedback;
|
||||
@ -274,7 +336,7 @@ void qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
|
||||
/*
|
||||
* Special case for a site that just was declared dead.
|
||||
*/
|
||||
if (queue->window == 0) {
|
||||
if (QMGR_QUEUE_THROTTLED(queue)) {
|
||||
queue->dsn = DSN_COPY(dsn);
|
||||
event_request_timer(qmgr_queue_unthrottle_wrapper,
|
||||
(char *) queue, var_min_backoff_time);
|
||||
@ -299,8 +361,8 @@ void qmgr_queue_done(QMGR_QUEUE *queue)
|
||||
queue->busy_refcount + queue->todo_refcount);
|
||||
if (queue->todo.next || queue->busy.next)
|
||||
msg_panic("%s: queue not empty: %s", myname, queue->name);
|
||||
if (queue->window <= 0)
|
||||
msg_panic("%s: window %d", myname, queue->window);
|
||||
if (!QMGR_QUEUE_READY(queue))
|
||||
msg_panic("%s: bad queue status: %s", myname, QMGR_QUEUE_STATUS(queue));
|
||||
if (queue->dsn)
|
||||
msg_panic("%s: queue %s: spurious reason %s",
|
||||
myname, queue->name, queue->dsn->reason);
|
||||
|
@ -389,7 +389,12 @@ QMGR_TRANSPORT *qmgr_transport_create(const char *name)
|
||||
transport->init_dest_concurrency =
|
||||
get_mail_conf_int2(name, _INIT_DEST_CON,
|
||||
var_init_dest_concurrency, 1, 0);
|
||||
transport->rate_delay = get_mail_conf_time2(name, _DEST_RATE_DELAY,
|
||||
var_dest_rate_delay,
|
||||
's', 0, 0);
|
||||
|
||||
if (transport->rate_delay > 0)
|
||||
transport->dest_concurrency_limit = 1;
|
||||
if (transport->dest_concurrency_limit != 0
|
||||
&& transport->dest_concurrency_limit < transport->init_dest_concurrency)
|
||||
transport->init_dest_concurrency = transport->dest_concurrency_limit;
|
||||
|
Loading…
x
Reference in New Issue
Block a user