Миграция MESSAGING версии < 3.10 на 3.10+
Внимание!
Процедура миграции обязательна для обновления с версии ниже 3.10. После миграции на 3.10+ откатывать messaging на предыдущие версии не рекомендуется! Возвращение на прошлые версии приведет к сложной процедуре ручного удаления новых данных и повторения миграции. В случае возникновения проблем при миграции, необходимо обратиться в службу технической поддержки для получения патча.
Примечание
Время простоя в момент миграции может достигать от 5 до 30 минут в зависимости от объема базы.
Определение размера таблиц
Для определения размера таблиц:
-
Войдите в веб-интерфейс администратора СУБД PostgreSQL и подключитесь к базе данных messaging_prod:
- если база данных реализована в контейнере Docker выполните команды:
docker exec -ti cts_postgres_1 psql -U postgres \c messaging_prod
- если база данных внешняя (без кластера) выполните команды:
su postgres psql \c messaging_prod
- если база данных внешняя (кластер на базе Patroni) выполните команды:
psql -h внутренний_ип_ноды -p 5432 -U express -d postgres \c messaging_prod
-
Выполните запрос размера таблиц базы данных:
select schemaname as table_schema, relname as table_name, pg_size_pretty(pg_total_relation_size(relid)) as total_size, pg_size_pretty(pg_relation_size(relid)) as data_size, pg_size_pretty(pg_total_relation_size(relid) - pg_relation_size(relid)) as external_size from pg_catalog.pg_statio_user_tables where relname = 'user_chat_events' or relname = 'chat_events' order by pg_total_relation_size(relid) desc, pg_relation_size(relid) desc;
Внимание!
Если total_size user_chat_events и/или chat_events превышает 20 Гб, то время простоя при миграции в автоматическом режиме может составлять > 1 часа. В этом случае рекомендуется провести миграцию по специальной инструкции, из раздела "Миграция больших баз данных". Для удобства рекомендуем заранее подготовить файл со всеми таймстемпами и запросами.
Примечание
Если таблицы в вашей базе данных меньше 20 Гб, вы можете мигрировать данные в автоматическом режиме (дополнительных действий не требуется).
Миграция больших баз данных
Некоторые запросы, выполняющиеся при подготовке к миграции, могут обрабатываться до десяти часов в зависимости от объемов базы данных. Например, миграция старой истории сообщений. В этом случае рекомендуется использовать специальные команды для выполнения долгих задач screen или nohup.
Пример использования:
- Добавьте запрос в текстовый файл с расширением .sql, например update.sql.
- Добавьте права на чтение файла пользователю от имени которого запускается psql.
Запустите миграцию в виртуальном терминале следующими способами:
- способ 1. С помощью команды screen (рекомендуется);
- способ 2. С помощью команды nohup (если знакомы с командой или нет возможности использовать screen).
- В терминале на сервере с базой данных выполните команду screen, откроется сессия терминала.
-
Выполните следующую команду (замените значение флага -u и -f на свой):
sudo -u postgres psql -d messaging_prod -f /home/postgres/update.sql
-
Выйдите из сессии нажав сочетания клавиш Ctrl-A + Ctrl-D. Ни в коем случае не нажимайте Ctrl-C и Ctrl-D пока находитесь в screen сессии и запрос не завершился.
- Выполните команду
screen -r
чтобы вернуться к screen сессии и убедиться, что сессия работает и запрос выполняется. После чего можно выйти из сессии нажав Ctrl-A + Ctrl-D. - Повторите процедуру из п. 4, чтобы проверить состояние задачи.
-
Проверьте статус выполнения запроса (после минуты выполнения):
SELECT pid, user, pg_stat_activity.query_start, now() - pg_stat_activity.query_start AS query_time, query, state, wait_event_type, wait_event FROM pg_stat_activity WHERE (now() - pg_stat_activity.query_start) > interval '1 minutes' AND datname = 'messaging_prod' AND state = 'active';
-
В результате отображается запрос. Screen сессия должена быть активной.
-
В терминале на сервере с базой данных выполните следующую команду (команда не должна требовать никакого input, например пароля от root юзера):
nohup sudo -u postgres psql -d messaging_prod -f /home/postgres/update.sql &
-
Проверьте статус задачи через команду
jobs -l
. - Проверьте вывод в файле honup.out.
-
Проверьте статус выполнения запроса (после минуты выполнения):
SELECT pid, user, pg_stat_activity.query_start, now() - pg_stat_activity.query_start AS query_time, query, state, wait_event_type, wait_event FROM pg_stat_activity WHERE (now() - pg_stat_activity.query_start) > interval '1 minutes' AND datname = 'messaging_prod' AND state = 'active';
-
В результате отображается запрос. Nohup процесс должен быть активным.
Подготовка к миграции
Внимание!
Перед началом операции убедитесь в достаточном количестве свободного места. Рекомендуется, чтобы объем свободного пространства в 2-3 раза превышал объем базы данных.
Примечание
Данные запросы выполняются при запущенных сервисах. В случае если база данных очень большая, подготовку можно начинать за несколько дней до миграции и выполнять этапы в технические окна.
Все запросы выполняются в psql, база данных messaging_prod.
-
Добавьте новые колонки в таблицу chat_events:
ALTER TABLE chat_events ADD COLUMN events_history_scope BOOLEAN, ADD COLUMN event_info_scope BOOLEAN;
-
Запишите текущий timestamp. Этот timestamp необходимо сохранить до момента будущей миграции:
messaging_test=# SELECT now(); now ------------------------------- 2023-12-25 11:06:06.037283+03 (1 row)
-
Запишите timestamp самого раннего сообщения:
messaging_test=# SELECT inserted_at FROM chat_events ORDER BY inserted_at ASC LIMIT 1; inserted_at ---------------------------- 2018-02-05 10:21:44.191786 (1 row)
-
Выполните миграцию историй сообщений от даты, полученной из таймстемпа в п. 2 + 1 минута до даты самого раннего события п. 3 с шагом в 1 год. Каждый запрос будет выполняться длительное время, если по каким-то причинам запрос выполнить не получается, можно уменьшить шаг до полугода/трех месяцев.
UPDATE chat_events ce SET events_history_scope = uce.events_history_scope, event_info_scope = uce.event_info_scope FROM user_chat_events uce WHERE ce.sync_id = uce.event_sync_id AND ce.event_type != 'routing_changed' AND ce.inserted_at >= '2023-01-01' AND ce.inserted_at <= '2023-12-25 11:07'; UPDATE chat_events ce SET events_history_scope = uce.events_history_scope, event_info_scope = uce.event_info_scope FROM user_chat_events uce WHERE ce.sync_id = uce.event_sync_id AND ce.event_type != 'routing_changed' AND ce.inserted_at >= '2022-01-01' AND ce.inserted_at <= '2023-01-01'; UPDATE chat_events ce SET events_history_scope = uce.events_history_scope, event_info_scope = uce.event_info_scope FROM user_chat_events uce WHERE ce.sync_id = uce.event_sync_id AND ce.event_type != 'routing_changed' AND ce.inserted_at >= '2021-01-01' AND ce.inserted_at <= '2022-01-01'; ... UPDATE chat_events ce SET events_history_scope = uce.events_history_scope, event_info_scope = uce.event_info_scope FROM user_chat_events uce WHERE ce.sync_id = uce.event_sync_id AND ce.event_type != 'routing_changed' AND ce.inserted_at >= '2018-02-05' AND ce.inserted_at <= '2019-01-01';
-
Если total_size ваших таблиц больше 200 Гб, то быстрее будет выполнить один долгий общий запрос без временных шагов:
--- запрос для миграции без временного шага, --- выполнять только в случае очень объемных баз, --- обязательно с использованием nohup или screen, --- время выполнения может достигать > 10 часов UPDATE chat_events ce SET events_history_scope = uce.events_history_scope, event_info_scope = uce.event_info_scope FROM user_chat_events uce WHERE ce.sync_id = uce.event_sync_id AND ce.event_type != 'routing_changed';
-
Создайте новые индексы для таблицы chat_events:
CREATE INDEX CONCURRENTLY chat_events_group_chat_id_inserted_at_stealth_index ON chat_events USING btree (group_chat_id, inserted_at) INCLUDE (sync_id, stealth) WHERE (NOT (stealth IS NULL)); CREATE INDEX CONCURRENTLY chat_events_group_chat_id_inserted_at_shared_index ON chat_events USING btree (group_chat_id, inserted_at) WHERE ((events_history_scope = true) AND (shared = true)); CREATE INDEX CONCURRENTLY chat_events_group_chat_id_inserted_at_non_shared_index ON chat_events USING btree (group_chat_id, inserted_at) WHERE ((events_history_scope = true) AND (shared = false)); CREATE INDEX CONCURRENTLY chat_events_sync_id_event_info_scope_index ON chat_events USING btree (sync_id) WHERE (event_info_scope = true); CREATE INDEX CONCURRENTLY chat_events_epoch_migration_index_1 ON chat_events (event_type) WHERE event_type='added_to_chat' OR event_type='user_joined_to_chat'; CREATE INDEX CONCURRENTLY chat_events_epoch_migration_index_2 ON chat_events (event_type) WHERE event_type='left_from_chat' OR event_type='deleted_from_chat' OR event_type='kicked_by_cts_logout';
-
Отслеживание прогресса создания индексов:
--- Отслеживание прогресса создания индексов SELECT phase, blocks_total, blocks_done, (blocks_total - blocks_done) as blocks_rest, tuples_total, tuples_done, (tuples_total - tuples_done) as tuples_rest FROM pg_stat_progress_create_index;
Миграция
Внимание!
Если этап подготовки к миграции был выполнен несколько дней назад, то необходимо мигрировать историю от текущего времени до времени начала этапа подготовки к миграции. Если вы выполняете этап подготовки к миграции и основную миграции в один день, то пропустите п. 1 и п. 2.
-
Запишите текущий timestamp:
messaging_test=# SELECT now(); now ------------------------------- 2023-12-27 12:14:01.026154+03 (1 row)
-
Обновите историю от времени полученного в разделе «Подготовка к миграции» п. 2:
UPDATE chat_events ce SET events_history_scope = uce.events_history_scope, event_info_scope = uce.event_info_scope FROM user_chat_events uce WHERE ce.sync_id = uce.event_sync_id AND ce.event_type != 'routing_changed' AND ce.inserted_at >= '2023-12-25 11:06:00';
-
Остановите все сервисы, кроме postgres:
- если база данных реализована в контейнере Docker, выполните команды:
docker ps | grep postgres_1 | awk {'print$1'} # получаем и запоминаем id контейнера postgres docker stop $(docker ps -q | grep -v 00000000) # вставляем вместо 00000000 id контейнера postgres
- если база данных внешняя, выполните команду:
dpl --dc stop
-
Зайдите в psql, подключитесь к базе данных messaging_prod.
-
С помощью следующей команды пропустите миграции, которые выполняли (текст команды вводится как указано, без изменений):
INSERT INTO schema_migrations (version, inserted_at) VALUES (20231031201400, now()), (20231031201640, now()), (20231101121946, now()), (20231101134329, now()), (20231116000454, now()), (20231129210158, now()), (20231205114132, now()), (20231205115340, now()), (20231205123846, now()), (20231211072757, now());
-
Запишите текущий timestamp:
messaging_test=# SELECT now(); now ------------------------------- 2023-12-27 12:34:39.258821+03 (1 row)
-
Мигрируйте историю от момента подготовки миграции. Используйте дату и время с точностью до минуты:
- из п. 1 если был выполнен процесс дополнительной миграции истории:
---- обновление с временем из пункта 1 UPDATE chat_events ce SET events_history_scope = uce.events_history_scope, event_info_scope = uce.event_info_scope FROM user_chat_events uce WHERE ce.sync_id = uce.event_sync_id AND ce.event_type != 'routing_changed' AND ce.inserted_at >= '2023-12-27 12:14:00';
- из п. 2 раздела «Подготовка к миграции», если дополнительной миграции истории не было:
---- обновление с датой из раздела "Подготовка к миграции" пункт 2 UPDATE chat_events ce SET events_history_scope = uce.events_history_scope, event_info_scope = uce.event_info_scope FROM user_chat_events uce WHERE ce.sync_id = uce.event_sync_id AND ce.event_type != 'routing_changed' AND ce.inserted_at >= '2023-12-25 11:06:00';
-
Запустите сервисы:
- если база данных реализована в контейнере Docker, выполните команду:
docker start $(docker ps -aq | grep -v 00000000) # вместо 00000000 вставляем id контейнера postgres
- если база данных внешняя, выполните команду:
dpl --dc start # дождитесь завершения автоматических миграций и запуска сервиса messaging
-
Мигрируйте следующие данные используя таймстемп полученный в п. 6:
INSERT INTO user_chat_events_v2 (user_huid, group_chat_id, event_sync_id, inserted_at) SELECT jsonb_array_elements_text((event_params->>'recipients')::jsonb)::uuid as user_huid, group_chat_id, sync_id, ce.inserted_at FROM chat_events ce INNER JOIN group_chats gc ON gc.id = ce.group_chat_id WHERE global = true AND shared = false AND chat_type = 'global' AND ce.inserted_at <= '2023-12-27 12:34:39'; INSERT INTO user_chat_events_v2 (user_huid, group_chat_id, event_sync_id, inserted_at) SELECT jsonb_array_elements_text((event_params->>'recipients')::jsonb)::uuid as user_huid, group_chat_id, sync_id, inserted_at FROM chat_events ce WHERE jsonb_array_length((event_params->>'recipients')::jsonb) > 0 AND event_type = ANY('{message_new,call_end}') AND ce.inserted_at <= '2023-12-27 12:34:39'; INSERT INTO user_chat_events_v2 (user_huid, group_chat_id, event_sync_id, inserted_at) SELECT jsonb_array_elements_text((event_params->>'recipients')::jsonb)::uuid as user_huid, group_chat_id, sync_id, inserted_at FROM chat_events ce WHERE jsonb_array_length((event_params->>'recipients')::jsonb) = 1 AND event_type = 'app_event' AND payload->>'event_type' = 'bot_notification' AND ce.inserted_at <= '2023-12-27 12:34:39';
-
Выполните дамп системных событий из каналов. Его можно будет удалить спустя время, когда релиз проверится временем:
CREATE TABLE channel_events_dump AS SELECT ce.* FROM chat_events ce INNER JOIN group_chats c ON c.id = ce.group_chat_id WHERE chat_type = 'channel' AND event_type = ANY('{added_to_chat,deleted_from_chat,left_from_chat,admin_added_to_chat,kicked_by_cts_logout,user_joined_to_chat}');
-
Удалите лишние системные события из каналов:
DELETE FROM chat_events ce USING group_chats c WHERE c.id = ce.group_chat_id AND chat_type = 'channel' AND event_type = ANY('{added_to_chat,deleted_from_chat,left_from_chat,admin_added_to_chat,kicked_by_cts_logout,user_joined_to_chat}');
-
Если на вашем сервере использовался notifications_bot от BS (не внутренний бот) вам необходимо мигрировать его сообщения, для этого выполните следующий запрос и подставьте в качестве bot_id идентификатор notifications_bot используя таймстемп полученный в п. 6:
INSERT INTO user_chat_events_v2 (user_huid, group_chat_id, event_sync_id, inserted_at) SELECT jsonb_array_elements_text((event_params->>'recipients')::jsonb)::uuid as user_huid, group_chat_id, sync_id, now() FROM chat_events ce WHERE jsonb_array_length((event_params->>'recipients')::jsonb) > 0 AND event_type = 'app_event' AND payload->>'event_type' = 'bot_notification' AND sender = 'bot_id' AND ce.inserted_at <= '2023-12-27 12:34:39';
-
Обновите статистику (см. раздел "Обновление статистики").
-
Удалите миграционные индексы:
DROP INDEX chat_events_epoch_migration_index_1; DROP INDEX chat_events_epoch_migration_index_2;
Обновление статистики
После автоматической или ручной миграции необходимо обновить статистику. В psql выполните следующие запросы:
VACUUM ANALYZE user_chat_events_v2;
VACUUM ANALYZE chat_event_meta;
VACUUM ANALYZE chat_member_epochs;
VACUUM ANALYZE chat_events;