Skip to content

Commit 6bb3487

Browse files
authored
Merge pull request #1260 from cortexproject/report-unready-reason
Check ingester has tokens before reporting ready
2 parents f44cd20 + d0f35d8 commit 6bb3487

File tree

3 files changed

+27
-15
lines changed

3 files changed

+27
-15
lines changed

pkg/ingester/ingester.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -569,9 +569,9 @@ func (i *Ingester) Watch(in *grpc_health_v1.HealthCheckRequest, stream grpc_heal
569569
// the addition removal of another ingester. Returns 204 when the ingester is
570570
// ready, 500 otherwise.
571571
func (i *Ingester) ReadinessHandler(w http.ResponseWriter, r *http.Request) {
572-
if i.lifecycler.IsReady(r.Context()) {
572+
if err := i.lifecycler.CheckReady(r.Context()); err == nil {
573573
w.WriteHeader(http.StatusNoContent)
574574
} else {
575-
w.WriteHeader(http.StatusServiceUnavailable)
575+
http.Error(w, "Not ready: "+err.Error(), http.StatusServiceUnavailable)
576576
}
577577
}

pkg/ring/lifecycler.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -153,30 +153,38 @@ func NewLifecycler(cfg LifecyclerConfig, flushTransferer FlushTransferer) (*Life
153153
return l, nil
154154
}
155155

156-
// IsReady is used to rate limit the number of ingesters that can be coming or
156+
// CheckReady is used to rate limit the number of ingesters that can be coming or
157157
// going at any one time, by only returning true if all ingesters are active.
158-
func (i *Lifecycler) IsReady(ctx context.Context) bool {
158+
// The state latches: once we have gone ready we don't go un-ready
159+
func (i *Lifecycler) CheckReady(ctx context.Context) error {
159160
i.readyLock.Lock()
160161
defer i.readyLock.Unlock()
161162

162163
if i.ready {
163-
return true
164+
return nil
164165
}
165166

166167
// Ingester always take at least minReadyDuration to become ready to work
167168
// around race conditions with ingesters exiting and updating the ring
168169
if time.Now().Sub(i.startTime) < i.cfg.MinReadyDuration {
169-
return false
170+
return fmt.Errorf("waiting for %v after startup", i.cfg.MinReadyDuration)
170171
}
171172

172173
ringDesc, err := i.KVStore.Get(ctx, ConsulKey)
173174
if err != nil {
174175
level.Error(util.Logger).Log("msg", "error talking to consul", "err", err)
175-
return false
176+
return fmt.Errorf("error talking to consul: %s", err)
176177
}
177178

178-
i.ready = i.ready || ringDesc.(*Desc).Ready(i.cfg.RingConfig.HeartbeatTimeout)
179-
return i.ready
179+
if len(i.getTokens()) == 0 {
180+
return fmt.Errorf("this ingester owns no tokens")
181+
}
182+
if err := ringDesc.(*Desc).Ready(i.cfg.RingConfig.HeartbeatTimeout); err != nil {
183+
return err
184+
}
185+
186+
i.ready = true
187+
return nil
180188
}
181189

182190
// GetState returns the state of this ingester.

pkg/ring/model.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package ring
22

33
import (
4+
"fmt"
45
"sort"
56
"time"
67

@@ -115,19 +116,22 @@ func (d *Desc) FindIngestersByState(state IngesterState) []IngesterDesc {
115116
return result
116117
}
117118

118-
// Ready is true when all ingesters are active and healthy.
119-
func (d *Desc) Ready(heartbeatTimeout time.Duration) bool {
119+
// Ready returns no error when all ingesters are active and healthy.
120+
func (d *Desc) Ready(heartbeatTimeout time.Duration) error {
120121
numTokens := len(d.Tokens)
121-
for _, ingester := range d.Ingesters {
122+
for id, ingester := range d.Ingesters {
122123
if time.Now().Sub(time.Unix(ingester.Timestamp, 0)) > heartbeatTimeout {
123-
return false
124+
return fmt.Errorf("ingester %s past heartbeat timeout", id)
124125
} else if ingester.State != ACTIVE {
125-
return false
126+
return fmt.Errorf("ingester %s in state %v", id, ingester.State)
126127
}
127128
numTokens += len(ingester.Tokens)
128129
}
129130

130-
return numTokens > 0
131+
if numTokens == 0 {
132+
return fmt.Errorf("Not ready: no tokens in ring")
133+
}
134+
return nil
131135
}
132136

133137
// TokensFor partitions the tokens into those for the given ID, and those for others.

0 commit comments

Comments
 (0)