curl (with realistic scenarios)redis-cli)Use this as your live demo script or QA checklist. Everything is self-contained.
# from the project root
docker compose up --buildWait for:
Open:
admin/admin)Enter Redis CLI (inside the container):
docker compose exec redis redis-cliYou’ll see 127.0.0.1:6379>.
SET greeting "Hello Redis"
GET greeting
DEL greeting
EXISTS greeting # → (integer) 0 after DELDEL page_views
INCR page_views # -> 1
INCRBY page_views 5 # -> 6
DECR page_views # -> 5
GET page_views # -> "5"SET temp "bye"
EXPIRE temp 5
TTL temp # shows countdown (e.g., 5,4,3…)
# wait ~5s
GET temp # -> (nil)DEL tasks
LPUSH tasks "job1"
LPUSH tasks "job2"
LRANGE tasks 0 -1 # -> "job2","job1"
RPOP tasks # FIFO behavior with LPUSH/RPOP
LRANGE tasks 0 -1DEL users
SADD users "sree"
SADD users "abhi"
SADD users "sree" # ignored duplicate
SMEMBERS users
SCARD users # count stays 2DEL visits
ZADD visits 1001 "req1"
ZADD visits 1002 "req2"
ZRANGE visits 0 -1 WITHSCORES
ZREMRANGEBYSCORE visits 0 1001
ZRANGE visits 0 -1 WITHSCORESDEL user:100
HSET user:100 name "Sree" plan "Gold" credits 10
HGETALL user:100
HINCRBY user:100 credits 5
HGET user:100 credits # -> "15"Terminal A:
SET stockA 10
SET stockB 5
WATCH stockA stockB
GET stockA # make your check in app code in real life
MULTI
DECRBY stockA 3
INCRBY stockB 3
EXEC # succeeds unless someone changed the watched keysTerminal B (to force abort, run between WATCH and EXEC in A):
INCR stockAEXEC returns null → safe abort (no partial updates). Then retry.Terminal 1:
SUBSCRIBE alertsTerminal 2:
PUBLISH alerts "Redis is awesome!"DEL balance
SET balance 100
EVAL "local bal=tonumber(redis.call('GET',KEYS[1]) or '0');local amt=tonumber(ARGV[1]);if bal>=amt then redis.call('DECRBY',KEYS[1],amt);return bal-amt else return -1 end" 1 balance 30
GET balance # -> "70"SCAN 0 MATCH * COUNT 10
TYPE stockASCAN to KEYS * in real systems.INFO memory
INFO stats
INFO commandstats
MONITOR # live stream of all commands (Ctrl+C to exit)FLUSHALL # ⚠Destroys ALL keys – dev/test onlyUse these
curltests in a separate terminal. Keep Redis Commander open to watch keys appear (or keepredis-cli MONITORrunning).
curl -s http://localhost:8001/health
# {"ok": true}curl -s "http://localhost:8001/counter/incr?key=page_views"
curl -s "http://localhost:8001/counter/get?key=page_views"INCR atomically.Seed:
curl -s -X POST "http://localhost:8001/stock/seed?item=a&qty=50"
curl -s -X POST "http://localhost:8001/stock/seed?item=b&qty=10"Transfer:
curl -s -X POST "http://localhost:8001/stock/transfer" \
-H "Content-Type: application/json" \
-d '{"from_item":"a","to_item":"b","qty":7}'Check:
curl -s "http://localhost:8001/stock/get?item=a"
curl -s "http://localhost:8001/stock/get?item=b"curl -s -X POST "http://localhost:8001/wallet/seed?user=sree&amount=100"
curl -s -X POST "http://localhost:8001/wallet/debit?user=sree&amount=30"
curl -s -X POST "http://localhost:8001/wallet/debit?user=sree&amount=100"X-API-Key + client IP)The service uses
X-Forwarded-Forif present; else it uses your client’s IP. All strategies generate keys per<apiKey>:<ip>to be fair.
for i in {1..10}; do
curl -s -H "X-API-Key: demo123" "http://localhost:8001/rate/fixed";
donerl:fixed:* → watch value & TTL.for i in {1..12}; do
curl -s -H "X-API-Key: demo123" "http://localhost:8001/rate/sliding";
donerl:slide:<apiKey>:<ip> → ZSET of timestamps.for i in {1..15}; do
curl -s -H "X-API-Key: gold" \
"http://localhost:8001/rate/tokenbucket?capacity=10&refill_per_sec=2&cost=1";
done
sleep 3
for i in {1..5}; do
curl -s -H "X-API-Key: gold" \
"http://localhost:8001/rate/tokenbucket?capacity=10&refill_per_sec=2&cost=1";
donerl:tb:tokens:<key> and rl:tb:ts:<key>.Simulate different IPs:
curl -s -H "X-API-Key: demo123" -H "X-Forwarded-For: 1.2.3.4" "http://localhost:8001/rate/fixed"
curl -s -H "X-API-Key: demo123" -H "X-Forwarded-For: 5.6.7.8" "http://localhost:8001/rate/fixed"If you don’t see a text filter, use Commands → Find Keys (SCAN) and enter:
rl:fixed:*rl:slide:*rl:tb:*Click a key to view: values, TTL, ZSET members, etc.
Refresh to see TTL countdown and expirations.
redis-cli (Terminal)In a separate terminal:
docker compose exec redis redis-cli MONITORNow hit API endpoints → watch commands stream live:
INCR for fixed windowZADD/ZREMRANGEBYSCORE for slidingEVAL for token bucket Lua/metricscurl -s http://localhost:8001/metrics | head -n 30You’ll see:
requests_total{endpoint="/rate/fixed",status="200",strategy="fixed"} ...rate_limited_total{strategy="sliding"} ...request_latency_seconds_bucket{le="0.005"} ...api:8001 UP.Allowed RPS by endpoint (1m):
sum by (endpoint) (rate(requests_total{status="200"}[1m]))Blocked RPS by strategy (1m):
sum by (strategy) (rate(rate_limited_total[1m]))P90 and P99 latency:
histogram_quantile(0.90, sum(rate(request_latency_seconds_bucket[5m])) by (le))
histogram_quantile(0.99, sum(rate(request_latency_seconds_bucket[5m])) by (le))Requests for a specific endpoint (e.g., token bucket):
sum(rate(requests_total{endpoint="/rate/tokenbucket"}[1m]))admin/adminTotal Requests (5m)
Increases as you fire curl loops.
Total Rate-Limited (5m) Jumps when you exceed limits (429s).
Requests by Endpoint & Status (1m rate)
/rate/fixed: you’ll see green plateau at your limit and red rise as blocks kick in.Rate-Limited by Strategy (1m rate) Compare fixed vs sliding vs token bucket.
Latency P50/P90/P99 Should remain very low (Redis ops are fast). Spikes hint at contention, network issues, or resource caps.
# 20 concurrent hits to fixed
seq 1 20 | xargs -n1 -P20 -I{} curl -s -H "X-API-Key: demo123" http://localhost:8001/rate/fixed >/dev/null
# 20 concurrent hits to sliding
seq 1 20 | xargs -n1 -P20 -I{} curl -s -H "X-API-Key: demo123" http://localhost:8001/rate/sliding >/dev/nullpython api/app/scripts/load_test.py "http://localhost:8001/rate/fixed" 25
python api/app/scripts/load_test.py "http://localhost:8001/rate/sliding" 30
python api/app/scripts/load_test.py "http://localhost:8001/rate/tokenbucket?capacity=10&refill_per_sec=2&cost=1" 30No keys in Redis Commander
rl:* or switch DB to 0. Click Refresh.Prometheus shows DOWN
/metrics is reachable:
curl -s http://localhost:8001/metrics | headGrafana panels empty
All 429s
X-API-Key or simulate another IP with X-Forwarded-For.Token bucket not refilling
rl:tb:tokens:* value in Redis Commander; ensure capacity and refill_per_sec query params are set.INCR, Lua)WATCH/MULTI/EXECMONITORIf you want, I can package this into a single “Test Playbook” PDF with checkboxes and a one-page Grafana legend for workshops and demos.