Mark Sapiro pushed to branch master at GNU Mailman / Mailman Core
Commits:
5e77179e by Mark Sapiro at 2021-04-17T22:21:31-07:00
Handle attempt to get a message from the message store with a missing file.
- - - - -
d711ba42 by Mark Sapiro at 2021-04-18T05:43:43+00:00
Merge branch 'task' into 'master'
Handle attempt to get a message from the message store with a missing file.
Closes #877
See merge request mailman/mailman!836
- - - - -
4 changed files:
- src/mailman/docs/NEWS.rst
- src/mailman/model/messagestore.py
- src/mailman/model/tests/test_messagestore.py
- src/mailman/runners/task.py
Changes:
=====================================
src/mailman/docs/NEWS.rst
=====================================
@@ -36,6 +36,8 @@ Bugs
* Pending probe bounce tokens now have a lifetime of 10 days. (Closes #869)
* Improve the performance of ``/users`` API when paginating by doing the
pagination in database layer. (Closes #876)
+* Attempts to get a message from the message store with a missing file are
+ now handled. (Closes #877)
Command line
------------
=====================================
src/mailman/model/messagestore.py
=====================================
@@ -21,6 +21,7 @@ import os
import errno
import pickle
+from contextlib import suppress
from mailman.config import config
from mailman.database.transaction import dbconnection
from mailman.interfaces.messages import IMessageStore
@@ -90,10 +91,16 @@ class MessageStore:
makedirs(os.path.dirname(path))
return hash32
- def _get_message(self, row):
+ @dbconnection
+ def _get_message(self, store, row):
path = os.path.join(config.MESSAGES_DIR, row.path)
- with open(path, 'rb') as fp:
- return pickle.load(fp)
+ # The message file may have been externally removed.
+ with suppress(FileNotFoundError):
+ with open(path, 'rb') as fp:
+ return pickle.load(fp)
+ # The message is gone. Delete the entry and return None.
+ store.delete(row)
+ return None
@dbconnection
def get_message_by_id(self, store, message_id):
=====================================
src/mailman/model/tests/test_messagestore.py
=====================================
@@ -95,6 +95,25 @@ Message-ID: <ant>
self._store.delete_message('<ant>')
self.assertEqual(len(list(self._store.messages)), 0)
+ def test_get_message_missing_returns_none_and_deletes(self):
+ # Getting a message which is in the store, but which doesn't appear
+ # on the file system returns None and removes the message from the
+ # store.
+ msg = mfs("""\
+Message-ID: <ant>
+
+""")
+ self._store.add(msg)
+ self.assertEqual(len(list(self._store.messages)), 1)
+ # We have to use the SQLAlchemy API because the .get_message_by_*()
+ # methods return email Message objects, not IMessages. The former
+ # does not give us the path to the object on the file system.
+ row = config.db.store.query(Message).filter_by(
+ message_id='<ant>').first()
+ os.remove(os.path.join(config.MESSAGES_DIR, row.path))
+ self.assertIsNone(self._store.get_message_by_id('<ant>'))
+ self.assertEqual(len(list(self._store.messages)), 0)
+
def test_message_id_hash(self):
# The new specification calls for a Message-ID-Hash header,
# specifically without the X- prefix.
=====================================
src/mailman/runners/task.py
=====================================
@@ -100,10 +100,12 @@ class TaskRunner(Runner):
count = 0
messages = getUtility(IMessageStore)
for msg in messages.messages:
- mid = msg.get('message-id')
- if mid not in mids:
- messages.delete_message(mid)
- count += 1
+ # msg can be None if file is already removed.
+ if msg is not None:
+ mid = msg.get('message-id')
+ if mid not in mids:
+ messages.delete_message(mid)
+ count += 1
tlog.info('Task runner deleted %d orphaned messages', count)
def _evict_cache(self):
View it on GitLab:
https://gitlab.com/mailman/mailman/-/compare/b2c30ac2e9ed39274a7c9603d96ce28c50bab868...d711ba42c1d0dfb3aed48f923d69853275c652d2
--
View it on GitLab:
https://gitlab.com/mailman/mailman/-/compare/b2c30ac2e9ed39274a7c9603d96ce28c50bab868...d711ba42c1d0dfb3aed48f923d69853275c652d2
You're receiving this email because of your account on gitlab.com.
_______________________________________________
Mailman-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/mailman-checkins.python.org/
Member address: [email protected]