github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/iratesce/impl.go (about)

     1  /*
     2   * Copyright (c) 2021-present Sigma-Soft, Ltd.
     3   * @author: Dmitry Molchanovsky
     4   */
     5  
     6  package iratesce
     7  
     8  import (
     9  	"time"
    10  
    11  	"github.com/voedger/voedger/pkg/irates"
    12  )
    13  
    14  // является ли состояние bucket'а неопределенным (нулевым)
    15  // is the bucket's state undefined (zero)
    16  func BucketStateIsZero(state *irates.BucketState) bool {
    17  	return state.TakenTokens == 0 && state.Period == 0 && state.MaxTokensPerPeriod == 0
    18  }
    19  
    20  // создание нового bucket'а с переданными параметрами
    21  // creating a new bucket with the passed parameters
    22  func newBucket(state irates.BucketState, now time.Time) (p *bucketType) {
    23  	b := bucketType{
    24  		state: state,
    25  	}
    26  	b.reset(now)
    27  	return &b
    28  }
    29  
    30  // применить к backet'у параметры state
    31  // apply state parameters to the backet
    32  func (bucket *bucketType) resetToState(state irates.BucketState, now time.Time) {
    33  	bucket.state = state
    34  	bucket.reset(now)
    35  }
    36  
    37  // пересчитывает количество токенов bucket.state.TakenTokens на время TimeFunc
    38  // recalculates the number of bucket.state tokens.TakenTokens for the TimeFunc time
    39  func (bucket *bucketType) recalcBuketState(now time.Time) {
    40  	_, _, tokens := bucket.limiter.advance(now)
    41  	value := float64(bucket.limiter.burst) - tokens
    42  	if value < 0 {
    43  		value = 0
    44  	}
    45  	bucket.state.TakenTokens = irates.NumTokensType(value)
    46  }
    47  
    48  // сбросить bucket в исходное состояние, соответствующее параметрам, с которыми он был создан (наполнить его токенами)
    49  // reset the bucket to its original state corresponding to the parameters with which it was created (fill it with tokens)
    50  func (bucket *bucketType) reset(now time.Time) {
    51  	var interval Limit
    52  	if bucket.state.MaxTokensPerPeriod > 0 {
    53  		interval = every(time.Duration(int64(bucket.state.Period) / int64(bucket.state.MaxTokensPerPeriod)))
    54  	}
    55  	bucket.limiter = *newLimiter(interval, int(bucket.state.MaxTokensPerPeriod))
    56  	bucket.limiter.allowN(now, int(bucket.state.TakenTokens))
    57  }
    58  
    59  // Попробуем взять n токенов из заданных ведер
    60  // Операция атомарна - либо доступные токены есть в ведрах для всех ключей, либо нет
    61  // Try to take n tokens from the given buckets
    62  // The operation must be atomic - either all buckets are modified or none
    63  func (b *bucketsType) TakeTokens(buckets []irates.BucketKey, n int) bool {
    64  	b.mu.Lock()
    65  	defer b.mu.Unlock()
    66  	var keyIdx int
    67  	res := true
    68  	t := b.timeFunc()
    69  	// проверим наличие токеноа по запрашиваемым ключам
    70  	// let's check the presence of a token using the requested keys
    71  	for keyIdx = 0; keyIdx < len(buckets); keyIdx++ {
    72  		bucket := b.bucketByKey(&buckets[keyIdx])
    73  		// если по каким-то причинам бакет для очередного ключа не найден, то его отсутствие не должно влиять на общий результат проверки
    74  		// ключ может содержать имя действия, для которого ограничение не было задано. В таком случае, ограничение данного действия не производится
    75  		// if for some reason the bucket for the next key is not found, then its absence should not affect the overall result of the check
    76  		// the key may contain the name of the action for which the restriction was not set. In this case, the restriction of this action is not performed
    77  		if bucket == nil {
    78  			continue
    79  		}
    80  
    81  		// если очередной токен не получен, то уходим из цикла запросов
    82  		// if the next token is not received, then we leave the request cycle
    83  		if !bucket.limiter.allowN(t, n) {
    84  			res = false
    85  			break
    86  		}
    87  
    88  	}
    89  
    90  	// если не получили токены по всем ключам, то вернем взятые токены обратно в вёдра
    91  	// if we have not received tokens for all keys, then we will return the tokens taken back to the buckets
    92  	if !res {
    93  		for i := 0; i < keyIdx; i++ {
    94  			if bucket := b.bucketByKey(&buckets[i]); bucket != nil {
    95  				bucket.limiter.allowN(t, -n)
    96  			}
    97  		}
    98  	}
    99  	return res
   100  }
   101  
   102  // вернет bucket из отображения
   103  // если для переваемого ключа бакета еще нет, то он будет предварительно создан с параметрами "по умолчанию"
   104  // returns bucket from the map
   105  // if there is no bucket for the requested key yet, it will be pre-created with the "default" parameters
   106  func (b *bucketsType) bucketByKey(key *irates.BucketKey) (bucket *bucketType) {
   107  	if bucket, ok := b.buckets[*key]; ok {
   108  		return bucket
   109  	}
   110  
   111  	// если для ключа bucket'а еще нет, то создадим его
   112  	// if there is no bucket for the key yet, then we will create it
   113  	bs, ok := b.defaultStates[key.RateLimitName]
   114  	if !ok {
   115  		return nil
   116  	}
   117  	bucket = newBucket(bs, b.timeFunc())
   118  	b.buckets[*key] = bucket
   119  	return bucket
   120  }
   121  
   122  // установка параметров ограничения "по умолчанию" для действия с именем RateLimitName
   123  // при этом работающие Bucket'ы параметры ограничений не меняют
   124  // для изменения параметров работающих бакетов используйте функцию ReserRateBuckets
   125  // at the same time, the working Bucket's parameters of restrictions do not change
   126  // to change the parameters of working buckets, use the ReserRateBuckets function
   127  // setting the "default" constraint parameters for an action named RateLimitName
   128  func (b *bucketsType) SetDefaultBucketState(rateLimitName string, bucketState irates.BucketState) {
   129  	b.mu.Lock()
   130  	defer b.mu.Unlock()
   131  	b.defaultStates[rateLimitName] = bucketState
   132  }
   133  
   134  // returns irates.ErrorRateLimitNotFound
   135  func (b *bucketsType) GetDefaultBucketsState(rateLimitName string) (state irates.BucketState, err error) {
   136  	b.mu.Lock()
   137  	defer b.mu.Unlock()
   138  	if state, ok := b.defaultStates[rateLimitName]; ok {
   139  		return state, nil
   140  	}
   141  	return state, irates.ErrorRateLimitNotFound
   142  }
   143  
   144  // изменить параметры ограничения с именем RateLimitName для работающих bucket'ов на bucketState
   145  // соответствующие bucket'ы будут "сброшены" до максимально допустимого количества доступных токенов
   146  // change the restriction parameters with the name RateLimitName for running buckets on bucketState
   147  // the corresponding buckets will be "reset" to the maximum allowed number of available tokens
   148  func (b *bucketsType) ResetRateBuckets(rateLimitName string, bucketState irates.BucketState) {
   149  	b.mu.Lock()
   150  	defer b.mu.Unlock()
   151  	_, ok := b.defaultStates[rateLimitName]
   152  
   153  	// если параметры "по умолчанию" для данного ограничения не были заданы ранее, то
   154  	// bucket'ов для этого ограничения точно нет. Просто уходим
   155  	// if the "default" parameters for this restriction were not set earlier, then
   156  	// there are definitely no buckets for this restriction. Just leave
   157  	if !ok {
   158  		return
   159  	}
   160  
   161  	for bucketKey, bucket := range b.buckets {
   162  		if bucketKey.RateLimitName == rateLimitName {
   163  			bucket.resetToState(bucketState, b.timeFunc())
   164  		}
   165  	}
   166  }
   167  
   168  // получение параметров ограничения для bucket'а, соответствующего передаваемому ключу
   169  // getting the restriction parameters for the bucket corresponding to the transmitted key
   170  func (b *bucketsType) GetBucketState(bucketKey irates.BucketKey) (state irates.BucketState, err error) {
   171  	b.mu.Lock()
   172  	defer b.mu.Unlock()
   173  	buc := b.bucketByKey(&bucketKey)
   174  
   175  	if buc != nil {
   176  		buc.recalcBuketState(b.timeFunc())
   177  		return buc.state, nil
   178  	}
   179  	return state, irates.ErrorRateLimitNotFound
   180  }
   181  
   182  // установить новые параметры ограничения для bucket'а, соответствующего ключу bucketKey
   183  func (b *bucketsType) SetBucketState(bucketKey irates.BucketKey, state irates.BucketState) (err error) {
   184  	b.mu.Lock()
   185  	defer b.mu.Unlock()
   186  	buc := b.bucketByKey(&bucketKey)
   187  
   188  	if buc == nil {
   189  		return irates.ErrorRateLimitNotFound
   190  	}
   191  
   192  	buc.resetToState(state, b.timeFunc())
   193  	return nil
   194  }