@@ -17,6 +17,7 @@ import (
17
17
"fmt"
18
18
"hash/fnv"
19
19
"sync"
20
+ "time"
20
21
21
22
"github.com/prometheus/client_golang/prometheus"
22
23
"github.com/prometheus/common/model"
@@ -39,22 +40,25 @@ var (
39
40
// Distributor is a storage.SampleAppender and a prism.Querier which
40
41
// forwards appends and queries to individual ingesters.
41
42
type Distributor struct {
42
- ring ReadRing
43
- clientFactory IngesterClientFactory
44
- clientsMtx sync.RWMutex
45
- clients map [string ]* IngesterClient
46
-
47
- queryDuration * prometheus.HistogramVec
48
- receivedSamples prometheus.Counter
49
- sendDuration * prometheus.HistogramVec
43
+ cfg DistributorConfig
44
+ clientsMtx sync.RWMutex
45
+ clients map [string ]* IngesterClient
46
+
47
+ queryDuration * prometheus.HistogramVec
48
+ receivedSamples prometheus.Counter
49
+ sendDuration * prometheus.HistogramVec
50
+ ingesterAppends * prometheus.CounterVec
51
+ ingesterAppendFailures * prometheus.CounterVec
52
+ ingesterQueries * prometheus.CounterVec
53
+ ingesterQueryFailures * prometheus.CounterVec
50
54
}
51
55
52
56
// ReadRing represents the read inferface to the ring.
53
57
type ReadRing interface {
54
58
prometheus.Collector
55
59
56
- Get (key uint32 ) (ring.IngesterDesc , error )
57
- GetAll () []ring.IngesterDesc
60
+ Get (key uint32 , n int , heartbeatTimeout time. Duration ) ([] ring.IngesterDesc , error )
61
+ GetAll (heartbeatTimeout time. Duration ) []ring.IngesterDesc
58
62
}
59
63
60
64
// IngesterClientFactory creates ingester clients.
@@ -65,14 +69,18 @@ type IngesterClientFactory func(string) (*IngesterClient, error)
65
69
type DistributorConfig struct {
66
70
Ring ReadRing
67
71
ClientFactory IngesterClientFactory
72
+
73
+ ReplicationFactor int
74
+ MinReadSuccesses int
75
+ MinWriteSuccesses int
76
+ HeartbeatTimeout time.Duration
68
77
}
69
78
70
79
// NewDistributor constructs a new Distributor
71
80
func NewDistributor (cfg DistributorConfig ) * Distributor {
72
81
return & Distributor {
73
- ring : cfg .Ring ,
74
- clientFactory : cfg .ClientFactory ,
75
- clients : map [string ]* IngesterClient {},
82
+ cfg : cfg ,
83
+ clients : map [string ]* IngesterClient {},
76
84
queryDuration : prometheus .NewHistogramVec (prometheus.HistogramOpts {
77
85
Namespace : "prometheus" ,
78
86
Name : "distributor_query_duration_seconds" ,
@@ -87,9 +95,29 @@ func NewDistributor(cfg DistributorConfig) *Distributor {
87
95
sendDuration : prometheus .NewHistogramVec (prometheus.HistogramOpts {
88
96
Namespace : "prometheus" ,
89
97
Name : "distributor_send_duration_seconds" ,
90
- Help : "Time spent sending sample batches to ingesters." ,
98
+ Help : "Time spent sending a sample batch to multiple replicated ingesters." ,
91
99
Buckets : []float64 {.001 , .0025 , .005 , .01 , .025 , .05 , .1 , .25 , .5 , 1 },
92
100
}, []string {"method" , "status_code" }),
101
+ ingesterAppends : prometheus .NewCounterVec (prometheus.CounterOpts {
102
+ Namespace : "prometheus" ,
103
+ Name : "distributor_ingester_appends_total" ,
104
+ Help : "The total number of batch appends sent to ingesters." ,
105
+ }, []string {"ingester" }),
106
+ ingesterAppendFailures : prometheus .NewCounterVec (prometheus.CounterOpts {
107
+ Namespace : "prometheus" ,
108
+ Name : "distributor_ingester_appends_total" ,
109
+ Help : "The total number of failed batch appends sent to ingesters." ,
110
+ }, []string {"ingester" }),
111
+ ingesterQueries : prometheus .NewCounterVec (prometheus.CounterOpts {
112
+ Namespace : "prometheus" ,
113
+ Name : "distributor_ingester_queries_total" ,
114
+ Help : "The total number of queries sent to ingesters." ,
115
+ }, []string {"ingester" }),
116
+ ingesterQueryFailures : prometheus .NewCounterVec (prometheus.CounterOpts {
117
+ Namespace : "prometheus" ,
118
+ Name : "distributor_ingester_appends_total" ,
119
+ Help : "The total number of failed queries sent to ingesters." ,
120
+ }, []string {"ingester" }),
93
121
}
94
122
}
95
123
@@ -108,7 +136,7 @@ func (d *Distributor) getClientFor(hostname string) (*IngesterClient, error) {
108
136
return client , nil
109
137
}
110
138
111
- client , err := d .clientFactory (hostname )
139
+ client , err := d .cfg . ClientFactory (hostname )
112
140
if err != nil {
113
141
return nil , err
114
142
}
@@ -140,12 +168,14 @@ func (d *Distributor) Append(ctx context.Context, samples []*model.Sample) error
140
168
samplesByIngester := map [string ][]* model.Sample {}
141
169
for _ , sample := range samples {
142
170
key := tokenForMetric (userID , sample .Metric )
143
- collector , err := d .ring . Get (key )
171
+ ingesters , err := d .cfg . Ring . Get (key , d . cfg . ReplicationFactor , d . cfg . HeartbeatTimeout )
144
172
if err != nil {
145
173
return err
146
174
}
147
- otherSamples := samplesByIngester [collector .Hostname ]
148
- samplesByIngester [collector .Hostname ] = append (otherSamples , sample )
175
+ for _ , ingester := range ingesters {
176
+ otherSamples := samplesByIngester [ingester .Hostname ]
177
+ samplesByIngester [ingester .Hostname ] = append (otherSamples , sample )
178
+ }
149
179
}
150
180
151
181
errs := make (chan error )
@@ -155,22 +185,34 @@ func (d *Distributor) Append(ctx context.Context, samples []*model.Sample) error
155
185
}(hostname , samples )
156
186
}
157
187
var lastErr error
188
+ successes := 0
158
189
for i := 0 ; i < len (samplesByIngester ); i ++ {
159
190
if err := <- errs ; err != nil {
160
191
lastErr = err
192
+ continue
161
193
}
194
+ successes ++
195
+ }
196
+
197
+ if successes < d .cfg .MinWriteSuccesses {
198
+ return fmt .Errorf ("too few successful writes, last error was: %v" , lastErr )
162
199
}
163
- return lastErr
200
+ return nil
164
201
}
165
202
166
203
func (d * Distributor ) sendSamples (ctx context.Context , hostname string , samples []* model.Sample ) error {
167
204
client , err := d .getClientFor (hostname )
168
205
if err != nil {
169
206
return err
170
207
}
171
- return instrument .TimeRequestHistogram ("send" , d .sendDuration , func () error {
208
+ err = instrument .TimeRequestHistogram ("send" , d .sendDuration , func () error {
172
209
return client .Append (ctx , samples )
173
210
})
211
+ if err != nil {
212
+ d .ingesterAppendFailures .WithLabelValues (hostname ).Inc ()
213
+ }
214
+ d .ingesterAppends .WithLabelValues (hostname ).Inc ()
215
+ return err
174
216
}
175
217
176
218
func metricNameFromLabelMatchers (matchers ... * metric.LabelMatcher ) (model.LabelValue , error ) {
@@ -189,6 +231,8 @@ func metricNameFromLabelMatchers(matchers ...*metric.LabelMatcher) (model.LabelV
189
231
func (d * Distributor ) Query (ctx context.Context , from , to model.Time , matchers ... * metric.LabelMatcher ) (model.Matrix , error ) {
190
232
var result model.Matrix
191
233
err := instrument .TimeRequestHistogram ("duration" , d .queryDuration , func () error {
234
+ fpToSampleStream := map [model.Fingerprint ]* model.SampleStream {}
235
+
192
236
metricName , err := metricNameFromLabelMatchers (matchers ... )
193
237
if err != nil {
194
238
return err
@@ -199,29 +243,85 @@ func (d *Distributor) Query(ctx context.Context, from, to model.Time, matchers .
199
243
return err
200
244
}
201
245
202
- collector , err := d .ring . Get (tokenFor (userID , metricName ))
246
+ ingesters , err := d .cfg . Ring . Get (tokenFor (userID , metricName ), d . cfg . ReplicationFactor , d . cfg . HeartbeatTimeout )
203
247
if err != nil {
204
248
return err
205
249
}
206
250
207
- client , err := d .getClientFor (collector .Hostname )
208
- if err != nil {
209
- return err
251
+ // Fetch samples from multiple ingesters and group them by fingerprint (unsorted
252
+ // and with overlap).
253
+ successes := 0
254
+ var lastErr error
255
+ for _ , ing := range ingesters {
256
+ client , err := d .getClientFor (ing .Hostname )
257
+ if err != nil {
258
+ return err
259
+ }
260
+ matrix , err := client .Query (ctx , from , to , matchers ... )
261
+ d .ingesterQueries .WithLabelValues (ing .Hostname ).Inc ()
262
+ if err != nil {
263
+ lastErr = err
264
+ d .ingesterQueryFailures .WithLabelValues (ing .Hostname ).Inc ()
265
+ continue
266
+ }
267
+ successes ++
268
+
269
+ for _ , ss := range matrix {
270
+ fp := ss .Metric .Fingerprint ()
271
+ if mss , ok := fpToSampleStream [fp ]; ! ok {
272
+ fpToSampleStream [fp ] = & model.SampleStream {
273
+ Metric : ss .Metric ,
274
+ Values : ss .Values ,
275
+ }
276
+ } else {
277
+ mss .Values = mergeSamples (fpToSampleStream [fp ].Values , ss .Values )
278
+ }
279
+ }
210
280
}
211
281
212
- result , err = client .Query (ctx , from , to , matchers ... )
213
- if err != nil {
214
- return err
282
+ if successes < d .cfg .MinReadSuccesses {
283
+ return fmt .Errorf ("too few successful reads, last error was: %v" , lastErr )
215
284
}
285
+
286
+ result = make (model.Matrix , 0 , len (fpToSampleStream ))
287
+ for _ , ss := range fpToSampleStream {
288
+ result = append (result , ss )
289
+ }
290
+
216
291
return nil
217
292
})
218
293
return result , err
219
294
}
220
295
296
+ func mergeSamples (a , b []model.SamplePair ) []model.SamplePair {
297
+ result := make ([]model.SamplePair , 0 , len (a )+ len (b ))
298
+ i , j := 0 , 0
299
+ for i < len (a ) && j < len (b ) {
300
+ if a [i ].Timestamp < b [j ].Timestamp {
301
+ result = append (result , a [i ])
302
+ i ++
303
+ } else if a [i ].Timestamp > b [j ].Timestamp {
304
+ result = append (result , b [j ])
305
+ j ++
306
+ } else {
307
+ result = append (result , a [i ])
308
+ i ++
309
+ j ++
310
+ }
311
+ }
312
+ for ; i < len (a ); i ++ {
313
+ result = append (result , a [i ])
314
+ }
315
+ for ; j < len (b ); j ++ {
316
+ result = append (result , b [j ])
317
+ }
318
+ return result
319
+ }
320
+
221
321
// LabelValuesForLabelName returns all of the label values that are associated with a given label name.
222
322
func (d * Distributor ) LabelValuesForLabelName (ctx context.Context , labelName model.LabelName ) (model.LabelValues , error ) {
223
323
valueSet := map [model.LabelValue ]struct {}{}
224
- for _ , c := range d .ring . GetAll () {
324
+ for _ , c := range d .cfg . Ring . GetAll (d . cfg . HeartbeatTimeout ) {
225
325
client , err := d .getClientFor (c .Hostname )
226
326
if err != nil {
227
327
return nil , err
@@ -252,7 +352,7 @@ func (d *Distributor) Describe(ch chan<- *prometheus.Desc) {
252
352
d .queryDuration .Describe (ch )
253
353
ch <- d .receivedSamples .Desc ()
254
354
d .sendDuration .Describe (ch )
255
- d .ring .Describe (ch )
355
+ d .cfg . Ring .Describe (ch )
256
356
ch <- numClientsDesc
257
357
}
258
358
@@ -261,7 +361,7 @@ func (d *Distributor) Collect(ch chan<- prometheus.Metric) {
261
361
d .queryDuration .Collect (ch )
262
362
ch <- d .receivedSamples
263
363
d .sendDuration .Collect (ch )
264
- d .ring .Collect (ch )
364
+ d .cfg . Ring .Collect (ch )
265
365
266
366
d .clientsMtx .RLock ()
267
367
defer d .clientsMtx .RUnlock ()
0 commit comments