{"id":32868604,"url":"https://github.com/ercansormaz/redis-rate-limiting","last_synced_at":"2026-05-07T19:04:00.576Z","repository":{"id":318886932,"uuid":"1076378789","full_name":"ercansormaz/redis-rate-limiting","owner":"ercansormaz","description":"Spring Boot project demonstrating Rate Limiting Algorithms based on Redis","archived":false,"fork":false,"pushed_at":"2026-01-31T10:17:40.000Z","size":27,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-31T22:52:29.488Z","etag":null,"topics":["rate-limit","rate-limiter","rate-limiting","redis-rate-limiter","spring","spring-boot"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ercansormaz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-14T19:20:48.000Z","updated_at":"2026-01-31T10:17:43.000Z","dependencies_parsed_at":"2025-10-17T02:34:50.134Z","dependency_job_id":"62d49564-763d-43c7-9aa2-c3541ffb5a62","html_url":"https://github.com/ercansormaz/redis-rate-limiting","commit_stats":null,"previous_names":["ercansormaz/redis-rate-limiting"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ercansormaz/redis-rate-limiting","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ercansormaz%2Fredis-rate-limiting","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ercansormaz%2Fredis-rate-limiting/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ercansormaz%2Fredis-rate-limiting/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ercansormaz%2Fredis-rate-limiting/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ercansormaz","download_url":"https://codeload.github.com/ercansormaz/redis-rate-limiting/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ercansormaz%2Fredis-rate-limiting/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32751759,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-07T02:14:30.463Z","status":"ssl_error","status_checked_at":"2026-05-07T02:14:29.405Z","response_time":62,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["rate-limit","rate-limiter","rate-limiting","redis-rate-limiter","spring","spring-boot"],"created_at":"2025-11-09T08:01:37.076Z","updated_at":"2026-05-07T19:04:00.553Z","avatar_url":"https://github.com/ercansormaz.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Redis Rate Limiting\n\nThis project demonstrates how to implement and compare different **rate limiting algorithms** using **Spring Boot** and **Redis**.  \n\nIt provides practical examples of how these algorithms behave under load and how Lua scripting can be used to ensure atomic operations in Redis.\n\n---\n\n## 🚀 Project Overview\n\nThe goal of this project is to explore and compare the performance and behavior of various **rate limiting strategies** implemented in a distributed environment.  \n\nEach algorithm is implemented in two ways:\n1. Using direct Redis operations from Java.\n2. Using **Lua scripts** for atomic execution within Redis.\n\nAll Java-based implementations remain commented out for educational purposes, allowing you to easily compare both approaches.\n\n---\n\n## 🧩 Implemented Algorithms\n\n| Algorithm                             | Description                                                                                    |\n|---------------------------------------|------------------------------------------------------------------------------------------------|\n| **Token Bucket**                      | Allows requests as long as there are available tokens. Tokens are replenished at a fixed rate. |\n| **Leaky Bucket**                      | Requests are processed at a steady rate, smoothing out traffic spikes.                         |\n| **Fixed Window**                      | Counts requests within a fixed time window. Simple but can suffer from boundary effects.       |\n| **Sliding Window Counter (Weighted)** | Uses proportionally weighted sub-windows for smoother and more accurate rate control.                                       |\n| **Sliding Window Log**                | Tracks timestamps of recent requests for the most precise limiting.                            |\n\nEach algorithm has its own annotation and Lua script to perform the rate-limiting check atomically within Redis.\n\n---\n\n## ⚡ Why Lua Scripts?\n\nRedis operations such as incrementing counters, adding timestamps, and trimming old entries must often be **atomic** to prevent race conditions in distributed systems.\n\nWhile Spring Data Redis operations can achieve this to some extent, they involve multiple round trips. Lua scripts execute directly **inside Redis**, ensuring:\n- Atomicity (no partial updates)\n- Better performance under high concurrency\n- Reduced network overhead\n\nEach algorithm in this project includes a Lua script that encapsulates the rate-limiting logic.\n\n### ⚙️ Script Execution Optimization\n\nIn this project, Lua scripts are executed using **`evalsha`** via low-level `RedisCommands` instead of Spring’s `RedisTemplate`.  \nThis approach provides significant performance benefits:\n\n- **`RedisTemplate`** executes scripts using `EVAL`, which means the script body is sent to Redis on every call.\n- **`RedisCommands` + `EVALSHA`** executes scripts by their SHA1 hash, allowing Redis to reuse the cached script instead of reloading it each time.\n\nIf the script is not yet cached (i.e., `NOSCRIPT` error occurs), the implementation automatically falls back to a one-time `EVAL` call, then resumes using `EVALSHA` for all subsequent executions.\n\nThis ensures both **atomic execution** and **high efficiency** even under heavy request loads.\n\n---\n\n## 🧠 Design Notes\n\n- **Annotation-driven architecture**\n\n  Each rate limiting strategy is exposed through a dedicated annotation, such as:\n  ```java\n  @TokenBucketRateLimit(...)\n  @LeakyBucketRateLimit(...)\n  @FixedWindowRateLimit(...)\n  @SlidingWindowCounterLimit(...)\n  @SlidingWindowLogLimit(...)\n  ```\n  These annotations are intercepted via **Spring AOP**, allowing the rate limiting logic to remain separate from business concerns while keeping the controller layer clean.\n\n\n- **Lua scripts for atomicity**\n\n  Although the first version of the project performed Redis operations directly from Java, this approach introduced potential race conditions in high-concurrency environments.\n\n  To address this, all algorithms were reimplemented using **Lua scripts**, ensuring atomic, server-side execution inside Redis.\n\n  The original Java implementations remain in the codebase as commented examples for educational comparison.\n\n\n- **Sliding Window Counter (Weighted approach)**\n\n  This project adopts the weighted sliding window counter variant rather than the basic counter model.\n\n  In the traditional counter method, each sub-window has equal weight, which may cause abrupt rate changes at window boundaries.\n\n  The weighted version, however, applies proportional weighting based on how much of the current time falls within each sub-window — providing smoother transitions and more accurate rate control under fluctuating load.\n\n\n- **Sliding Window Algorithms Optimization**\n\n  In many reference implementations, requests that exceed the rate limit are still added to Redis.\n\n  This project deliberately avoids storing **rejected requests**.\n\n  The reason: when every incoming request (including rejected ones) is logged, the sliding window becomes permanently saturated during high traffic, blocking all further requests.\n  \n  By excluding rejected requests, the limiter preserves **capacity awareness**, allowing new valid requests once earlier ones expire — resulting in a more stable throughput under heavy load.\n\n---\n\n## 🧪 Example API Usage\n\nAll endpoints are accessible under:\n```bash\nhttp://localhost:8080/rate-limiter/\n```\n\nEndpoints:\n```bash\nGET /rate-limiter/token-bucket\nGET /rate-limiter/leaky-bucket\nGET /rate-limiter/fixed-window\nGET /rate-limiter/sliding-window-counter\nGET /rate-limiter/sliding-window-log\n```\n\nEach endpoint is annotated with the corresponding rate limiter annotation, demonstrating how the request flow is controlled.\n\n### 🧾 Response Behavior\n\nAll endpoints return only HTTP status codes — there is no response body.\n\n| Status Code               | Meaning                                                                       |\n|---------------------------|-------------------------------------------------------------------------------|\n| **202 Accepted**          | The request has been accepted and processed successfully (within rate limit). |\n| **429 Too Many Requests** | The request has been rejected because the rate limit has been reached.        |\n\nThis lightweight response design makes it easier to benchmark and observe the limiter’s behavior under different load conditions.\n\n---\n\n## 🧰 Running the Project\n\n#### 1. Start a Redis instance (via Podman, Docker, or locally):\n```bash\npodman run --name myredis -p 6379:6379 -d redis redis-server --requirepass s3cret\n```\n\n#### 2. Clone this repository:\n```bash\ngit clone https://github.com/ercansormaz/redis-rate-limiting.git\n```\n\n#### 3. Navigate to the project folder and build:\n```bash\nmvn clean install\n```\n\n#### 4. Run the application:\n```bash\nmvn spring-boot:run\n```\n\n#### 5. Access any of the test endpoints listed above to observe the rate limiting behavior.\n\n---\n\n## 📚 Further Reading\nYou can read a detailed explanation of this project in the blog post here:  \n👉 [Read the Blog Post](https://ercan.dev/blog/notes/spring-boot-rate-limiting-with-redis)\n\n---\n\n## 🤝 Contributing\nContributions are welcome! Feel free to fork the repo, submit pull requests or open issues.\n\n---\n\n## 📜 License\nThis project is licensed under the MIT License.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fercansormaz%2Fredis-rate-limiting","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fercansormaz%2Fredis-rate-limiting","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fercansormaz%2Fredis-rate-limiting/lists"}