Repository navigation
[PERF] ni_service, ni_patient, ni_questionnaire: declare production indexes, stop stray Meeting activities - #177
Merged
Conversation
Deleting an ir.attachment checks every ON DELETE SET NULL foreign key that points at it. Without an index on message_main_attachment_id (ni.patient, res.partner) and ir.attachment.original_id, each check scans the whole table, which made a single delete take ~141 s on production. The btree_not_null field indexes generate the same names as the indexes already created by hand on production, so -u is a no-op there. Part of #174
Declare the indexes added by hand on production during query tuning, so new or rebuilt databases get them too: - calendar_attendee_event_id_index for attendee reads by event_id - calendar_event_id_filter_idx, a covering index for the calendar view search (~79 s -> ~1.5 s) - btree_not_null indexes on the attachment foreign keys of ni.service.event and calendar.event, used by ir.attachment deletes Names match production exactly: Odoo skips an index by name, so -u does not rebuild them on the multi-million-row tables. Part of #174
Creating a service event from the patient form runs with active_model = "ni.patient" and active_id = <patient> in the context. Through _inherits, calendar.event.default_get turns them into res_model/res_id, and calendar.event.create then adds a Meeting mail.activity on the patient, assigned to the creator. On production this grew mail_activity to millions of rows and left users with thousands of overdue activities, slowing activity searches and the systray. Clear active_model/active_id before calling super().create(); default_* keys from the action still apply. Part of #174
Deleting an ir.attachment checks survey_user_input's message_main_attachment_id foreign key, which scanned the whole table. The btree_not_null index matches the name already on production. Closes #174
piruin
force-pushed
the
16.0-ni_service-declare-production-indexes
branch
from
October 23, 2026 11:13
2f4c323 to
b76e413
Compare
Contributor
Author
|
Decision: index Overriding the field once on
If another table (for example |
This was referenced Oct 29, 2026
Closed
This was referenced Oct 29, 2026
Merged
Merged
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.
Closes #174
Stray Meeting activities (root cause of
mail_activitygrowth)When a service event is created from the patient form, the context carries
active_model = "ni.patient"andactive_id = <patient>.ni.service.eventuses_inheritsfromcalendar.event, socalendar.event.default_getturns those keys intores_model/res_id.calendar.event.createthen adds a Meetingmail.activityon the patient. That is howmail_activitygrew to millions of rows.ServiceEvent.create()now callssuper()withactive_model=False, active_id=False.default_*keys from the action still apply, andni.service.create_event()(default_res_model = "ni.service") is unaffected. Covered bytest_create_from_patient_form_adds_no_activity_on_patient, which fails without the fix.Indexes
Every name matches production exactly. Odoo skips an existing index by name, so
-uon production builds nothing.calendar_attendee_event_id_indexcalendar_attendee (event_id)ni_serviceevent_idfield override,index=Truecalendar_event_id_filter_idxcalendar_event (id) INCLUDE (active, start, stop, allday, start_date)ni_serviceinit()ni_service_event_message_main_attachment_id_indexWHERE ... IS NOT NULLni_serviceindex="btree_not_null"ni_service_event_displayed_image_id_indexWHERE ... IS NOT NULLni_serviceindex="btree_not_null"calendar_event_message_main_attachment_id_indexWHERE ... IS NOT NULLni_serviceindex="btree_not_null"ni_patient_message_main_attachment_id_indexWHERE ... IS NOT NULLni_patientindex="btree_not_null"res_partner_message_main_attachment_id_indexWHERE ... IS NOT NULLni_patientindex="btree_not_null"ir_attachment_original_id_indexWHERE ... IS NOT NULLni_patientindex="btree_not_null"survey_user_input_message_main_attachment_id_indexWHERE ... IS NOT NULLni_questionnaireindex="btree_not_null"mail_activity_user_id_date_deadline_id_idxandmail_activity_id_res_model_res_id_idxare intentionally not declared. The plan is to drop them on production after the activity cleanup.ir.attachment.original_idcomes fromweb_editor.ni_patientalready depends on it throughportal, so there's no new dependency.After deploy (ops, per database using
ni_service)ni.patientthat are linked to a calendar event. Match onres_model = 'ni.patient', activity type categorymeeting, andcalendar_event_id IS NOT NULL. Delete in batches at night, then runVACUUM (ANALYZE) mail_activity.mail_activity_*_idxindexes withDROP INDEX CONCURRENTLY.Verification
ni_service,ni_questionnairecreates all 9 indexes, and eachindexdefmatches the issue. Nomail_activity_*_idxindex is created.-u ni_patient,ni_service,ni_questionnaireon that database left every index OID unchanged, so nothing was rebuilt or duplicated./ni_patient,/ni_service,/ni_questionnaire: 0 failed, 0 errors of 80.EXPLAINof the query shapes. A small local database won't pick these indexes, so this has to be checked on production.Related: nirun-life/nirun-dop#70 (
create_uidindexes inni_community_care).