diff options
Diffstat (limited to 'src/mailman/runners/docs/outgoing.txt')
| -rw-r--r-- | src/mailman/runners/docs/outgoing.txt | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/src/mailman/runners/docs/outgoing.txt b/src/mailman/runners/docs/outgoing.txt new file mode 100644 index 000000000..a69fa36c5 --- /dev/null +++ b/src/mailman/runners/docs/outgoing.txt @@ -0,0 +1,413 @@ +===================== +Outgoing queue runner +===================== + +The outgoing queue runner is the process that delivers messages to the +directly upstream SMTP server. It is this upstream SMTP server that performs +final delivery to the intended recipients. + +Messages that appear in the outgoing queue are processed individually through +a *delivery module*, essentially a pluggable interface for determining how the +recipient set will be batched, whether messages will be personalized and +VERP'd, etc. The outgoing runner doesn't itself support retrying but it can +move messages to the 'retry queue' for handling delivery failures. +:: + + >>> mlist = create_list('test@example.com') + + >>> from mailman.app.membership import add_member + >>> from mailman.interfaces.member import DeliveryMode + >>> add_member(mlist, 'aperson@example.com', 'Anne Person', + ... 'password', DeliveryMode.regular, 'en') + <Member: Anne Person <aperson@example.com> + on test@example.com as MemberRole.member> + >>> add_member(mlist, 'bperson@example.com', 'Bart Person', + ... 'password', DeliveryMode.regular, 'en') + <Member: Bart Person <bperson@example.com> + on test@example.com as MemberRole.member> + >>> add_member(mlist, 'cperson@example.com', 'Cris Person', + ... 'password', DeliveryMode.regular, 'en') + <Member: Cris Person <cperson@example.com> + on test@example.com as MemberRole.member> + +Normally, messages would show up in the outgoing queue after the message has +been processed by the rule set and pipeline. But we can simulate that here by +injecting a message directly into the outgoing queue. First though, we must +call the ``calculate-recipients`` handler so that the message metadata will be +populated with the list of addresses to deliver the message to. +:: + + >>> msg = message_from_string("""\ + ... From: aperson@example.com + ... To: test@example.com + ... Subject: My first post + ... Message-ID: <first> + ... + ... First post! + ... """) + + >>> msgdata = {} + >>> handler = config.handlers['calculate-recipients'] + >>> handler.process(mlist, msg, msgdata) + >>> outgoing_queue = config.switchboards['out'] + +The ``to-outgoing`` handler populates the message metadata with the +destination mailing list name. Simulate that here too. + + >>> ignore = outgoing_queue.enqueue( + ... msg, msgdata, + ... tolist=True, + ... listname=mlist.fqdn_listname) + +Running the outgoing queue runner processes the message, delivering it to the +upstream SMTP. + + >>> from mailman.runners.outgoing import OutgoingRunner + >>> from mailman.testing.helpers import make_testable_runner + >>> outgoing = make_testable_runner(OutgoingRunner, 'out') + >>> outgoing.run() + +Every recipient got the same copy of the message. +:: + + >>> messages = list(smtpd.messages) + >>> len(messages) + 1 + + >>> print messages[0].as_string() + From: aperson@example.com + To: test@example.com + Subject: My first post + Message-ID: <first> + X-Peer: ... + X-MailFrom: test-bounces@example.com + X-RcptTo: cperson@example.com, bperson@example.com, aperson@example.com + <BLANKLINE> + First post! + + +Personalization +=============== + +Mailman supports sending individual messages to each recipient by +personalizing delivery. This increases the bandwidth between Mailman and the +upstream mail server, and between the upstream mail server and the remote +recipient mail servers. The benefit is that personalization provides for a +much better user experience, because the messages can be tailored for each +recipient. + + >>> from mailman.interfaces.mailinglist import Personalization + >>> mlist.personalize = Personalization.individual + >>> transaction.commit() + +Now when we send the message, our mail server will get three copies instead of +just one. + + >>> ignore = outgoing_queue.enqueue( + ... msg, msgdata, + ... listname=mlist.fqdn_listname) + >>> outgoing.run() + >>> messages = list(smtpd.messages) + >>> len(messages) + 3 + +Since we've done no other configuration, the only difference in the messages +is the recipient address. Specifically, the Sender header is the same for all +recipients. +:: + + >>> from operator import itemgetter + >>> def show_headers(messages): + ... for message in sorted(messages, key=itemgetter('x-rcptto')): + ... print message['X-RcptTo'], message['X-MailFrom'] + + >>> show_headers(messages) + aperson@example.com test-bounces@example.com + bperson@example.com test-bounces@example.com + cperson@example.com test-bounces@example.com + + +VERP +==== + +An even more interesting personalization opportunity arises if VERP_ is +enabled. Here, Mailman takes advantage of the fact that it's sending +individualized messages anyway, so it also encodes the recipients address in +the Sender header. + +.. _VERP: ../../mta/docs/verp.html + + +Forcing VERP +------------ + +A handler can force VERP by setting the ``verp`` key in the message metadata. +:: + + >>> ignore = outgoing_queue.enqueue( + ... msg, msgdata, + ... verp=True, + ... listname=mlist.fqdn_listname) + >>> outgoing.run() + >>> messages = list(smtpd.messages) + >>> len(messages) + 3 + + >>> show_headers(messages) + aperson@example.com test-bounces+aperson=example.com@example.com + bperson@example.com test-bounces+bperson=example.com@example.com + cperson@example.com test-bounces+cperson=example.com@example.com + + +VERP personalized deliveries +---------------------------- + +The site administrator can enable VERP whenever messages are personalized. + + >>> config.push('verp', """ + ... [mta] + ... verp_personalized_deliveries: yes + ... """) + +Again, we get three individual messages, with VERP'd ``Sender`` headers. +:: + + >>> ignore = outgoing_queue.enqueue( + ... msg, msgdata, + ... listname=mlist.fqdn_listname) + >>> outgoing.run() + >>> messages = list(smtpd.messages) + >>> len(messages) + 3 + + >>> show_headers(messages) + aperson@example.com test-bounces+aperson=example.com@example.com + bperson@example.com test-bounces+bperson=example.com@example.com + cperson@example.com test-bounces+cperson=example.com@example.com + + >>> config.pop('verp') + >>> mlist.personalize = Personalization.none + >>> transaction.commit() + + +VERP every once in a while +-------------------------- + +Perhaps personalization is too much of an overhead, but the list owners would +still like to occasionally get the benefits of VERP. The site administrator +can enable occasional VERPing of messages every so often, by setting a +delivery interval. Every N non-personalized deliveries turns on VERP for just +the next one. +:: + + >>> config.push('verp occasionally', """ + ... [mta] + ... verp_delivery_interval: 3 + ... """) + + # Reset the list's post_id, which is used to calculate the intervals. + >>> mlist.post_id = 1 + >>> transaction.commit() + +The first message is sent to the list, and it is neither personalized nor +VERP'd. +:: + + >>> ignore = outgoing_queue.enqueue( + ... msg, msgdata, + ... listname=mlist.fqdn_listname) + >>> outgoing.run() + >>> messages = list(smtpd.messages) + >>> len(messages) + 1 + + >>> show_headers(messages) + cperson@example.com, bperson@example.com, aperson@example.com + test-bounces@example.com + + # Perform post-delivery bookkeeping. + >>> after = config.handlers['after-delivery'] + >>> after.process(mlist, msg, msgdata) + >>> transaction.commit() + +The second message sent to the list is also not VERP'd. +:: + + >>> ignore = outgoing_queue.enqueue( + ... msg, msgdata, + ... listname=mlist.fqdn_listname) + >>> outgoing.run() + >>> messages = list(smtpd.messages) + >>> len(messages) + 1 + + >>> show_headers(messages) + cperson@example.com, bperson@example.com, aperson@example.com + test-bounces@example.com + + # Perform post-delivery bookkeeping. + >>> after.process(mlist, msg, msgdata) + >>> transaction.commit() + +The third message though is VERP'd. +:: + + >>> ignore = outgoing_queue.enqueue( + ... msg, msgdata, + ... listname=mlist.fqdn_listname) + >>> outgoing.run() + >>> messages = list(smtpd.messages) + >>> len(messages) + 3 + + >>> show_headers(messages) + aperson@example.com test-bounces+aperson=example.com@example.com + bperson@example.com test-bounces+bperson=example.com@example.com + cperson@example.com test-bounces+cperson=example.com@example.com + + # Perform post-delivery bookkeeping. + >>> after.process(mlist, msg, msgdata) + >>> transaction.commit() + +The next one is back to bulk delivery. +:: + + >>> ignore = outgoing_queue.enqueue( + ... msg, msgdata, + ... listname=mlist.fqdn_listname) + >>> outgoing.run() + >>> messages = list(smtpd.messages) + >>> len(messages) + 1 + + >>> show_headers(messages) + cperson@example.com, bperson@example.com, aperson@example.com + test-bounces@example.com + + >>> config.pop('verp occasionally') + + +VERP every time +--------------- + +If the site administrator wants to enable VERP for every delivery, even if no +personalization is going on, they can set the interval to 1. +:: + + >>> config.push('always verp', """ + ... [mta] + ... verp_delivery_interval: 1 + ... """) + + # Reset the list's post_id, which is used to calculate the intervals. + >>> mlist.post_id = 1 + >>> transaction.commit() + +The first message is VERP'd. +:: + + >>> ignore = outgoing_queue.enqueue( + ... msg, msgdata, + ... listname=mlist.fqdn_listname) + >>> outgoing.run() + >>> messages = list(smtpd.messages) + >>> len(messages) + 3 + + >>> show_headers(messages) + aperson@example.com test-bounces+aperson=example.com@example.com + bperson@example.com test-bounces+bperson=example.com@example.com + cperson@example.com test-bounces+cperson=example.com@example.com + + # Perform post-delivery bookkeeping. + >>> after.process(mlist, msg, msgdata) + >>> transaction.commit() + +As is the second message. +:: + + >>> ignore = outgoing_queue.enqueue( + ... msg, msgdata, + ... listname=mlist.fqdn_listname) + >>> outgoing.run() + >>> messages = list(smtpd.messages) + >>> len(messages) + 3 + + >>> show_headers(messages) + aperson@example.com test-bounces+aperson=example.com@example.com + bperson@example.com test-bounces+bperson=example.com@example.com + cperson@example.com test-bounces+cperson=example.com@example.com + + # Perform post-delivery bookkeeping. + >>> after.process(mlist, msg, msgdata) + >>> transaction.commit() + +And the third message. +:: + + >>> ignore = outgoing_queue.enqueue( + ... msg, msgdata, + ... listname=mlist.fqdn_listname) + >>> outgoing.run() + >>> messages = list(smtpd.messages) + >>> len(messages) + 3 + + >>> show_headers(messages) + aperson@example.com test-bounces+aperson=example.com@example.com + bperson@example.com test-bounces+bperson=example.com@example.com + cperson@example.com test-bounces+cperson=example.com@example.com + + # Perform post-delivery bookkeeping. + >>> after.process(mlist, msg, msgdata) + >>> transaction.commit() + + >>> config.pop('always verp') + + +Never VERP +---------- + +Similarly, the site administrator can disable occasional VERP'ing of +non-personalized messages by setting the interval to zero. +:: + + >>> config.push('never verp', """ + ... [mta] + ... verp_delivery_interval: 0 + ... """) + + # Reset the list's post_id, which is used to calculate the intervals. + >>> mlist.post_id = 1 + >>> transaction.commit() + +Neither the first message... +:: + + >>> ignore = outgoing_queue.enqueue( + ... msg, msgdata, + ... listname=mlist.fqdn_listname) + >>> outgoing.run() + >>> messages = list(smtpd.messages) + >>> len(messages) + 1 + + >>> show_headers(messages) + cperson@example.com, bperson@example.com, aperson@example.com + test-bounces@example.com + +...nor the second message is VERP'd. +:: + + >>> ignore = outgoing_queue.enqueue( + ... msg, msgdata, + ... listname=mlist.fqdn_listname) + >>> outgoing.run() + >>> messages = list(smtpd.messages) + >>> len(messages) + 1 + + >>> show_headers(messages) + cperson@example.com, bperson@example.com, aperson@example.com + test-bounces@example.com |
