Как предотвратить дублирование входящих запросов?

Ответ

Для предотвращения дублирования запросов можно использовать идемпотентные ключи (idempotency keys). Клиент генерирует уникальный ключ для каждого запроса, сервер сохраняет его и результат обработки. При повторном запросе с тем же ключом возвращается сохранённый результат.

Пример реализации (для демонстрации, в реальных системах используйте внешние хранилища):

var cache = sync.Map{}

func handleRequest(w http.ResponseWriter, r *http.Request) {
    key := r.Header.Get("Idempotency-Key")
    if key == "" {
        http.Error(w, "Idempotency-Key header required", http.StatusBadRequest)
        return
    }

    // Попытка загрузить результат из кэша
    if val, ok := cache.Load(key); ok {
        w.Write(val.([]byte))
        return
    }

    // Обработка запроса (выполняется только если ключ не найден в кэше)
    response := []byte("response data for " + key)
    cache.Store(key, response)
    w.Write(response)
}

Важные аспекты и дополнительные подходы:

  • Внешние хранилища: В продакшн-среде sync.Map не подходит, так как она не персистентна и не распределена. Используйте Redis, Memcached или базу данных для хранения идемпотентных ключей и результатов.
  • Время жизни ключей (TTL): Устанавливайте ограниченное время жизни для ключей, чтобы избежать переполнения хранилища и обрабатывать устаревшие запросы как новые.
  • Транзакционность: Убедитесь, что сохранение ключа и выполнение операции атомарны или обрабатываются в рамках одной транзакции, чтобы избежать частичных состояний.
  • Проверка ключей на уровне базы данных: Для операций, изменяющих данные, можно использовать уникальные ограничения или условные вставки в БД, чтобы предотвратить дублирование на уровне данных.