github.com/m3db/m3@v1.5.0/src/dbnode/storage/limits/permits/lookback_limit_permit.go (about) 1 // Copyright (c) 2021 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package permits 22 23 import ( 24 "github.com/m3db/m3/src/dbnode/storage/limits" 25 "github.com/m3db/m3/src/x/context" 26 "github.com/m3db/m3/src/x/instrument" 27 ) 28 29 // LookbackLimitPermitManager manages permits which enforce a global lookback limit. 30 // This implementation is used for backwards compatibility migration from time-based 31 // lookback limits to more precise permits implementations. 32 type LookbackLimitPermitManager struct { 33 Limit limits.LookbackLimit 34 } 35 36 // LookbackLimitPermit is a permit modeled on top of lookback-based query limits. 37 // On acquisition, the permit increments the underlying limit. Before costly code 38 // paths, callers can check the limit to see if requests should be allowed to 39 // proceed. 40 type LookbackLimitPermit struct { 41 limit limits.LookbackLimit 42 source []byte 43 } 44 45 var _ Manager = (*LookbackLimitPermitManager)(nil) 46 47 var ( 48 _ Permits = (*LookbackLimitPermit)(nil) 49 // use a single permit for everybody to avoid allocations. since limits don't track quotas it's fine 50 // to share the same instance. 51 singlePermit = &limitPermit{} 52 ) 53 54 // NewLookbackLimitPermitsManager builds a new lookback limit permits manager. 55 func NewLookbackLimitPermitsManager( 56 name string, 57 opts limits.LookbackLimitOptions, 58 instrumentOpts instrument.Options, 59 sourceLoggerBuilder limits.SourceLoggerBuilder, 60 ) *LookbackLimitPermitManager { 61 lookbackLimit := limits.NewLookbackLimit(name, opts, instrumentOpts, sourceLoggerBuilder) 62 63 // We expose this implementation type to allow caller to use Start/Stop 64 // lookback functions which are not part of the Permits interface. 65 return &LookbackLimitPermitManager{ 66 Limit: lookbackLimit, 67 } 68 } 69 70 // NewPermits returns a new set of permits. 71 func (p *LookbackLimitPermitManager) NewPermits(ctx context.Context) (Permits, error) { 72 s := sourceFromContext(ctx) 73 // Ensure currently under limit. 74 if err := p.Limit.Inc(0, s); err != nil { 75 return nil, limits.NewQueryLimitExceededError(err.Error()) 76 } 77 78 return &LookbackLimitPermit{ 79 limit: p.Limit, 80 source: s, 81 }, nil 82 } 83 84 // Start starts background handling of the lookback limit for the permits. 85 func (p *LookbackLimitPermitManager) Start() { 86 p.Limit.Start() 87 } 88 89 // Stop stops the background handling of the lookback limit for the permits. 90 func (p *LookbackLimitPermitManager) Stop() { 91 p.Limit.Stop() 92 } 93 94 // Acquire increments the underlying querying limit. 95 func (p *LookbackLimitPermit) Acquire(context.Context) (AcquireResult, error) { 96 err := p.limit.Inc(1, p.source) 97 waited := err != nil 98 if p.limit.Options().ForceWaited { 99 waited = true 100 } 101 return AcquireResult{Permit: singlePermit, Waited: waited}, err 102 } 103 104 // TryAcquire increments the underlying querying limit. Functionally equivalent 105 // to Acquire. 106 func (p *LookbackLimitPermit) TryAcquire(context.Context) (Permit, error) { 107 return singlePermit, p.limit.Inc(1, p.source) 108 } 109 110 // Release is a no-op in this implementation. 111 func (p *LookbackLimitPermit) Release(_ Permit) { 112 } 113 114 // Close is a no-op in this implementation. 115 func (p *LookbackLimitPermit) Close() { 116 } 117 118 func sourceFromContext(ctx context.Context) []byte { 119 val := ctx.GoContext().Value(limits.SourceContextKey) 120 parsed, ok := val.([]byte) 121 if !ok { 122 return nil 123 } 124 return parsed 125 } 126 127 type limitPermit struct{} 128 129 func (l limitPermit) PostRelease() { 130 } 131 132 func (l limitPermit) PreAcquire() { 133 } 134 135 func (l limitPermit) AllowedQuota() int64 { 136 return 1 137 } 138 139 func (l limitPermit) QuotaRemaining() int64 { 140 return 0 141 } 142 143 func (l limitPermit) Use(_ int64) { 144 }