Восстановление завершается ошибкой из-за нехватки места на диске при миграции, вызванной 70 млн событий календаря

У меня стандартная установка, которая пытается восстановить базу данных. Ошибка возникает на этапе миграции.


ALTER TABLE
Миграция базы данных...
EXCEPTION: rake db:migrate
Не удалось выполнить миграцию базы данных.
rake aborted!
StandardError: Произошла ошибка, эта и все последующие миграции отменены: (StandardError)

PG::DiskFull: ERROR: не удалось записать в файл "base/pgsql_tmp/pgsql_tmp11009.51": на устройстве не осталось места
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rack-mini-profiler-4.0.1/lib/patches/db/pg/alias_method.rb:109:in `exec'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rack-mini-profiler-4.0.1/lib/patches/db/pg/alias_method.rb:109:in `async_exec'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.4/lib/active_record/connection_adapters/postgresql/database_statements.rb:167:in `perform_query'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.4/lib/active_record/connection_adapters/abstract/database_statements.rb:556:in `block (2 levels) in raw_execute'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.4/lib/active_record/connection_adapters/abstract_adapter.rb:1017:in `block in with_raw_connection'

На диске свободно 90 ГБ. На исходном диске каталог postgres занимает всего 23 ГБ.

Как может база данных размером 23 ГБ не восстановиться во время миграции, если свободно 90 ГБ?

Источник – Версия сервера: 3.5.0.beta5-dev (Коммит: b16fb6a60b3f1db475cbb91a51b7d4c734370083 — 7 мая 2025 г.)

Версия сервера назначения: 2026.2.0-latest (Коммит: b39866eb8891648a54764755e2e36eb725bd6c73 — 4 дня назад)

23G     /var/discourse/shared/standalone/postgres_data/
-rw-r--r-- 1 discourse discourse  16G 10 фев 21:13 site-2026-02-10-174058-v20250507013646.sql
-rw-r--r-- 1 discourse discourse 2.9G 10 фев 21:11 site-2026-02-10-174058-v20250507013646.sql.gz
root@forum-data:/shared# # после удаления
root@forum-data:/shared# du -hs postgres_data/; df -h .
24G     postgres_data/
Файловая система     Размер  Использовано  Свободно  Исполь%  Смонтировано на
/dev/vda1            154G           87G       68G     56%    /shared
....
Файловая система     Размер  Использовано  Свободно  Исполь%  Смонтировано на
/dev/vda1            154G          154G      607M    100%   /shared
overlay              154G          154G      607M    100%   /
91G     postgres_data/
Файловая система     Размер  Использовано  Свободно  Исполь%  Смонтировано на
/dev/vda1            154G          154G       82M    100%   /shared
overlay              154G          154G       82M    100%   /
1.1G    postgres_data/
Файловая система     Размер  Использовано  Свободно  Исполь%  Смонтировано на
/dev/vda1            154G           65G       90G     42%   /shared
overlay              154G           65G       90G     42%   /
1.1G    postgres_data/
Файловая система     Размер  Использовано  Свободно  Исполь%  Смонтировано на
/dev/vda1            154G           65G       90G     42%   /shared
overlay              154G           65G       90G     42%   /
1.1G    postgres_data/
Файловая система     Размер  Использовано  Свободно  Исполь%  Смонтировано на
/dev/vda1            154G           65G       90G     42%   /shared
overlay              154G           65G       90G     42%   /
1.1G    postgres_data/

После восстановления базы данных размер postgres_data составлял 24 ГБ.

В ходе миграции postgres_data увеличился до 91 ГБ, что привело к заполнению диска и сбою восстановления. Это произошло на чистой установке с версиями Discourse, указанными выше.

Похоже, это ошибка, поэтому я меняю категорию. Я также сталкивался с этим при попытке обновления. Раньше я думал, что решением для обновления будет перенос на новый сервер, но теперь вижу, что это не сработает.

Следующим шагом, вероятно, будет попытка выяснить, какой запрос или таблица вызывает эту проблему. Я не совсем уверен, как это сделать.

Я размещаю это в службу поддержки. Это сообщение приходит напрямую из файловой системы. Если PostgreSQL считает, что у него нет места, то, вероятно, места действительно нет, потому что для него выделено недостаточно пространства.

У вас есть какие-либо подсказки, как база данных размером 24 ГБ могла увеличиться до 90 ГБ во время миграции базы данных?

Не уверен… Я бы, вероятно, запустил ncdu или что-то подобное и посмотрел, что произошло в этой ситуации.

Используя du и df, я отслеживал размер каталога postgres_data, который вырос с 25 ГБ до 90 ГБ, и наблюдал, как свободное место на диске практически исчерпалось перед сбоем.

Полагаю, мне нужно найти способ отслеживать выполнение запроса в следующий раз.

Я сталкивался с этим как минимум на двух сайтах: один при обновлении, другой при восстановлении. В обоих случаях изначально свободного места было больше, чем размер базы данных.

Множество миграций полностью переписывают таблицы, что может временно занимать больше места, а PostgreSQL не слишком агрессивно возвращает это место системе.

Есть ли в том форуме AI с включёнными эмбеддингами?

Я как раз думал о чём-то подобном. . .

Кажется, это наиболее вероятная причина. . . . вздох. Нет. Установлен даже не плагин AI.

Если вы хотите углубиться в детали, можно вручную восстановить окружение на версию с коммита от мая 2025 года, затем перейти к текущему коммиту и выполнять миграции по одной, выводя размер до и после каждой — разумеется, с помощью скрипта :stuck_out_tongue:.

OK. Я выполняю восстановление на продакшн-сайте. Идет миграция.

Вот запрос, который выполняется уже 30 минут:

discourse=# SELECT pid, usename, state, query, query_start
FROM pg_stat_activity
WHERE state = 'active';
 pid |  usename  | state  |                                                    query                                                     |          query_start
-----+-----------+--------+--------------------------------------------------------------------------------------------------------------+-------------------------------
 519 | postgres  | active | SELECT pid, usename, state, query, query_start                                                              +| 2026-02-14 17:58:35.473337+00
     |           |        | FROM pg_stat_activity                                                                                       +|
     |           |        | WHERE state = 'active';                                                                                      |
 308 | discourse | active | DELETE                                                                                                      +| 2026-02-14 17:26:08.12598+00
     |           |        |   FROM calendar_events ce                                                                                   +|
     |           |        | WHERE                                                                                                       +|
     |           |        |   ce.id IN (SELECT DISTINCT(ce3.id) FROM calendar_events ce2                                                +|
     |           |        |             LEFT JOIN calendar_events ce3 ON ce3.user_id = ce2.user_id AND ce3.description = ce2.description+|
     |           |        |             WHERE ce2.start_date >= (ce3.start_date - INTERVAL '1 days')                                    +|
     |           |        |               AND ce2.start_date <= (ce3.start_date + INTERVAL '1 days')                                    +|
     |           |        |               AND ce2.timezone IS NOT NULL                                                                  +|
     |           |        |               AND ce3.timezone IS NULL                                                                      +|
     |           |        |               AND ce3.id != ce2.id                                                                          +|
     |           |        |               AND ce2.post_id IS NULL                                                                       +|
     |           |        |               AND ce3.post_id IS NULL                                                                       +|
     |           |        |   )                                                                                                         +|
     |           |        |                                                                                                              |
(2 rows)

Это из этого файла:

Здесь видно, что последняя миграция новее, но я полагаю, что это потому, что эта миграция относится к плагину, который был добавлен только что.

discourse=# SELECT version FROM schema_migrations ORDER BY version DESC LIMIT 1;
    version
----------------
 20250507013646
(1 row)

Событий календаря очень много!!! 69 724 384!

discourse=# select count(*) from calendar_events;
  count
----------
 69724384
(1 row)

Так что, похоже, это и есть проблема.

… но на исходном сайте плагин discourse_calendar даже не установлен!?

Но в таблице calendar_events на исходном сайте как раз такое количество событий…

Но в post_custom_fields есть только 4 записи, связанные с событием календаря?.

Так что есть много событий, не привязанных к теме или посту. Обычные праздники, такие как Рождество, Новый год, День подарков.

Вот решение

delete from  calendar_events where topic_id=0 and post_id is null and post_number is null;

После этого осталось 99 событий.

Может быть, SiteSetting.delete_expired_event_posts_after был установлен в 0, и хотя плагин толком не использовался, он создал все эти события?

И да! SiteSetting.delete_expired_event_posts_after установлен в -1.

О, но -1 — это значение по умолчанию. Так что, возможно, это и есть проблема?

Нет. Это касается ПОСТОВ событий. Я не совсем понимаю, откуда взялось 70 миллионов событий.

https://github.com/discourse/discourse/blob/main/plugins/discourse-calendar/config/settings.yml#L11-L13