Repository navigation
[19.0][IMP] mail_tracking: index the messages that need action - #328
Merged
OCA-git-bot merged 1 commit intoNov 1, 2026
Merged
Conversation
pedrobaeza
reviewed
Oct 30, 2026
CLaurelB
reviewed
Oct 30, 2026
moylop260
force-pushed
the
19.0-mail_tracking-needs-action-index-moy
branch
from
October 30, 2026 14:54
f8d4527 to
fa42860
Compare
hugho-ad
approved these changes
Oct 30, 2026
CLaurelB
approved these changes
Oct 30, 2026
pedrobaeza
reviewed
Oct 31, 2026
moylop260
force-pushed
the
19.0-mail_tracking-needs-action-index-moy
branch
from
October 31, 2026 13:29
fa42860 to
91f347a
Compare
res.users._init_messaging calls mail.message.get_failed_count, so every web client load (/mail/data) searches is_failed_message. That search filters on mail_tracking_needs_action, which has no index. Only a handful of messages carry the flag, but PostgreSQL starts from the failed trackings and probes mail_message by primary key once per tracking. With 122M messages and 29k failed trackings the query took 152 s cold and 5 s warm. Add a partial index on the flagged messages. It is tiny (584 kB on that database), and a write only touches it when the flag changes. The index is built once, by the first update after this change. Later updates keep it, because Odoo only rebuilds an index whose definition differs from the one stored in its comment. On a very large table that build blocks writes (13 minutes on 122M messages), so INSTALL.md explains how to create it beforehand with CREATE INDEX CONCURRENTLY and the comment the ORM stores on it.
moylop260
force-pushed
the
19.0-mail_tracking-needs-action-index-moy
branch
from
October 31, 2026 13:32
91f347a to
8b718a7
Compare
Contributor
|
On my way to merge this fine PR! |
Contributor
|
Congratulations, your PR was merged at 175e491. Thanks a lot for contributing to OCA. ❤️ |
Contributor
Author
|
Gracias Pedro |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
res.users._init_messagingis called by the web client every time it loads its messaging data, andmail_trackingextends it to add the "Failed" mailbox counter:/mail/datawithinit_messagingcallsuser._init_messaging(store)for every logged-in user (backend included, it is the Discuss/chatter bootstrap).mail_tracking:_init_messagingcallsget_failed_count.get_failed_countis asearch_countonis_failed_message, whose domain filters onmail_tracking_needs_action = True(field defined here).So the query runs once per web client load, for every user.
mail_tracking_needs_actionis not indexed, and only a handful of messages carry it (it is set when a tracking gets a failed state and cleared byset_need_action_done). Without an index PostgreSQL starts from the failedmail.tracking.emailrows and probesmail_messageby primary key once per tracking, discarding almost all of them. The cost grows with the number of failed trackings, not with the number of flagged messages.Change
A partial index on the flagged messages, declared with
models.Index:index=Trueon the field would not help (a plain btree over a boolean that is almost always false), andbtree_not_nullkeeps every row because the default isFalse, notNULL. The partial index only contains the flagged rows, and a write only touches it when the flag changes.Measurements
Staging copy of a production database:
mail_message122M rows (53 GB),mail_tracking_emailwith 29,442 rows in a failed state, 25,772 flagged messages. SQL taken from the slow-query report of a test pipeline (the oneget_failed_countgenerates for a user with 4 failed messages), run withEXPLAIN (ANALYZE, BUFFERS)on a clone of that database, same server and settings before and after.mail_message, 4 rows keptCREATE INDEXmaintenance_work_mem2 GB, 2 parallel workers)In production, the same query was the top time consumer of the Playwright pass of that pipeline: 3 min 10 s over 37 executions, up to 38 s each.
Write cost: updates of 5,000 rows were within the noise of other work on the same server (with and without the index), so I cannot give a clean figure; by construction the index entry is only written when
mail_tracking_needs_actionchanges.Size
Sizes on the same staging copy, to show how light the index is next to the table it serves:
mail_messagetable (with TOAST)mail_messageindexes (all)mail_message_pkeymail_message__message_id_index(largest)mail_tracking_emailtable / indexesThe new index is about 0.02% of the primary key and about 0.001% of the table, because it only holds the 0.02% of the messages that are flagged.
Notes
mail_message, soINSTALL.mdexplains how to create it beforehand withCREATE INDEX CONCURRENTLY, and the same comment the ORM stores on the index, so the update keeps it._searchofmail.tracking.email(the access check) and does not change this plan.