Skip to content

Commit 9e35c6f

Browse files
committed
[IMP] autovacuum_message_attachment: search/unlink optimization
1 parent 676afde commit 9e35c6f

1 file changed

Lines changed: 49 additions & 18 deletions

File tree

autovacuum_message_attachment/models/autovacuum_mixin.py

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,36 @@
1010

1111
_logger = logging.getLogger(__name__)
1212

13+
# Number of records deleted per unlink() call. Bounds the id list, the SQL
14+
# DELETE size and the per-commit work, independently of how many records the
15+
# rule retrieves (rule.batch_size).
16+
UNLINK_BATCH_SIZE = 5000
17+
1318

1419
class AutovacuumMixin(models.AbstractModel):
1520
_name = "autovacuum.mixin"
1621
_description = "Mixin used to delete messages or attachments"
1722

18-
def batch_unlink(self):
23+
def batch_unlink(self, batch_size=0):
24+
# batch_size <= 0 => delete everything in a single unlink (no chunking).
1925
with Registry(self.env.cr.dbname).cursor() as new_cr:
26+
if batch_size == -1:
27+
batch_size = len(self)
28+
if not batch_size or batch_size < 0:
29+
batch_size = new_cr.IN_MAX
30+
elif batch_size > new_cr.IN_MAX:
31+
# Adapt cursor IN_MAX to batch_size if needed:
32+
# unlink silently chunk using IN_MAX (1000-rows)
33+
# Assigning on the instance shadows the class attribute
34+
# for this cursor only, not the class
35+
# (Cursor.IN_MAX stays 1000)
36+
new_cr.IN_MAX = batch_size
2037
new_env = api.Environment(new_cr, self.env.uid, self.env.context)
2138
try:
22-
while self:
23-
batch_delete = self[0:1000]
24-
self -= batch_delete
39+
remaining = self
40+
while remaining:
41+
batch_delete = remaining[0:batch_size]
42+
remaining -= batch_delete
2543
# do not attach new env to self because it may be
2644
# huge, and the cache is cleaned after each unlink
2745
# so we do not want to much record is the env in
@@ -38,7 +56,13 @@ def autovacuum(self, ttype="message"):
3856
rules = self.env["vacuum.rule"].search([("ttype", "=", ttype)])
3957
for rule in rules:
4058
records = rule._search_autovacuum_records()
41-
records.batch_unlink()
59+
_logger.info(
60+
"Autovacuum rule %s: %s %s record(s) matched, deleting...",
61+
rule.name,
62+
len(records),
63+
self._name,
64+
)
65+
records.batch_unlink(UNLINK_BATCH_SIZE)
4266

4367
def _get_autovacuum_domain(self, rule):
4468
return []
@@ -95,21 +119,28 @@ def _get_autovacuum_records_model(self, rule):
95119
self._prefix_domain_fields(autovacuum_relation, mixin_domain),
96120
]
97121
)
98-
records = self.env[rule.model_id.model].search(
99-
record_domain,
100-
# Thanks to the optimization with _prefix_domain_fields,
101-
# We now have at least 1 mixin-record per related-record
102-
# => We can also limit this search here
103-
# and this won't affect the final search
104-
limit=limit,
105-
)
122+
related_model = self.env[rule.model_id.model]
123+
# Optimizations
124+
# 1. We have at least 1 mixin-record per related-record
125+
# (see _prefix_domain_fields)
126+
# => we can limit this search and get a free optimization.
127+
# Reason: len(models) <= len(models mails)
128+
129+
# 2. The Query is injected directly into the domain as a SQL subquery
130+
# (res_id IN (SELECT ...)) instead of materialising ids in Python.
131+
# _search:
132+
# "No default order is applied when
133+
# the method is invoked without parameter ``order``."
134+
related_query = related_model._search(record_domain, limit=limit)
106135
mixin_domain = expression.AND(
107-
[mixin_domain, [("res_id", "in", records.ids)]]
136+
[mixin_domain, [("res_id", "in", related_query)]]
108137
)
109-
return self.search(
110-
mixin_domain,
111-
limit=limit,
112-
)
138+
# Use _search (not search): deletion order is irrelevant and _search
139+
# applies no ORDER BY. The model default (mail.message._order =
140+
# 'id desc') combined with LIMIT would force Postgres to sort the whole
141+
# matching set on every batch (~18s for the 15M stock.picking backlog);
142+
# without it the LIMIT short-circuits the index scan (~70ms).
143+
return self.browse(self._search(mixin_domain, limit=limit))
113144

114145
# Retro-compatibility
115146
_get_autovacuum_records = _get_autovacuum_records_model

0 commit comments

Comments
 (0)