Автор фото: Stable Diffusion, pure Evolution v2
Тип integer в колонке id как бомба замедленного действия
Если вы работаете с Legacy проектом, то загляните в структуру ваших таблиц - возможно некоторые из них используют integer в качестве id поля, и это очень плохо.
В чём проблема
Тип integer имеет лимиты от -2147483648 до +2147483647 (от 1 до 2147483647 для serial) в PostgresQL. К этому лимиту довольно легко подойти, особенно если система была создана уже значительно время назад.
Особенность этой проблемы:
- она не будет тригериться деплоем или другими действиями с кодом
- она не проявится от аномальной нагрузки
- может затронуть очень разный функционал
Как проверить
Если у вас небольшой проект и таблиц немного, то можно руками и глазами проверить каждую таблицу. Однако это плохо масштабируется.
Можно проверить размерность всех id колонок во всех таблицах, однако в разное время даже в одном проекте могут использоваться разные конвенции именования, поэтому часть таблиц могут иметь колонки вроде product_id или rid и т.п.
Ещё один более масштабируемый вариант - проверить текущие значения в последовательностях используемых для автоинкрементных колонок. Для этого подойдёт такой запрос:
SELECT c.relname, nextval(c.relname::regclass) AS seq_val
FROM pg_class c
WHERE c.relkind = 'S'
AND nextval(c.relname::regclass) > 1000000000
ORDER BY seq_val desc;
В результате получим список последовательностей превысивших 1 млрд., отсортированных по убывающей. Верхний лимит integer как мы помним больше 2 млрд. поэтому значение в 1 млрд. выглядит как то, с чего стоит начать в первую очередь. По названию sequence можно довольно легко определить какая таблица её использует.
Не забываем про таблицы ссылающиеся на изменённые id в качестве внешних ключей для связи многие-к-одному или многие-ко-многим. Если foreign key constraint прописаны для таких таблиц, то их можно найти следующим запросом (здесь мы ищем внешние ключи ссылающиеся на my_table_name(id)):
SELECT la.attrelid::regclass AS referencing_table,
la.attname AS referencing_column
FROM pg_constraint AS c
JOIN pg_index AS i
ON i.indexrelid = c.conindid
JOIN pg_attribute AS la
ON la.attrelid = c.conrelid
AND la.attnum = c.conkey[1]
JOIN pg_attribute AS ra
ON ra.attrelid = c.confrelid
AND ra.attnum = c.confkey[1]
WHERE c.confrelid = 'my_table_name'::regclass
AND c.contype = 'f'
AND ra.attname = 'id'
AND cardinality(c.confkey) = 1;
Однако foreign key constraint есть не всегда (а порой его создание может быть вообще невозможным), поэтому стоит принять это во внимание - на этом автоматизация заканчивается и дальше придётся проверять руками и глазами.
На что заменить
Простым решением для текущего состояния будет перевести тип колонки из int в bigint - запаса прочности этого типа данных должно хватить.
Однако не стоит считать что bigint это серебряная пуля при выборе типа id колонки в новых таблицах - всё конечно же будет зависеть от ваших сценариев использования и предназначения таблицы, так что ответ здесь как всегда - it depends.