Skip to content

Commit 303f861

Browse files
committed
receiver: avoid race of hashring
Signed-off-by: YaoZengzeng <[email protected]>
1 parent 4cf32d0 commit 303f861

File tree

1 file changed

+29
-1
lines changed

1 file changed

+29
-1
lines changed

pkg/receive/handler.go

+29-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"net"
1010
"net/http"
1111
"strconv"
12+
"sync"
1213
"sync/atomic"
1314

1415
"github.com/go-kit/kit/log"
@@ -46,10 +47,12 @@ type Handler struct {
4647
logger log.Logger
4748
receiver *Writer
4849
router *route.Router
49-
hashring Hashring
5050
options *Options
5151
listener net.Listener
5252

53+
mtx sync.RWMutex
54+
hashring Hashring
55+
5356
// Metrics
5457
requestDuration *prometheus.HistogramVec
5558
requestsTotal *prometheus.CounterVec
@@ -140,6 +143,9 @@ func (h *Handler) StorageReady() {
140143
// Hashring sets the hashring for the handler and marks the hashring as ready.
141144
// If the hashring is nil, then the hashring is marked as not ready.
142145
func (h *Handler) Hashring(hashring Hashring) {
146+
h.mtx.Lock()
147+
defer h.mtx.Unlock()
148+
143149
if hashring == nil {
144150
atomic.StoreUint32(&h.hashringReady, 0)
145151
h.hashring = nil
@@ -275,6 +281,15 @@ func (h *Handler) receive(w http.ResponseWriter, r *http.Request) {
275281
func (h *Handler) forward(ctx context.Context, tenant string, r replica, wreq *prompb.WriteRequest) error {
276282
wreqs := make(map[string]*prompb.WriteRequest)
277283
replicas := make(map[string]replica)
284+
285+
h.mtx.RLock()
286+
// It is possible that hashring is ready in testReady() but become unready now,
287+
// so we need to lock here.
288+
if h.hashring == nil {
289+
h.mtx.RUnlock()
290+
return errors.New("hashring is not ready")
291+
}
292+
278293
// Batch all of the time series in the write request
279294
// into several smaller write requests that are
280295
// grouped by target endpoint. This ensures that
@@ -285,6 +300,7 @@ func (h *Handler) forward(ctx context.Context, tenant string, r replica, wreq *p
285300
for i := range wreq.Timeseries {
286301
endpoint, err := h.hashring.GetN(tenant, &wreq.Timeseries[i], r.n)
287302
if err != nil {
303+
h.mtx.RUnlock()
288304
return err
289305
}
290306
if _, ok := wreqs[endpoint]; !ok {
@@ -294,6 +310,7 @@ func (h *Handler) forward(ctx context.Context, tenant string, r replica, wreq *p
294310
wr := wreqs[endpoint]
295311
wr.Timeseries = append(wr.Timeseries, wreq.Timeseries[i])
296312
}
313+
h.mtx.RUnlock()
297314

298315
return h.parallelizeRequests(ctx, tenant, replicas, wreqs)
299316
}
@@ -400,14 +417,25 @@ func (h *Handler) replicate(ctx context.Context, tenant string, wreq *prompb.Wri
400417
wreqs := make(map[string]*prompb.WriteRequest)
401418
replicas := make(map[string]replica)
402419
var i uint64
420+
421+
h.mtx.RLock()
422+
// It is possible that hashring is ready in testReady() but become unready now,
423+
// so we need to lock here.
424+
if h.hashring == nil {
425+
h.mtx.RLock()
426+
return errors.New("hashring is not ready")
427+
}
428+
403429
for i = 0; i < h.options.ReplicationFactor; i++ {
404430
endpoint, err := h.hashring.GetN(tenant, &wreq.Timeseries[0], i)
405431
if err != nil {
432+
h.mtx.RUnlock()
406433
return err
407434
}
408435
wreqs[endpoint] = wreq
409436
replicas[endpoint] = replica{i, true}
410437
}
438+
h.mtx.RUnlock()
411439

412440
err := h.parallelizeRequests(ctx, tenant, replicas, wreqs)
413441
if errs, ok := err.(terrors.MultiError); ok {

0 commit comments

Comments
 (0)