@@ -18,7 +18,7 @@ import (
1818
1919// ingestLimitsFrontendClient is used for tests.
2020type ingestLimitsFrontendClient interface {
21- exceedsLimits (context.Context , * proto.ExceedsLimitsRequest ) (* proto.ExceedsLimitsResponse , error )
21+ ExceedsLimits (context.Context , * proto.ExceedsLimitsRequest ) (* proto.ExceedsLimitsResponse , error )
2222}
2323
2424// ingestLimitsFrontendRingClient uses the ring to query ingest-limits frontends.
@@ -35,7 +35,7 @@ func newIngestLimitsFrontendRingClient(ring ring.ReadRing, pool *ring_client.Poo
3535}
3636
3737// Implements the ingestLimitsFrontendClient interface.
38- func (c * ingestLimitsFrontendRingClient ) exceedsLimits (ctx context.Context , req * proto.ExceedsLimitsRequest ) (* proto.ExceedsLimitsResponse , error ) {
38+ func (c * ingestLimitsFrontendRingClient ) ExceedsLimits (ctx context.Context , req * proto.ExceedsLimitsRequest ) (* proto.ExceedsLimitsResponse , error ) {
3939 // We use an FNV-1 of all stream hashes in the request to load balance requests
4040 // to limits-frontends instances.
4141 h := fnv .New32 ()
@@ -78,64 +78,85 @@ func (c *ingestLimitsFrontendRingClient) exceedsLimits(ctx context.Context, req
7878
7979type ingestLimits struct {
8080 client ingestLimitsFrontendClient
81- limitsFailures prometheus.Counter
81+ requests prometheus.Counter
82+ requestsFailed prometheus.Counter
8283}
8384
8485func newIngestLimits (client ingestLimitsFrontendClient , r prometheus.Registerer ) * ingestLimits {
8586 return & ingestLimits {
8687 client : client ,
87- limitsFailures : promauto .With (r ).NewCounter (prometheus.CounterOpts {
88- Name : "loki_distributor_ingest_limits_failures_total" ,
89- Help : "The total number of failures checking ingest limits." ,
88+ requests : promauto .With (r ).NewCounter (prometheus.CounterOpts {
89+ Name : "loki_distributor_ingest_limits_requests_total" ,
90+ Help : "The total number of requests." ,
91+ }),
92+ requestsFailed : promauto .With (r ).NewCounter (prometheus.CounterOpts {
93+ Name : "loki_distributor_ingest_limits_requests_failed_total" ,
94+ Help : "The total number of requests that failed." ,
9095 }),
9196 }
9297}
9398
94- // enforceLimits returns a slice of streams that are within the per-tenant
95- // limits, and in the case where one or more streams exceed per-tenant
96- // limits, the reasons those streams were not included in the result.
97- // An error is returned if per-tenant limits could not be enforced.
98- func (l * ingestLimits ) enforceLimits (ctx context.Context , tenant string , streams []KeyedStream ) ([]KeyedStream , map [uint64 ][]string , error ) {
99- exceedsLimits , reasons , err := l .exceedsLimits (ctx , tenant , streams )
100- if ! exceedsLimits || err != nil {
101- return streams , nil , err
99+ // EnforceLimits checks all streams against the per-tenant limits and returns
100+ // a slice containing the streams that are accepted (within the per-tenant
101+ // limits). Any streams that could not have their limits checked are also
102+ // accepted.
103+ func (l * ingestLimits ) EnforceLimits (ctx context.Context , tenant string , streams []KeyedStream ) ([]KeyedStream , error ) {
104+ results , err := l .ExceedsLimits (ctx , tenant , streams )
105+ if err != nil {
106+ return streams , err
107+ }
108+ // Fast path. No results means all streams were accepted and there were
109+ // no failures, so we can return the input streams.
110+ if len (results ) == 0 {
111+ return streams , nil
102112 }
103113 // We can do this without allocation if needed, but doing so will modify
104114 // the original backing array. See "Filtering without allocation" from
105115 // https://go.dev/wiki/SliceTricks.
106- withinLimits := make ([]KeyedStream , 0 , len (streams ))
116+ accepted := make ([]KeyedStream , 0 , len (streams ))
107117 for _ , s := range streams {
108- if _ , ok := reasons [s .HashKeyNoShard ]; ! ok {
109- withinLimits = append (withinLimits , s )
118+ // Check each stream to see if it failed.
119+ // TODO(grobinson): We have an O(N*M) loop here. Need to benchmark if
120+ // its faster to do this or if we should create a map instead.
121+ var (
122+ found bool
123+ reason uint32
124+ )
125+ for _ , res := range results {
126+ if res .StreamHash == s .HashKeyNoShard {
127+ found = true
128+ reason = res .Reason
129+ break
130+ }
131+ }
132+ if ! found || reason == uint32 (limits .ReasonFailed ) {
133+ accepted = append (accepted , s )
110134 }
111135 }
112- return withinLimits , reasons , nil
136+ return accepted , nil
113137}
114138
115- // ExceedsLimits returns true if one or more streams exceeds per-tenant limits,
116- // and false if no streams exceed per-tenant limits. In the case where one or
117- // more streams exceeds per-tenant limits, it returns the reasons for each stream.
118- // An error is returned if per-tenant limits could not be checked.
119- func (l * ingestLimits ) exceedsLimits (ctx context.Context , tenant string , streams []KeyedStream ) (bool , map [uint64 ][]string , error ) {
139+ // ExceedsLimits checks all streams against the per-tenant limits. It returns
140+ // an error if the client failed to send the request or receive a response
141+ // from the server. Any streams that could not have their limits checked
142+ // and returned in the results with the reason "ReasonFailed".
143+ func (l * ingestLimits ) ExceedsLimits (
144+ ctx context.Context ,
145+ tenant string ,
146+ streams []KeyedStream ,
147+ ) ([]* proto.ExceedsLimitsResult , error ) {
148+ l .requests .Inc ()
120149 req , err := newExceedsLimitsRequest (tenant , streams )
121150 if err != nil {
122- return false , nil , err
151+ l .requestsFailed .Inc ()
152+ return nil , err
123153 }
124- resp , err := l .client .exceedsLimits (ctx , req )
154+ resp , err := l .client .ExceedsLimits (ctx , req )
125155 if err != nil {
126- return false , nil , err
127- }
128- if len (resp .Results ) == 0 {
129- return false , nil , nil
156+ l .requestsFailed .Inc ()
157+ return nil , err
130158 }
131- reasonsForHashes := make (map [uint64 ][]string )
132- for _ , result := range resp .Results {
133- reasons := reasonsForHashes [result .StreamHash ]
134- humanized := limits .Reason (result .Reason ).String ()
135- reasons = append (reasons , humanized )
136- reasonsForHashes [result .StreamHash ] = reasons
137- }
138- return true , reasonsForHashes , nil
159+ return resp .Results , nil
139160}
140161
141162func newExceedsLimitsRequest (tenant string , streams []KeyedStream ) (* proto.ExceedsLimitsRequest , error ) {
@@ -156,10 +177,3 @@ func newExceedsLimitsRequest(tenant string, streams []KeyedStream) (*proto.Excee
156177 Streams : streamMetadata ,
157178 }, nil
158179}
159-
160- func firstReasonForHashes (reasonsForHashes map [uint64 ][]string ) string {
161- for _ , reasons := range reasonsForHashes {
162- return reasons [0 ]
163- }
164- return "unknown reason"
165- }
0 commit comments