-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add support for blocking a policy to be ingested #16203
Changes from 1 commit
9f9622d
e1df362
be5ceb6
4218d81
bfcc228
0eaa021
84a6931
6d0204e
49bd76a
70bd1e1
1b7fdb4
119d358
585b096
aaeadbf
d41480f
fd275ad
80d72f7
6ca9ec5
0e908b9
cfbe78b
23e8d07
2a1a87d
626fd51
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -556,6 +556,16 @@ func (d *Distributor) Push(ctx context.Context, req *logproto.PushRequest) (*log | |
continue | ||
} | ||
|
||
if ok, until := d.ShouldBlockPolicy(lbs, tenantID); ok { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, we should pass the already resolved policy here to avoid resolving it twice. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
err := fmt.Errorf(validation.BlockedIngestionPolicyErrorMsg, tenantID, until.Format(time.RFC3339), d.validator.Limits.BlockIngestionPolicyStatusCode(tenantID, policy)) | ||
d.writeFailuresManager.Log(tenantID, err) | ||
validationErrors.Add(err) | ||
validation.DiscardedSamples.WithLabelValues(validation.BlockedIngestionPolicy, tenantID, retentionHours, policy).Add(float64(len(stream.Entries))) | ||
discardedBytes := util.EntriesTotalSize(stream.Entries) | ||
validation.DiscardedBytes.WithLabelValues(validation.BlockedIngestionPolicy, tenantID, retentionHours, policy).Add(float64(discardedBytes)) | ||
continue | ||
} | ||
|
||
n := 0 | ||
pushSize := 0 | ||
prevTs := stream.Entries[0].Timestamp | ||
|
@@ -1293,3 +1303,33 @@ func newRingAndLifecycler(cfg RingConfig, instanceCount *atomic.Uint32, logger l | |
func (d *Distributor) HealthyInstancesCount() int { | ||
return int(d.healthyInstancesCount.Load()) | ||
} | ||
|
||
// ShouldBlockPolicy checks if ingestion should be blocked for the given labels based on their policy. | ||
// It returns true if ingestion should be blocked. | ||
func (d *Distributor) ShouldBlockPolicy(lbs labels.Labels, tenantID string) (bool, time.Time) { | ||
// Get policy mappings for the tenant | ||
mapping := d.validator.Limits.PoliciesStreamMapping(tenantID) | ||
if mapping == nil { | ||
// No policy mappings defined, don't block | ||
return false, time.Time{} | ||
} | ||
|
||
// Get the policy for these labels | ||
policy := mapping.PolicyFor(lbs) | ||
if policy == "" { | ||
// No specific policy, don't block | ||
return false, time.Time{} | ||
} | ||
|
||
// Check if this policy is blocked in tenant configs | ||
blockUntil := d.validator.Limits.BlockIngestionPolicyUntil(tenantID, policy) | ||
if blockUntil.IsZero() { | ||
return false, time.Time{} | ||
} | ||
|
||
if time.Now().Before(blockUntil) { | ||
return true, blockUntil | ||
} | ||
|
||
return false, time.Time{} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -227,10 +227,12 @@ type Limits struct { | |
OTLPConfig push.OTLPConfig `yaml:"otlp_config" json:"otlp_config" doc:"description=OTLP log ingestion configurations"` | ||
GlobalOTLPConfig push.GlobalOTLPConfig `yaml:"-" json:"-"` | ||
|
||
BlockIngestionUntil dskit_flagext.Time `yaml:"block_ingestion_until" json:"block_ingestion_until"` | ||
BlockIngestionStatusCode int `yaml:"block_ingestion_status_code" json:"block_ingestion_status_code"` | ||
EnforcedLabels []string `yaml:"enforced_labels" json:"enforced_labels" category:"experimental"` | ||
PolicyStreamMapping PolicyStreamMapping `yaml:"policy_stream_mapping" json:"policy_stream_mapping" category:"experimental" doc:"description=Map of policies to stream selectors with a priority. Experimental. Example:\n policy_stream_mapping: \n finance: \n - selector: '{namespace=\"prod\", container=\"billing\"}' \n priority: 2 \n ops: \n - selector: '{namespace=\"prod\", container=\"ops\"}' \n priority: 1 \n staging: \n - selector: '{namespace=\"staging\"}' \n priority: 1"` | ||
BlockIngestionUntil dskit_flagext.Time `yaml:"block_ingestion_until" json:"block_ingestion_until"` | ||
BlockIngestionStatusCode int `yaml:"block_ingestion_status_code" json:"block_ingestion_status_code"` | ||
BlockIngestionPolicyUntil map[string]dskit_flagext.Time `yaml:"block_ingestion_policy_until" json:"block_ingestion_policy_until"` | ||
BlockIngestionPolicyStatusCode map[string]int `yaml:"block_ingestion_policy_status_code" json:"block_ingestion_policy_status_code"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need a per policy status code? I think we can just reuse the per-tenant BlockIngestionStatusCode. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not really. got rid of it, ty |
||
EnforcedLabels []string `yaml:"enforced_labels" json:"enforced_labels" category:"experimental"` | ||
PolicyStreamMapping PolicyStreamMapping `yaml:"policy_stream_mapping" json:"policy_stream_mapping" category:"experimental" doc:"description=Map of policies to stream selectors with a priority. Experimental. Example:\n policy_stream_mapping: \n finance: \n - selector: '{namespace=\"prod\", container=\"billing\"}' \n priority: 2 \n ops: \n - selector: '{namespace=\"prod\", container=\"ops\"}' \n priority: 1 \n staging: \n - selector: '{namespace=\"staging\"}' \n priority: 1"` | ||
|
||
IngestionPartitionsTenantShardSize int `yaml:"ingestion_partitions_tenant_shard_size" json:"ingestion_partitions_tenant_shard_size" category:"experimental"` | ||
|
||
|
@@ -1120,6 +1122,32 @@ func (o *Overrides) BlockIngestionStatusCode(userID string) int { | |
return o.getOverridesForUser(userID).BlockIngestionStatusCode | ||
} | ||
|
||
func (o *Overrides) BlockIngestionPolicyUntil(userID string, policy string) time.Time { | ||
limits := o.getOverridesForUser(userID) | ||
if limits == nil || limits.BlockIngestionPolicyUntil == nil { | ||
return time.Time{} // Zero time means no blocking | ||
} | ||
|
||
if blockUntil, ok := limits.BlockIngestionPolicyUntil[policy]; ok { | ||
return time.Time(blockUntil) | ||
} | ||
return time.Time{} // Zero time means no blocking | ||
} | ||
|
||
// BlockIngestionPolicyStatusCode returns the status code to use when blocking ingestion for a given policy. | ||
func (o *Overrides) BlockIngestionPolicyStatusCode(userID string, policy string) int { | ||
limits := o.getOverridesForUser(userID) | ||
if limits == nil { | ||
return defaultBlockedIngestionStatusCode | ||
} | ||
|
||
if statusCode, ok := limits.BlockIngestionPolicyStatusCode[policy]; ok { | ||
return statusCode | ||
} | ||
|
||
return defaultBlockedIngestionStatusCode | ||
} | ||
|
||
func (o *Overrides) EnforcedLabels(userID string) []string { | ||
return o.getOverridesForUser(userID).EnforcedLabels | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly to how we enforce labels, I think this one should also replace the
d.validator.ShouldBlockIngestion
down below which enforces the per-tenant block.loki/pkg/distributor/distributor.go
Lines 652 to 653 in 9f9622d