github.com/thanos-io/thanos@v0.32.5/pkg/receive/request_limiter.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package receive 5 6 import ( 7 "github.com/prometheus/client_golang/prometheus" 8 "github.com/prometheus/client_golang/prometheus/promauto" 9 ) 10 11 const ( 12 seriesLimitName = "series" 13 samplesLimitName = "samples" 14 sizeBytesLimitName = "body_size" 15 ) 16 17 var unlimitedRequestLimitsConfig = NewEmptyRequestLimitsConfig(). 18 SetSizeBytesLimit(0). 19 SetSeriesLimit(0). 20 SetSamplesLimit(0) 21 22 // configRequestLimiter implements requestLimiter interface. 23 type configRequestLimiter struct { 24 tenantLimits map[string]*requestLimitsConfig 25 cachedDefaultLimits *requestLimitsConfig 26 limitsHit *prometheus.SummaryVec 27 configuredLimits *prometheus.GaugeVec 28 } 29 30 func newConfigRequestLimiter(reg prometheus.Registerer, writeLimits *WriteLimitsConfig) *configRequestLimiter { 31 // Merge the default limits configuration with an unlimited configuration 32 // to ensure the nils are overwritten with zeroes. 33 defaultRequestLimits := writeLimits.DefaultLimits.RequestLimits.OverlayWith(unlimitedRequestLimitsConfig) 34 35 // Load up the request limits into a map with the tenant name as key and 36 // merge with the defaults to provide easy and fast access when checking 37 // limits. 38 // The merge with the default happen because a tenant limit that isn't 39 // present means the value is inherited from the default configuration. 40 tenantsLimits := writeLimits.TenantsLimits 41 tenantRequestLimits := make(map[string]*requestLimitsConfig) 42 for tenant, limitConfig := range tenantsLimits { 43 if limitConfig.RequestLimits != nil { 44 tenantRequestLimits[tenant] = limitConfig.RequestLimits.OverlayWith(defaultRequestLimits) 45 } 46 } 47 48 limiter := configRequestLimiter{ 49 tenantLimits: tenantRequestLimits, 50 cachedDefaultLimits: defaultRequestLimits, 51 } 52 limiter.registerMetrics(reg) 53 return &limiter 54 } 55 56 func (l *configRequestLimiter) registerMetrics(reg prometheus.Registerer) { 57 l.limitsHit = promauto.With(reg).NewSummaryVec( 58 prometheus.SummaryOpts{ 59 Namespace: "thanos", 60 Subsystem: "receive", 61 Name: "write_limits_hit", 62 Help: "Summary of how much beyond the limit a refused remote write request was.", 63 Objectives: map[float64]float64{0.50: 0.1, 0.95: 0.1, 0.99: 0.001}, 64 }, []string{"tenant", "limit"}, 65 ) 66 l.configuredLimits = promauto.With(reg).NewGaugeVec( 67 prometheus.GaugeOpts{ 68 Namespace: "thanos", 69 Subsystem: "receive", 70 Name: "write_limits", 71 Help: "The configured write limits.", 72 }, []string{"tenant", "limit"}, 73 ) 74 for tenant, limits := range l.tenantLimits { 75 l.configuredLimits.WithLabelValues(tenant, sizeBytesLimitName).Set(float64(*limits.SizeBytesLimit)) 76 l.configuredLimits.WithLabelValues(tenant, seriesLimitName).Set(float64(*limits.SeriesLimit)) 77 l.configuredLimits.WithLabelValues(tenant, samplesLimitName).Set(float64(*limits.SamplesLimit)) 78 } 79 l.configuredLimits.WithLabelValues("", sizeBytesLimitName).Set(float64(*l.cachedDefaultLimits.SizeBytesLimit)) 80 l.configuredLimits.WithLabelValues("", seriesLimitName).Set(float64(*l.cachedDefaultLimits.SeriesLimit)) 81 l.configuredLimits.WithLabelValues("", samplesLimitName).Set(float64(*l.cachedDefaultLimits.SamplesLimit)) 82 } 83 84 func (l *configRequestLimiter) AllowSizeBytes(tenant string, contentLengthBytes int64) bool { 85 limit := l.limitsFor(tenant).SizeBytesLimit 86 if *limit <= 0 { 87 return true 88 } 89 90 allowed := *limit >= contentLengthBytes 91 if !allowed { 92 l.limitsHit. 93 WithLabelValues(tenant, sizeBytesLimitName). 94 Observe(float64(contentLengthBytes - *limit)) 95 } 96 return allowed 97 } 98 99 func (l *configRequestLimiter) AllowSeries(tenant string, amount int64) bool { 100 limit := l.limitsFor(tenant).SeriesLimit 101 if *limit <= 0 { 102 return true 103 } 104 105 allowed := *limit >= amount 106 if !allowed && l.limitsHit != nil { 107 l.limitsHit. 108 WithLabelValues(tenant, seriesLimitName). 109 Observe(float64(amount - *limit)) 110 } 111 return allowed 112 } 113 114 func (l *configRequestLimiter) AllowSamples(tenant string, amount int64) bool { 115 limit := l.limitsFor(tenant).SamplesLimit 116 if *limit <= 0 { 117 return true 118 } 119 allowed := *limit >= amount 120 if !allowed && l.limitsHit != nil { 121 l.limitsHit. 122 WithLabelValues(tenant, samplesLimitName). 123 Observe(float64(amount - *limit)) 124 } 125 return allowed 126 } 127 128 func (l *configRequestLimiter) limitsFor(tenant string) *requestLimitsConfig { 129 limits, ok := l.tenantLimits[tenant] 130 if !ok { 131 limits = l.cachedDefaultLimits 132 } 133 return limits 134 } 135 136 type noopRequestLimiter struct{} 137 138 func (l *noopRequestLimiter) AllowSizeBytes(tenant string, contentLengthBytes int64) bool { 139 return true 140 } 141 142 func (l *noopRequestLimiter) AllowSeries(tenant string, amount int64) bool { 143 return true 144 } 145 146 func (l *noopRequestLimiter) AllowSamples(tenant string, amount int64) bool { 147 return true 148 }