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 -- mailman-checkins@python.org To unsubscribe send an email to mailman-checkins-le...@python.org https://mail.python.org/mailman3/lists/mailman-checkins.python.org/ Member address: arch...@jab.org