Sitemap
Stackademic

Stackademic is a learning hub for programmers, devs, coders, and engineers. Our goal is to democratize free coding education for the world.

Member-only story

Your Go service isn’t idempotent — just because you added an idempotency key

--

The hidden failure modes that make seemingly safe requests dangerous

Press enter or click to view image in full size
Photo by Ferenc Almasi on Unsplash

So you added an Idempotency-Key header, stored the key in a table, and wrote a test that sends the same request twice. Green. You ship it.

Three weeks later, a customer is charged twice.

An idempotency key is an input. Idempotency is a property of your system under failure. The gap between the two is where money gets duplicated.

Let’s walk through the failure modes, in the order they usually bite.

1. The check-then-act race

Here is the version most of us write first:

func (s *Service) Transfer(ctx context.Context, key string, req TransferRequest) (*TransferResponse, error) {
// 1. Have we seen this key?
if resp, found := s.store.Get(ctx, key); found {
return resp, nil
}

// 2. Do the work
resp, err := s.doTransfer(ctx, req)
if err != nil {
return nil, err
}

// 3. Remember the result
s.store.Put(ctx, key, resp)
return resp, nil
}

It passes the “send twice” test because the requests are sequential. Now send them concurrently, which is exactly what a client with an aggressive timeout and a retry loop does:

--

--

Yaninyz witty
Yaninyz witty

Written by Yaninyz witty

Writing about golang, next js, typescript, devops, aws

Stackademic
Stackademic

Published in Stackademic

Stackademic is a learning hub for programmers, devs, coders, and engineers. Our goal is to democratize free coding education for the world.