Sobes.tech
Senior

Ar egzistuoja mikroservisas, skirtas išskaičiuoti lėšas iš vartotojo balanso? Kai paslauga yra replikuojama, kyla race condition problema. Kaip išspręsti šią problemą?

sobes.tech AI

Atsakymas iš AI

Проблема race condition при списании средств в микросервисе с репликацией возникает из-за одновременного доступа нескольких экземпляров к одним и тем же данным. Чтобы решить её, можно использовать следующие подходы:

  • Оптимистичная блокировка (Optimistic Locking): хранить версию записи или timestamp, при обновлении проверять, что версия не изменилась. Если изменилась — повторять операцию.
  • Транзакции и блокировки на уровне базы данных: использовать транзакции с блокировкой строк (например, SELECT ... FOR UPDATE), чтобы гарантировать последовательный доступ.
  • Использование распределённых блокировок: например, через Redis с Redlock или Zookeeper, чтобы только один сервис мог списывать средства в момент времени.
  • Идемпотентные операции и очереди: обрабатывать списания через очередь сообщений, чтобы гарантировать последовательность и избежать параллельных изменений.

Пример с оптимистичной блокировкой в Go (псевдокод):

func DeductBalance(userID string, amount float64) error {
    for i := 0; i < maxRetries; i++ {
        user, err := db.GetUser(userID)
        if err != nil {
            return err
        }
        if user.Balance < amount {
            return errors.New("недостаточно средств")
        }
        newBalance := user.Balance - amount
        // Обновляем с проверкой версии
        updated, err := db.UpdateBalanceIfVersionMatches(userID, newBalance, user.Version)
        if err != nil {
            return err
        }
        if updated {
            return nil // успешно
        }
        // Иначе версия изменилась, повторяем
    }
    return errors.New("не удалось списать средства из-за конфликта")
}