@@ -3,7 +3,6 @@ package bloomgateway
3
3
import (
4
4
"context"
5
5
"flag"
6
- "fmt"
7
6
"io"
8
7
"math"
9
8
"sort"
@@ -15,13 +14,15 @@ import (
15
14
ringclient "github.com/grafana/dskit/ring/client"
16
15
"github.com/pkg/errors"
17
16
"github.com/prometheus/client_golang/prometheus"
17
+ "golang.org/x/exp/slices"
18
18
"google.golang.org/grpc"
19
19
"google.golang.org/grpc/health/grpc_health_v1"
20
20
21
21
"github.com/grafana/loki/v3/pkg/logproto"
22
22
"github.com/grafana/loki/v3/pkg/logqlmodel/stats"
23
23
"github.com/grafana/loki/v3/pkg/querier/plan"
24
24
"github.com/grafana/loki/v3/pkg/queue"
25
+ v1 "github.com/grafana/loki/v3/pkg/storage/bloom/v1"
25
26
"github.com/grafana/loki/v3/pkg/storage/chunk/cache"
26
27
"github.com/grafana/loki/v3/pkg/storage/chunk/cache/resultscache"
27
28
"github.com/grafana/loki/v3/pkg/storage/stores/shipper/bloomshipper"
@@ -258,17 +259,6 @@ func (c *GatewayClient) FilterChunks(ctx context.Context, tenant string, interva
258
259
return rs .groups [i ].Fingerprint < rs .groups [j ].Fingerprint
259
260
})
260
261
261
- level .Info (c .logger ).Log (
262
- "msg" , "do FilterChunkRefs for addresses" ,
263
- "part" , fmt .Sprintf ("%d/%d" , i + 1 , len (servers )),
264
- "addr" , rs .addr ,
265
- "from" , interval .Start .Time (),
266
- "through" , interval .End .Time (),
267
- "series" , len (rs .groups ),
268
- "blocks" , len (rs .blocks ),
269
- "tenant" , tenant ,
270
- )
271
-
272
262
return c .doForAddrs ([]string {rs .addr }, func (client logproto.BloomGatewayClient ) error {
273
263
req := & logproto.FilterChunkRefRequest {
274
264
From : interval .Start ,
@@ -290,15 +280,95 @@ func (c *GatewayClient) FilterChunks(ctx context.Context, tenant string, interva
290
280
if err != nil {
291
281
return nil , err
292
282
}
293
- return flatten (results , count ), nil
283
+
284
+ buf := make ([]* logproto.GroupedChunkRefs , 0 , count )
285
+ return mergeSeries (results , buf )
294
286
}
295
287
296
- func flatten (input [][]* logproto.GroupedChunkRefs , n int ) []* logproto.GroupedChunkRefs {
297
- result := make ([]* logproto.GroupedChunkRefs , 0 , n )
298
- for _ , res := range input {
299
- result = append (result , res ... )
288
+ // mergeSeries combines respones from multiple FilterChunkRefs calls and deduplicates
289
+ // chunks from series that appear in multiple responses.
290
+ // To avoid allocations, an optional slice can be passed as second argument.
291
+ func mergeSeries (input [][]* logproto.GroupedChunkRefs , buf []* logproto.GroupedChunkRefs ) ([]* logproto.GroupedChunkRefs , error ) {
292
+ // clear provided buffer
293
+ buf = buf [:0 ]
294
+
295
+ iters := make ([]v1.PeekingIterator [* logproto.GroupedChunkRefs ], 0 , len (input ))
296
+ for _ , inp := range input {
297
+ iters = append (iters , v1 .NewPeekingIter (v1 .NewSliceIter (inp )))
300
298
}
301
- return result
299
+
300
+ heapIter := v1 .NewHeapIterator [* logproto.GroupedChunkRefs ](
301
+ func (a , b * logproto.GroupedChunkRefs ) bool {
302
+ return a .Fingerprint < b .Fingerprint
303
+ },
304
+ iters ... ,
305
+ )
306
+
307
+ dedupeIter := v1 .NewDedupingIter [* logproto.GroupedChunkRefs , * logproto.GroupedChunkRefs ](
308
+ // eq
309
+ func (a , b * logproto.GroupedChunkRefs ) bool { return a .Fingerprint == b .Fingerprint },
310
+ // from
311
+ v1 .Identity [* logproto .GroupedChunkRefs ],
312
+ // merge
313
+ func (a , b * logproto.GroupedChunkRefs ) * logproto.GroupedChunkRefs {
314
+ return & logproto.GroupedChunkRefs {
315
+ Fingerprint : a .Fingerprint ,
316
+ Tenant : a .Tenant ,
317
+ Refs : mergeChunks (a .Refs , b .Refs ),
318
+ }
319
+ },
320
+ // iterator
321
+ v1 .NewPeekingIter (heapIter ),
322
+ )
323
+
324
+ return v1 .CollectInto (dedupeIter , buf )
325
+ }
326
+
327
+ func mergeChunks (inputs ... []* logproto.ShortRef ) []* logproto.ShortRef {
328
+ if len (inputs ) == 0 {
329
+ return nil
330
+ }
331
+
332
+ if len (inputs ) == 1 {
333
+ slices .SortFunc (
334
+ inputs [0 ],
335
+ func (a , b * logproto.ShortRef ) int {
336
+ if a .Equal (b ) {
337
+ return 0
338
+ }
339
+ if a .From .Before (b .From ) || (a .From .Equal (b .From ) && a .Through .Before (b .Through )) {
340
+ return - 1
341
+ }
342
+ return 1
343
+ },
344
+ )
345
+ return inputs [0 ]
346
+ }
347
+
348
+ iters := make ([]v1.PeekingIterator [* logproto.ShortRef ], 0 , len (inputs ))
349
+ for _ , inp := range inputs {
350
+ iters = append (iters , v1 .NewPeekingIter (v1 .NewSliceIter (inp )))
351
+ }
352
+
353
+ chunkDedupe := v1 .NewDedupingIter [* logproto.ShortRef , * logproto.ShortRef ](
354
+ // eq
355
+ func (a , b * logproto.ShortRef ) bool { return a .Equal (b ) },
356
+ // from
357
+ v1 .Identity [* logproto .ShortRef ],
358
+ // merge
359
+ func (a , b * logproto.ShortRef ) * logproto.ShortRef { return a },
360
+ // iterator
361
+ v1.NewPeekingIter [* logproto.ShortRef ](
362
+ v1 .NewHeapIterator [* logproto.ShortRef ](
363
+ func (a , b * logproto.ShortRef ) bool {
364
+ return a .From .Before (b .From ) || (a .From .Equal (b .From ) && a .Through .Before (b .Through ))
365
+ },
366
+ iters ... ,
367
+ ),
368
+ ),
369
+ )
370
+ merged , _ := v1 .Collect (chunkDedupe )
371
+ return merged
302
372
}
303
373
304
374
// doForAddrs sequetially calls the provided callback function fn for each
0 commit comments