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 }