⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠ You can decompress Drawing data with the command palette: ‘Decompress current Excalidraw file’. For more info check in plugin settings under ‘Saving’

Excalidraw Data

Text Elements

Batch pre-check: CanAllow()

(Transport) GAS / Spex | | cmd: voucher.ss.distribution.batch_distribute_voucher_with_reference v (Generated Spex dispatcher) _Distribution_BatchDistributeVoucherWithReferenceHandler(ctx, req, resp) DistributionServer.service.BatchDistributeVoucherWithReference(ctx, req, resp) v (Handler entry) BatchDistributeVoucherWithReferenceImpl.execute(ctx, req, resp) - validateBatchReq(req.GetRequests()) - for each request: distributeService.DistributeVouchersWithReference(ctx, region, infos) v (Distribute orchestration) distribute.Service.DistributeVouchersWithReference(ctx, region, infos) - meta := newDefaultDistributionMeta(region, infos) - steps := [checkInfoConsistency, checkRateLimitAvailability, checkUserExistence, loadVouchers, checkMultiKeyRateLimitAvailability, …] - meta, err := ExecuteSteps(ctx, meta, steps, …) v (Step execution) ExecuteSteps(ctx, meta, steps, …) step#5 checkMultiKeyRateLimitAvailability(meta)(ctx) - for each valid meta.infos[i]: - v := meta.voucherMap[voucherID] - for keyPrefix, voucherTypeFunc := range multiKeyRateLimterMap: - if voucherTypeFunc(v) CheckVoucherRateLimitByKeyPrefix(ctx, userID, ownerID, voucherID, referenceID, keyPrefix) - fillErrorToResult(meta.results, errs) | (results[i].Errs filled this item will NOT continue in later steps) | v CheckVoucherRateLimitByKeyPrefix(ctx, userID, ownerID, voucherID, referenceID, keyPrefix) - keys, hashTag := getVoucherRateLimitKeyByKeyPrefix(…) // e.g. per_user, per_shop, reference_id keys - ok, subOks, err := s.multiKeyLimiterV2.CanAllow(ctx, hashTag, keys, keyPrefix) - return rate limit error if !ok or any !subOks | v MultiKeyLimiterV2.CanAllow(ctx, hashTag, keys, keyPrefix) - limiter := kvLimiters[keyPrefix] // ShadowAwareMultiKeyRateLimit per prefix (ads, smart, youtube, etc.) - ok, subOks, err := limiter.CanAllow(ctx, hashTag, keys) | v ShadowAwareMultiKeyRateLimit.CanAllow(ctx, hashTag, keys) - return s.pick(ctx).CanAllow(ctx, hashTag, keys) // normal vs shadow by shadow.IsShadow(ctx) | v InadaptiveMultiKeyDistrLimiter.CanAllow(ctx, hashTag, keys) - return canAllowN(ctx, hashTag, keys, 1) | v InadaptiveMultiKeyDistrLimiter.canAllowN(ctx, hashTag, keys, n) - waitTimesMicroSec, err := runLuaScriptForWaitTime(ctx, inadaptiveMultiKeyCanAllowLua, keys, n) - ok = all waitTimesMicroSec[i] 0; subOks[i] = (waitTimesMicroSec[i] 0) | v runLuaScriptForWaitTime(ctx, inadaptiveMultiKeyCanAllowLua, keys, n) - redisKeys := keyPrefix + ”:” + key for each key - args := [now_sec, now_microsec, window_1, limit_1, window_2, limit_2, …] - rawResult, err := redisClient.Eval(ctx, inadaptiveMultiKeyCanAllowLua, redisKeys, args) | v Redis EVAL(inadaptiveMultiKeyCanAllowLua) - Read-only: ZRANGE, ZCARD, ZREMRANGEBYSCORE (cleanup old entries) - Returns wait time per key (no token consumption)

Pre-item consume: Allow()

(Transport) GAS / Spex | | cmd: voucher.ss.distribution.batch_distribute_voucher_with_reference v (Generated Spex dispatcher) _Distribution_BatchDistributeVoucherWithReferenceHandler(ctx, req, resp) DistributionServer.service.BatchDistributeVoucherWithReference(ctx, req, resp) v (Handler entry) BatchDistributeVoucherWithReferenceImpl.execute(ctx, req, resp) - validateBatchReq(req.GetRequests()) - for each request: distributeService.DistributeVouchersWithReference(ctx, region, infos) v (Distribute orchestration) Service.DistributeVouchersWithReference(ctx, region, infos) - meta := newDefaultDistributionMeta(region, infos) - steps := […, checkMultiKeyRateLimitAvailability, …, consumeMultiKeyRateLimitAvailability, …] - meta, err := ExecuteSteps(ctx, meta, steps, …) // CanAllow already ran in checkMultiKeyRateLimitAvailability v (Step execution) ExecuteSteps(ctx, meta, steps, …) step consumeMultiKeyRateLimitAvailability(meta)(ctx) - for each i where isValidMetaEntry(meta.infos[i], meta.results[i]): - for keyPrefix, voucherFunc := range multiKeyRateLimterMap: - if voucherFunc(v) consumeVoucherRateLimitByKeyPrefix(ctx, userID, ownerID, voucherID, referenceID, keyPrefix) - fillErrorToResult(meta.results, errs) | v consumeVoucherRateLimitByKeyPrefix(ctx, userID, ownerID, voucherID, referenceID, keyPrefix) - keys, hashTag := getVoucherRateLimitKeyByKeyPrefix(…) - ok, subOks, err := s.multiKeyLimiterV2.Allow(ctx, hashTag, keys, keyPrefix) - return rate limit error if !ok or any !subOks | v MultiKeyLimiterV2.Allow(ctx, hashTag, keys, keyPrefix) - limiter := kvLimiters[keyPrefix] // ShadowAwareMultiKeyRateLimit - ok, subOks, err := limiter.Allow(ctx, hashTag, keys) | v ShadowAwareMultiKeyRateLimit.Allow(ctx, hashTag, keys) - return s.pick(ctx).Allow(ctx, hashTag, keys) // normal vs shadow by shadow.IsShadow(ctx) | v InadaptiveMultiKeyDistrLimiter.Allow(ctx, hashTag, keys) - return allowN(ctx, hashTag, keys, 1) | v InadaptiveMultiKeyDistrLimiter.allowN(ctx, hashTag, keys, n) - waitTimesMicroSec, err := runLuaScriptForWaitTime(ctx, inadaptiveMultiKeyAllowLua, keys, n) - ok = all waitTimesMicroSec[i] 0; subOks[i] = (waitTimesMicroSec[i] 0) | v runLuaScriptForWaitTime(ctx, inadaptiveMultiKeyAllowLua, keys, n) - redisKeys := keyPrefix + ”:” + key for each key - args := [now_sec, now_microsec, window_1, limit_1, window_2, limit_2, …] - rawResult, err := redisClient.Eval(ctx, inadaptiveMultiKeyAllowLua, redisKeys, args) | v Redis EVAL(inadaptiveMultiKeyAllowLua) - ZREMRANGEBYSCORE (cleanup old entries) - ZCARD, ZRANGE (read current count) - if allowed: ZADD key score, EXPIRE key TTL (mutates — consume token) - returns wait time per key