core: Ultraslow logbook queries when context ids have low cardinality
The problem
After upgrading to 2023.4.4 logbook database queries are terribly slow. The sensor history graphs are fast, but the logbooks needs minutes to appear. Queries like the following are running in the background. This one is quite fast, as it requires only 39 seconds, but some other entities logbooks appear only after waiting minutes.
I think many instances are affected with this problem:
- https://github.com/home-assistant/core/issues/91069#issuecomment-1501203131 (“I have only a loading spinner when I request the history of my devices” - MariaDB, not PostgreSQL)
- https://community.home-assistant.io/t/upgrade-to-2023-4-4-with-mariadb-no-more-history-slow-queries-on-db-is-it-the-migration/560627
WITH anon_1 AS
(SELECT anon_2.context_id_bin AS context_id_bin
FROM (SELECT events.context_id_bin AS context_id_bin
FROM events LEFT OUTER JOIN event_types ON events.event_type_id = event_types.event_type_id LEFT OUTER JOIN event_data ON events.data_id = event_data.data_id
WHERE events.time_fired_ts > 1681587500.67 AND events.time_fired_ts < 1681673900.677599 AND events.event_type_id IN (SELECT event_types.event_type_id
FROM event_types
WHERE event_types.event_type IN ('automation_triggered', 'logbook_entry', 'script_started')) AND ((CAST(event_data.shared_data AS JSONB) -> 'entity_id') IS NOT NULL AND CAST((CAST(event_data.shared_data AS JSONB) -> 'entity_id') AS TEXT) IN ('"person.xy"') OR (CAST(events.event_data AS JSONB) -> 'entity_id') IS NOT NULL AND CAST((CAST(events.event_data AS JSONB) -> 'entity_id') AS TEXT) IN ('"person.xy"')) UNION ALL SELECT states.context_id_bin AS context_id_bin
FROM states
WHERE states.last_updated_ts > 1681587500.67 AND states.last_updated_ts < 1681673900.677599 AND states.metadata_id IN (275)) AS anon_2 GROUP BY anon_2.context_id_bin)
SELECT events.event_id AS event_id, event_types.event_type AS event_type, events.event_data AS event_data, events.time_fired_ts AS time_fired_ts, events.context_id_bin AS context_id_bin, events.context_user_id_bin AS context_user_id_bin, events.context_parent_id_bin AS context_parent_id_bin, event_data.shared_data AS shared_data, 0 AS state_id, NULL AS state, NULL AS entity_id, NULL AS icon, NULL AS old_format_icon, NULL AS context_only
FROM events LEFT OUTER JOIN event_types ON events.event_type_id = event_types.event_type_id LEFT OUTER JOIN event_data ON events.data_id = event_data.data_id
WHERE events.time_fired_ts > 1681587500.67 AND events.time_fired_ts < 1681673900.677599 AND events.event_type_id IN (SELECT event_types.event_type_id
FROM event_types
WHERE event_types.event_type IN ('automation_triggered', 'logbook_entry', 'script_started')) AND ((CAST(event_data.shared_data AS JSONB) -> 'entity_id') IS NOT NULL AND CAST((CAST(event_data.shared_data AS JSONB) -> 'entity_id') AS TEXT) IN ('"person.xy"') OR (CAST(events.event_data AS JSONB) -> 'entity_id') IS NOT NULL AND CAST((CAST(events.event_data AS JSONB) -> 'entity_id') AS TEXT) IN ('"person.xy"')) UNION ALL SELECT NULL AS event_id, NULL AS event_type, NULL AS event_data, states.last_updated_ts AS time_fired_ts, states.context_id_bin AS context_id_bin, states.context_user_id_bin AS context_user_id_bin, states.context_parent_id_bin AS context_parent_id_bin, NULL AS shared_data, states.state_id AS state_id, states.state AS state, states_meta.entity_id AS entity_id, CAST(CAST(state_attributes.shared_attrs AS JSON) ->> 'icon' AS VARCHAR) AS icon, CAST(CAST(states.attributes AS JSON) ->> 'icon' AS VARCHAR) AS old_format_icon, NULL AS context_only
FROM states LEFT OUTER JOIN states AS old_state ON states.old_state_id = old_state.state_id LEFT OUTER JOIN state_attributes ON states.attributes_id = state_attributes.attributes_id LEFT OUTER JOIN states_meta ON states.metadata_id = states_meta.metadata_id
WHERE states.last_updated_ts > 1681587500.67 AND states.last_updated_ts < 1681673900.677599 AND old_state.state_id IS NOT NULL AND states.state != old_state.state AND states.state IS NOT NULL AND ((states_meta.entity_id NOT LIKE 'proximity.%' AND states_meta.entity_id NOT LIKE 'counter.%' AND states_meta.entity_id NOT LIKE 'sensor.%') OR ((states_meta.entity_id LIKE 'sensor.%') AND (state_attributes.shared_attrs NOT LIKE '%"unit_of_measurement":%' OR states.attributes NOT LIKE '%"unit_of_measurement":%'))) AND (states.last_updated_ts = states.last_changed_ts OR states.last_changed_ts IS NULL) AND states.metadata_id IN (275) UNION ALL SELECT events.event_id AS event_id, event_types.event_type AS event_type, events.event_data AS event_data, events.time_fired_ts AS time_fired_ts, events.context_id_bin AS context_id_bin, events.context_user_id_bin AS context_user_id_bin, events.context_parent_id_bin AS context_parent_id_bin, event_data.shared_data AS shared_data, 0 AS state_id, NULL AS state, NULL AS entity_id, NULL AS icon, NULL AS old_format_icon, '1' AS context_only
FROM anon_1 LEFT OUTER JOIN events ON anon_1.context_id_bin = events.context_id_bin LEFT OUTER JOIN event_types ON events.event_type_id = event_types.event_type_id LEFT OUTER JOIN event_data ON events.data_id = event_data.data_id UNION ALL SELECT NULL AS event_id, NULL AS event_type, NULL AS event_data, states.last_updated_ts AS time_fired_ts, states.context_id_bin AS context_id_bin, states.context_user_id_bin AS context_user_id_bin, states.context_parent_id_bin AS context_parent_id_bin, NULL AS shared_data, states.state_id AS state_id, states.state AS state, states_meta.entity_id AS entity_id, NULL AS icon, NULL AS old_format_icon, '1' AS context_only
FROM anon_1 LEFT OUTER JOIN states ON anon_1.context_id_bin = states.context_id_bin LEFT OUTER JOIN states_meta ON states.metadata_id = states_meta.metadata_id ORDER BY time_fired_ts;
What version of Home Assistant Core has the issue?
2023.4.4
What was the last working version of Home Assistant Core?
2021.x.x
What type of installation are you running?
Home Assistant Core
Integration causing the issue
No response
Link to integration documentation on our website
No response
Diagnostics information
PostgreSQL 15. The database migration part of the upgrade is over. The database is big (6 years of data), but was fast with version 2021.x.x
Example YAML snippet
No response
Anything in the logs that might be useful for us?
No response
Additional information
No response
About this issue
- Original URL
- State: closed
- Created a year ago
- Reactions: 1
- Comments: 61 (30 by maintainers)
Commits related to this issue
- Use cache to lookup event type ids in logbook noticed we can speed this up while looking at https://github.com/home-assistant/core/issues/91514 Note: this will not fix that issue as there is more go... — committed to home-assistant/core by bdraco a year ago
- Use cache to lookup event type ids in logbook (#91576) noticed we can speed this up while looking at https://github.com/home-assistant/core/issues/91514 Note: this will not fix that issue as ther... — committed to home-assistant/core by bdraco a year ago
- Use cache to lookup event type ids in logbook (#91576) noticed we can speed this up while looking at https://github.com/home-assistant/core/issues/91514 Note: this will not fix that issue as ther... — committed to teharris1/home-assistant by bdraco a year ago
- Optimize logbook context queries for postgresql fixes #91514 — committed to home-assistant/core by bdraco a year ago
- Handle long format context UUIDs during migration In https://github.com/home-assistant/core/issues/91514 is was discovered these exist in older versions — committed to home-assistant/core by bdraco a year ago
- Handle long format context UUIDs during migration (#91657) In https://github.com/home-assistant/core/issues/91514 is was discovered these exist in older versions — committed to home-assistant/core by bdraco a year ago
- Fallback to generating a new ULID on migraiton if context is missing or invalid It was discovered that postgresql will do a full scan if there is a low cardinality on the index because of missing con... — committed to home-assistant/core by bdraco a year ago
- Fallback to generating a new ULID on migraiton if context is missing or invalid (#91704) * Fallback to generating a new ULID on migraiton if context is missing or invalid It was discovered that po... — committed to home-assistant/core by bdraco a year ago
- Handle long format context UUIDs during migration (#91657) In https://github.com/home-assistant/core/issues/91514 is was discovered these exist in older versions — committed to home-assistant/core by bdraco a year ago
- Fallback to generating a new ULID on migraiton if context is missing or invalid (#91704) * Fallback to generating a new ULID on migraiton if context is missing or invalid It was discovered that po... — committed to home-assistant/core by bdraco a year ago
They link events together to find the trigger in the logbook. The random data is likely just fine in the db. I’ll adjust the migration so we create a fresh ULID instead of empty bytes so others don’t have the district distribution issue if they are migrating for large data sets.
Its what is used for figuring out that the switch you pressed in HA turned on the light. They have decreasing value over time as its unlikely you need to know which user turned on a light 3 years ago 😃
Almost midnight here so I’ll check back in the morning. Good luck
Disconnect the DB from HA by changing configuration.yaml Downgrade HA to 2023.3.x Restore your database from backup when it was in 2023.3.x format Fix any schema problems Restart HA with it pointing to your db in your configuration.yaml Upgrade to 2023.4.5
after all the
metadata_idare set on the states table it will go back and clear theentity_idfieldAll the entity_ids should be null now. entity_ids are stored in states_meta now.
Once you have the schema corrected if the query is still slow please post an explain on it and we will try to optimize it.
I think foreign keys are at least missing on my
eventstable toevent_typesandevent_datatables:Here are mine (PostgreSQL). As I see I have one more index (
ix_states_event_id) onstatesthan you have:I think the migration failed to create some indexes somewhere.
These are mine to compare :