Repository: grafana/loki
Pull request #16937: fix(ingest-limits): Get usage from owing instances only
Author: login: periklis
Labels: name: size/L
name: type/docs
description: Issues related to technical documentation; the Docs Squad uses this label across many repositories
Created: 2025-03-27T19:31:41Z | Merged: 2025-04-04T13:41:54Z

**What this PR does / why we need it**:
This PR updates the stream usage retrieval routine in the ingest-limits-frontend component to query only ingest-limits instances that own the requested streams. The purpose of this optimization is to streamline traffic between these components to necessary minimum per `ExceedLimits` request.

**Which issue(s) this PR fixes**:
Fixes #<issue number>

**Special notes for your reviewer**:

**Checklist**
- [x] Reviewed the [`CONTRIBUTING.md`](https://github.com/grafana/loki/blob/main/CONTRIBUTING.md) guide (**required**)
- [x] Documentation added
- [x] Tests updated
- [x] Title matches the required conventional commits format, see [here](https://www.conventionalcommits.org/en/v1.0.0/)
  - **Note** that Promtail is considered to be feature complete, and future development for logs collection will be in [Grafana Alloy](https://github.com/grafana/alloy). As such, `feat` PRs are unlikely to be accepted unless a case can be made for the feature actually being a bug fix to existing behavior.
- [ ] Changes that require user attention or interaction to upgrade are documented in `docs/sources/setup/upgrade/_index.md`
- [ ] If the change is deprecating or removing a configuration option, update the `deprecated-config.yaml` and `deleted-config.yaml` files respectively in the `tools/deprecated-config-checker` directory. [Example PR](https://github.com/grafana/loki/pull/10840/commits/0d4416a4b03739583349934b96f272fb4f685d15)



Diff:
diff --git a/docs/sources/shared/configuration.md b/docs/sources/shared/configuration.md
index 8fd5eee31cf..caf4b440483 100644
--- a/docs/sources/shared/configuration.md
+++ b/docs/sources/shared/configuration.md
@@ -1205,6 +1205,10 @@ ingest_limits_frontend:
   # CLI flag: -ingest-limits-frontend.recheck-period
   [recheck_period: <duration> | default = 10s]
 
+  # The number of partitions to use for the ring.
+  # CLI flag: -ingest-limits-frontend.num-partitions
+  [num_partitions: <int> | default = 64]
+
 ingest_limits_frontend_client:
   # Configures client gRPC connections to limits service.
   # The CLI flags prefix for this block configuration is:
diff --git a/pkg/limits/frontend/config.go b/pkg/limits/frontend/config.go
index d740f57ce03..bd3bcce71d6 100644
--- a/pkg/limits/frontend/config.go
+++ b/pkg/limits/frontend/config.go
@@ -16,12 +16,14 @@ type Config struct {
 	ClientConfig     limits_client.Config  `yaml:"client_config"`
 	LifecyclerConfig ring.LifecyclerConfig `yaml:"lifecycler,omitempty"`
 	RecheckPeriod    time.Duration         `yaml:"recheck_period"`
+	NumPartitions    int                   `yaml:"num_partitions"`
 }
 
 func (cfg *Config) RegisterFlags(f *flag.FlagSet) {
 	cfg.ClientConfig.RegisterFlagsWithPrefix("ingest-limits-frontend", f)
 	cfg.LifecyclerConfig.RegisterFlagsWithPrefix("ingest-limits-frontend.", f, util_log.Logger)
 	f.DurationVar(&cfg.RecheckPeriod, "ingest-limits-frontend.recheck-period", 10*time.Second, "The period to recheck per tenant ingestion rate limit configuration.")
+	f.IntVar(&cfg.NumPartitions, "ingest-limits-frontend.num-partitions", 64, "The number of partitions to use for the ring.")
 }
 
 func (cfg *Config) Validate() error {
diff --git a/pkg/limits/frontend/frontend.go b/pkg/limits/frontend/frontend.go
index 17bb6ebb483..c8111fef492 100644
--- a/pkg/limits/frontend/frontend.go
+++ b/pkg/limits/frontend/frontend.go
@@ -84,7 +84,7 @@ func New(cfg Config, ringName string, limitsRing ring.ReadRing, limits Limits, l
 	)
 
 	rateLimiter := limiter.NewRateLimiter(newRateLimitsAdapter(limits), cfg.RecheckPeriod)
-	streamUsage := NewRingStreamUsageGatherer(limitsRing, clientPool, logger)
+	streamUsage := NewRingStreamUsageGatherer(limitsRing, clientPool, logger, cfg.NumPartitions)
 
 	f := &Frontend{
 		cfg:         cfg,
diff --git a/pkg/limits/frontend/frontend_test.go b/pkg/limits/frontend/frontend_test.go
index d312a487600..5f7dcd61622 100644
--- a/pkg/limits/frontend/frontend_test.go
+++ b/pkg/limits/frontend/frontend_test.go
@@ -48,7 +48,6 @@ func TestFrontend_ExceedsLimits(t *testing.T) {
 		expectedStreamUsageRequest: []*logproto.GetStreamUsageRequest{{
 			Tenant:       "test",
 			StreamHashes: []uint64{0x1},
-			Partitions:   []int32{0},
 		}},
 		getStreamUsageResponses: []*logproto.GetStreamUsageResponse{{}},
 		maxGlobalStreams:        10,
@@ -71,7 +70,6 @@ func TestFrontend_ExceedsLimits(t *testing.T) {
 		expectedStreamUsageRequest: []*logproto.GetStreamUsageRequest{{
 			Tenant:       "test",
 			StreamHashes: []uint64{0x1, 0x2},
-			Partitions:   []int32{0},
 		}},
 		getStreamUsageResponses: []*logproto.GetStreamUsageResponse{{
 			Tenant:        "test",
@@ -98,7 +96,6 @@ func TestFrontend_ExceedsLimits(t *testing.T) {
 		expectedStreamUsageRequest: []*logproto.GetStreamUsageRequest{{
 			Tenant:       "test",
 			StreamHashes: []uint64{0x1, 0x2},
-			Partitions:   []int32{0},
 		}},
 		getStreamUsageResponses: []*logproto.GetStreamUsageResponse{{
 			Tenant:         "test",
@@ -134,7 +131,6 @@ func TestFrontend_ExceedsLimits(t *testing.T) {
 		expectedStreamUsageRequest: []*logproto.GetStreamUsageRequest{{
 			Tenant:       "test",
 			StreamHashes: []uint64{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7},
-			Partitions:   []int32{0},
 		}},
 		getStreamUsageResponses: []*logproto.GetStreamUsageResponse{{
 			Tenant:         "test",
@@ -176,11 +172,9 @@ func TestFrontend_ExceedsLimits(t *testing.T) {
 		expectedStreamUsageRequest: []*logproto.GetStreamUsageRequest{{
 			Tenant:       "test",
 			StreamHashes: []uint64{0x1, 0x2},
-			Partitions:   []int32{0},
 		}, {
 			Tenant:       "test",
 			StreamHashes: []uint64{0x1, 0x2},
-			Partitions:   []int32{1},
 		}},
 		// Each instance will respond stating that it doesn't know about the
 		// other stream.
@@ -293,7 +287,7 @@ func TestFrontend_ExceedsLimits(t *testing.T) {
 			clients := make([]logproto.IngestLimitsClient, len(test.getAssignedPartitionsResponses))
 			instances := make([]ring.InstanceDesc, len(clients))
 
-			for i := 0; i < len(test.getAssignedPartitionsResponses); i++ {
+			for i := range test.getAssignedPartitionsResponses {
 				clients[i] = &mockIngestLimitsClient{
 					getAssignedPartitionsResponse: test.getAssignedPartitionsResponses[i],
 					getStreamUsageResponse:        test.getStreamUsageResponses[i],
@@ -315,7 +309,7 @@ func TestFrontend_ExceedsLimits(t *testing.T) {
 			f := Frontend{
 				limits:      l,
 				rateLimiter: rl,
-				streamUsage: NewRingStreamUsageGatherer(readRing, clientPool, log.NewNopLogger()),
+				streamUsage: NewRingStreamUsageGatherer(readRing, clientPool, log.NewNopLogger(), 2),
 				metrics:     newMetrics(prometheus.NewRegistry()),
 			}
 
diff --git a/pkg/limits/frontend/ring.go b/pkg/limits/frontend/ring.go
index 36db8f8e6a9..d1364cd3ba2 100644
--- a/pkg/limits/frontend/ring.go
+++ b/pkg/limits/frontend/ring.go
@@ -2,9 +2,9 @@ package frontend
 
 import (
 	"context"
-	"slices"
 
 	"github.com/go-kit/log"
+	"github.com/go-kit/log/level"
 	"github.com/grafana/dskit/ring"
 	ring_client "github.com/grafana/dskit/ring/client"
 	"golang.org/x/sync/errgroup"
@@ -24,17 +24,19 @@ var (
 // RingStreamUsageGatherer implements StreamUsageGatherer. It uses a ring to find
 // limits instances.
 type RingStreamUsageGatherer struct {
-	logger log.Logger
-	ring   ring.ReadRing
-	pool   *ring_client.Pool
+	logger        log.Logger
+	ring          ring.ReadRing
+	pool          *ring_client.Pool
+	numPartitions int
 }
 
 // NewRingStreamUsageGatherer returns a new RingStreamUsageGatherer.
-func NewRingStreamUsageGatherer(ring ring.ReadRing, pool *ring_client.Pool, logger log.Logger) *RingStreamUsageGatherer {
+func NewRingStreamUsageGatherer(ring ring.ReadRing, pool *ring_client.Pool, logger log.Logger, numPartitions int) *RingStreamUsageGatherer {
 	return &RingStreamUsageGatherer{
-		logger: logger,
-		ring:   ring,
-		pool:   pool,
+		logger:        logger,
+		ring:          ring,
+		pool:          pool,
+		numPartitions: numPartitions,
 	}
 }
 
@@ -56,32 +58,48 @@ func (g *RingStreamUsageGatherer) forAllBackends(ctx context.Context, r GetStrea
 }
 
 func (g *RingStreamUsageGatherer) forGivenReplicaSet(ctx context.Context, rs ring.ReplicationSet, r GetStreamUsageRequest) ([]GetStreamUsageResponse, error) {
-	partitions, err := g.getPartitionConsumers(ctx, rs)
+	partitionConsumers, err := g.getPartitionConsumers(ctx, rs)
 	if err != nil {
 		return nil, err
 	}
 
+	instancesToQuery := make(map[string][]uint64)
+	for _, hash := range r.StreamHashes {
+		partitionID := int32(hash % uint64(g.numPartitions))
+		addr, ok := partitionConsumers[partitionID]
+		if !ok {
+			// TODO Replace with a metric for partitions missing owners.
+			level.Warn(g.logger).Log("msg", "no instance found for partition", "partition", partitionID)
+			continue
+		}
+		instancesToQuery[addr] = append(instancesToQuery[addr], hash)
+	}
+
 	errg, ctx := errgroup.WithContext(ctx)
-	responses := make([]GetStreamUsageResponse, len(rs.Instances))
+	responses := make([]GetStreamUsageResponse, len(instancesToQuery))
 
-	// TODO: We shouldn't query all instances since we know which instance holds which stream.
-	for i, instance := range rs.Instances {
+	// Query each instance for stream usage
+	i := 0
+	for addr, hashes := range instancesToQuery {
+		j := i
+		i++
 		errg.Go(func() error {
-			client, err := g.pool.GetClientFor(instance.Addr)
+			client, err := g.pool.GetClientFor(addr)
 			if err != nil {
 				return err
 			}
+
 			protoReq := &logproto.GetStreamUsageRequest{
 				Tenant:       r.Tenant,
-				StreamHashes: r.StreamHashes,
-				Partitions:   partitions[instance.Addr],
+				StreamHashes: hashes,
 			}
 
 			resp, err := client.(logproto.IngestLimitsClient).GetStreamUsage(ctx, protoReq)
 			if err != nil {
 				return err
 			}
-			responses[i] = GetStreamUsageResponse{Addr: instance.Addr, Response: resp}
+
+			responses[j] = GetStreamUsageResponse{Addr: addr, Response: resp}
 			return nil
 		})
 	}
@@ -98,7 +116,7 @@ type getAssignedPartitionsResponse struct {
 	Response *logproto.GetAssignedPartitionsResponse
 }
 
-func (g *RingStreamUsageGatherer) getPartitionConsumers(ctx context.Context, rs ring.ReplicationSet) (map[string][]int32, error) {
+func (g *RingStreamUsageGatherer) getPartitionConsumers(ctx context.Context, rs ring.ReplicationSet) (map[int32]string, error) {
 	errg, ctx := errgroup.WithContext(ctx)
 	responses := make([]getAssignedPartitionsResponse, len(rs.Instances))
 
@@ -139,16 +157,5 @@ func (g *RingStreamUsageGatherer) getPartitionConsumers(ctx context.Context, rs
 		}
 	}
 
-	// Return a slice of partition IDs for each instance.
-	result := make(map[string][]int32)
-	for partition, addr := range assigned {
-		result[addr] = append(result[addr], partition)
-	}
-
-	// Sort the partition IDs.
-	for instance := range result {
-		slices.Sort(result[instance])
-	}
-
-	return result, nil
+	return assigned, nil
 }
diff --git a/pkg/limits/frontend/ring_test.go b/pkg/limits/frontend/ring_test.go
index 00321b7b087..e7621d0cf69 100644
--- a/pkg/limits/frontend/ring_test.go
+++ b/pkg/limits/frontend/ring_test.go
@@ -14,6 +14,7 @@ import (
 )
 
 func TestRingStreamUsageGatherer_GetStreamUsage(t *testing.T) {
+	const numPartitions = 2 // Using 2 partitions for simplicity in tests
 	tests := []struct {
 		name                              string
 		getStreamUsageRequest             GetStreamUsageRequest
@@ -35,18 +36,17 @@ func TestRingStreamUsageGatherer_GetStreamUsage(t *testing.T) {
 		name: "one stream",
 		getStreamUsageRequest: GetStreamUsageRequest{
 			Tenant:       "test",
-			StreamHashes: []uint64{0x1},
+			StreamHashes: []uint64{1}, // Hash 1 maps to partition 1
 		},
 		expectedAssignedPartitionsRequest: []*logproto.GetAssignedPartitionsRequest{{}},
 		getAssignedPartitionsResponses: []*logproto.GetAssignedPartitionsResponse{{
 			AssignedPartitions: map[int32]int64{
-				0: time.Now().UnixNano(),
+				1: time.Now().UnixNano(),
 			},
 		}},
 		expectedStreamUsageRequest: []*logproto.GetStreamUsageRequest{{
 			Tenant:       "test",
-			StreamHashes: []uint64{0x1},
-			Partitions:   []int32{0},
+			StreamHashes: []uint64{1},
 		}},
 		getStreamUsageResponses: []*logproto.GetStreamUsageResponse{{
 			Tenant:        "test",
@@ -63,14 +63,12 @@ func TestRingStreamUsageGatherer_GetStreamUsage(t *testing.T) {
 		}},
 	}, {
 		// When there is one stream, and two instances each owning separate
-		// partitions, the stream usage should be queried from both instances.
-		// Each GetStreamUsageRequest will contain the partition IDs of the
-		// partition owned by each instance. The instance should only return
-		// usage if it still owns that partition.
+		// partitions, only the instance owning the partition for the stream hash
+		// should be queried.
 		name: "one stream two instances",
 		getStreamUsageRequest: GetStreamUsageRequest{
 			Tenant:       "test",
-			StreamHashes: []uint64{0x1},
+			StreamHashes: []uint64{1}, // Hash 1 maps to partition 1
 		},
 		expectedAssignedPartitionsRequest: []*logproto.GetAssignedPartitionsRequest{{}, {}},
 		getAssignedPartitionsResponses: []*logproto.GetAssignedPartitionsResponse{{
@@ -82,80 +80,52 @@ func TestRingStreamUsageGatherer_GetStreamUsage(t *testing.T) {
 				1: time.Now().UnixNano(),
 			},
 		}},
-		expectedStreamUsageRequest: []*logproto.GetStreamUsageRequest{{
-			Tenant:       "test",
-			StreamHashes: []uint64{0x1},
-			Partitions:   []int32{0},
-		}, {
+		expectedStreamUsageRequest: []*logproto.GetStreamUsageRequest{{}, {
 			Tenant:       "test",
-			StreamHashes: []uint64{0x1},
-			Partitions:   []int32{1},
+			StreamHashes: []uint64{1},
 		}},
-		getStreamUsageResponses: []*logproto.GetStreamUsageResponse{{
+		getStreamUsageResponses: []*logproto.GetStreamUsageResponse{{}, {
 			Tenant:        "test",
 			ActiveStreams: 1,
 			Rate:          10,
-		}, {
-			Tenant:         "test",
-			UnknownStreams: []uint64{0x1},
 		}},
 		expectedResponses: []GetStreamUsageResponse{{
-			Addr: "instance-0",
+			Addr: "instance-1",
 			Response: &logproto.GetStreamUsageResponse{
 				Tenant:        "test",
 				ActiveStreams: 1,
 				Rate:          10,
 			},
-		}, {
-			Addr: "instance-1",
-			Response: &logproto.GetStreamUsageResponse{
-				Tenant:         "test",
-				UnknownStreams: []uint64{0x1},
-			},
 		}},
 	}, {
 		// When there is one stream, and two instances owning overlapping
-		// partitions, the instance with the latest timestamp should respond
-		// to the request. The other instance should respond with zeros
-		// as it is no longer assigned that partition.
-		name: "one stream two instances, two assigned partitions at the same time",
+		// partitions, only the instance with the latest timestamp for the relevant
+		// partition should be queried.
+		name: "one stream two instances, overlapping partition ownership",
 		getStreamUsageRequest: GetStreamUsageRequest{
 			Tenant:       "test",
-			StreamHashes: []uint64{0x1},
+			StreamHashes: []uint64{1}, // Hash 1 maps to partition 1
 		},
 		expectedAssignedPartitionsRequest: []*logproto.GetAssignedPartitionsRequest{{}, {}},
 		getAssignedPartitionsResponses: []*logproto.GetAssignedPartitionsResponse{{
 			AssignedPartitions: map[int32]int64{
-				0: time.Now().Add(-time.Second).UnixNano(),
+				1: time.Now().Add(-time.Second).UnixNano(),
 			},
 		}, {
 			AssignedPartitions: map[int32]int64{
-				0: time.Now().UnixNano(),
 				1: time.Now().UnixNano(),
 			},
 		}},
-		expectedStreamUsageRequest: []*logproto.GetStreamUsageRequest{{
+		expectedStreamUsageRequest: []*logproto.GetStreamUsageRequest{{}, {
 			Tenant:       "test",
-			StreamHashes: []uint64{0x1},
-			Partitions:   nil,
-		}, {
-			Tenant:       "test",
-			StreamHashes: []uint64{0x1},
-			Partitions:   []int32{0, 1},
+			StreamHashes: []uint64{1},
 		}},
-		getStreamUsageResponses: []*logproto.GetStreamUsageResponse{{
-			Tenant: "test",
-		}, {
+		getStreamUsageResponses: []*logproto.GetStreamUsageResponse{{}, {
 			Tenant:        "test",
 			ActiveStreams: 1,
 			Rate:          10,
 		}},
 		expectedResponses: []GetStreamUsageResponse{{
-			Addr: "instance-0",
-			Response: &logproto.GetStreamUsageResponse{
-				Tenant: "test",
-			},
-		}, {
 			Addr: "instance-1",
 			Response: &logproto.GetStreamUsageResponse{
 				Tenant:        "test",
@@ -171,14 +141,26 @@ func TestRingStreamUsageGatherer_GetStreamUsage(t *testing.T) {
 			clients := make([]logproto.IngestLimitsClient, len(test.expectedAssignedPartitionsRequest))
 			instances := make([]ring.InstanceDesc, len(clients))
 
-			for i := 0; i < len(test.expectedAssignedPartitionsRequest); i++ {
+			// Set up the mock clients for the assigned partitions requests.
+			// Note: Every client will return a response for its assigned partitions.
+			for i := range test.expectedAssignedPartitionsRequest {
 				clients[i] = &mockIngestLimitsClient{
+					t:                                 t,
 					expectedAssignedPartitionsRequest: test.expectedAssignedPartitionsRequest[i],
 					getAssignedPartitionsResponse:     test.getAssignedPartitionsResponses[i],
-					expectedStreamUsageRequest:        test.expectedStreamUsageRequest[i],
-					getStreamUsageResponse:            test.getStreamUsageResponses[i],
-					t:                                 t,
 				}
+			}
+
+			// Set up the mock clients for the stream usage requests.
+			// Note: Not every client will have a stream usage request because the
+			// requested stream hashes are owned only by a subset of partition consumers.
+			for i := range test.expectedStreamUsageRequest {
+				clients[i].(*mockIngestLimitsClient).expectedStreamUsageRequest = test.expectedStreamUsageRequest[i]
+				clients[i].(*mockIngestLimitsClient).getStreamUsageResponse = test.getStreamUsageResponses[i]
+			}
+
+			// Set up the instances for the ring.
+			for i := range len(clients) {
 				instances[i] = ring.InstanceDesc{
 					Addr: fmt.Sprintf("instance-%d", i),
 				}
@@ -186,7 +168,7 @@ func TestRingStreamUsageGatherer_GetStreamUsage(t *testing.T) {
 
 			// Set up the mocked ring and client pool for the tests.
 			readRing, clientPool := newMockRingWithClientPool(t, "test", clients, instances)
-			g := NewRingStreamUsageGatherer(readRing, clientPool, log.NewNopLogger())
+			g := NewRingStreamUsageGatherer(readRing, clientPool, log.NewNopLogger(), numPartitions)
 
 			// Set a maximum upper bound on the test execution time.
 			ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
diff --git a/pkg/limits/ingest_limits.go b/pkg/limits/ingest_limits.go
index c6fef26fda0..177b4c67d3c 100644
--- a/pkg/limits/ingest_limits.go
+++ b/pkg/limits/ingest_limits.go
@@ -562,25 +562,11 @@ func (s *IngestLimits) GetStreamUsage(_ context.Context, req *logproto.GetStream
 		totalSize     uint64
 	)
 
-	for _, requestedID := range req.Partitions {
-		// Consider the recorded stream if it's partition
-		// is one of the partitions we are still assigned to.
-		assigned := false
-		for assignedID := range partitions {
-			if requestedID == assignedID {
-				assigned = true
-				break
-			}
-		}
-
-		if !assigned {
-			continue
-		}
-
-		// If the stream is written into a partition we are
-		// assigned to and has been seen within the window,
-		// it is an active stream.
-		for _, stream := range partitions[requestedID] {
+	// If the stream is written into a partition we are
+	// assigned to and has been seen within the window,
+	// it is an active stream.
+	for _, streams := range partitions {
+		for _, stream := range streams {
 			if stream.lastSeenAt < cutoff {
 				continue
 			}
@@ -620,16 +606,6 @@ func (s *IngestLimits) GetStreamUsage(_ context.Context, req *logproto.GetStream
 	// Calculate rate using only data from within the rate window
 	rate := float64(totalSize) / s.cfg.RateWindow.Seconds()
 
-	// Debug logging to help diagnose rate calculation issues
-	level.Debug(s.logger).Log(
-		"msg", "calculated stream usage",
-		"tenant", req.Tenant,
-		"active_streams", activeStreams,
-		"total_size", util.HumanizeBytes(totalSize),
-		"rate_window_seconds", s.cfg.RateWindow.Seconds(),
-		"calculated_rate", util.HumanizeBytes(uint64(rate)),
-	)
-
 	return &logproto.GetStreamUsageResponse{
 		Tenant:         req.Tenant,
 		ActiveStreams:  activeStreams,
diff --git a/pkg/limits/ingest_limits_test.go b/pkg/limits/ingest_limits_test.go
index 5be09d997e3..3bc9f85b864 100644
--- a/pkg/limits/ingest_limits_test.go
+++ b/pkg/limits/ingest_limits_test.go
@@ -312,7 +312,6 @@ func TestIngestLimits_GetStreamUsage(t *testing.T) {
 			// Call GetStreamUsage.
 			req := &logproto.GetStreamUsageRequest{
 				Tenant:       tt.tenantID,
-				Partitions:   tt.partitionIDs,
 				StreamHashes: tt.streamHashes,
 			}
 
@@ -376,7 +375,6 @@ func TestIngestLimits_GetStreamUsage_Concurrent(t *testing.T) {
 
 			req := &logproto.GetStreamUsageRequest{
 				Tenant:       "tenant1",
-				Partitions:   []int32{0},
 				StreamHashes: []uint64{1, 2, 3, 4, 5},
 			}
 
diff --git a/pkg/logproto/logproto.pb.go b/pkg/logproto/logproto.pb.go
index 4ae0b3a8b15..f4acc5bbb58 100644
--- a/pkg/logproto/logproto.pb.go
+++ b/pkg/logproto/logproto.pb.go
@@ -443,8 +443,7 @@ func (m *RejectedStream) GetReason() string {
 
 type GetStreamUsageRequest struct {
 	Tenant       string   `protobuf:"bytes,1,opt,name=tenant,proto3" json:"tenant,omitempty"`
-	Partitions   []int32  `protobuf:"varint,2,rep,packed,name=partitions,proto3" json:"partitions,omitempty"`
-	StreamHashes []uint64 `protobuf:"varint,3,rep,packed,name=streamHashes,proto3" json:"streamHashes,omitempty"`
+	StreamHashes []uint64 `protobuf:"varint,2,rep,packed,name=streamHashes,proto3" json:"streamHashes,omitempty"`
 }
 
 func (m *GetStreamUsageRequest) Reset()      { *m = GetStreamUsageRequest{} }
@@ -486,13 +485,6 @@ func (m *GetStreamUsageRequest) GetTenant() string {
 	return ""
 }
 
-func (m *GetStreamUsageRequest) GetPartitions() []int32 {
-	if m != nil {
-		return m.Partitions
-	}
-	return nil
-}
-
 func (m *GetStreamUsageRequest) GetStreamHashes() []uint64 {
 	if m != nil {
 		return m.StreamHashes
@@ -3595,7 +3587,7 @@ func init() {
 func init() { proto.RegisterFile("pkg/logproto/logproto.proto", fileDescriptor_c28a5f14f1f4c79a) }
 
 var fileDescriptor_c28a5f14f1f4c79a = []byte{
-	// 3147 bytes of a gzipped FileDescriptorProto
+	// 3135 bytes of a gzipped FileDescriptorProto
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x3a, 0x4d, 0x6c, 0x1b, 0xc7,
 	0xd5, 0x5c, 0xfe, 0x89, 0x7c, 0xa4, 0x64, 0x79, 0x24, 0xcb, 0x84, 0x6c, 0x93, 0xca, 0x20, 0x9f,
 	0xed, 0xc4, 0x8e, 0x68, 0x2b, 0x5f, 0xfc, 0x25, 0xce, 0x97, 0xa6, 0xa6, 0x64, 0x2b, 0x76, 0xe4,
@@ -3629,170 +3621,169 @@ var fileDescriptor_c28a5f14f1f4c79a = []byte{
 	0xac, 0xc9, 0x13, 0x8d, 0x88, 0x03, 0x38, 0x94, 0x98, 0x43, 0x49, 0x70, 0xdc, 0x24, 0x0d, 0x38,
 	0xe0, 0xd3, 0xbb, 0xb4, 0xc9, 0x68, 0x6b, 0x7d, 0xdc, 0x64, 0x24, 0x86, 0x40, 0x92, 0x03, 0xf0,
 	0x2b, 0x30, 0x15, 0x47, 0xd9, 0x53, 0xc8, 0x73, 0x90, 0xf7, 0xa9, 0x1d, 0x78, 0xae, 0x90, 0x6f,
-	0x91, 0xa8, 0x16, 0x5f, 0xfe, 0x2a, 0x65, 0x92, 0xc8, 0xad, 0xc0, 0x6e, 0xd3, 0xbd, 0x64, 0x54,
-	0x05, 0xe8, 0xd9, 0x3e, 0x73, 0x98, 0xe3, 0xb9, 0x72, 0xe5, 0x39, 0x62, 0x40, 0x10, 0x86, 0x72,
-	0x34, 0x2d, 0x0d, 0x2a, 0x99, 0x85, 0xcc, 0xc9, 0x2c, 0x89, 0xc1, 0xf0, 0xf7, 0x2c, 0x98, 0x4b,
-	0xce, 0xba, 0x87, 0xd4, 0x9e, 0x84, 0x49, 0xbb, 0xc9, 0x9c, 0xfb, 0x34, 0x92, 0x19, 0x67, 0x31,
-	0x0e, 0x44, 0x08, 0xb2, 0xbe, 0xcd, 0xa4, 0x59, 0x64, 0x88, 0xf8, 0x46, 0xc7, 0x61, 0xaa, 0xef,
-	0x6e, 0xb9, 0xde, 0x03, 0x57, 0x0f, 0xcd, 0x8a, 0x25, 0x25, 0xa0, 0xf8, 0x27, 0x16, 0x40, 0x64,
-	0xd5, 0x7b, 0x0a, 0xf4, 0x34, 0x1c, 0x8c, 0x5a, 0xd7, 0xbc, 0xf5, 0x4d, 0xdb, 0x6f, 0xa9, 0x45,
-	0x0d, 0x77, 0x8c, 0x5c, 0x58, 0xc4, 0x6a, 0x36, 0xc6, 0xea, 0x1c, 0xe4, 0xb9, 0x2f, 0xa3, 0x41,
-	0x25, 0xb7, 0x60, 0x9d, 0x9c, 0x24, 0xaa, 0x85, 0xab, 0x70, 0x74, 0x95, 0xb2, 0x0b, 0x41, 0xe0,
-	0xb4, 0x5d, 0xda, 0xba, 0x11, 0x8a, 0x5c, 0xef, 0xe4, 0x3f, 0x5b, 0x70, 0x6c, 0x0c, 0x82, 0x12,
-	0xae, 0x07, 0xc8, 0x1e, 0xea, 0x55, 0x7b, 0xfb, 0xe5, 0xc8, 0xfa, 0x3e, 0x95, 0xc8, 0xe2, 0x70,
-	0x97, 0xf4, 0x75, 0x23, 0x48, 0xcf, 0x5f, 0x84, 0xc3, 0x63, 0xd0, 0x4d, 0x1f, 0x98, 0x93, 0x3e,
-	0x70, 0xd6, 0xf4, 0x81, 0x19, 0xd3, 0xcd, 0xfd, 0x23, 0x03, 0xe5, 0xd7, 0xfa, 0xd4, 0xdf, 0xd6,
-	0xc6, 0x59, 0x85, 0x42, 0x40, 0x3b, 0xb4, 0xc9, 0x3c, 0x5f, 0xda, 0x49, 0x23, 0x5d, 0xb1, 0x48,
-	0x08, 0xe3, 0xa4, 0x3a, 0x7c, 0x37, 0x0a, 0x52, 0x93, 0x44, 0x36, 0xd0, 0x79, 0xc8, 0x05, 0xcc,
-	0xf6, 0x99, 0xd0, 0x42, 0x69, 0x69, 0x7e, 0x51, 0x86, 0x9f, 0x45, 0x1d, 0x7e, 0x16, 0x6f, 0xea,
-	0xf0, 0xd3, 0x28, 0xbc, 0x3b, 0xa8, 0xa5, 0xde, 0xfe, 0x4b, 0xcd, 0x22, 0x72, 0x08, 0x3a, 0x07,
-	0x19, 0xea, 0xb6, 0x84, 0xa6, 0x3e, 0xeb, 0x48, 0x3e, 0x00, 0x9d, 0x85, 0x62, 0xcb, 0xf1, 0x69,
-	0x93, 0x73, 0x2e, 0xf4, 0x39, 0xb5, 0x34, 0x13, 0x49, 0x7a, 0x45, 0x77, 0x91, 0x08, 0x0b, 0x9d,
-	0x86, 0x7c, 0xc0, 0x8d, 0x26, 0xa8, 0x4c, 0x70, 0xff, 0xdd, 0x98, 0xdd, 0x1d, 0xd4, 0xa6, 0x25,
-	0xe4, 0xb4, 0xd7, 0x75, 0x18, 0xed, 0xf6, 0xd8, 0x36, 0x51, 0x38, 0xe8, 0x69, 0x98, 0x68, 0xd1,
-	0x0e, 0xe5, 0x4e, 0xba, 0x20, 0x14, 0x39, 0x6d, 0x90, 0x17, 0x1d, 0x44, 0x23, 0xa0, 0x37, 0x21,
-	0xdb, 0xeb, 0xd8, 0x6e, 0xa5, 0x28, 0xb8, 0x98, 0x8a, 0x10, 0x6f, 0x74, 0x6c, 0xb7, 0xf1, 0xc2,
-	0x07, 0x83, 0xda, 0x73, 0x6d, 0x87, 0x6d, 0xf6, 0xef, 0x2c, 0x36, 0xbd, 0x6e, 0xbd, 0xed, 0xdb,
-	0x1b, 0xb6, 0x6b, 0xd7, 0x3b, 0xde, 0x96, 0x53, 0xbf, 0xff, 0x6c, 0x9d, 0x07, 0xd5, 0x7b, 0x7d,
-	0xea, 0x3b, 0xd4, 0xaf, 0x73, 0x32, 0x8b, 0x42, 0x25, 0x7c, 0x28, 0x11, 0x64, 0xd1, 0x15, 0x1e,
-	0x33, 0x3c, 0x9f, 0x2e, 0xf3, 0x88, 0x1b, 0x54, 0x40, 0xcc, 0x72, 0x38, 0x9a, 0x45, 0xc0, 0x09,
-	0xdd, 0x58, 0xf5, 0xbd, 0x7e, 0xaf, 0x71, 0x60, 0x77, 0x50, 0x33, 0xf1, 0x89, 0xd9, 0xb8, 0x92,
-	0x2d, 0xe4, 0xa7, 0x27, 0xf0, 0x3b, 0x19, 0x40, 0xeb, 0x76, 0xb7, 0xd7, 0xa1, 0xfb, 0x52, 0x7f,
-	0xa8, 0xe8, 0xf4, 0x23, 0x2b, 0x3a, 0xb3, 0x5f, 0x45, 0x47, 0x5a, 0xcb, 0xee, 0x4f, 0x6b, 0xb9,
-	0xcf, 0xaa, 0xb5, 0xfc, 0x17, 0x5e, 0x6b, 0xb8, 0x02, 0x59, 0x4e, 0x99, 0x6f, 0x6e, 0xdf, 0x7e,
-	0x20, 0x74, 0x53, 0x26, 0xfc, 0x13, 0xaf, 0x41, 0x5e, 0xf2, 0x85, 0xe6, 0x93, 0xca, 0x8b, 0xef,
-	0xdb, 0x48, 0x71, 0x19, 0xad, 0x92, 0xe9, 0x48, 0x25, 0x19, 0x21, 0x6c, 0xfc, 0x5b, 0x0b, 0x26,
-	0x95, 0x45, 0x28, 0xd7, 0x76, 0x27, 0x0a, 0xdd, 0xd2, 0x9f, 0x1d, 0x4e, 0x86, 0xee, 0x0b, 0x2d,
-	0xbb, 0xc7, 0xa8, 0xdf, 0xa8, 0xbf, 0x3b, 0xa8, 0x59, 0x1f, 0x0c, 0x6a, 0x27, 0xc6, 0x09, 0x4d,
-	0x67, 0x94, 0x3a, 0xc7, 0xd1, 0x84, 0xd1, 0x29, 0xb1, 0x3a, 0x16, 0x28, 0xb3, 0x3a, 0xb0, 0x28,
-	0x13, 0xd1, 0xcb, 0x6e, 0x9b, 0x06, 0x9c, 0x72, 0x96, 0x5b, 0x04, 0x91, 0x38, 0x9c, 0xcd, 0x07,
-	0xb6, 0xef, 0x3a, 0x6e, 0x5b, 0xc6, 0xc0, 0x22, 0x09, 0xdb, 0xf8, 0x47, 0x16, 0xcc, 0xc4, 0xcc,
-	0x5a, 0x31, 0xf1, 0x3c, 0xe4, 0x03, 0xae, 0x29, 0xcd, 0x83, 0x61, 0x14, 0xeb, 0x02, 0xde, 0x98,
-	0x52, 0x8b, 0xcf, 0xcb, 0x36, 0x51, 0xf8, 0x8f, 0x6f, 0x69, 0x7f, 0xb0, 0xa0, 0x2c, 0x92, 0x49,
-	0xbd, 0xd7, 0x10, 0x64, 0x5d, 0xbb, 0x4b, 0x95, 0xaa, 0xc4, 0xb7, 0x91, 0x61, 0xf2, 0xe9, 0x0a,
-	0x3a, 0xc3, 0xdc, 0xaf, 0x83, 0xb5, 0x1e, 0xd9, 0xc1, 0x5a, 0xd1, 0xbe, 0x9b, 0x85, 0x1c, 0x37,
-	0xef, 0x6d, 0xe1, 0x5c, 0x8b, 0x44, 0x36, 0xf0, 0x09, 0x98, 0x54, 0x5c, 0x44, 0x79, 0xc5, 0xc8,
-	0xa4, 0xb8, 0x0b, 0x79, 0xa9, 0x09, 0xf4, 0x24, 0x14, 0xc3, 0xe3, 0x87, 0xe0, 0x36, 0xd3, 0xc8,
-	0xef, 0x0e, 0x6a, 0x69, 0x16, 0x90, 0xa8, 0x03, 0xd5, 0xcc, 0x20, 0x65, 0x35, 0x8a, 0xbb, 0x83,
-	0x9a, 0x04, 0xa8, 0x78, 0x85, 0x8e, 0x42, 0x76, 0x93, 0x67, 0x0c, 0x22, 0x33, 0x6d, 0x14, 0x76,
-	0x07, 0x35, 0xd1, 0x26, 0xe2, 0x17, 0xaf, 0x42, 0x79, 0x8d, 0xb6, 0xed, 0xe6, 0xb6, 0x9a, 0x34,
-	0x8c, 0x79, 0x7c, 0x42, 0x4b, 0xd3, 0x78, 0x02, 0xca, 0xe1, 0x8c, 0x6f, 0xa9, 0x5c, 0x27, 0x43,
-	0x4a, 0x21, 0xec, 0x6a, 0x80, 0x7f, 0x6c, 0x81, 0xb2, 0x01, 0x84, 0x8d, 0x13, 0x0a, 0xf7, 0x85,
-	0xb0, 0x3b, 0xa8, 0x29, 0x88, 0x3e, 0x80, 0xa0, 0x17, 0x61, 0x22, 0x10, 0x33, 0xea, 0x64, 0xd3,
-	0x34, 0x2d, 0xd1, 0xd1, 0x38, 0xc0, 0x4d, 0x64, 0x77, 0x50, 0xd3, 0x88, 0x44, 0x7f, 0xa0, 0xc5,
-	0x58, 0x2a, 0x24, 0x19, 0x9b, 0xda, 0x1d, 0xd4, 0x0c, 0xa8, 0x99, 0x1a, 0xe1, 0x4f, 0x2c, 0x28,
-	0xdd, 0xb4, 0x9d, 0xd0, 0x84, 0x2a, 0x5a, 0x45, 0x91, 0xaf, 0x96, 0x00, 0x6e, 0x89, 0x2d, 0xda,
-	0xb1, 0xb7, 0x2f, 0x79, 0xbe, 0xa0, 0x3b, 0x49, 0xc2, 0x76, 0x14, 0xc3, 0xb3, 0x23, 0x63, 0x78,
-	0x6e, 0xff, 0xae, 0xfd, 0xbf, 0xeb, 0x48, 0xaf, 0x64, 0x0b, 0xe9, 0xe9, 0x0c, 0x7e, 0xc7, 0x82,
-	0xb2, 0x64, 0x5e, 0x59, 0xde, 0x37, 0x20, 0x2f, 0x65, 0x23, 0xd8, 0xff, 0x14, 0xc7, 0x74, 0x6a,
-	0x3f, 0x4e, 0x49, 0xd1, 0x44, 0x2f, 0xc3, 0x54, 0xcb, 0xf7, 0x7a, 0xbd, 0xe4, 0x61, 0xc2, 0x98,
-	0x65, 0xc5, 0xec, 0x27, 0x09, 0x74, 0xfc, 0x47, 0x0b, 0x26, 0x95, 0x33, 0x51, 0xea, 0x0a, 0x45,
-	0x6c, 0x3d, 0x72, 0xf4, 0x4c, 0xef, 0x37, 0x7a, 0xce, 0x41, 0xbe, 0xcd, 0xe3, 0x8b, 0x76, 0x48,
-	0xaa, 0xb5, 0xbf, 0xa8, 0x8a, 0xaf, 0xc0, 0x94, 0x66, 0x65, 0x8c, 0x47, 0x9d, 0x4f, 0x7a, 0xd4,
-	0xcb, 0x2d, 0xea, 0x32, 0x67, 0xc3, 0x09, 0x7d, 0xa4, 0xc2, 0xc7, 0xdf, 0xb5, 0x60, 0x3a, 0x89,
-	0x82, 0x56, 0x12, 0xc5, 0x80, 0xe3, 0xe3, 0xc9, 0x99, 0x75, 0x00, 0x4d, 0x5a, 0x55, 0x03, 0x9e,
-	0xdb, 0xab, 0x1a, 0x10, 0xcb, 0x84, 0x8b, 0xca, 0x2b, 0xe0, 0x1f, 0x5a, 0x30, 0x19, 0xd3, 0x25,
-	0x7a, 0x1e, 0xb2, 0x1b, 0xbe, 0xd7, 0xdd, 0x97, 0xa2, 0xc4, 0x08, 0xf4, 0xbf, 0x90, 0x66, 0xde,
-	0xbe, 0xd4, 0x94, 0x66, 0x1e, 0xd7, 0x92, 0x62, 0x3f, 0x23, 0x4f, 0x2c, 0xb2, 0x85, 0x9f, 0x83,
-	0xa2, 0x60, 0xe8, 0x86, 0xed, 0xf8, 0x23, 0x03, 0xc6, 0x68, 0x86, 0x5e, 0x84, 0x03, 0xd2, 0x19,
-	0x8e, 0x1e, 0x5c, 0x1e, 0x35, 0xb8, 0xac, 0x07, 0x1f, 0x81, 0x9c, 0x48, 0x3a, 0xf8, 0x10, 0x7e,
-	0x22, 0xd7, 0x43, 0xf8, 0x37, 0x3e, 0x04, 0x33, 0x7c, 0x0f, 0x52, 0x3f, 0x58, 0xf6, 0xfa, 0x2e,
-	0xd3, 0x27, 0xa4, 0xd3, 0x30, 0x1b, 0x07, 0x2b, 0x2b, 0x99, 0x85, 0x5c, 0x93, 0x03, 0x04, 0x8d,
-	0x49, 0x22, 0x1b, 0xf8, 0x67, 0x16, 0xa0, 0x55, 0xca, 0xc4, 0x2c, 0x97, 0x57, 0xc2, 0xed, 0x31,
-	0x0f, 0x85, 0xae, 0xcd, 0x9a, 0x9b, 0xd4, 0x0f, 0x74, 0xfe, 0xa2, 0xdb, 0x9f, 0x47, 0xe2, 0x89,
-	0xcf, 0xc2, 0x4c, 0x6c, 0x95, 0x8a, 0xa7, 0x79, 0x28, 0x34, 0x15, 0x4c, 0x85, 0xbc, 0xb0, 0x8d,
-	0x7f, 0x95, 0x86, 0x82, 0x4e, 0xeb, 0xd0, 0x59, 0x28, 0x6d, 0x38, 0x6e, 0x9b, 0xfa, 0x3d, 0xdf,
-	0x51, 0x22, 0xc8, 0xca, 0x34, 0xcf, 0x00, 0x13, 0xb3, 0x81, 0x9e, 0x81, 0x89, 0x7e, 0x40, 0xfd,
-	0xb7, 0x1c, 0xb9, 0xd3, 0x8b, 0x8d, 0xd9, 0x9d, 0x41, 0x2d, 0x7f, 0x2b, 0xa0, 0xfe, 0xe5, 0x15,
-	0x1e, 0x7c, 0xfa, 0xe2, 0x8b, 0xc8, 0xff, 0x16, 0x7a, 0x55, 0x99, 0xa9, 0x48, 0xe0, 0x1a, 0xff,
-	0xc7, 0x97, 0x9f, 0x70, 0x75, 0x3d, 0xdf, 0xeb, 0x52, 0xb6, 0x49, 0xfb, 0x41, 0xbd, 0xe9, 0x75,
-	0xbb, 0x9e, 0x5b, 0x17, 0xf5, 0x3e, 0xc1, 0x34, 0x8f, 0xa0, 0x7c, 0xb8, 0xb2, 0xdc, 0x9b, 0x30,
-	0xc1, 0x36, 0x7d, 0xaf, 0xdf, 0xde, 0x14, 0x81, 0x21, 0xd3, 0x38, 0xbf, 0x7f, 0x7a, 0x9a, 0x02,
-	0xd1, 0x1f, 0xe8, 0x09, 0x2e, 0x2d, 0xda, 0xdc, 0x0a, 0xfa, 0x5d, 0x79, 0xea, 0x6e, 0xe4, 0x76,
-	0x07, 0x35, 0xeb, 0x19, 0x12, 0x82, 0xf1, 0x05, 0x98, 0x8c, 0xa5, 0xc2, 0xe8, 0x0c, 0x64, 0x7d,
-	0xba, 0xa1, 0x5d, 0x01, 0x1a, 0xce, 0x98, 0x65, 0xf4, 0xe7, 0x38, 0x44, 0xfc, 0xe2, 0xef, 0xa4,
-	0xa1, 0x66, 0x54, 0xea, 0x2e, 0x79, 0xfe, 0x55, 0xca, 0x7c, 0xa7, 0x79, 0xcd, 0xee, 0x86, 0x75,
-	0x97, 0x1a, 0x94, 0xba, 0x02, 0xf8, 0x96, 0xb1, 0x8b, 0xa0, 0x1b, 0xe2, 0xa1, 0x63, 0x00, 0x62,
-	0xdb, 0xc9, 0x7e, 0xb9, 0xa1, 0x8a, 0x02, 0x22, 0xba, 0x97, 0x63, 0xc2, 0xae, 0xef, 0x53, 0x38,
-	0x4a, 0xc8, 0x97, 0x93, 0x42, 0xde, 0x37, 0x9d, 0x50, 0xb2, 0xe6, 0x76, 0xc9, 0xc5, 0xb7, 0x0b,
-	0xfe, 0xbb, 0x05, 0xd5, 0x35, 0xbd, 0xf2, 0x47, 0x14, 0x87, 0xe6, 0x37, 0xfd, 0x98, 0xf8, 0xcd,
-	0x3c, 0x46, 0x7e, 0xb3, 0x09, 0x7e, 0xab, 0x00, 0x6b, 0x8e, 0x4b, 0x2f, 0x39, 0x1d, 0x46, 0xfd,
-	0x11, 0x87, 0xa4, 0xef, 0x67, 0x22, 0x8f, 0x43, 0xe8, 0x86, 0x96, 0xc1, 0xb2, 0xe1, 0xe6, 0x1f,
-	0x07, 0x8b, 0xe9, 0xc7, 0xc8, 0x62, 0x26, 0xe1, 0x01, 0x5d, 0x98, 0xd8, 0x10, 0xec, 0xc9, 0x88,
-	0x1d, 0xab, 0x19, 0x47, 0xbc, 0x37, 0xbe, 0xa2, 0x26, 0x3f, 0xb7, 0x47, 0xc2, 0x25, 0x6a, 0xff,
-	0xf5, 0x60, 0xdb, 0x65, 0xf6, 0x43, 0x63, 0x3c, 0xd1, 0x93, 0x20, 0x5b, 0xe5, 0x74, 0xb9, 0x91,
-	0x39, 0xdd, 0x4b, 0x6a, 0x9a, 0xff, 0x24, 0xaf, 0xc3, 0xed, 0xc8, 0xc1, 0x0a, 0xa5, 0x28, 0x07,
-	0x7b, 0x7c, 0xaf, 0xed, 0x2f, 0x37, 0x3d, 0x3a, 0x19, 0x3f, 0x9a, 0x95, 0xc3, 0xa3, 0x59, 0x8b,
-	0x3e, 0x8c, 0x9d, 0xcb, 0xf0, 0xef, 0x2c, 0x98, 0x5e, 0xa5, 0x2c, 0x9e, 0x8d, 0x7d, 0x89, 0x94,
-	0x8f, 0x5f, 0x81, 0x83, 0xc6, 0xfa, 0x95, 0x9c, 0x9e, 0x4d, 0xa4, 0x60, 0x87, 0x22, 0x49, 0x09,
-	0x19, 0xa8, 0x93, 0x6d, 0x3c, 0xfb, 0xba, 0x01, 0x25, 0xa3, 0x13, 0x5d, 0x48, 0xe4, 0x5d, 0x33,
-	0x89, 0x4b, 0x18, 0x9e, 0x3b, 0x34, 0x66, 0x15, 0x4f, 0xf2, 0xfc, 0xaa, 0xb2, 0xea, 0x30, 0x47,
-	0x59, 0x07, 0x24, 0x14, 0x2b, 0xc8, 0x9a, 0x51, 0x52, 0x40, 0x5f, 0x0d, 0x13, 0xb0, 0xb0, 0x8d,
-	0x9e, 0x80, 0xac, 0xef, 0x3d, 0xd0, 0x09, 0xf5, 0xa4, 0x51, 0x9d, 0xf7, 0x1e, 0x10, 0xd1, 0x85,
-	0x5f, 0x84, 0x0c, 0xf1, 0x1e, 0xa0, 0x2a, 0x80, 0x6f, 0xbb, 0x6d, 0x7a, 0x3b, 0x3c, 0xca, 0x95,
-	0x89, 0x01, 0x19, 0x93, 0xc1, 0x2c, 0xc3, 0x41, 0x73, 0x45, 0x52, 0xdd, 0x8b, 0x30, 0xf1, 0x5a,
-	0xdf, 0x14, 0xd7, 0x6c, 0x42, 0x5c, 0xb2, 0x62, 0xa0, 0x91, 0xb8, 0xcd, 0x40, 0x04, 0x47, 0x47,
-	0xa1, 0xc8, 0xec, 0x3b, 0x1d, 0x7a, 0x2d, 0x72, 0x96, 0x11, 0x80, 0xf7, 0xf2, 0x53, 0xe8, 0x6d,
-	0x23, 0x15, 0x8b, 0x00, 0xe8, 0x69, 0x98, 0x8e, 0xd6, 0x7c, 0xc3, 0xa7, 0x1b, 0xce, 0x43, 0xa1,
-	0xe1, 0x32, 0x19, 0x82, 0xa3, 0x93, 0x70, 0x20, 0x82, 0xad, 0x8b, 0x94, 0x27, 0x2b, 0x50, 0x93,
-	0x60, 0x2e, 0x1b, 0xc1, 0xee, 0xc5, 0x7b, 0x7d, 0xbb, 0x23, 0xb6, 0x69, 0x99, 0x18, 0x10, 0xfc,
-	0x7b, 0x0b, 0x0e, 0x4a, 0x55, 0xf3, 0x3d, 0xf0, 0x65, 0xb4, 0xfa, 0x9f, 0x5b, 0x80, 0x4c, 0x0e,
-	0x94, 0x69, 0xfd, 0x8f, 0x59, 0x91, 0xe2, 0x39, 0x55, 0x49, 0x1c, 0xae, 0x25, 0x28, 0x2a, 0x2a,
-	0x61, 0xc8, 0x37, 0x65, 0xe5, 0x4d, 0x5c, 0x1e, 0xc8, 0xd3, 0xbb, 0x84, 0x10, 0xf5, 0x8f, 0x6a,
-	0x90, 0xbb, 0xb3, 0xcd, 0x68, 0xa0, 0xce, 0xde, 0xa2, 0xe8, 0x20, 0x00, 0x44, 0xfe, 0xf1, 0xb9,
-	0xd4, 0x7d, 0x99, 0x50, 0x83, 0x9a, 0x4b, 0x81, 0x88, 0xfe, 0xc0, 0xff, 0x4c, 0xc3, 0xe4, 0x6d,
-	0xaf, 0xd3, 0x8f, 0xc2, 0xeb, 0x97, 0x29, 0xb4, 0xc4, 0x0a, 0x02, 0x39, 0x5d, 0x10, 0x40, 0x90,
-	0x0d, 0x18, 0xed, 0x09, 0xcb, 0xca, 0x10, 0xf1, 0x8d, 0x30, 0x94, 0x99, 0xed, 0xb7, 0x29, 0x93,
-	0xc7, 0xac, 0x4a, 0x5e, 0xe4, 0xbf, 0x31, 0x18, 0x5a, 0x80, 0x92, 0xdd, 0x6e, 0xfb, 0xb4, 0x6d,
-	0x33, 0xda, 0xd8, 0xae, 0x4c, 0x88, 0xc9, 0x4c, 0x10, 0xba, 0x02, 0x53, 0x4d, 0xbb, 0xb9, 0xe9,
-	0xb8, 0xed, 0xeb, 0x3d, 0x79, 0x53, 0x52, 0x10, 0x1e, 0xfc, 0xe8, 0xa2, 0x79, 0x8d, 0xbc, 0xb8,
-	0x1c, 0xc3, 0x51, 0x7e, 0x2c, 0x31, 0x12, 0xbf, 0x01, 0x53, 0x5a, 0xf0, 0xca, 0x3c, 0xce, 0xc0,
-	0xc4, 0x7d, 0x01, 0x19, 0x51, 0xec, 0x93, 0xa8, 0x8a, 0x94, 0x46, 0x8b, 0x5f, 0x6a, 0x68, 0xfe,
-	0xf1, 0x15, 0xc8, 0x4b, 0x74, 0x74, 0xd4, 0x3c, 0x78, 0xc9, 0xdc, 0x93, 0xb7, 0xd5, 0x29, 0x0a,
-	0x43, 0x5e, 0x12, 0x52, 0x46, 0x24, 0xec, 0x4c, 0x42, 0x88, 0xfa, 0xc7, 0x3f, 0x48, 0xc3, 0xa1,
-	0x15, 0xca, 0xc4, 0xbd, 0xe2, 0x25, 0x87, 0x76, 0x5a, 0x9f, 0x6b, 0x4d, 0x20, 0xac, 0xec, 0x65,
-	0x8c, 0xca, 0x1e, 0xf7, 0x61, 0x1d, 0xc7, 0xa5, 0x6b, 0x46, 0x69, 0x28, 0x02, 0x44, 0x32, 0xca,
-	0x99, 0x45, 0x23, 0x6d, 0x23, 0x79, 0xc3, 0x46, 0xa2, 0x82, 0xe0, 0x44, 0xac, 0x86, 0xa9, 0x4f,
-	0xa0, 0x85, 0xe8, 0xf8, 0x8a, 0x7f, 0x63, 0xc1, 0x5c, 0x52, 0x2e, 0x4a, 0x8d, 0x17, 0x21, 0xbf,
-	0x21, 0x20, 0xc3, 0x65, 0xe7, 0xd8, 0x08, 0x59, 0xb9, 0x90, 0xa8, 0x66, 0xe5, 0x42, 0x42, 0xd0,
-	0x53, 0xb1, 0x0b, 0xab, 0xc6, 0xcc, 0xee, 0xa0, 0x76, 0x40, 0x00, 0x0c, 0x5c, 0xc5, 0xcc, 0xe9,
-	0x70, 0xe1, 0x99, 0xa8, 0x24, 0x22, 0x21, 0x26, 0x61, 0x55, 0xdf, 0xfc, 0x97, 0x05, 0x93, 0xb1,
-	0x85, 0x08, 0x11, 0xf1, 0x2d, 0xa0, 0xc2, 0x83, 0x6c, 0xa0, 0xa7, 0x20, 0xcb, 0xb6, 0x7b, 0x2a,
-	0x2a, 0x34, 0x0e, 0x7d, 0x32, 0xa8, 0x1d, 0x8c, 0x0d, 0xbb, 0xb9, 0xdd, 0xa3, 0x44, 0xa0, 0xf0,
-	0x9d, 0xd3, 0xb4, 0xfd, 0x96, 0xe3, 0xda, 0x1d, 0x87, 0x6d, 0xab, 0x2b, 0x78, 0x13, 0xc4, 0xdd,
-	0x51, 0xcf, 0xf6, 0x03, 0x9d, 0x04, 0x16, 0xa5, 0x3b, 0x52, 0x20, 0xa2, 0x3f, 0x44, 0x71, 0x67,
-	0x8b, 0xb2, 0xe6, 0xa6, 0x0c, 0x0b, 0xaa, 0xb8, 0x23, 0x20, 0xb1, 0xe2, 0x8e, 0x80, 0xa0, 0x25,
-	0x28, 0xdc, 0x0d, 0x3c, 0xf7, 0x86, 0xcd, 0x36, 0xe5, 0x86, 0x6e, 0xcc, 0xed, 0x0e, 0x6a, 0x48,
-	0xc3, 0x8c, 0x11, 0x21, 0x1e, 0xfe, 0xa9, 0x15, 0x19, 0xb4, 0xdc, 0xf7, 0x5f, 0x38, 0x83, 0xc6,
-	0x5f, 0x8b, 0x6c, 0x4b, 0x2f, 0x51, 0xd9, 0xd6, 0xcb, 0x30, 0xd5, 0x8a, 0xf5, 0x8c, 0xb7, 0x31,
-	0x59, 0xec, 0x4e, 0xa0, 0xe3, 0x7e, 0xa4, 0x7b, 0x01, 0x19, 0xa3, 0xfb, 0x84, 0x42, 0xd3, 0xc3,
-	0x0a, 0x8d, 0x34, 0x95, 0xd9, 0x5b, 0x53, 0x4f, 0x1f, 0x87, 0x62, 0x78, 0xb1, 0x89, 0x4a, 0x30,
-	0x71, 0xe9, 0x3a, 0x79, 0xfd, 0x02, 0x59, 0x99, 0x4e, 0xa1, 0x32, 0x14, 0x1a, 0x17, 0x96, 0x5f,
-	0x15, 0x2d, 0x6b, 0xe9, 0x97, 0x79, 0x9d, 0xec, 0xf8, 0xe8, 0xff, 0x21, 0x27, 0x33, 0x98, 0xb9,
-	0x88, 0x39, 0xf3, 0xce, 0x6f, 0xfe, 0xf0, 0x10, 0x5c, 0x4a, 0x09, 0xa7, 0xce, 0x58, 0xe8, 0x1a,
-	0x94, 0x04, 0x50, 0x55, 0xd5, 0x8f, 0x26, 0x8b, 0xdb, 0x31, 0x4a, 0xc7, 0xc6, 0xf4, 0x1a, 0xf4,
-	0xce, 0x43, 0x4e, 0x0a, 0x6c, 0x2e, 0x91, 0x68, 0x8e, 0x58, 0x4d, 0xec, 0x9e, 0x01, 0xa7, 0xd0,
-	0x0b, 0x90, 0xbd, 0x69, 0x3b, 0x1d, 0x64, 0xe4, 0xb9, 0x46, 0x31, 0x7c, 0x7e, 0x2e, 0x09, 0x36,
-	0xa6, 0x7d, 0x29, 0xac, 0xe9, 0x1f, 0x4e, 0x16, 0x16, 0xf5, 0xf0, 0xca, 0x70, 0x47, 0x38, 0xf3,
-	0x75, 0x59, 0x79, 0xd6, 0xe5, 0x2d, 0x74, 0x2c, 0x3e, 0x55, 0xa2, 0x1a, 0x36, 0x5f, 0x1d, 0xd7,
-	0x1d, 0x12, 0x5c, 0x83, 0x92, 0x51, 0x5a, 0x32, 0xc5, 0x3a, 0x5c, 0x17, 0x33, 0xc5, 0x3a, 0xa2,
-	0x1e, 0x85, 0x53, 0x68, 0x15, 0x0a, 0xe2, 0xd1, 0x87, 0xcd, 0x02, 0x74, 0x24, 0x79, 0x08, 0x30,
-	0x92, 0xbf, 0xf9, 0xa3, 0xa3, 0x3b, 0x43, 0x42, 0x5f, 0x85, 0xe2, 0x2a, 0x65, 0x2a, 0xea, 0x1d,
-	0x4e, 0x86, 0xcd, 0x11, 0x92, 0x8a, 0x87, 0x5e, 0x9c, 0x42, 0x6f, 0x88, 0x83, 0x4a, 0xdc, 0xa5,
-	0xa3, 0xda, 0x18, 0xd7, 0x1d, 0xae, 0x6b, 0x61, 0x3c, 0x42, 0x48, 0xf9, 0xf5, 0x18, 0x65, 0x95,
-	0x6b, 0xd4, 0xc6, 0x6c, 0xd8, 0x90, 0x72, 0x6d, 0x8f, 0x47, 0x65, 0x38, 0xb5, 0xf4, 0xa6, 0x7e,
-	0x9d, 0xb2, 0x62, 0x33, 0x1b, 0x5d, 0x87, 0xa9, 0xf0, 0x01, 0x8d, 0x78, 0x78, 0x15, 0xb3, 0xf9,
-	0xa1, 0x57, 0x5e, 0x31, 0x9b, 0x1f, 0x7e, 0xed, 0x85, 0x53, 0x4b, 0x77, 0x61, 0x56, 0x5e, 0x16,
-	0xca, 0x57, 0x4c, 0x97, 0x7c, 0xcf, 0x65, 0xdc, 0x67, 0x11, 0x98, 0x8c, 0x3d, 0x6f, 0x42, 0x86,
-	0xd5, 0x8c, 0x7a, 0x5b, 0x65, 0xb2, 0x32, 0xf2, 0x5d, 0x14, 0x4e, 0x2d, 0xfd, 0xc9, 0x82, 0xb2,
-	0x39, 0x19, 0xba, 0x65, 0x70, 0x23, 0x9e, 0x03, 0x99, 0x12, 0x1b, 0xf9, 0x3c, 0xc9, 0xd4, 0xc5,
-	0xe8, 0x97, 0x44, 0x38, 0x85, 0xee, 0x8a, 0xb7, 0x4d, 0xc3, 0x0f, 0x50, 0xd0, 0xf1, 0x3d, 0xdf,
-	0xba, 0xc8, 0x49, 0x4e, 0x7c, 0xc6, 0x37, 0x31, 0x38, 0xd5, 0x78, 0xf3, 0xbd, 0x0f, 0xab, 0xa9,
-	0xf7, 0x3f, 0xac, 0xa6, 0x3e, 0xfe, 0xb0, 0x6a, 0x7d, 0x6b, 0xa7, 0x6a, 0xfd, 0x62, 0xa7, 0x6a,
-	0xbd, 0xbb, 0x53, 0xb5, 0xde, 0xdb, 0xa9, 0x5a, 0x7f, 0xdd, 0xa9, 0x5a, 0x7f, 0xdb, 0xa9, 0xa6,
-	0x3e, 0xde, 0xa9, 0x5a, 0x6f, 0x7f, 0x54, 0x4d, 0xbd, 0xf7, 0x51, 0x35, 0xf5, 0xfe, 0x47, 0xd5,
-	0xd4, 0xd7, 0x4f, 0xec, 0x5d, 0xfe, 0x90, 0x61, 0x25, 0x2f, 0xfe, 0x9e, 0xfd, 0x77, 0x00, 0x00,
-	0x00, 0xff, 0xff, 0xac, 0xff, 0x12, 0xd2, 0x6f, 0x29, 0x00, 0x00,
+	0x91, 0xa8, 0x16, 0x5e, 0x87, 0x43, 0xab, 0x94, 0x49, 0x22, 0xb7, 0x02, 0xbb, 0x4d, 0xf7, 0x92,
+	0x11, 0x86, 0x72, 0x44, 0x96, 0xca, 0xb5, 0x67, 0x49, 0x0c, 0x86, 0xbf, 0x67, 0xc1, 0x5c, 0x92,
+	0xea, 0x1e, 0x52, 0x79, 0x12, 0x26, 0xed, 0x26, 0x73, 0xee, 0xd3, 0x48, 0x26, 0x9c, 0x85, 0x38,
+	0x10, 0x21, 0xc8, 0xfa, 0x36, 0x93, 0x6a, 0xcf, 0x10, 0xf1, 0x8d, 0x8e, 0xc3, 0x54, 0xdf, 0xdd,
+	0x72, 0xbd, 0x07, 0xae, 0x1e, 0x9a, 0x15, 0x4b, 0x4a, 0x40, 0xf1, 0x4f, 0x2c, 0x80, 0xc8, 0x6a,
+	0xf7, 0x14, 0xd8, 0x69, 0x38, 0x18, 0xb5, 0xae, 0x79, 0xeb, 0x9b, 0xb6, 0xdf, 0x52, 0x8b, 0x1a,
+	0xee, 0x18, 0xb9, 0xb0, 0x88, 0xd5, 0x6c, 0x8c, 0xd5, 0x39, 0xc8, 0x73, 0x5f, 0x45, 0x83, 0x4a,
+	0x6e, 0xc1, 0x3a, 0x39, 0x49, 0x54, 0x0b, 0x57, 0xe1, 0xe8, 0x2a, 0x65, 0x17, 0x82, 0xc0, 0x69,
+	0xbb, 0xb4, 0x75, 0xc3, 0xf6, 0x99, 0xc3, 0x1c, 0xcf, 0x0d, 0x77, 0xea, 0x9f, 0x2d, 0x38, 0x36,
+	0x06, 0x41, 0x09, 0xd7, 0x03, 0x64, 0x0f, 0xf5, 0xaa, 0xbd, 0xfb, 0x72, 0x64, 0x5d, 0x9f, 0x4a,
+	0x64, 0x71, 0xb8, 0x4b, 0xfa, 0xb2, 0x11, 0xa4, 0xe7, 0x2f, 0xc2, 0xe1, 0x31, 0xe8, 0xa6, 0x8f,
+	0xcb, 0x49, 0x1f, 0x37, 0x6b, 0xfa, 0xb8, 0x8c, 0xe9, 0xc6, 0xfe, 0x91, 0x81, 0xf2, 0x6b, 0x7d,
+	0xea, 0x6f, 0x6b, 0xe3, 0xab, 0x42, 0x21, 0xa0, 0x1d, 0xda, 0x64, 0x9e, 0x2f, 0xed, 0xa4, 0x91,
+	0xae, 0x58, 0x24, 0x84, 0x71, 0x52, 0x1d, 0xbe, 0xdb, 0x04, 0xa9, 0x49, 0x22, 0x1b, 0xe8, 0x3c,
+	0xe4, 0x02, 0x66, 0xfb, 0x4c, 0x68, 0xa1, 0xb4, 0x34, 0xbf, 0x28, 0xc3, 0xcb, 0xa2, 0x0e, 0x2f,
+	0x8b, 0x37, 0x75, 0x78, 0x69, 0x14, 0xde, 0x1d, 0xd4, 0x52, 0x6f, 0xff, 0xa5, 0x66, 0x11, 0x39,
+	0x04, 0x9d, 0x83, 0x0c, 0x75, 0x5b, 0x42, 0x53, 0x9f, 0x75, 0x24, 0x1f, 0x80, 0xce, 0x42, 0xb1,
+	0xe5, 0xf8, 0xb4, 0xc9, 0x39, 0x17, 0xfa, 0x9c, 0x5a, 0x9a, 0x89, 0x24, 0xbd, 0xa2, 0xbb, 0x48,
+	0x84, 0x85, 0x4e, 0x43, 0x3e, 0xe0, 0x46, 0x13, 0x54, 0x26, 0xb8, 0x7f, 0x6e, 0xcc, 0xee, 0x0e,
+	0x6a, 0xd3, 0x12, 0x72, 0xda, 0xeb, 0x3a, 0x8c, 0x76, 0x7b, 0x6c, 0x9b, 0x28, 0x1c, 0xf4, 0x34,
+	0x4c, 0xb4, 0x68, 0x87, 0x72, 0x27, 0x5c, 0x10, 0x8a, 0x9c, 0x36, 0xc8, 0x8b, 0x0e, 0xa2, 0x11,
+	0xd0, 0x9b, 0x90, 0xed, 0x75, 0x6c, 0xb7, 0x52, 0x14, 0x5c, 0x4c, 0x45, 0x88, 0x37, 0x3a, 0xb6,
+	0xdb, 0x78, 0xe1, 0x83, 0x41, 0xed, 0xb9, 0xb6, 0xc3, 0x36, 0xfb, 0x77, 0x16, 0x9b, 0x5e, 0xb7,
+	0xde, 0xf6, 0xed, 0x0d, 0xdb, 0xb5, 0xeb, 0x1d, 0x6f, 0xcb, 0xa9, 0xdf, 0x7f, 0xb6, 0xce, 0x83,
+	0xe6, 0xbd, 0x3e, 0xf5, 0x1d, 0xea, 0xd7, 0x39, 0x99, 0x45, 0xa1, 0x12, 0x3e, 0x94, 0x08, 0xb2,
+	0xe8, 0x0a, 0x8f, 0x09, 0x9e, 0x4f, 0x97, 0x79, 0x44, 0x0d, 0x2a, 0x20, 0x66, 0x39, 0x1c, 0xcd,
+	0x22, 0xe0, 0x84, 0x6e, 0xac, 0xfa, 0x5e, 0xbf, 0xd7, 0x38, 0xb0, 0x3b, 0xa8, 0x99, 0xf8, 0xc4,
+	0x6c, 0x5c, 0xc9, 0x16, 0xf2, 0xd3, 0x13, 0xf8, 0x9d, 0x0c, 0xa0, 0x75, 0xbb, 0xdb, 0xeb, 0xd0,
+	0x7d, 0xa9, 0x3f, 0x54, 0x74, 0xfa, 0x91, 0x15, 0x9d, 0xd9, 0xaf, 0xa2, 0x23, 0xad, 0x65, 0xf7,
+	0xa7, 0xb5, 0xdc, 0x67, 0xd5, 0x5a, 0xfe, 0x0b, 0xaf, 0x35, 0x5c, 0x81, 0x2c, 0xa7, 0xcc, 0x37,
+	0xb7, 0x6f, 0x3f, 0x10, 0xba, 0x29, 0x13, 0xfe, 0x89, 0xd7, 0x20, 0x2f, 0xf9, 0x42, 0xf3, 0x49,
+	0xe5, 0xc5, 0xf7, 0x6d, 0xa4, 0xb8, 0x8c, 0x56, 0xc9, 0x74, 0xa4, 0x92, 0x8c, 0x10, 0x36, 0xfe,
+	0xad, 0x05, 0x93, 0xca, 0x22, 0x94, 0x6b, 0xbb, 0x13, 0x85, 0x66, 0xe9, 0xcf, 0x0e, 0x27, 0x43,
+	0xf3, 0x85, 0x96, 0xdd, 0x63, 0xd4, 0x6f, 0xd4, 0xdf, 0x1d, 0xd4, 0xac, 0x0f, 0x06, 0xb5, 0x13,
+	0xe3, 0x84, 0xa6, 0x33, 0x46, 0x9d, 0xc3, 0x68, 0xc2, 0xe8, 0x94, 0x58, 0x1d, 0x0b, 0x94, 0x59,
+	0x1d, 0x58, 0x94, 0x89, 0xe6, 0x65, 0xb7, 0x4d, 0x03, 0x4e, 0x39, 0xcb, 0x2d, 0x82, 0x48, 0x1c,
+	0xce, 0xe6, 0x03, 0xdb, 0x77, 0x1d, 0xb7, 0x1d, 0x54, 0x32, 0x22, 0xcf, 0x0a, 0xdb, 0xf8, 0x47,
+	0x16, 0xcc, 0xc4, 0xcc, 0x5a, 0x31, 0xf1, 0x3c, 0xe4, 0x03, 0xae, 0x29, 0xcd, 0x83, 0x61, 0x14,
+	0xeb, 0x02, 0xde, 0x98, 0x52, 0x8b, 0xcf, 0xcb, 0x36, 0x51, 0xf8, 0x8f, 0x6f, 0x69, 0x7f, 0xb0,
+	0xa0, 0x2c, 0x92, 0x45, 0xbd, 0xd7, 0x10, 0x64, 0x5d, 0xbb, 0x4b, 0x95, 0xaa, 0xc4, 0xb7, 0x91,
+	0x41, 0xf2, 0xe9, 0x0a, 0x3a, 0x83, 0xdc, 0xaf, 0x83, 0xb5, 0x1e, 0xd9, 0xc1, 0x5a, 0xd1, 0xbe,
+	0x9b, 0x85, 0x1c, 0x37, 0xef, 0x6d, 0xe1, 0x5c, 0x8b, 0x44, 0x36, 0xf0, 0x09, 0x98, 0x54, 0x5c,
+	0x44, 0x79, 0xc5, 0xc8, 0xa4, 0xb7, 0x0b, 0x79, 0xa9, 0x09, 0xf4, 0x24, 0x14, 0xc3, 0xe3, 0x85,
+	0xe0, 0x36, 0xd3, 0xc8, 0xef, 0x0e, 0x6a, 0x69, 0x16, 0x90, 0xa8, 0x03, 0xd5, 0xcc, 0x20, 0x65,
+	0x35, 0x8a, 0xbb, 0x83, 0x9a, 0x04, 0xa8, 0x78, 0x85, 0x8e, 0x42, 0x76, 0x93, 0x67, 0x0c, 0x22,
+	0xf3, 0x6c, 0x14, 0x76, 0x07, 0x35, 0xd1, 0x26, 0xe2, 0x17, 0xaf, 0x42, 0x79, 0x8d, 0xb6, 0xed,
+	0xe6, 0xb6, 0x9a, 0x34, 0x8c, 0x79, 0x7c, 0x42, 0x4b, 0xd3, 0x78, 0x02, 0xca, 0xe1, 0x8c, 0x6f,
+	0xa9, 0x5c, 0x27, 0x43, 0x4a, 0x21, 0xec, 0x6a, 0x80, 0x7f, 0x6c, 0x81, 0xb2, 0x01, 0x84, 0x8d,
+	0x13, 0x08, 0xf7, 0x85, 0xb0, 0x3b, 0xa8, 0x29, 0x88, 0x3e, 0x60, 0xa0, 0x17, 0x61, 0x22, 0x10,
+	0x33, 0xea, 0x64, 0xd2, 0x34, 0x2d, 0xd1, 0xd1, 0x38, 0xc0, 0x4d, 0x64, 0x77, 0x50, 0xd3, 0x88,
+	0x44, 0x7f, 0xa0, 0xc5, 0x58, 0x2a, 0x24, 0x19, 0x9b, 0xda, 0x1d, 0xd4, 0x0c, 0xa8, 0x99, 0x1a,
+	0xe1, 0x4f, 0x2c, 0x28, 0xdd, 0xb4, 0x9d, 0xd0, 0x84, 0x2a, 0x5a, 0x45, 0x91, 0xaf, 0x96, 0x00,
+	0x6e, 0x89, 0x2d, 0xda, 0xb1, 0xb7, 0x2f, 0x79, 0xbe, 0xa0, 0x3b, 0x49, 0xc2, 0x76, 0x14, 0xc3,
+	0xb3, 0x23, 0x63, 0x78, 0x6e, 0xff, 0xae, 0xfd, 0xbf, 0xeb, 0x48, 0xaf, 0x64, 0x0b, 0xe9, 0xe9,
+	0x0c, 0x7e, 0xc7, 0x82, 0xb2, 0x64, 0x5e, 0x59, 0xde, 0x37, 0x20, 0x2f, 0x65, 0x23, 0xd8, 0xff,
+	0x14, 0xc7, 0x74, 0x6a, 0x3f, 0x4e, 0x49, 0xd1, 0x44, 0x2f, 0xc3, 0x54, 0xcb, 0xf7, 0x7a, 0xbd,
+	0xe4, 0x61, 0xc1, 0x98, 0x65, 0xc5, 0xec, 0x27, 0x09, 0x74, 0xfc, 0x47, 0x0b, 0x26, 0x95, 0x33,
+	0x51, 0xea, 0x0a, 0x45, 0x6c, 0x3d, 0x72, 0xf4, 0x4c, 0xef, 0x37, 0x7a, 0xce, 0x41, 0xbe, 0xcd,
+	0xe3, 0x8b, 0x76, 0x48, 0xaa, 0xb5, 0xbf, 0xa8, 0x8a, 0xaf, 0xc0, 0x94, 0x66, 0x65, 0x8c, 0x47,
+	0x9d, 0x4f, 0x7a, 0xd4, 0xcb, 0x2d, 0xea, 0x32, 0x67, 0xc3, 0x09, 0x7d, 0xa4, 0xc2, 0xc7, 0xdf,
+	0xb5, 0x60, 0x3a, 0x89, 0x82, 0x56, 0x12, 0x87, 0xfd, 0xe3, 0xe3, 0xc9, 0x99, 0xe7, 0x7c, 0x4d,
+	0x5a, 0x9d, 0xf6, 0x9f, 0xdb, 0xeb, 0xb4, 0x1f, 0xcb, 0x84, 0x8b, 0xca, 0x2b, 0xe0, 0x1f, 0x5a,
+	0x30, 0x19, 0xd3, 0x25, 0x7a, 0x1e, 0xb2, 0x1b, 0xbe, 0xd7, 0xdd, 0x97, 0xa2, 0xc4, 0x08, 0xf4,
+	0xbf, 0x90, 0x66, 0xde, 0xbe, 0xd4, 0x94, 0x66, 0x1e, 0xd7, 0x92, 0x62, 0x3f, 0x23, 0x4f, 0x2c,
+	0xb2, 0x85, 0x9f, 0x83, 0xa2, 0x60, 0xe8, 0x86, 0xed, 0xf8, 0x23, 0x03, 0xc6, 0x68, 0x86, 0x5e,
+	0x84, 0x03, 0xd2, 0x19, 0x8e, 0x1e, 0x5c, 0x1e, 0x35, 0xb8, 0xac, 0x07, 0x1f, 0x81, 0x9c, 0x48,
+	0x3a, 0xf8, 0x10, 0x7e, 0xe2, 0xd6, 0x43, 0xf8, 0x37, 0x3e, 0x04, 0x33, 0x7c, 0x0f, 0x52, 0x3f,
+	0x58, 0xf6, 0xfa, 0x2e, 0xd3, 0x27, 0xa4, 0xd3, 0x30, 0x1b, 0x07, 0x2b, 0x2b, 0x99, 0x85, 0x5c,
+	0x93, 0x03, 0x04, 0x8d, 0x49, 0x22, 0x1b, 0xf8, 0x67, 0x16, 0xa0, 0x55, 0xca, 0xc4, 0x2c, 0x97,
+	0x57, 0xc2, 0xed, 0x31, 0x0f, 0x85, 0xae, 0xcd, 0x9a, 0x9b, 0xd4, 0x0f, 0x74, 0xfe, 0xa2, 0xdb,
+	0x9f, 0x47, 0xe2, 0x89, 0xcf, 0xc2, 0x4c, 0x6c, 0x95, 0x8a, 0xa7, 0x79, 0x28, 0x34, 0x15, 0x4c,
+	0x85, 0xbc, 0xb0, 0x8d, 0x7f, 0x95, 0x86, 0x82, 0x4e, 0xeb, 0xd0, 0x59, 0x28, 0x6d, 0x38, 0x6e,
+	0x9b, 0xfa, 0x3d, 0xdf, 0x51, 0x22, 0xc8, 0xca, 0x34, 0xcf, 0x00, 0x13, 0xb3, 0x81, 0x9e, 0x81,
+	0x89, 0x7e, 0x40, 0xfd, 0xb7, 0x1c, 0xb9, 0xd3, 0x8b, 0x8d, 0xd9, 0x9d, 0x41, 0x2d, 0x7f, 0x2b,
+	0xa0, 0xfe, 0xe5, 0x15, 0x1e, 0x7c, 0xfa, 0xe2, 0x8b, 0xc8, 0xff, 0x16, 0x7a, 0x55, 0x99, 0xa9,
+	0x48, 0xe0, 0x1a, 0xff, 0xc7, 0x97, 0x9f, 0x70, 0x75, 0x3d, 0xdf, 0xeb, 0x52, 0xb6, 0x49, 0xfb,
+	0x41, 0xbd, 0xe9, 0x75, 0xbb, 0x9e, 0x5b, 0x17, 0xf5, 0x3c, 0xc1, 0x34, 0x8f, 0xa0, 0x7c, 0xb8,
+	0xb2, 0xdc, 0x9b, 0x30, 0xc1, 0x36, 0x7d, 0xaf, 0xdf, 0xde, 0x14, 0x81, 0x21, 0xd3, 0x38, 0xbf,
+	0x7f, 0x7a, 0x9a, 0x02, 0xd1, 0x1f, 0xe8, 0x09, 0x2e, 0x2d, 0xda, 0xdc, 0x0a, 0xfa, 0x5d, 0x79,
+	0xea, 0x6e, 0xe4, 0x76, 0x07, 0x35, 0xeb, 0x19, 0x12, 0x82, 0xf1, 0x05, 0x98, 0x8c, 0xa5, 0xc2,
+	0xe8, 0x0c, 0x64, 0x7d, 0xba, 0xa1, 0x5d, 0x01, 0x1a, 0xce, 0x98, 0x65, 0xf4, 0xe7, 0x38, 0x44,
+	0xfc, 0xe2, 0xef, 0xa4, 0xa1, 0x66, 0x54, 0xe2, 0x2e, 0x79, 0xfe, 0x55, 0xca, 0x7c, 0xa7, 0x79,
+	0xcd, 0xee, 0x86, 0x75, 0x95, 0x1a, 0x94, 0xba, 0x02, 0xf8, 0x96, 0xb1, 0x8b, 0xa0, 0x1b, 0xe2,
+	0xa1, 0x63, 0x00, 0x62, 0xdb, 0xc9, 0x7e, 0xb9, 0xa1, 0x8a, 0x02, 0x22, 0xba, 0x97, 0x63, 0xc2,
+	0xae, 0xef, 0x53, 0x38, 0x4a, 0xc8, 0x97, 0x93, 0x42, 0xde, 0x37, 0x9d, 0x50, 0xb2, 0xe6, 0x76,
+	0xc9, 0xc5, 0xb7, 0x0b, 0xfe, 0xbb, 0x05, 0xd5, 0x35, 0xbd, 0xf2, 0x47, 0x14, 0x87, 0xe6, 0x37,
+	0xfd, 0x98, 0xf8, 0xcd, 0x3c, 0x46, 0x7e, 0xb3, 0x09, 0x7e, 0xab, 0x00, 0x6b, 0x8e, 0x4b, 0x2f,
+	0x39, 0x1d, 0x46, 0xfd, 0x11, 0x87, 0xa4, 0xef, 0x67, 0x22, 0x8f, 0x43, 0xe8, 0x86, 0x96, 0xc1,
+	0xb2, 0xe1, 0xe6, 0x1f, 0x07, 0x8b, 0xe9, 0xc7, 0xc8, 0x62, 0x26, 0xe1, 0x01, 0x5d, 0x98, 0xd8,
+	0x10, 0xec, 0xc9, 0x88, 0x1d, 0xab, 0x09, 0x47, 0xbc, 0x37, 0xbe, 0xa2, 0x26, 0x3f, 0xb7, 0x47,
+	0xc2, 0x25, 0x6a, 0xfb, 0xf5, 0x60, 0xdb, 0x65, 0xf6, 0x43, 0x63, 0x3c, 0xd1, 0x93, 0x20, 0x5b,
+	0xe5, 0x74, 0xb9, 0x91, 0x39, 0xdd, 0x4b, 0x6a, 0x9a, 0xff, 0x24, 0xaf, 0xc3, 0xed, 0xc8, 0xc1,
+	0x0a, 0xa5, 0x28, 0x07, 0x7b, 0x7c, 0xaf, 0xed, 0x2f, 0x37, 0x3d, 0x3a, 0x19, 0x3f, 0x9a, 0x95,
+	0xc3, 0xa3, 0x59, 0x8b, 0x3e, 0x8c, 0x9d, 0xcb, 0xf0, 0xef, 0x2c, 0x98, 0x5e, 0xa5, 0x2c, 0x9e,
+	0x8d, 0x7d, 0x89, 0x94, 0x8f, 0x5f, 0x81, 0x83, 0xc6, 0xfa, 0x95, 0x9c, 0x9e, 0x4d, 0xa4, 0x60,
+	0x87, 0x22, 0x49, 0x09, 0x19, 0xa8, 0x93, 0x6d, 0x3c, 0xfb, 0xba, 0x01, 0x25, 0xa3, 0x13, 0x5d,
+	0x48, 0xe4, 0x5d, 0x33, 0x89, 0x4b, 0x16, 0x9e, 0x3b, 0x34, 0x66, 0x15, 0x4f, 0xf2, 0xfc, 0xaa,
+	0xb2, 0xea, 0x30, 0x47, 0x59, 0x07, 0x24, 0x14, 0x2b, 0xc8, 0x9a, 0x51, 0x52, 0x40, 0x5f, 0x0d,
+	0x13, 0xb0, 0xb0, 0x8d, 0x9e, 0x80, 0xac, 0xef, 0x3d, 0xd0, 0x09, 0xf5, 0xa4, 0x51, 0x7d, 0xf7,
+	0x1e, 0x10, 0xd1, 0x85, 0x5f, 0x84, 0x0c, 0xf1, 0x1e, 0xa0, 0x2a, 0x80, 0x6f, 0xbb, 0x6d, 0x7a,
+	0x3b, 0x3c, 0xca, 0x95, 0x89, 0x01, 0x19, 0x93, 0xc1, 0x2c, 0xc3, 0x41, 0x73, 0x45, 0x52, 0xdd,
+	0x8b, 0x30, 0xf1, 0x5a, 0xdf, 0x14, 0xd7, 0x6c, 0x42, 0x5c, 0xb2, 0x62, 0xa0, 0x91, 0xb8, 0xcd,
+	0x40, 0x04, 0x47, 0x47, 0xa1, 0xc8, 0xec, 0x3b, 0x1d, 0x7a, 0x2d, 0x72, 0x96, 0x11, 0x80, 0xf7,
+	0xf2, 0x53, 0xe8, 0x6d, 0x23, 0x15, 0x8b, 0x00, 0xe8, 0x69, 0x98, 0x8e, 0xd6, 0x7c, 0xc3, 0xa7,
+	0x1b, 0xce, 0x43, 0xa1, 0xe1, 0x32, 0x19, 0x82, 0xa3, 0x93, 0x70, 0x20, 0x82, 0xad, 0x8b, 0x94,
+	0x27, 0x2b, 0x50, 0x93, 0x60, 0x2e, 0x1b, 0xc1, 0xee, 0xc5, 0x7b, 0x7d, 0xbb, 0x23, 0xb6, 0x69,
+	0x99, 0x18, 0x10, 0xfc, 0x7b, 0x0b, 0x0e, 0x4a, 0x55, 0xf3, 0x3d, 0xf0, 0x65, 0xb4, 0xfa, 0x9f,
+	0x5b, 0x80, 0x4c, 0x0e, 0x94, 0x69, 0xfd, 0x8f, 0x59, 0x91, 0xe2, 0x39, 0x55, 0x49, 0x1c, 0xae,
+	0x25, 0x28, 0x2a, 0x2a, 0x61, 0xc8, 0x37, 0x65, 0xe5, 0x4d, 0x5c, 0x1e, 0xc8, 0xd3, 0xbb, 0x84,
+	0x10, 0xf5, 0x8f, 0x6a, 0x90, 0xbb, 0xb3, 0xcd, 0x68, 0xa0, 0xce, 0xde, 0xa2, 0xe8, 0x20, 0x00,
+	0x44, 0xfe, 0xf1, 0xb9, 0xd4, 0x7d, 0x98, 0x50, 0x83, 0x9a, 0x4b, 0x81, 0x88, 0xfe, 0xc0, 0xff,
+	0x4c, 0xc3, 0xe4, 0x6d, 0xaf, 0xd3, 0x8f, 0xc2, 0xeb, 0x97, 0x29, 0xb4, 0xc4, 0x0a, 0x02, 0x39,
+	0x5d, 0x10, 0x40, 0x90, 0x0d, 0x18, 0xed, 0x09, 0xcb, 0xca, 0x10, 0xf1, 0x8d, 0x30, 0x94, 0x99,
+	0xed, 0xb7, 0x29, 0x93, 0xc7, 0xac, 0x4a, 0x5e, 0xe4, 0xbf, 0x31, 0x18, 0x5a, 0x80, 0x92, 0xdd,
+	0x6e, 0xfb, 0xb4, 0x6d, 0x33, 0xda, 0xd8, 0xae, 0x4c, 0x88, 0xc9, 0x4c, 0x10, 0xba, 0x02, 0x53,
+	0x4d, 0xbb, 0xb9, 0xe9, 0xb8, 0xed, 0xeb, 0x3d, 0x79, 0x53, 0x52, 0x10, 0x1e, 0xfc, 0xe8, 0xa2,
+	0x79, 0x4d, 0xbc, 0xb8, 0x1c, 0xc3, 0x51, 0x7e, 0x2c, 0x31, 0x12, 0xbf, 0x01, 0x53, 0x5a, 0xf0,
+	0xca, 0x3c, 0xce, 0xc0, 0xc4, 0x7d, 0x01, 0x19, 0x51, 0xec, 0x93, 0xa8, 0x8a, 0x94, 0x46, 0x8b,
+	0x5f, 0x6a, 0x68, 0xfe, 0xf1, 0x15, 0xc8, 0x4b, 0x74, 0x74, 0xd4, 0x3c, 0x78, 0xc9, 0xdc, 0x93,
+	0xb7, 0xd5, 0x29, 0x0a, 0x43, 0x5e, 0x12, 0x52, 0x46, 0x24, 0xec, 0x4c, 0x42, 0x88, 0xfa, 0xc7,
+	0x3f, 0x48, 0xc3, 0xa1, 0x15, 0xca, 0xc4, 0xbd, 0xe1, 0x25, 0x87, 0x76, 0x5a, 0x9f, 0x6b, 0x4d,
+	0x20, 0xac, 0xec, 0x65, 0x8c, 0xca, 0x1e, 0xf7, 0x61, 0x1d, 0xc7, 0xa5, 0x6b, 0x46, 0x69, 0x28,
+	0x02, 0x44, 0x32, 0xca, 0x99, 0x45, 0x23, 0x6d, 0x23, 0x79, 0xc3, 0x46, 0xa2, 0x82, 0xe0, 0x44,
+	0xac, 0x86, 0xa9, 0x4f, 0xa0, 0x85, 0xe8, 0xf8, 0x8a, 0x7f, 0x63, 0xc1, 0x5c, 0x52, 0x2e, 0x4a,
+	0x8d, 0x17, 0x21, 0xbf, 0x21, 0x20, 0xc3, 0x65, 0xe7, 0xd8, 0x08, 0x59, 0xb9, 0x90, 0xa8, 0x66,
+	0xe5, 0x42, 0x42, 0xd0, 0x53, 0xb1, 0x0b, 0xab, 0xc6, 0xcc, 0xee, 0xa0, 0x76, 0x40, 0x00, 0x0c,
+	0x5c, 0xc5, 0xcc, 0xe9, 0x70, 0xe1, 0x99, 0xa8, 0x24, 0x22, 0x21, 0x26, 0x61, 0x55, 0xdf, 0xfc,
+	0x97, 0x05, 0x93, 0xb1, 0x85, 0x08, 0x11, 0xf1, 0x2d, 0xa0, 0xc2, 0x83, 0x6c, 0xa0, 0xa7, 0x20,
+	0xcb, 0xb6, 0x7b, 0x2a, 0x2a, 0x34, 0x0e, 0x7d, 0x32, 0xa8, 0x1d, 0x8c, 0x0d, 0xbb, 0xb9, 0xdd,
+	0xa3, 0x44, 0xa0, 0xf0, 0x9d, 0xd3, 0xb4, 0xfd, 0x96, 0xe3, 0xda, 0x1d, 0x87, 0x6d, 0xab, 0x2b,
+	0x76, 0x13, 0xc4, 0xdd, 0x51, 0xcf, 0xf6, 0x03, 0x9d, 0x04, 0x16, 0xa5, 0x3b, 0x52, 0x20, 0xa2,
+	0x3f, 0x44, 0x71, 0x67, 0x8b, 0xb2, 0xe6, 0xa6, 0x0c, 0x0b, 0xaa, 0xb8, 0x23, 0x20, 0xb1, 0xe2,
+	0x8e, 0x80, 0xa0, 0x25, 0x28, 0xdc, 0x0d, 0x3c, 0xf7, 0x86, 0xcd, 0x36, 0xe5, 0x86, 0x6e, 0xcc,
+	0xed, 0x0e, 0x6a, 0x48, 0xc3, 0x8c, 0x11, 0x21, 0x1e, 0xfe, 0xa9, 0x15, 0x19, 0xb4, 0xdc, 0xf7,
+	0x5f, 0x38, 0x83, 0xc6, 0x5f, 0x8b, 0x6c, 0x4b, 0x2f, 0x51, 0xd9, 0xd6, 0xcb, 0x30, 0xd5, 0x8a,
+	0xf5, 0x8c, 0xb7, 0x31, 0x59, 0xec, 0x4e, 0xa0, 0xe3, 0x7e, 0xa4, 0x7b, 0x01, 0x19, 0xa3, 0xfb,
+	0x84, 0x42, 0xd3, 0xc3, 0x0a, 0x8d, 0x34, 0x95, 0xd9, 0x5b, 0x53, 0x4f, 0x1f, 0x87, 0x62, 0x78,
+	0xb1, 0x89, 0x4a, 0x30, 0x71, 0xe9, 0x3a, 0x79, 0xfd, 0x02, 0x59, 0x99, 0x4e, 0xa1, 0x32, 0x14,
+	0x1a, 0x17, 0x96, 0x5f, 0x15, 0x2d, 0x6b, 0xe9, 0x97, 0x79, 0x9d, 0xec, 0xf8, 0xe8, 0xff, 0x21,
+	0x27, 0x33, 0x98, 0xb9, 0x88, 0x39, 0xf3, 0xce, 0x6f, 0xfe, 0xf0, 0x10, 0x5c, 0x4a, 0x09, 0xa7,
+	0xce, 0x58, 0xe8, 0x1a, 0x94, 0x04, 0x50, 0x55, 0xd5, 0x8f, 0x26, 0x8b, 0xdb, 0x31, 0x4a, 0xc7,
+	0xc6, 0xf4, 0x1a, 0xf4, 0xce, 0x43, 0x4e, 0x0a, 0x6c, 0x2e, 0x91, 0x68, 0x8e, 0x58, 0x4d, 0xec,
+	0x9e, 0x01, 0xa7, 0xd0, 0x0b, 0x90, 0xbd, 0x69, 0x3b, 0x1d, 0x64, 0xe4, 0xb9, 0x46, 0x31, 0x7c,
+	0x7e, 0x2e, 0x09, 0x36, 0xa6, 0x7d, 0x29, 0xac, 0xe9, 0x1f, 0x4e, 0x16, 0x16, 0xf5, 0xf0, 0xca,
+	0x70, 0x47, 0x38, 0xf3, 0x75, 0x59, 0x79, 0xd6, 0xe5, 0x2d, 0x74, 0x2c, 0x3e, 0x55, 0xa2, 0x1a,
+	0x36, 0x5f, 0x1d, 0xd7, 0x1d, 0x12, 0x5c, 0x83, 0x92, 0x51, 0x5a, 0x32, 0xc5, 0x3a, 0x5c, 0x17,
+	0x33, 0xc5, 0x3a, 0xa2, 0x1e, 0x85, 0x53, 0x68, 0x15, 0x0a, 0xe2, 0xd1, 0x87, 0xcd, 0x02, 0x74,
+	0x24, 0x79, 0x08, 0x30, 0x92, 0xbf, 0xf9, 0xa3, 0xa3, 0x3b, 0x43, 0x42, 0x5f, 0x85, 0xe2, 0x2a,
+	0x65, 0x2a, 0xea, 0x1d, 0x4e, 0x86, 0xcd, 0x11, 0x92, 0x8a, 0x87, 0x5e, 0x9c, 0x42, 0x6f, 0x88,
+	0x83, 0x4a, 0xdc, 0xa5, 0xa3, 0xda, 0x18, 0xd7, 0x1d, 0xae, 0x6b, 0x61, 0x3c, 0x42, 0x48, 0xf9,
+	0xf5, 0x18, 0x65, 0x95, 0x6b, 0xd4, 0xc6, 0x6c, 0xd8, 0x90, 0x72, 0x6d, 0x8f, 0x47, 0x63, 0x38,
+	0xb5, 0xf4, 0xa6, 0x7e, 0x9d, 0xb2, 0x62, 0x33, 0x1b, 0x5d, 0x87, 0xa9, 0xf0, 0x01, 0x8d, 0x78,
+	0x58, 0x15, 0xb3, 0xf9, 0xa1, 0x57, 0x5c, 0x31, 0x9b, 0x1f, 0x7e, 0xcd, 0x85, 0x53, 0x4b, 0x77,
+	0x61, 0x56, 0x5e, 0x16, 0xca, 0x57, 0x4a, 0x97, 0x7c, 0xcf, 0x65, 0xdc, 0x67, 0x11, 0x98, 0x8c,
+	0x3d, 0x5f, 0x42, 0x86, 0xd5, 0x8c, 0x7a, 0x3b, 0x65, 0xb2, 0x32, 0xf2, 0xdd, 0x13, 0x4e, 0x2d,
+	0xfd, 0xc9, 0x82, 0xb2, 0x39, 0x19, 0xba, 0x65, 0x70, 0x23, 0x9e, 0x03, 0x99, 0x12, 0x1b, 0xf9,
+	0xfc, 0xc8, 0xd4, 0xc5, 0xe8, 0x97, 0x44, 0x38, 0x85, 0xee, 0x8a, 0xb7, 0x4b, 0xc3, 0x0f, 0x50,
+	0xd0, 0xf1, 0x3d, 0xdf, 0xba, 0xc8, 0x49, 0x4e, 0x7c, 0xc6, 0x37, 0x31, 0x38, 0xd5, 0x78, 0xf3,
+	0xbd, 0x0f, 0xab, 0xa9, 0xf7, 0x3f, 0xac, 0xa6, 0x3e, 0xfe, 0xb0, 0x6a, 0x7d, 0x6b, 0xa7, 0x6a,
+	0xfd, 0x62, 0xa7, 0x6a, 0xbd, 0xbb, 0x53, 0xb5, 0xde, 0xdb, 0xa9, 0x5a, 0x7f, 0xdd, 0xa9, 0x5a,
+	0x7f, 0xdb, 0xa9, 0xa6, 0x3e, 0xde, 0xa9, 0x5a, 0x6f, 0x7f, 0x54, 0x4d, 0xbd, 0xf7, 0x51, 0x35,
+	0xf5, 0xfe, 0x47, 0xd5, 0xd4, 0xd7, 0x4f, 0xec, 0x5d, 0xfe, 0x90, 0x61, 0x25, 0x2f, 0xfe, 0x9e,
+	0xfd, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc3, 0x7e, 0xe1, 0xde, 0x4f, 0x29, 0x00, 0x00,
 }
 
 func (x Direction) String() string {
@@ -4053,14 +4044,6 @@ func (this *GetStreamUsageRequest) Equal(that interface{}) bool {
 	if this.Tenant != that1.Tenant {
 		return false
 	}
-	if len(this.Partitions) != len(that1.Partitions) {
-		return false
-	}
-	for i := range this.Partitions {
-		if this.Partitions[i] != that1.Partitions[i] {
-			return false
-		}
-	}
 	if len(this.StreamHashes) != len(that1.StreamHashes) {
 		return false
 	}
@@ -5972,10 +5955,9 @@ func (this *GetStreamUsageRequest) GoString() string {
 	if this == nil {
 		return "nil"
 	}
-	s := make([]string, 0, 7)
+	s := make([]string, 0, 6)
 	s = append(s, "&logproto.GetStreamUsageRequest{")
 	s = append(s, "Tenant: "+fmt.Sprintf("%#v", this.Tenant)+",\n")
-	s = append(s, "Partitions: "+fmt.Sprintf("%#v", this.Partitions)+",\n")
 	s = append(s, "StreamHashes: "+fmt.Sprintf("%#v", this.StreamHashes)+",\n")
 	s = append(s, "}")
 	return strings.Join(s, "")
@@ -7818,25 +7800,6 @@ func (m *GetStreamUsageRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		copy(dAtA[i:], dAtA3[:j2])
 		i = encodeVarintLogproto(dAtA, i, uint64(j2))
 		i--
-		dAtA[i] = 0x1a
-	}
-	if len(m.Partitions) > 0 {
-		dAtA5 := make([]byte, len(m.Partitions)*10)
-		var j4 int
-		for _, num1 := range m.Partitions {
-			num := uint64(num1)
-			for num >= 1<<7 {
-				dAtA5[j4] = uint8(uint64(num)&0x7f | 0x80)
-				num >>= 7
-				j4++
-			}
-			dAtA5[j4] = uint8(num)
-			j4++
-		}
-		i -= j4
-		copy(dAtA[i:], dAtA5[:j4])
-		i = encodeVarintLogproto(dAtA, i, uint64(j4))
-		i--
 		dAtA[i] = 0x12
 	}
 	if len(m.Tenant) > 0 {
@@ -7870,20 +7833,20 @@ func (m *GetStreamUsageResponse) MarshalToSizedBuffer(dAtA []byte) (int, error)
 	var l int
 	_ = l
 	if len(m.UnknownStreams) > 0 {
-		dAtA7 := make([]byte, len(m.UnknownStreams)*10)
-		var j6 int
+		dAtA5 := make([]byte, len(m.UnknownStreams)*10)
+		var j4 int
 		for _, num := range m.UnknownStreams {
 			for num >= 1<<7 {
-				dAtA7[j6] = uint8(uint64(num)&0x7f | 0x80)
+				dAtA5[j4] = uint8(uint64(num)&0x7f | 0x80)
 				num >>= 7
-				j6++
+				j4++
 			}
-			dAtA7[j6] = uint8(num)
-			j6++
+			dAtA5[j4] = uint8(num)
+			j4++
 		}
-		i -= j6
-		copy(dAtA[i:], dAtA7[:j6])
-		i = encodeVarintLogproto(dAtA, i, uint64(j6))
+		i -= j4
+		copy(dAtA[i:], dAtA5[:j4])
+		i = encodeVarintLogproto(dAtA, i, uint64(j4))
 		i--
 		dAtA[i] = 0x22
 	}
@@ -8090,20 +8053,20 @@ func (m *QueryRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i--
 		dAtA[i] = 0x28
 	}
-	n10, err10 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.End, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.End):])
-	if err10 != nil {
-		return 0, err10
+	n8, err8 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.End, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.End):])
+	if err8 != nil {
+		return 0, err8
 	}
-	i -= n10
-	i = encodeVarintLogproto(dAtA, i, uint64(n10))
+	i -= n8
+	i = encodeVarintLogproto(dAtA, i, uint64(n8))
 	i--
 	dAtA[i] = 0x22
-	n11, err11 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Start, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Start):])
-	if err11 != nil {
-		return 0, err11
+	n9, err9 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Start, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Start):])
+	if err9 != nil {
+		return 0, err9
 	}
-	i -= n11
-	i = encodeVarintLogproto(dAtA, i, uint64(n11))
+	i -= n9
+	i = encodeVarintLogproto(dAtA, i, uint64(n9))
 	i--
 	dAtA[i] = 0x1a
 	if m.Limit != 0 {
@@ -8188,20 +8151,20 @@ func (m *SampleQueryRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 			dAtA[i] = 0x22
 		}
 	}
-	n14, err14 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.End, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.End):])
-	if err14 != nil {
-		return 0, err14
+	n12, err12 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.End, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.End):])
+	if err12 != nil {
+		return 0, err12
 	}
-	i -= n14
-	i = encodeVarintLogproto(dAtA, i, uint64(n14))
+	i -= n12
+	i = encodeVarintLogproto(dAtA, i, uint64(n12))
 	i--
 	dAtA[i] = 0x1a
-	n15, err15 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Start, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Start):])
-	if err15 != nil {
-		return 0, err15
+	n13, err13 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Start, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Start):])
+	if err13 != nil {
+		return 0, err13
 	}
-	i -= n15
-	i = encodeVarintLogproto(dAtA, i, uint64(n15))
+	i -= n13
+	i = encodeVarintLogproto(dAtA, i, uint64(n13))
 	i--
 	dAtA[i] = 0x12
 	if len(m.Selector) > 0 {
@@ -8424,22 +8387,22 @@ func (m *LabelRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		dAtA[i] = 0x2a
 	}
 	if m.End != nil {
-		n18, err18 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.End, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.End):])
-		if err18 != nil {
-			return 0, err18
+		n16, err16 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.End, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.End):])
+		if err16 != nil {
+			return 0, err16
 		}
-		i -= n18
-		i = encodeVarintLogproto(dAtA, i, uint64(n18))
+		i -= n16
+		i = encodeVarintLogproto(dAtA, i, uint64(n16))
 		i--
 		dAtA[i] = 0x22
 	}
 	if m.Start != nil {
-		n19, err19 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Start, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Start):])
-		if err19 != nil {
-			return 0, err19
+		n17, err17 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Start, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Start):])
+		if err17 != nil {
+			return 0, err17
 		}
-		i -= n19
-		i = encodeVarintLogproto(dAtA, i, uint64(n19))
+		i -= n17
+		i = encodeVarintLogproto(dAtA, i, uint64(n17))
 		i--
 		dAtA[i] = 0x1a
 	}
@@ -8649,12 +8612,12 @@ func (m *TailRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i--
 		dAtA[i] = 0x32
 	}
-	n21, err21 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Start, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Start):])
-	if err21 != nil {
-		return 0, err21
+	n19, err19 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Start, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Start):])
+	if err19 != nil {
+		return 0, err19
 	}
-	i -= n21
-	i = encodeVarintLogproto(dAtA, i, uint64(n21))
+	i -= n19
+	i = encodeVarintLogproto(dAtA, i, uint64(n19))
 	i--
 	dAtA[i] = 0x2a
 	if m.Limit != 0 {
@@ -8764,20 +8727,20 @@ func (m *SeriesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 			dAtA[i] = 0x1a
 		}
 	}
-	n23, err23 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.End, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.End):])
-	if err23 != nil {
-		return 0, err23
+	n21, err21 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.End, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.End):])
+	if err21 != nil {
+		return 0, err21
 	}
-	i -= n23
-	i = encodeVarintLogproto(dAtA, i, uint64(n23))
+	i -= n21
+	i = encodeVarintLogproto(dAtA, i, uint64(n21))
 	i--
 	dAtA[i] = 0x12
-	n24, err24 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Start, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Start):])
-	if err24 != nil {
-		return 0, err24
+	n22, err22 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Start, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Start):])
+	if err22 != nil {
+		return 0, err22
 	}
-	i -= n24
-	i = encodeVarintLogproto(dAtA, i, uint64(n24))
+	i -= n22
+	i = encodeVarintLogproto(dAtA, i, uint64(n22))
 	i--
 	dAtA[i] = 0xa
 	return len(dAtA) - i, nil
@@ -8921,20 +8884,20 @@ func (m *DroppedStream) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i--
 		dAtA[i] = 0x1a
 	}
-	n25, err25 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.To, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.To):])
-	if err25 != nil {
-		return 0, err25
+	n23, err23 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.To, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.To):])
+	if err23 != nil {
+		return 0, err23
 	}
-	i -= n25
-	i = encodeVarintLogproto(dAtA, i, uint64(n25))
+	i -= n23
+	i = encodeVarintLogproto(dAtA, i, uint64(n23))
 	i--
 	dAtA[i] = 0x12
-	n26, err26 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.From, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.From):])
-	if err26 != nil {
-		return 0, err26
+	n24, err24 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.From, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.From):])
+	if err24 != nil {
+		return 0, err24
 	}
-	i -= n26
-	i = encodeVarintLogproto(dAtA, i, uint64(n26))
+	i -= n24
+	i = encodeVarintLogproto(dAtA, i, uint64(n24))
 	i--
 	dAtA[i] = 0xa
 	return len(dAtA) - i, nil
@@ -9115,20 +9078,20 @@ func (m *GetChunkIDsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
-	n27, err27 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.End, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.End):])
-	if err27 != nil {
-		return 0, err27
+	n25, err25 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.End, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.End):])
+	if err25 != nil {
+		return 0, err25
 	}
-	i -= n27
-	i = encodeVarintLogproto(dAtA, i, uint64(n27))
+	i -= n25
+	i = encodeVarintLogproto(dAtA, i, uint64(n25))
 	i--
 	dAtA[i] = 0x1a
-	n28, err28 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Start, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Start):])
-	if err28 != nil {
-		return 0, err28
+	n26, err26 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Start, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Start):])
+	if err26 != nil {
+		return 0, err26
 	}
-	i -= n28
-	i = encodeVarintLogproto(dAtA, i, uint64(n28))
+	i -= n26
+	i = encodeVarintLogproto(dAtA, i, uint64(n26))
 	i--
 	dAtA[i] = 0x12
 	if len(m.Matchers) > 0 {
@@ -10087,20 +10050,20 @@ func (m *DetectedFieldsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i--
 		dAtA[i] = 0x1a
 	}
-	n32, err32 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.End, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.End):])
-	if err32 != nil {
-		return 0, err32
+	n30, err30 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.End, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.End):])
+	if err30 != nil {
+		return 0, err30
 	}
-	i -= n32
-	i = encodeVarintLogproto(dAtA, i, uint64(n32))
+	i -= n30
+	i = encodeVarintLogproto(dAtA, i, uint64(n30))
 	i--
 	dAtA[i] = 0x12
-	n33, err33 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Start, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Start):])
-	if err33 != nil {
-		return 0, err33
+	n31, err31 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Start, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Start):])
+	if err31 != nil {
+		return 0, err31
 	}
-	i -= n33
-	i = encodeVarintLogproto(dAtA, i, uint64(n33))
+	i -= n31
+	i = encodeVarintLogproto(dAtA, i, uint64(n31))
 	i--
 	dAtA[i] = 0xa
 	return len(dAtA) - i, nil
@@ -10251,20 +10214,20 @@ func (m *DetectedLabelsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i--
 		dAtA[i] = 0x1a
 	}
-	n34, err34 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.End, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.End):])
-	if err34 != nil {
-		return 0, err34
+	n32, err32 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.End, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.End):])
+	if err32 != nil {
+		return 0, err32
 	}
-	i -= n34
-	i = encodeVarintLogproto(dAtA, i, uint64(n34))
+	i -= n32
+	i = encodeVarintLogproto(dAtA, i, uint64(n32))
 	i--
 	dAtA[i] = 0x12
-	n35, err35 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Start, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Start):])
-	if err35 != nil {
-		return 0, err35
+	n33, err33 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Start, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Start):])
+	if err33 != nil {
+		return 0, err33
 	}
-	i -= n35
-	i = encodeVarintLogproto(dAtA, i, uint64(n35))
+	i -= n33
+	i = encodeVarintLogproto(dAtA, i, uint64(n33))
 	i--
 	dAtA[i] = 0xa
 	return len(dAtA) - i, nil
@@ -10503,13 +10466,6 @@ func (m *GetStreamUsageRequest) Size() (n int) {
 	if l > 0 {
 		n += 1 + l + sovLogproto(uint64(l))
 	}
-	if len(m.Partitions) > 0 {
-		l = 0
-		for _, e := range m.Partitions {
-			l += sovLogproto(uint64(e))
-		}
-		n += 1 + sovLogproto(uint64(l)) + l
-	}
 	if len(m.StreamHashes) > 0 {
 		l = 0
 		for _, e := range m.StreamHashes {
@@ -11739,7 +11695,6 @@ func (this *GetStreamUsageRequest) String() string {
 	}
 	s := strings.Join([]string{`&GetStreamUsageRequest{`,
 		`Tenant:` + fmt.Sprintf("%v", this.Tenant) + `,`,
-		`Partitions:` + fmt.Sprintf("%v", this.Partitions) + `,`,
 		`StreamHashes:` + fmt.Sprintf("%v", this.StreamHashes) + `,`,
 		`}`,
 	}, "")
@@ -13399,82 +13354,6 @@ func (m *GetStreamUsageRequest) Unmarshal(dAtA []byte) error {
 			m.Tenant = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
 		case 2:
-			if wireType == 0 {
-				var v int32
-				for shift := uint(0); ; shift += 7 {
-					if shift >= 64 {
-						return ErrIntOverflowLogproto
-					}
-					if iNdEx >= l {
-						return io.ErrUnexpectedEOF
-					}
-					b := dAtA[iNdEx]
-					iNdEx++
-					v |= int32(b&0x7F) << shift
-					if b < 0x80 {
-						break
-					}
-				}
-				m.Partitions = append(m.Partitions, v)
-			} else if wireType == 2 {
-				var packedLen int
-				for shift := uint(0); ; shift += 7 {
-					if shift >= 64 {
-						return ErrIntOverflowLogproto
-					}
-					if iNdEx >= l {
-						return io.ErrUnexpectedEOF
-					}
-					b := dAtA[iNdEx]
-					iNdEx++
-					packedLen |= int(b&0x7F) << shift
-					if b < 0x80 {
-						break
-					}
-				}
-				if packedLen < 0 {
-					return ErrInvalidLengthLogproto
-				}
-				postIndex := iNdEx + packedLen
-				if postIndex < 0 {
-					return ErrInvalidLengthLogproto
-				}
-				if postIndex > l {
-					return io.ErrUnexpectedEOF
-				}
-				var elementCount int
-				var count int
-				for _, integer := range dAtA[iNdEx:postIndex] {
-					if integer < 128 {
-						count++
-					}
-				}
-				elementCount = count
-				if elementCount != 0 && len(m.Partitions) == 0 {
-					m.Partitions = make([]int32, 0, elementCount)
-				}
-				for iNdEx < postIndex {
-					var v int32
-					for shift := uint(0); ; shift += 7 {
-						if shift >= 64 {
-							return ErrIntOverflowLogproto
-						}
-						if iNdEx >= l {
-							return io.ErrUnexpectedEOF
-						}
-						b := dAtA[iNdEx]
-						iNdEx++
-						v |= int32(b&0x7F) << shift
-						if b < 0x80 {
-							break
-						}
-					}
-					m.Partitions = append(m.Partitions, v)
-				}
-			} else {
-				return fmt.Errorf("proto: wrong wireType = %d for field Partitions", wireType)
-			}
-		case 3:
 			if wireType == 0 {
 				var v uint64
 				for shift := uint(0); ; shift += 7 {
diff --git a/pkg/logproto/logproto.proto b/pkg/logproto/logproto.proto
index 280f7936fe2..cc7082d25d6 100644
--- a/pkg/logproto/logproto.proto
+++ b/pkg/logproto/logproto.proto
@@ -87,8 +87,7 @@ service IngestLimits {
 
 message GetStreamUsageRequest {
   string tenant = 1;
-  repeated int32 partitions = 2;
-  repeated uint64 streamHashes = 3;
+  repeated uint64 streamHashes = 2;
 }
 
 message GetStreamUsageResponse {


Reviews:
user: login: grobinson-grafana
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T13:56:50Z
user: login: grobinson-grafana
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T14:00:39Z
user: login: grobinson-grafana
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T14:51:07Z
user: login: periklis
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-03T09:30:31Z
user: login: grobinson-grafana
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T13:31:26Z
user: login: grobinson-grafana
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T13:46:56Z
user: login: grobinson-grafana
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T13:47:26Z
user: login: grobinson-grafana
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T13:49:38Z
user: login: periklis
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-03T13:54:57Z
user: login: grobinson-grafana
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T14:36:37Z
user: login: grobinson-grafana
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T14:41:12Z
user: login: grobinson-grafana
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T14:53:57Z
user: login: grobinson-grafana
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T12:32:00Z

Review comments:
diff_hunk: @@ -54,9 +54,6 @@ func (m *mockIngestLimitsClient) GetAssignedPartitions(_ context.Context, r *log
 }
 
 func (m *mockIngestLimitsClient) GetStreamUsage(_ context.Context, r *logproto.GetStreamUsageRequest, _ ...grpc.CallOption) (*logproto.GetStreamUsageResponse, error) {
-	if expected := m.expectedStreamUsageRequest; expected != nil {
path: pkg/limits/frontend/mock_test.go
user: login: grobinson-grafana
body: ?
created_at: 2025-04-02T13:06:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1205,6 +1205,10 @@ ingest_limits_frontend:
   # CLI flag: -ingest-limits-frontend.recheck-period
   [recheck_period: <duration> | default = 10s]
 
+  # The number of partitions to use for the ring.
+  # CLI flag: -ingest-limits-frontend.num-partitions
path: docs/sources/shared/configuration.md
user: login: grobinson-grafana
body: I am a bit worried that someone configures this for `ingest-limits` and forgets to configure it for `ingest-limits-frontend`. Perhaps we could share the configuration here? I'm not sure what's best...
created_at: 2025-04-02T14:00:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -62,10 +64,32 @@ func (g *RingStreamUsageGatherer) forGivenReplicaSet(ctx context.Context, rs rin
 	}
 
 	errg, ctx := errgroup.WithContext(ctx)
-	responses := make([]GetStreamUsageResponse, len(rs.Instances))
+	var owningInstances []ring.InstanceDesc
+
+outer:
path: pkg/limits/frontend/ring.go
user: login: grobinson-grafana
body: You've chose three nested loops here (including lines 78 to 82) to calculate which instance owns which stream? If we have 20 stream hashes, 50 instances, and 50 owning instances, we are looking at almost 50,000 iterations in the worst case? What I would recommend here is inverting the data structure we return from `getPartitionConsumers`, you can then do it in linear time.
created_at: 2025-04-02T14:51:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,9 +54,6 @@ func (m *mockIngestLimitsClient) GetAssignedPartitions(_ context.Context, r *log
 }
 
 func (m *mockIngestLimitsClient) GetStreamUsage(_ context.Context, r *logproto.GetStreamUsageRequest, _ ...grpc.CallOption) (*logproto.GetStreamUsageResponse, error) {
-	if expected := m.expectedStreamUsageRequest; expected != nil {
path: pkg/limits/frontend/mock_test.go
user: login: periklis
body: Accidentally removed.
created_at: 2025-04-03T09:30:31Z
author_association: COLLABORATOR
diff_hunk: @@ -56,32 +57,49 @@ func (g *RingStreamUsageGatherer) forAllBackends(ctx context.Context, r GetStrea
 }
 
 func (g *RingStreamUsageGatherer) forGivenReplicaSet(ctx context.Context, rs ring.ReplicationSet, r GetStreamUsageRequest) ([]GetStreamUsageResponse, error) {
-	partitions, err := g.getPartitionConsumers(ctx, rs)
+	partitionConsumers, err := g.getPartitionConsumers(ctx, rs)
 	if err != nil {
 		return nil, err
 	}
 
+	requests := make(map[string]*logproto.GetStreamUsageRequest)
path: pkg/limits/frontend/ring.go
user: login: grobinson-grafana
body: What about something like this? If you would rather use a mutex with `append`, feel free, I don't think the overhead is significant, and it will make the code easier to understand.

```diff
diff --cc pkg/limits/frontend/ring.go
index 616f55f7b,013309e4e..000000000
--- a/pkg/limits/frontend/ring.go
+++ b/pkg/limits/frontend/ring.go
@@@ -62,44 -61,33 +62,34 @@@ func (g *RingStreamUsageGatherer) forGi
                return nil, err
        }

-       requests := make(map[string]*logproto.GetStreamUsageRequest)
 -      //
+       instancesToQuery := make(map[string][]uint64)
        for _, hash := range r.StreamHashes {
                partitionID := int32(hash % uint64(g.numPartitions))
-               addr, ok := partitionConsumers[partitionID]
-               if !ok {
-                       continue
-               }
-
-               req, ok := requests[addr]
-               if !ok {
-                       req = &logproto.GetStreamUsageRequest{
-                               Tenant:       r.Tenant,
-                               StreamHashes: []uint64{hash},
-                       }
-               } else {
-                       req.StreamHashes = append(req.StreamHashes, hash)
-               }
-
-               requests[addr] = req
 -              instance := partitions[partitionID]
 -              instancesToQuery[instance] = append(instancesToQuery[instance], hash)
++              addr := partitions[partitionID]
++              instancesToQuery[addr] = append(instancesToQuery[addr], hash)
        }

        errg, ctx := errgroup.WithContext(ctx)
-       responses := make(map[string]*logproto.GetStreamUsageResponse)
-
-       // Query each instance for stream usage
-       for addr, req := range requests {
+       responses := make([]GetStreamUsageResponse, len(instancesToQuery))
 -      for instance, hashes := range instancesToQuery {
++      i := 0
++      for addr, hashes := range instancesToQuery {
++              j := i
++              i++
                errg.Go(func() error {
 -                      client, err := g.pool.GetClientFor(instance.Addr)
 +                      client, err := g.pool.GetClientFor(addr)
                        if err != nil {
                                return err
                        }
+                       protoReq := &logproto.GetStreamUsageRequest{
+                               Tenant:       r.Tenant,
 -                              StreamHashes: r.StreamHashes,
 -                              Partitions:   partitions[instance.Addr],
++                              StreamHashes: hashes,
+                       }

-                       resp, err := client.(logproto.IngestLimitsClient).GetStreamUsage(ctx, req)
+                       resp, err := client.(logproto.IngestLimitsClient).GetStreamUsage(ctx, protoReq)
                        if err != nil {
                                return err
                        }
-
-                       responses[addr] = resp
 -                      responses[i] = GetStreamUsageResponse{Addr: instance.Addr, Response: resp}
++                      responses[j] = GetStreamUsageResponse{Addr: addr, Response: resp}
                        return nil
                })
        }
```
created_at: 2025-04-03T13:31:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -56,32 +57,43 @@ func (g *RingStreamUsageGatherer) forAllBackends(ctx context.Context, r GetStrea
 }
 
 func (g *RingStreamUsageGatherer) forGivenReplicaSet(ctx context.Context, rs ring.ReplicationSet, r GetStreamUsageRequest) ([]GetStreamUsageResponse, error) {
-	partitions, err := g.getPartitionConsumers(ctx, rs)
+	partitionConsumers, err := g.getPartitionConsumers(ctx, rs)
 	if err != nil {
 		return nil, err
 	}
 
+	instancesToQuery := make(map[string][]uint64)
+	for _, hash := range r.StreamHashes {
+		partitionID := int32(hash % uint64(g.numPartitions))
+		addr := partitionConsumers[partitionID]
+		instancesToQuery[addr] = append(instancesToQuery[addr], hash)
+	}
+
 	errg, ctx := errgroup.WithContext(ctx)
-	responses := make([]GetStreamUsageResponse, len(rs.Instances))
+	responses := make([]GetStreamUsageResponse, len(instancesToQuery))
 
-	// TODO: We shouldn't query all instances since we know which instance holds which stream.
-	for i, instance := range rs.Instances {
+	// Query each instance for stream usage
+	i := 0
+	for addr, hashes := range instancesToQuery {
+		j := 0
path: pkg/limits/frontend/ring.go
user: login: grobinson-grafana
body: ```suggestion
		j := i
```
created_at: 2025-04-03T13:46:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -56,32 +57,43 @@ func (g *RingStreamUsageGatherer) forAllBackends(ctx context.Context, r GetStrea
 }
 
 func (g *RingStreamUsageGatherer) forGivenReplicaSet(ctx context.Context, rs ring.ReplicationSet, r GetStreamUsageRequest) ([]GetStreamUsageResponse, error) {
-	partitions, err := g.getPartitionConsumers(ctx, rs)
+	partitionConsumers, err := g.getPartitionConsumers(ctx, rs)
 	if err != nil {
 		return nil, err
 	}
 
+	instancesToQuery := make(map[string][]uint64)
+	for _, hash := range r.StreamHashes {
+		partitionID := int32(hash % uint64(g.numPartitions))
+		addr := partitionConsumers[partitionID]
+		instancesToQuery[addr] = append(instancesToQuery[addr], hash)
+	}
+
 	errg, ctx := errgroup.WithContext(ctx)
-	responses := make([]GetStreamUsageResponse, len(rs.Instances))
+	responses := make([]GetStreamUsageResponse, len(instancesToQuery))
 
-	// TODO: We shouldn't query all instances since we know which instance holds which stream.
-	for i, instance := range rs.Instances {
+	// Query each instance for stream usage
+	i := 0
+	for addr, hashes := range instancesToQuery {
+		j := 0
path: pkg/limits/frontend/ring.go
user: login: grobinson-grafana
body: If you would rather `append` with mutex it's fine with me
created_at: 2025-04-03T13:47:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -172,11 +142,21 @@ func TestRingStreamUsageGatherer_GetStreamUsage(t *testing.T) {
 			instances := make([]ring.InstanceDesc, len(clients))
 
 			for i := 0; i < len(test.expectedAssignedPartitionsRequest); i++ {
+				expectedStreamUsageReq := (*logproto.GetStreamUsageRequest)(nil)
path: pkg/limits/frontend/ring_test.go
user: login: grobinson-grafana
body: What does this do (the whole block of code rather than the specific line)? I'm not sure I follow here
created_at: 2025-04-03T13:49:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -172,11 +142,21 @@ func TestRingStreamUsageGatherer_GetStreamUsage(t *testing.T) {
 			instances := make([]ring.InstanceDesc, len(clients))
 
 			for i := 0; i < len(test.expectedAssignedPartitionsRequest); i++ {
+				expectedStreamUsageReq := (*logproto.GetStreamUsageRequest)(nil)
path: pkg/limits/frontend/ring_test.go
user: login: periklis
body: Glad that you point that out. The whole loop needs an overhaul here. This setup was written when the two requests types matched one to one.
created_at: 2025-04-03T13:54:57Z
author_association: COLLABORATOR
diff_hunk: @@ -56,32 +57,43 @@ func (g *RingStreamUsageGatherer) forAllBackends(ctx context.Context, r GetStrea
 }
 
 func (g *RingStreamUsageGatherer) forGivenReplicaSet(ctx context.Context, rs ring.ReplicationSet, r GetStreamUsageRequest) ([]GetStreamUsageResponse, error) {
-	partitions, err := g.getPartitionConsumers(ctx, rs)
+	partitionConsumers, err := g.getPartitionConsumers(ctx, rs)
 	if err != nil {
 		return nil, err
 	}
 
+	instancesToQuery := make(map[string][]uint64)
+	for _, hash := range r.StreamHashes {
+		partitionID := int32(hash % uint64(g.numPartitions))
+		addr := partitionConsumers[partitionID]
+		instancesToQuery[addr] = append(instancesToQuery[addr], hash)
+	}
+
 	errg, ctx := errgroup.WithContext(ctx)
-	responses := make([]GetStreamUsageResponse, len(rs.Instances))
+	responses := make([]GetStreamUsageResponse, len(instancesToQuery))
 
-	// TODO: We shouldn't query all instances since we know which instance holds which stream.
-	for i, instance := range rs.Instances {
+	// Query each instance for stream usage
+	i := 0
+	for addr, hashes := range instancesToQuery {
+		j := 0
path: pkg/limits/frontend/ring.go
user: login: grobinson-grafana
body: Here is another option:

```diff
diff --git a/pkg/limits/frontend/ring.go b/pkg/limits/frontend/ring.go
index aecabd41a..8f6b8d180 100644
--- a/pkg/limits/frontend/ring.go
+++ b/pkg/limits/frontend/ring.go
@@ -81,24 +81,20 @@ func (g *RingStreamUsageGatherer) forGivenReplicaSet(ctx context.Context, rs rin
        // Query each instance for stream usage
        i := 0
        for addr, hashes := range instancesToQuery {
-               j := 0
-               i++
                errg.Go(func() error {
                        client, err := g.pool.GetClientFor(addr)
                        if err != nil {
                                return err
                        }
-
                        protoReq := &logproto.GetStreamUsageRequest{
                                Tenant:       r.Tenant,
                                StreamHashes: hashes,
                        }
-
                        resp, err := client.(logproto.IngestLimitsClient).GetStreamUsage(ctx, protoReq)
                        if err != nil {
                                return err
                        }
-
+                       j := atomic.AddInt64(&i, 1)
                        responses[j] = GetStreamUsageResponse{Addr: addr, Response: resp}
                        return nil
                })
```
created_at: 2025-04-03T14:36:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -56,32 +57,43 @@ func (g *RingStreamUsageGatherer) forAllBackends(ctx context.Context, r GetStrea
 }
 
 func (g *RingStreamUsageGatherer) forGivenReplicaSet(ctx context.Context, rs ring.ReplicationSet, r GetStreamUsageRequest) ([]GetStreamUsageResponse, error) {
-	partitions, err := g.getPartitionConsumers(ctx, rs)
+	partitionConsumers, err := g.getPartitionConsumers(ctx, rs)
 	if err != nil {
 		return nil, err
 	}
 
+	instancesToQuery := make(map[string][]uint64)
+	for _, hash := range r.StreamHashes {
+		partitionID := int32(hash % uint64(g.numPartitions))
+		addr := partitionConsumers[partitionID]
+		instancesToQuery[addr] = append(instancesToQuery[addr], hash)
+	}
+
 	errg, ctx := errgroup.WithContext(ctx)
-	responses := make([]GetStreamUsageResponse, len(rs.Instances))
+	responses := make([]GetStreamUsageResponse, len(instancesToQuery))
 
-	// TODO: We shouldn't query all instances since we know which instance holds which stream.
-	for i, instance := range rs.Instances {
+	// Query each instance for stream usage
+	i := 0
+	for addr, hashes := range instancesToQuery {
+		j := 0
path: pkg/limits/frontend/ring.go
user: login: grobinson-grafana
body: Here is an example with channels, but it requires an extra allocation for the channel:

```diff
diff --git a/pkg/limits/frontend/ring.go b/pkg/limits/frontend/ring.go
index aecabd41a..222c81ba6 100644
--- a/pkg/limits/frontend/ring.go
+++ b/pkg/limits/frontend/ring.go
@@ -76,30 +76,24 @@ func (g *RingStreamUsageGatherer) forGivenReplicaSet(ctx context.Context, rs rin
        }

        errg, ctx := errgroup.WithContext(ctx)
-       responses := make([]GetStreamUsageResponse, len(instancesToQuery))

        // Query each instance for stream usage
-       i := 0
+       responses := make(chan GetStreamUsageResponse, len(instancesToQuery))
        for addr, hashes := range instancesToQuery {
-               j := 0
-               i++
                errg.Go(func() error {
                        client, err := g.pool.GetClientFor(addr)
                        if err != nil {
                                return err
                        }
-
                        protoReq := &logproto.GetStreamUsageRequest{
                                Tenant:       r.Tenant,
                                StreamHashes: hashes,
                        }
-
                        resp, err := client.(logproto.IngestLimitsClient).GetStreamUsage(ctx, protoReq)
                        if err != nil {
                                return err
                        }
-
-                       responses[j] = GetStreamUsageResponse{Addr: addr, Response: resp}
+                       responses <- GetStreamUsageResponse{Addr: addr, Response: resp}
                        return nil
                })
        }
@@ -108,7 +102,12 @@ func (g *RingStreamUsageGatherer) forGivenReplicaSet(ctx context.Context, rs rin
                return nil, err
        }

-       return responses, nil
+       close(responses)
+       results := make([]GetStreamUsageResponse, 0, len(instancesToQuery))
+       for r := range responses {
+               results = append(results, r)
+       }
+       return results, nil
 }
```
created_at: 2025-04-03T14:41:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -172,11 +142,21 @@ func TestRingStreamUsageGatherer_GetStreamUsage(t *testing.T) {
 			instances := make([]ring.InstanceDesc, len(clients))
 
 			for i := 0; i < len(test.expectedAssignedPartitionsRequest); i++ {
+				expectedStreamUsageReq := (*logproto.GetStreamUsageRequest)(nil)
path: pkg/limits/frontend/ring_test.go
user: login: grobinson-grafana
body: If I delete this, the tests still pass 😕 

```go
clients[i] = &mockIngestLimitsClient{
					expectedAssignedPartitionsRequest: test.expectedAssignedPartitionsRequest[i],
					getAssignedPartitionsResponse:     test.getAssignedPartitionsResponses[i],
					expectedStreamUsageRequest:        test.expectedStreamUsageRequest[i],
					getStreamUsageResponse:            test.getStreamUsageResponses[i],
					t:                                 t,
				}
```
created_at: 2025-04-03T14:53:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -171,22 +141,34 @@ func TestRingStreamUsageGatherer_GetStreamUsage(t *testing.T) {
 			clients := make([]logproto.IngestLimitsClient, len(test.expectedAssignedPartitionsRequest))
 			instances := make([]ring.InstanceDesc, len(clients))
 
-			for i := 0; i < len(test.expectedAssignedPartitionsRequest); i++ {
+			// Set up the mock clients for the assigned partitions requests.
+			// Note: Every client will return a response for its assigned partitions.
+			for i := range test.expectedAssignedPartitionsRequest {
 				clients[i] = &mockIngestLimitsClient{
+					t:                                 t,
 					expectedAssignedPartitionsRequest: test.expectedAssignedPartitionsRequest[i],
 					getAssignedPartitionsResponse:     test.getAssignedPartitionsResponses[i],
-					expectedStreamUsageRequest:        test.expectedStreamUsageRequest[i],
-					getStreamUsageResponse:            test.getStreamUsageResponses[i],
-					t:                                 t,
 				}
+			}
+
+			// Set up the mock clients for the stream usage requests.
path: pkg/limits/frontend/ring_test.go
user: login: grobinson-grafana
body: Sorry but I still don't understand this? The tests pass without it. If you want to put in `nils`, wouldn't this work?

```diff
diff --git a/pkg/limits/frontend/ring_test.go b/pkg/limits/frontend/ring_test.go
index e7621d0cf..b3f2b7a48 100644
--- a/pkg/limits/frontend/ring_test.go
+++ b/pkg/limits/frontend/ring_test.go
@@ -106,7 +106,7 @@ func TestRingStreamUsageGatherer_GetStreamUsage(t *testing.T) {
                        Tenant:       "test",
                        StreamHashes: []uint64{1}, // Hash 1 maps to partition 1
                },
-               expectedAssignedPartitionsRequest: []*logproto.GetAssignedPartitionsRequest{{}, {}},
+               expectedAssignedPartitionsRequest: []*logproto.GetAssignedPartitionsRequest{nil, nil},
                getAssignedPartitionsResponses: []*logproto.GetAssignedPartitionsResponse{{
                        AssignedPartitions: map[int32]int64{
                                1: time.Now().Add(-time.Second).UnixNano(),
@@ -116,15 +116,17 @@ func TestRingStreamUsageGatherer_GetStreamUsage(t *testing.T) {
                                1: time.Now().UnixNano(),
                        },
                }},
-               expectedStreamUsageRequest: []*logproto.GetStreamUsageRequest{{}, {
-                       Tenant:       "test",
-                       StreamHashes: []uint64{1},
-               }},
-               getStreamUsageResponses: []*logproto.GetStreamUsageResponse{{}, {
-                       Tenant:        "test",
-                       ActiveStreams: 1,
-                       Rate:          10,
-               }},
+               expectedStreamUsageRequest: []*logproto.GetStreamUsageRequest{
+                       nil, {
+                               Tenant:       "test",
+                               StreamHashes: []uint64{1},
+                       }},
+               getStreamUsageResponses: []*logproto.GetStreamUsageResponse{
+                       nil, {
+                               Tenant:        "test",
+                               ActiveStreams: 1,
+                               Rate:          10,
+                       }},
                expectedResponses: []GetStreamUsageResponse{{
                        Addr: "instance-1",
                        Response: &logproto.GetStreamUsageResponse{
@@ -148,17 +150,11 @@ func TestRingStreamUsageGatherer_GetStreamUsage(t *testing.T) {
                                        t:                                 t,
                                        expectedAssignedPartitionsRequest: test.expectedAssignedPartitionsRequest[i],
                                        getAssignedPartitionsResponse:     test.getAssignedPartitionsResponses[i],
+                                       expectedStreamUsageRequest:        test.expectedStreamUsageRequest[i],
+                                       getStreamUsageResponse:            test.getStreamUsageResponses[i],
                                }
                        }

-                       // Set up the mock clients for the stream usage requests.
-                       // Note: Not every client will have a stream usage request because the
-                       // requested stream hashes are owned only by a subset of partition consumers.
-                       for i := range test.expectedStreamUsageRequest {
-                               clients[i].(*mockIngestLimitsClient).expectedStreamUsageRequest = test.expectedStreamUsageRequest[i]
-                               clients[i].(*mockIngestLimitsClient).getStreamUsageResponse = test.getStreamUsageResponses[i]
-                       }
-
                        // Set up the instances for the ring.
                        for i := range len(clients) {
                                instances[i] = ring.InstanceDesc{
```
created_at: 2025-04-04T12:15:42Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-27T19:33:38Z
body: :computer: Deploy preview deleted.
author_association: CONTRIBUTOR
user: login: grobinson-grafana
created_at: 2025-04-02T14:04:40Z
body: Can we remove `partitions` from `GetStreamUsageRequest` in `logproto.proto`? Does it still serve a use?
author_association: CONTRIBUTOR
user: login: periklis
created_at: 2025-04-03T09:32:08Z
body: > Can we remove `partitions` from `GetStreamUsageRequest` in `logproto.proto`? Does it still serve a use?

~~Yes it does because we want to check the requested patitions vs. the assigned ones. Between a the two RPC calls we still can get a re-assignment.~~
author_association: COLLABORATOR
user: login: grobinson-grafana
created_at: 2025-04-03T14:30:42Z
body: One of the tests is panicking in `frontend.go` it seems:

```
// Sum the number of active streams and rates of all responses.
	for _, resp := range resps {
		activeStreamsTotal += resp.Response.ActiveStreams
		rateTotal += float64(resp.Response.Rate)
	}
```
author_association: CONTRIBUTOR
user: login: grobinson-grafana
created_at: 2025-04-03T14:52:21Z
body: > One of the tests is panicking in `frontend.go` it seems:
> 
> ```
> // Sum the number of active streams and rates of all responses.
> 	for _, resp := range resps {
> 		activeStreamsTotal += resp.Response.ActiveStreams
> 		rateTotal += float64(resp.Response.Rate)
> 	}
> ```

It's because of `j := 0` instead of `j := i`.
author_association: CONTRIBUTOR