@@ -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
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
43
+ cfg DistributorConfig
44
+ clientsMtx sync.RWMutex
45
+ clients map [string ]* IngesterClient
46
46
47
47
queryDuration * prometheus.HistogramVec
48
48
receivedSamples prometheus.Counter
@@ -53,8 +53,8 @@ type Distributor struct {
53
53
type ReadRing interface {
54
54
prometheus.Collector
55
55
56
- Get (key uint32 ) (ring.IngesterDesc , error )
57
- GetAll () []ring.IngesterDesc
56
+ Get (key uint32 , n int , heartbeatTimeout time. Duration ) ([] ring.IngesterDesc , error )
57
+ GetAll (heartbeatTimeout time. Duration ) []ring.IngesterDesc
58
58
}
59
59
60
60
// IngesterClientFactory creates ingester clients.
@@ -65,14 +65,19 @@ type IngesterClientFactory func(string) (*IngesterClient, error)
65
65
type DistributorConfig struct {
66
66
Ring ReadRing
67
67
ClientFactory IngesterClientFactory
68
+
69
+ ReadReplicas int
70
+ WriteReplicas int
71
+ MinReadSuccesses int
72
+ MinWriteSuccesses int
73
+ HeartbeatTimeout time.Duration
68
74
}
69
75
70
76
// NewDistributor constructs a new Distributor
71
77
func NewDistributor (cfg DistributorConfig ) * Distributor {
72
78
return & Distributor {
73
- ring : cfg .Ring ,
74
- clientFactory : cfg .ClientFactory ,
75
- clients : map [string ]* IngesterClient {},
79
+ cfg : cfg ,
80
+ clients : map [string ]* IngesterClient {},
76
81
queryDuration : prometheus .NewHistogramVec (prometheus.HistogramOpts {
77
82
Namespace : "prometheus" ,
78
83
Name : "distributor_query_duration_seconds" ,
@@ -108,7 +113,7 @@ func (d *Distributor) getClientFor(hostname string) (*IngesterClient, error) {
108
113
return client , nil
109
114
}
110
115
111
- client , err := d .clientFactory (hostname )
116
+ client , err := d .cfg . ClientFactory (hostname )
112
117
if err != nil {
113
118
return nil , err
114
119
}
@@ -140,12 +145,14 @@ func (d *Distributor) Append(ctx context.Context, samples []*model.Sample) error
140
145
samplesByIngester := map [string ][]* model.Sample {}
141
146
for _ , sample := range samples {
142
147
key := tokenForMetric (userID , sample .Metric )
143
- collector , err := d .ring . Get (key )
148
+ ingesters , err := d .cfg . Ring . Get (key , d . cfg . WriteReplicas , d . cfg . HeartbeatTimeout )
144
149
if err != nil {
145
150
return err
146
151
}
147
- otherSamples := samplesByIngester [collector .Hostname ]
148
- samplesByIngester [collector .Hostname ] = append (otherSamples , sample )
152
+ for _ , ingester := range ingesters {
153
+ otherSamples := samplesByIngester [ingester .Hostname ]
154
+ samplesByIngester [ingester .Hostname ] = append (otherSamples , sample )
155
+ }
149
156
}
150
157
151
158
errs := make (chan error )
@@ -155,12 +162,19 @@ func (d *Distributor) Append(ctx context.Context, samples []*model.Sample) error
155
162
}(hostname , samples )
156
163
}
157
164
var lastErr error
165
+ successes := 0
158
166
for i := 0 ; i < len (samplesByIngester ); i ++ {
159
167
if err := <- errs ; err != nil {
160
168
lastErr = err
169
+ continue
161
170
}
171
+ successes ++
172
+ }
173
+
174
+ if successes < d .cfg .MinWriteSuccesses {
175
+ return fmt .Errorf ("too few successful writes, last error was: %v" , lastErr )
162
176
}
163
- return lastErr
177
+ return nil
164
178
}
165
179
166
180
func (d * Distributor ) sendSamples (ctx context.Context , hostname string , samples []* model.Sample ) error {
@@ -189,6 +203,8 @@ func metricNameFromLabelMatchers(matchers ...*metric.LabelMatcher) (model.LabelV
189
203
func (d * Distributor ) Query (ctx context.Context , from , to model.Time , matchers ... * metric.LabelMatcher ) (model.Matrix , error ) {
190
204
var result model.Matrix
191
205
err := instrument .TimeRequestHistogram ("duration" , d .queryDuration , func () error {
206
+ fpToSampleStream := map [model.Fingerprint ]* model.SampleStream {}
207
+
192
208
metricName , err := metricNameFromLabelMatchers (matchers ... )
193
209
if err != nil {
194
210
return err
@@ -199,29 +215,83 @@ func (d *Distributor) Query(ctx context.Context, from, to model.Time, matchers .
199
215
return err
200
216
}
201
217
202
- collector , err := d .ring . Get (tokenFor (userID , metricName ))
218
+ ingesters , err := d .cfg . Ring . Get (tokenFor (userID , metricName ), d . cfg . ReadReplicas , d . cfg . HeartbeatTimeout )
203
219
if err != nil {
204
220
return err
205
221
}
206
222
207
- client , err := d .getClientFor (collector .Hostname )
208
- if err != nil {
209
- return err
223
+ // Fetch samples from multiple ingesters and group them by fingerprint (unsorted
224
+ // and with overlap).
225
+ successes := 0
226
+ var lastErr error
227
+ for _ , ing := range ingesters {
228
+ client , err := d .getClientFor (ing .Hostname )
229
+ if err != nil {
230
+ return err
231
+ }
232
+ matrix , err := client .Query (ctx , from , to , matchers ... )
233
+ if err != nil {
234
+ lastErr = err
235
+ continue
236
+ }
237
+ successes ++
238
+
239
+ for _ , ss := range matrix {
240
+ fp := ss .Metric .Fingerprint ()
241
+ if mss , ok := fpToSampleStream [fp ]; ! ok {
242
+ fpToSampleStream [fp ] = & model.SampleStream {
243
+ Metric : ss .Metric ,
244
+ Values : ss .Values ,
245
+ }
246
+ } else {
247
+ mss .Values = mergeSamples (fpToSampleStream [fp ].Values , ss .Values )
248
+ }
249
+ }
210
250
}
211
251
212
- result , err = client .Query (ctx , from , to , matchers ... )
213
- if err != nil {
214
- return err
252
+ if successes < d .cfg .MinReadSuccesses {
253
+ return fmt .Errorf ("too few successful reads, last error was: %v" , lastErr )
254
+ }
255
+
256
+ result = make (model.Matrix , 0 , len (fpToSampleStream ))
257
+ for _ , ss := range fpToSampleStream {
258
+ result = append (result , ss )
215
259
}
260
+
216
261
return nil
217
262
})
218
263
return result , err
219
264
}
220
265
266
+ func mergeSamples (a , b []model.SamplePair ) []model.SamplePair {
267
+ result := make ([]model.SamplePair , 0 , len (a )+ len (b ))
268
+ i , j := 0 , 0
269
+ for i < len (a ) && j < len (b ) {
270
+ if a [i ].Timestamp < b [j ].Timestamp {
271
+ result = append (result , a [i ])
272
+ i ++
273
+ } else if a [i ].Timestamp > b [j ].Timestamp {
274
+ result = append (result , b [j ])
275
+ j ++
276
+ } else {
277
+ result = append (result , a [i ])
278
+ i ++
279
+ j ++
280
+ }
281
+ }
282
+ for ; i < len (a ); i ++ {
283
+ result = append (result , a [i ])
284
+ }
285
+ for ; j < len (b ); j ++ {
286
+ result = append (result , b [j ])
287
+ }
288
+ return result
289
+ }
290
+
221
291
// LabelValuesForLabelName returns all of the label values that are associated with a given label name.
222
292
func (d * Distributor ) LabelValuesForLabelName (ctx context.Context , labelName model.LabelName ) (model.LabelValues , error ) {
223
293
valueSet := map [model.LabelValue ]struct {}{}
224
- for _ , c := range d .ring . GetAll () {
294
+ for _ , c := range d .cfg . Ring . GetAll (d . cfg . HeartbeatTimeout ) {
225
295
client , err := d .getClientFor (c .Hostname )
226
296
if err != nil {
227
297
return nil , err
@@ -252,7 +322,7 @@ func (d *Distributor) Describe(ch chan<- *prometheus.Desc) {
252
322
d .queryDuration .Describe (ch )
253
323
ch <- d .receivedSamples .Desc ()
254
324
d .sendDuration .Describe (ch )
255
- d .ring .Describe (ch )
325
+ d .cfg . Ring .Describe (ch )
256
326
ch <- numClientsDesc
257
327
}
258
328
@@ -261,7 +331,7 @@ func (d *Distributor) Collect(ch chan<- prometheus.Metric) {
261
331
d .queryDuration .Collect (ch )
262
332
ch <- d .receivedSamples
263
333
d .sendDuration .Collect (ch )
264
- d .ring .Collect (ch )
334
+ d .cfg . Ring .Collect (ch )
265
335
266
336
d .clientsMtx .RLock ()
267
337
defer d .clientsMtx .RUnlock ()
0 commit comments