diff --git a/api/block_confirmation.go b/api/block_confirmation.go index f96b523e..fc0ecdef 100644 --- a/api/block_confirmation.go +++ b/api/block_confirmation.go @@ -55,6 +55,9 @@ func (app *ApiServer) BlockConfirmation(c *fiber.Ctx) error { return err } + // Clients poll this until their block is indexed; a cached "not yet" + // keeps them waiting after it has been. + c.Set(fiber.HeaderCacheControl, "no-store") return c.JSON(fiber.Map{ "data": res, }) diff --git a/api/v1_unclaimed_id.go b/api/v1_unclaimed_id.go index fe77126b..8ccb6408 100644 --- a/api/v1_unclaimed_id.go +++ b/api/v1_unclaimed_id.go @@ -34,6 +34,10 @@ func (app *ApiServer) v1UnclaimedId(c *fiber.Ctx, tableName, idField string, low } id, _ := trashid.EncodeHashId(freeId) + // Each response must be a fresh id: a cached one hands the same id to + // concurrent callers, and every write after the first that uses it is + // rejected as a duplicate. + c.Set(fiber.HeaderCacheControl, "no-store") return c.JSON(fiber.Map{ "data": id, }) diff --git a/api/v1_unclaimed_id_test.go b/api/v1_unclaimed_id_test.go new file mode 100644 index 00000000..7e4d93df --- /dev/null +++ b/api/v1_unclaimed_id_test.go @@ -0,0 +1,34 @@ +package api + +import ( + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +// An edge cache in front of these routes hands one response to every caller, +// so each must tell caches not to store it. +func TestUnclaimedIdAndBlockConfirmationAreNotCacheable(t *testing.T) { + app := emptyTestApp(t) + + for _, path := range []string{ + "/v1/users/unclaimed_id", + "/v1/tracks/unclaimed_id", + "/v1/playlists/unclaimed_id", + "/v1/playlists/unclaimed-id", + "/v1/comments/unclaimed_id", + "/v1/comments/unclaimed-id", + "/v1/events/unclaimed_id", + "/v1/events/unclaimed-id", + "/block_confirmation?blocknumber=1&blockhash=0xabc", + "/block-confirmation?blocknumber=1&blockhash=0xabc", + } { + t.Run(path, func(t *testing.T) { + res, err := app.Test(httptest.NewRequest("GET", path, nil), -1) + assert.NoError(t, err) + assert.Equal(t, 200, res.StatusCode) + assert.Equal(t, "no-store", res.Header.Get("Cache-Control")) + }) + } +}