Выплату нужно выполнять одной транзакцией с защитой обеих записей от конкурентных изменений. float32 для денег и локальный счётчик totalPayouts создают дополнительные риски.
func (r *PayoutRepository) Payout(advertiserID, partnerID string, amount float32) error {
Выплату нужно выполнять одной транзакцией с защитой обеих записей от конкурентных изменений. float32 для денег и локальный счётчик totalPayouts создают дополнительные риски.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
func (r *PayoutRepository) Payout(advertiserID, partnerID string, amount float32) error { advertiser, err := r.find(advertiserID) // добавить транзакцию if err != nil { return err } partner, err := r.find(partnerID) if err != nil { return err }
advertiser.Balance -= amount // защитить SELECT FOR UPDATE
partner.Hold -= amount
partner.Balance += amount
if err := r.save(advertiser); err != nil {
return err
}
if err := r.save(partner); err != nil {
return err
}
r.totalPayouts++
return nil
}
Ответ
Выплату нужно выполнять одной транзакцией с защитой обеих записей от конкурентных изменений. float32 для денег и локальный счётчик totalPayouts создают дополнительные риски.
Используйте целые минимальные единицы или decimal, проверяйте amount>0 и достаточность balance/hold. SELECT FOR UPDATE выполняйте через одну tx с одинаковым порядком блокировки идентификаторов во всех вызовах. Обработайте случай одинаковых сторон.
Сохранения также должны идти через tx; Commit проверяется, Rollback предусмотрен. Повтор платежной команды защищают уникальным ключом. Счётчик в памяти не заменяет транзакционную запись факта выплаты.