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  }