Каким способом можно блокировать доступ к данным на уровне кода или запросов
Что проверяют
Формулировка двусмысленная, и первым делом стоит уточнить, о чём речь: о конкурентном доступе к одним данным или о том, кому эти данные можно отдавать. Интервьюер смотрит, различаете ли вы блокировку внутри процесса и в базе, и следом обычно спрашивает, что будет, если сервис запущен в нескольких экземплярах.
Как отвечать
Если речь о конкурентности, разделите уровни:
- код:
sync.Mutexдля эксклюзивного доступа,sync.RWMutex, когда читателей много, а писателей мало,sync/atomicдля счётчиков. Гонки ловитgo test -race; - пессимистичная блокировка в базе:
SELECT ... FOR UPDATEдержит строки до конца транзакции,SKIP LOCKEDдаёт нескольким воркерам разбирать очередь без ожидания друг друга,NOWAITсразу возвращает ошибку; - оптимистичная: колонка
versionиUPDATE ... WHERE id = $1 AND version = $2. ЕслиRowsAffected()вернул 0, данные успели поменять, повторяем или отдаём конфликт; - advisory lock:
pg_advisory_xact_lock(key)в PostgreSQL, когда блокировать нужно не строку, а действие.
Если речь о правах, ответ другой: middleware проверяет токен и кладёт пользователя в context.Context, условие владения входит в сам запрос (WHERE id = $1 AND owner_id = $2), у сервиса своя роль в базе с минимальными GRANT, а в PostgreSQL можно включить Row Level Security. Параметры передаются плейсхолдерами, никогда через fmt.Sprintf.
Где спотыкаются
Считают мьютекс защитой данных в базе. Он работает внутри одного процесса, а при двух репликах сервиса нужна блокировка на стороне базы или распределённая.
Пишут FOR UPDATE вне транзакции или открывают tx через db.BeginTx, а запрос выполняют через db. Он уходит по другому соединению в своей автотранзакции, и блокировка снимается сразу после него.
Проверяют только аутентификацию: пользователь вошёл, значит, запись по id можно отдать. Владельца не сверяют, и это классическая уязвимость IDOR: чужой заказ отдаётся по подобранному номеру.