Mark's backport of file preservation in the queue runners. Test and cleanup
[mailman.git] / mailman / queue / docs / switchboard.txt
blob741d435e11aa66bc24b472d0b253812f7da9bcd5
1 The switchboard
2 ===============
4 The switchboard is subsystem that moves messages between queues.  Each
5 instance of a switchboard is responsible for one queue directory.
7     >>> msg = message_from_string("""\
8     ... From: aperson@example.com
9     ... To: _xtest@example.com
10     ...
11     ... A test message.
12     ... """)
14 Create a switchboard by giving its queue directory.
16     >>> import os
17     >>> queue_directory = os.path.join(config.QUEUE_DIR, 'test')
18     >>> from mailman.queue import Switchboard
19     >>> switchboard = Switchboard(queue_directory)
20     >>> switchboard.queue_directory == queue_directory
21     True
23 Here's a helper function for ensuring things work correctly.
25     >>> def check_qfiles(directory=None):
26     ...     if directory is None:
27     ...         directory = queue_directory
28     ...     files = {}
29     ...     for qfile in os.listdir(directory):
30     ...         root, ext = os.path.splitext(qfile)
31     ...         files[ext] = files.get(ext, 0) + 1
32     ...     return sorted(files.items())
35 Enqueing and dequeing
36 ---------------------
38 The message can be enqueued with metadata specified in the passed in
39 dictionary.
41     >>> filebase = switchboard.enqueue(msg)
42     >>> check_qfiles()
43     [('.pck', 1)]
45 To read the contents of a queue file, dequeue it.
47     >>> msg, msgdata = switchboard.dequeue(filebase)
48     >>> print msg.as_string()
49     From: aperson@example.com
50     To: _xtest@example.com
51     <BLANKLINE>
52     A test message.
53     <BLANKLINE>
54     >>> sorted(msgdata.items())
55     [('_parsemsg', False), ('received_time', ...), ('version', 3)]
56     >>> check_qfiles()
57     [('.bak', 1)]
59 To complete the dequeing process, removing all traces of the message file,
60 finish it (without preservation).
62     >>> switchboard.finish(filebase)
63     >>> check_qfiles()
64     []
66 When enqueing a file, you can provide additional metadata keys by using
67 keyword arguments.
69     >>> filebase = switchboard.enqueue(msg, {'foo': 1}, bar=2)
70     >>> msg, msgdata = switchboard.dequeue(filebase)
71     >>> switchboard.finish(filebase)
72     >>> sorted(msgdata.items())
73     [('_parsemsg', False),
74      ('bar', 2), ('foo', 1),
75      ('received_time', ...), ('version', 3)]
77 Keyword arguments override keys from the metadata dictionary.
79     >>> filebase = switchboard.enqueue(msg, {'foo': 1}, foo=2)
80     >>> msg, msgdata = switchboard.dequeue(filebase)
81     >>> switchboard.finish(filebase)
82     >>> sorted(msgdata.items())
83     [('_parsemsg', False),
84      ('foo', 2),
85      ('received_time', ...), ('version', 3)]
88 Iterating over files
89 --------------------
91 There are two ways to iterate over all the files in a switchboard's queue.
92 Normally, queue files end in .pck (for 'pickle') and the easiest way to
93 iterate over just these files is to use the .files attribute.
95     >>> filebase_1 = switchboard.enqueue(msg, foo=1)
96     >>> filebase_2 = switchboard.enqueue(msg, foo=2)
97     >>> filebase_3 = switchboard.enqueue(msg, foo=3)
98     >>> filebases = sorted((filebase_1, filebase_2, filebase_3))
99     >>> sorted(switchboard.files) == filebases
100     True
101     >>> check_qfiles()
102     [('.pck', 3)]
104 You can also use the .get_files() method if you want to iterate over all the
105 file bases for some other extension.
107     >>> for filebase in switchboard.get_files():
108     ...     msg, msgdata = switchboard.dequeue(filebase)
109     >>> bakfiles = sorted(switchboard.get_files('.bak'))
110     >>> bakfiles == filebases
111     True
112     >>> check_qfiles()
113     [('.bak', 3)]
114     >>> for filebase in switchboard.get_files('.bak'):
115     ...     switchboard.finish(filebase)
116     >>> check_qfiles()
117     []
120 Recovering files
121 ----------------
123 Calling .dequeue() without calling .finish() leaves .bak backup files in
124 place.  These can be recovered when the switchboard is instantiated.
126     >>> filebase_1 = switchboard.enqueue(msg, foo=1)
127     >>> filebase_2 = switchboard.enqueue(msg, foo=2)
128     >>> filebase_3 = switchboard.enqueue(msg, foo=3)
129     >>> for filebase in switchboard.files:
130     ...     msg, msgdata = switchboard.dequeue(filebase)
131     ...     # Don't call .finish()
132     >>> check_qfiles()
133     [('.bak', 3)]
134     >>> switchboard_2 = Switchboard(queue_directory, recover=True)
135     >>> check_qfiles()
136     [('.pck', 3)]
138 The files can be recovered explicitly.
140     >>> for filebase in switchboard.files:
141     ...     msg, msgdata = switchboard.dequeue(filebase)
142     ...     # Don't call .finish()
143     >>> check_qfiles()
144     [('.bak', 3)]
145     >>> switchboard.recover_backup_files()
146     >>> check_qfiles()
147     [('.pck', 3)]
149 But the files will only be recovered at most three times before they are
150 considered defective.  In order to prevent mail bombs and loops, once this
151 maximum is reached, the files will be preserved in the 'bad' queue.
153     >>> for filebase in switchboard.files:
154     ...     msg, msgdata = switchboard.dequeue(filebase)
155     ...     # Don't call .finish()
156     >>> check_qfiles()
157     [('.bak', 3)]
158     >>> switchboard.recover_backup_files()
159     >>> check_qfiles()
160     []
162     >>> bad = config.switchboards['bad']
163     >>> check_qfiles(bad.queue_directory)
164     [('.psv', 3)]
166 Clean up
168     >>> for file in os.listdir(bad.queue_directory):
169     ...     os.remove(os.path.join(bad.queue_directory, file))
170     >>> check_qfiles(bad.queue_directory)
171     []
174 Queue slices
175 ------------
177 XXX Add tests for queue slices.