Что такое фантомное чтение
Что проверяют
Понимаете ли вы, что изоляция настраивается на соединении с базой, а не библиотекой поверх неё. У питониста вопрос обычно упирается в ORM: транзакция спрятана за контекстным менеджером, и кандидат нередко не знает, какой уровень получает по умолчанию и что при этом гарантировано.
Как отвечать
Фантом это изменение состава строк под одним и тем же условием: первый запрос вернул один набор, чужая транзакция вставила или удалила подходящую строку, повторный запрос вернул другой. Не путать с неповторяющимся чтением, где меняется содержимое уже прочитанной строки.
Дальше про практику. В PostgreSQL по умолчанию действует чтение зафиксированного, каждый запрос видит свежий снимок, то есть фантом внутри одной транзакции вполне возможен. Повторяемое чтение даёт один снимок на всю транзакцию: состав не изменится, но при конфликте записи транзакция завершится ошибкой сериализации и её надо будет повторить целиком.
Уровень задаётся на соединении или на транзакции, в SQLAlchemy через параметр движка, в Django через настройки соединения. И главное: select_for_update фантом не закрывает, блокировать нечего, строк ещё нет. Гонку вида get_or_create лечат уникальным ограничением и перехватом ошибки целостности.
Где спотыкаются
Ждут, что ORM разрулит конкуренцию сам. Транзакцией управляет база, аномалии решаются её средствами, а библиотека только передаёт настройку и оборачивает вызовы.
Ставят строгий уровень и не пишут повтор. Ошибку сериализации база возвращает штатно, и без обработки она превращается в пятисотую ошибку на ровном месте под нагрузкой.
Проверяют существование строки запросом, а следующей командой вставляют. В зазор успевает влезть чужая транзакция, и спасает только уникальный индекс: он работает на любом уровне изоляции.