Skip to content

Commit

Permalink
receive: fix maxBufferedResponses channel size to avoid deadlock (tha…
Browse files Browse the repository at this point in the history
…nos-io#7978)

* Fix maxBufferedResponses channel size to avoid deadlock

Fixes thanos-io#7977

Signed-off-by: Remi Vichery <[email protected]>

* Add changelog entry

Signed-off-by: Remi Vichery <[email protected]>

* adjust line numbers in docs/components/receive.md to match updated code

Signed-off-by: Remi Vichery <[email protected]>

---------

Signed-off-by: Remi Vichery <[email protected]>
  • Loading branch information
rvichery authored Dec 18, 2024
1 parent 1ca8292 commit 1b58ed1
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ We use *breaking :warning:* to mark changes that are not backward compatible (re

### Fixed

- [#7978](https://github.com/thanos-io/thanos/pull/7978) Receive: Fix deadlock during local writes when `split-tenant-label-name` is used

### Added

- [#7907](https://github.com/thanos-io/thanos/pull/7907) Receive: Add `--receive.grpc-service-config` flag to configure gRPC service config for the receivers.
Expand Down
2 changes: 1 addition & 1 deletion docs/components/receive.md
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ Please see the metric `thanos_receive_forward_delay_seconds` to see if you need

The following formula is used for calculating quorum:

```go mdox-exec="sed -n '1012,1022p' pkg/receive/handler.go"
```go mdox-exec="sed -n '1015,1025p' pkg/receive/handler.go"
// writeQuorum returns minimum number of replicas that has to confirm write success before claiming replication success.
func (h *Handler) writeQuorum() int {
// NOTE(GiedriusS): this is here because otherwise RF=2 doesn't make sense as all writes
Expand Down
5 changes: 4 additions & 1 deletion pkg/receive/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,10 @@ func (h *Handler) fanoutForward(ctx context.Context, params remoteWriteParams) (

// Prepare a buffered channel to receive the responses from the local and remote writes. Remote writes will all go
// asynchronously and with this capacity we will never block on writing to the channel.
maxBufferedResponses := len(localWrites)
var maxBufferedResponses int
for er := range localWrites {
maxBufferedResponses += len(localWrites[er])
}
for er := range remoteWrites {
maxBufferedResponses += len(remoteWrites[er])
}
Expand Down
51 changes: 51 additions & 0 deletions pkg/receive/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1820,6 +1820,57 @@ func TestDistributeSeries(t *testing.T) {
require.Equal(t, map[string]struct{}{"bar": {}, "boo": {}}, hr.seenTenants)
}

func TestHandlerSplitTenantLabelLocalWrite(t *testing.T) {
const tenantIDLabelName = "thanos_tenant_id"

appendable := &fakeAppendable{
appender: newFakeAppender(nil, nil, nil),
}

h := NewHandler(nil, &Options{
Endpoint: "localhost",
SplitTenantLabelName: tenantIDLabelName,
ReceiverMode: RouterIngestor,
ReplicationFactor: 1,
ForwardTimeout: 1 * time.Second,
Writer: NewWriter(
log.NewNopLogger(),
newFakeTenantAppendable(appendable),
&WriterOptions{},
),
})

// initialize hashring with a single local endpoint matching the handler endpoint to force
// using local write
hashring, err := newSimpleHashring([]Endpoint{
{
Address: h.options.Endpoint,
},
})
require.NoError(t, err)
hr := &hashringSeenTenants{Hashring: hashring}
h.Hashring(hr)

response, err := h.RemoteWrite(context.Background(), &storepb.WriteRequest{
Timeseries: []prompb.TimeSeries{
{
Labels: labelpb.ZLabelsFromPromLabels(
labels.FromStrings("a", "b", tenantIDLabelName, "bar"),
),
},
{
Labels: labelpb.ZLabelsFromPromLabels(
labels.FromStrings("b", "a", tenantIDLabelName, "foo"),
),
},
},
})

require.NoError(t, err)
require.NotNil(t, response)
require.Equal(t, map[string]struct{}{"bar": {}, "foo": {}}, hr.seenTenants)
}

func TestHandlerFlippingHashrings(t *testing.T) {
t.Parallel()

Expand Down

0 comments on commit 1b58ed1

Please sign in to comment.