From bdf9298496c721916aaec6064a17fad2f5d9da69 Mon Sep 17 00:00:00 2001 From: Milas Bowman Date: Mon, 10 Mar 2025 22:54:48 -0400 Subject: [PATCH] chore(poll): return NotModifiedTimeoutErr on 304 --- client.go | 3 +++ poll_batch.go | 7 ++++++- poll_batch_test.go | 35 +++++++++++++++++++++++++++++++++++ poll_single_test.go | 21 +++++++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/client.go b/client.go index 4bc6a1c..36a074b 100644 --- a/client.go +++ b/client.go @@ -22,6 +22,7 @@ const CausalityTokenHeader = "X-Garage-Causality-Token" var TombstoneItemErr = errors.New("item is a tombstone") var NoSuchItemErr = errors.New("item does not exist") var ConcurrentItemsErr = errors.New("item has multiple concurrent values") +var NotModifiedTimeoutErr = errors.New("not modified within timeout") var awsSigner = v4.NewSigner() @@ -300,6 +301,8 @@ func (c *Client) readItemSingle(ctx context.Context, b Bucket, pk string, sk str return nil, "", NoSuchItemErr case http.StatusConflict: return nil, ct, ConcurrentItemsErr + case http.StatusNotModified: + return nil, "", NotModifiedTimeoutErr default: return nil, "", fmt.Errorf("http status code %d", resp.StatusCode) } diff --git a/poll_batch.go b/poll_batch.go index c3ed20f..83df65b 100644 --- a/poll_batch.go +++ b/poll_batch.go @@ -67,7 +67,12 @@ func (c *Client) PollRange(ctx context.Context, b Bucket, pk string, q PollRange return nil, err } - if resp.StatusCode != http.StatusOK { + switch resp.StatusCode { + case http.StatusOK: + break + case http.StatusNotModified: + return nil, NotModifiedTimeoutErr + default: return nil, fmt.Errorf("http status code %d: %s", resp.StatusCode, body) } diff --git a/poll_batch_test.go b/poll_batch_test.go index 546bede..384aafa 100644 --- a/poll_batch_test.go +++ b/poll_batch_test.go @@ -80,3 +80,38 @@ func TestClient_PollRange(t *testing.T) { require.Equal(t, "hello3", string(result.Items[0].Values[0])) } } + +func TestClient_PollRange_Timeout(t *testing.T) { + if testing.Short() { + t.Skip("Skipping in short mode: 1 sec minimum to trigger timeout") + return + } + t.Parallel() + + f, ctx := newFixture(t) + + pk := randomPk() + sk := randomSk() + + for i := range 5 { + err := f.cli.InsertItem(ctx, f.bucket, pk, sk+"-"+strconv.Itoa(i), "", []byte("hello1")) + require.NoError(t, err) + } + + // first read should complete immediately + q := k2v.PollRangeQuery{ + Start: sk, + } + result, err := f.cli.PollRange(ctx, f.bucket, pk, q, 5*time.Second) + require.NoError(t, err) + require.NotEmpty(t, result.SeenMarker) + require.Len(t, result.Items, 5) + for i := range result.Items { + require.Len(t, result.Items[i].Values, 1) + require.Equal(t, "hello1", string(result.Items[i].Values[0])) + } + + q.SeenMarker = result.SeenMarker + result, err = f.cli.PollRange(ctx, f.bucket, pk, q, 1*time.Second) + require.ErrorIs(t, err, k2v.NotModifiedTimeoutErr) +} diff --git a/poll_single_test.go b/poll_single_test.go index 21f3276..dcca3de 100644 --- a/poll_single_test.go +++ b/poll_single_test.go @@ -46,3 +46,24 @@ func TestClient_PollItem(t *testing.T) { require.NotEmpty(t, ct) require.NoError(t, <-updateErrCh) } + +func TestClient_PollItem_Timeout(t *testing.T) { + if testing.Short() { + t.Skip("Skipping in short mode: 1 sec minimum to trigger timeout") + return + } + t.Parallel() + + f, ctx := newFixture(t) + + pk := randomPk() + sk := randomSk() + + err := f.cli.InsertItem(ctx, f.bucket, pk, sk, "", []byte("hello1")) + require.NoError(t, err) + + _, ct, err := f.cli.ReadItemSingle(ctx, f.bucket, pk, sk) + item, ct, err := f.cli.PollItem(ctx, f.bucket, pk, sk, ct, 1*time.Second) + require.ErrorIs(t, err, k2v.NotModifiedTimeoutErr) + require.Empty(t, item) +}