To enforce one quota across multiple Node.js instances, store each bucket in Redis and make the quota check and update one atomic operation, such as a short Lua script. Use Redis time inside time-based scripts, decide in advance whether a Redis outage should fail open or closed, and define what a policy change does to buckets already in Redis. Those decisions matter as much as the algorithm: Redis gives instances shared state, but it does not by itself guarantee that state survives every failover or that a new limit has an unambiguous effect on old buckets.
Why a shared limiter needs shared state
A counter in a Node.js process is local to that process. If a user has a limit of 100 requests and traffic reaches four instances, four independent counters can each allow requests against what the application treats as one quota. A central Redis keyspace lets all instances consult the same state, whether the limit is keyed by user, IP address, tenant, endpoint, or a global resource.
As an Amazon Associate I earn from qualifying purchases.
For a time-based algorithm, the critical operation is not just reading a counter. It is reading the current state, calculating what has refilled or expired, deciding whether the request is allowed, and writing the updated state. If separate commands perform those steps, concurrent requests can read the same remaining allowance and both spend it. A short Redis Lua script can perform the decision and update atomically, so another command cannot interleave while that script runs.
Free tools Windows power users keep installed
One-click scans. No signup required.
Atomicity is not a reason to put arbitrary work in Lua. Redis runs a script without serving other activity while it executes. Keep its inputs bounded, avoid loops whose size grows with request volume, and make the script do only the calculation and state update required for the decision.
#1 Best Overall
- Includes Raspberry Pi 5 with 2.4Ghz 64-bit quad-core CPU (8GB RAM)
- Includes 128GB Micro SD Card pre-loaded with 64-bit Raspberry Pi OS, USB MicroSD Card Reader
- CanaKit Turbine Black Case for the Raspberry Pi 5
- CanaKit Low Noise Bearing System Fan
- Mega Heat Sink - Black Anodized
Choose an algorithm for the burst behavior you want
The right algorithm depends on whether clients may burst, how precise the quota must be, and how much state the service can afford. The characteristics below are the qualitative trade-offs described in Redis’s rate-limiting guide, not workload-specific benchmarks.
| Algorithm | Accuracy and burst behavior | State and fit |
|---|---|---|
| Token bucket | Exact in the guide’s comparison; allows controlled bursts up to the bucket capacity. | One hash key. A useful fit for APIs that should tolerate short bursts while limiting sustained use. |
| Fixed window | Approximate; requests at a window boundary can allow roughly twice the configured limit over a short span. | One string key. Simple and low-state when boundary behavior is acceptable. |
| Sliding-window log | Exact in the comparison and does not allow bursts. | O(n) entries for requests in the window. Consider where exact history is worth the storage and maintenance cost. |
| Sliding-window counter | Near-exact; smooths the boundary effect of a fixed window. | Two string keys. A compromise when smoother enforcement matters without retaining every request. |
| Leaky-bucket policing | Exact in the comparison and does not allow bursts. | One hash key. Suits strict no-burst enforcement. |
For a token bucket, capacity determines the maximum accumulated burst, while the refill rate determines how quickly capacity returns. A fixed-window policy is easier to explain but can be surprising near a boundary. A sliding log retains more detail; that may be justified for high-value or audit-sensitive limits, but its state grows with the number of recorded requests. Select the policy from the desired client behavior and expected key cardinality, not from an assumption that one algorithm is universally best.
Implement the decision as one Redis operation
A token bucket illustrates the shared-state pattern. Store the available token count and last-refill timestamp in a Redis hash. In one script, get Redis time, refill up to the configured capacity, check whether the request’s cost can be paid, subtract the cost if allowed, persist the result, and return the decision with the remaining balance and retry delay.
The following Lua sketch shows the core calculation. It expects one key and arguments for capacity, refill rate in tokens per second, and request cost. The example expires a bucket after the time it would take an empty bucket to refill to capacity; after that, treating a missing bucket as full is consistent with this bucket policy. Adapt argument validation, key naming, expiry, and numeric bounds to the application before deployment.
Rank #2
- Includes Raspberry Pi 5 16GB with 2.4Ghz 64-bit quad-core CPU (16GB RAM)
- Includes 128GB Micro SD Card pre-loaded with 64-bit Raspberry Pi OS, USB MicroSD Card Reader
- CanaKit Turbine Black Case for the Raspberry Pi 5
- CanaKit Low Noise Bearing System Fan
- Mega Heat Sink - Black Anodized
-- KEYS[1]: bucket hash
-- ARGV[1]: capacity, ARGV[2]: refill tokens per second, ARGV[3]: request cost
local capacity = tonumber(ARGV[1])
local refill_per_second = tonumber(ARGV[2])
local cost = tonumber(ARGV[3])
if not capacity or not refill_per_second or not cost
or capacity <= 0 or refill_per_second <= 0
or cost <= 0 or cost > capacity then
return redis.error_reply('invalid token bucket parameters')
end
local now = redis.call('TIME')
local now_ms = tonumber(now[1]) * 1000 + math.floor(tonumber(now[2]) / 1000)
local tokens = tonumber(redis.call('HGET', KEYS[1], 'tokens')) or capacity
local last_ms = tonumber(redis.call('HGET', KEYS[1], 'last_ms')) or now_ms
local elapsed_ms = math.max(0, now_ms - last_ms)
local refill_per_ms = refill_per_second / 1000
tokens = math.min(capacity, tokens + elapsed_ms * refill_per_ms)
local allowed = 0
local retry_ms = 0
if tokens >= cost then
tokens = tokens - cost
allowed = 1
else
retry_ms = math.ceil((cost - tokens) / refill_per_ms)
end
redis.call('HSET', KEYS[1], 'tokens', tokens, 'last_ms', now_ms)
redis.call('PEXPIRE', KEYS[1], math.ceil(capacity / refill_per_ms))
return {allowed, math.floor(tokens), retry_ms}
Validate parameters before they reach the script as well, and cap them to sensible ranges. In particular, the script assumes a request cost does not exceed capacity; the example rejects a larger cost rather than making it permanently impossible to pass. Decide whether fractional tokens are meaningful for the application and round returned values consistently with the policy.
With node-redis, load the script and call it asynchronously using its SHA. A missing script cache entry can produce a NOSCRIPT error after Redis restarts or a failover, so handle that case by evaluating the source again or reloading it and retrying. Do not treat every Redis error as a cache miss: connection errors and script errors need the application’s failure policy.
const script = `-- Lua script from above`;
let sha = await client.scriptLoad(script);
async function checkBucket(key, capacity, refillPerSecond, cost) {
const keys = [key];
const arguments_ = [
String(capacity),
String(refillPerSecond),
String(cost)
];
try {
return await client.evalSha(sha, { keys, arguments: arguments_ });
} catch (error) {
if (!String(error.message).includes('NOSCRIPT')) throw error;
sha = await client.scriptLoad(script);
return await client.evalSha(sha, { keys, arguments: arguments_ });
}
}
Match the client method names and error shape to the node-redis version actually deployed. Redis Functions are another server-side option beginning with Redis 7.0; whether to use scripts or functions is an operational choice, not a change to the requirement that the check and update share one atomic decision.
Recommended Free Tools
Use Redis time deliberately
If elapsed time affects a quota, having every Node.js instance call Date.now() independently can make the same bucket produce different decisions when application clocks drift. A script that calls Redis TIME uses one time source for the calculation, preventing differences between application-server clocks from driving refill decisions.
Rank #3
- CanaKit Raspberry Pi 5 Essentials Starter Kit
This centralizes the time source; it does not prove that the Redis host clock is synchronized or immune to jumps. Synchronize and monitor the host clock, and consider how a forward or backward clock change affects elapsed-time arithmetic. The example clamps negative elapsed time to zero, but a large forward jump can still refill a bucket quickly. If that risk matters, define and test a policy for abnormal clock changes rather than assuming that central time alone solves them.
Choose fail-open or fail-closed behavior for Redis errors
A lost Redis connection can make the asynchronous limiter call throw. The application must decide what protected requests do when it cannot obtain a quota decision; neither behavior is universally correct.
- Fail open: allow the operation when the limiter is unavailable. This favors service availability, but traffic can pass without the shared quota being enforced.
- Fail closed: reject or defer the operation when the limiter is unavailable. This protects the downstream resource, but legitimate clients can be blocked during a Redis incident.
Choose per protected operation. A limit protecting an expensive or abuse-sensitive resource may justify a different policy from one whose main purpose is smoothing ordinary traffic. Make the behavior visible in logs and metrics, alert on limiter errors, and avoid silently presenting a Redis failure as an ordinary quota denial.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
A local emergency limiter can be a useful fallback only if its weaker semantics are intentional: it does not recreate one shared quota across the fleet. Each process has its own local state, so the aggregate allowance can differ from the intended limit. If used, document it as degraded enforcement rather than equivalent enforcement.
Rank #4
- All-in-One Complete Kit: This SANOOV RPi 5 bundle comes with Raspberry Pi 5 4GB RAM single board, active cooler, durable ABS case and screwdriver. No extra parts needed, ready to use right out of the box for beginners and hobbyists
- Powerful Single Board Computer: Equipped with 4GB RAM and high-performance processor, delivers fast running speed for 4K playback, AI projects, programming and daily computing tasks. SANOOV for raspberry pi 5 4GB is equipped with broadcom 64 quad-core Arm Cortex A76 processor with gigabit ethernet and upgraded with IEEE 802.11ac Wi-Fi, Bluetooth 5.0 dual-band 2.4Ghz and 5Ghz and Power Over Ethernet (POE). Upgrading delivers 2-3 x speed vs Pi 4, redefining the experience
- Efficient Active Cooler: Effectively lowers operating temperature and prevents performance throttling. Runs quietly even under long-time heavy load, ensures stable operation all day long. SANOOV RPi 5 4GB kit offer an active cooler, which combines an aluminium heatsink with a high-performance PWM fan. Active cooler is fully compatible with the Pi OS, which can effectively reduce the temperature of RPi5 and ensure its good performance during long-term high load operation
- Sturdy ABS Protective Case: Well-fitted for Raspberry Pi 5 board, can be secured with 4 screws to effectively protect the Pi 5 motherboard from damage, reserves full access to all ports and buttons. SANOOV uses ABS material to produce the case, which has a softer texture and feel. Meanwhile, SANOOV case adopts a layered design for easy disassembly and installation. (Tip: The Case cannot install M.2 HAT Add on Board and Solid State Drive!)
- Wide Application & Full Compatibility: Seamlessly compatible with official OS and mainstream peripheral accessories for Raspberry Pi 5. Whether you are a beginner, student, electronics hobbyist or professional developer, this all-in-one kit meets your diverse needs. It excels in IoT projects, robotics design, retro gaming devices, home media servers and other DIY creations. Backed by a large global community, you can easily find guides, technical support and shared projects online
Make response headers agree with the decision
Return the limit and remaining allowance in response headers if clients need to pace themselves. When a request is denied, return HTTP 429 and a Retry-After value. Derive the retry delay from the same server-side calculation that made the allow-or-deny decision; separately calculating it in Node.js can report a time that does not match the bucket state.
For the token-bucket sketch, the returned delay is in milliseconds. Convert it to the representation expected by the API’s chosen header convention, rounding up so the client is not told to retry before enough capacity should be available. Define what the headers mean for a request whose cost exceeds capacity or for a request rejected because Redis is unavailable; those cases are not ordinary bucket denials.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Plan for script loss and Redis failover
Script atomicity means concurrent commands cannot interleave with one script execution. It does not mean limiter state is guaranteed to survive every restart, replication change, or failover exactly as clients observed it. Redis scripting documentation describes script-cache reload requirements after restart or failover, while Redis high-availability behavior depends on the deployed topology and replication state.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsSpecify the Redis deployment’s persistence and replication assumptions, what state loss is acceptable, and what the application does during failover. Redis failover can include a write outage; it may last longer under heavy write traffic or when the selected replica is behind. Do not promise seamless quota continuity unless the specific deployment’s behavior has been established and tested against the service’s requirements.
Best Value
- 【What you Get】You will get 1*Pi 5 8GB Single Board,1*RasTech Case,1*Active Cooler,1*Screwdriver,1*Installation instructions,12-month free warranty, lifetime service, 24-hour prompt and friendly response.
- 【More Connectors】There are two USB 3.0 ports(5Gbps simultaneously) and two USB 2.0 ports, which triple total bandwidth ,support any combination of up to two cameras or displays. Peak SD card performance is doubled through support for the SDR104 high-speed mode. It provides a smooth desktop experience for you. Offer Gigabit Ethernet and a PCIe interface, along with dual-band Wi-Fi and Bluetooth 5.0/BLE wireless capability. The RasTech Pi 5 Kit use the new 27W 5.1V 5A USB-C power connector.
- 【 Support Dual 4Kp60 Display 】Each of the two microHDMI sockets can control a 4K display at 60 Hertz, now support HDR, offering super HD video for media streaming projects. RPi 5 is the first RPi model that comes with a PCI Express port (PCIe 2.0 x1 with 500 MB/s) to attach SSDs (requires separate M.2 HAT).
- 【 Excellent Chips And Applications】Pi 5 is a full-size Pi computer using silicon built in-house at Pi. The RP1 “southbridge” provides the bulk of the I/O capabilities for Pi 5. Pi 5 is more friendly and convenient in the development of Internet of Things, Web development, machine identification, automatic control and other electronic equipment applications and network.
- 【 Faster CPU, Better GPU 】 Pi 5 features a Broadcom BCM2712 64-bit quad-core Arm Cortex-A76 processor running at 2.4GHz, it delivers a 2–3× increase in CPU performance relative to RaspberryPi 4. The 800MHz VideoCore VII GPU is compatible to OpenGL ES 3.1 and Vulkan 1.2, substantial uplift in graphics performance. Pi 5 Offers lightning-fast CPU speed, a PCI Express interface, a Real Time Clock (RTC) and a power button and runs significantly cooler than Pi 4.
Scripts also have operational limits. Redis can return -BUSY when a script exceeds the configured Lua script time limit. SCRIPT KILL can safely stop a script only if it has not modified the dataset. Keep limiter scripts short, and investigate slow execution rather than treating script termination as routine recovery.
Check the deployed Redis version and scripting replication mode. Redis documentation notes that effects-based replication became the default in Redis 5.0 and verbatim script replication was removed in Redis 7.0; time-dependent scripts make version and mode relevant to deployment review. If relying on Redis 8’s HEXPIRE, verify that the service actually runs a compatible version. Production configuration should follow the deployment platform’s configuration mechanism rather than relying on a no-file startup intended for testing or development.
Define how changed limits affect existing buckets
A stored bucket has state created under a policy: for example, a remaining token count and last-refill time. Changing capacity or refill rate does not inherently specify how that old state should be interpreted. Pick and document one transition rule.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware match- Version the key by policy generation. New requests use a distinct bucket under the new policy. This makes the transition clear, but a new full bucket may grant fresh burst capacity. If old and new buckets overlap, define which one governs during the overlap.
- Keep the key and reinterpret state. Update the script so it applies new parameters to existing state. When capacity shrinks, explicitly clamp stored tokens to the new capacity; decide how to interpret the stored timestamp and any refill accrued under the old rate.
- Delay the change until current state expires. This can avoid mid-window reinterpretation when the algorithm and product permit waiting, but it delays enforcement of the new policy.
Make the state transition atomic with the decision when using a script. Also ensure every application instance sees a consistent policy version: use a central configuration/version source, synchronized rollout, or a policy version embedded in the key. If some instances apply different parameters to one shared key, clients can receive inconsistent enforcement unless the script and rollout deliberately support that mixture.
Quick Recap
Operational checklist before release
- Choose the identity and scope of each quota key, and bound key cardinality with an appropriate expiry policy.
- Select an algorithm whose burst behavior and state cost match the protected operation.
- Keep check, update, and returned remaining/retry values in one atomic server-side operation.
- Use Redis time for elapsed-time calculations and monitor the Redis host’s clock.
- Test concurrent requests against the same key, script-cache loss, Redis disconnection, and the chosen fail-open or fail-closed response.
- Document failover assumptions, acceptable state loss, and how policy changes transform active buckets.
- Verify script behavior and features against the deployed Redis version and client-library version.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




