Real-time Leaderboard Service using gRPC and Redis.
Peak v2 introduces architectural improvements and cleaner separation of concerns while preserving the original real-time streaming model.
- Shared ranking logic (Standard Competition Ranking – ties share same rank)
- Separation of mutation and query responsibilities
- Added unary
GetLeaderboardRPC for read-only dashboard access - Refactored snapshot-building into a dedicated service layer
- Improved validation for JOIN and SCORE_UPDATE events
- Cleaner layered architecture (Redis → Business → gRPC)
Client (Streaming) → LeaderboardGrpcService
Client (Unary) → LeaderboardQueryGrpcService
↓
LeaderboardSnapshotService
↓
LeaderboardRedisService
↓
Redis (ZSET)
- Redis remains the single source of truth.
- Business logic (ranking + snapshot building) is isolated in a dedicated service.
- gRPC layer handles transport only.
- Full leaderboard snapshots are sent after each mutation.
- Unary RPC enables dashboard-style leaderboard viewing without joining.
Implements Standard Competition Ranking (shared ranks for equal scores).
Example:
| User | Score | Rank |
|---|---|---|
| A | 100 | 1 |
| B | 100 | 1 |
| C | 90 | 2 |
Ranks are calculated in memory after fetching sorted data from Redis.
JOIN– Adds user (if not existing) with default score 0 and returns snapshotSCORE_UPDATE– Updates score and returns updated snapshotSNAPSHOT– Full leaderboard snapshot streamed to client
getLeaderboard– Returns current leaderboard snapshot (read-only)
syntax = "proto3";
import "google/protobuf/empty.proto";
package peak;
option java_multiple_files = true;
option java_package = "com.akansha.peak.grpc";
option java_outer_classname = "LeaderboardProto";
service LeaderboardService {
rpc streamLeaderboard(stream ClientEvent) returns (stream ServerEvent);
}
service LeaderboardQueryService {
rpc getLeaderboard(google.protobuf.Empty) returns (LeaderboardSnapshot);
}
message ClientEvent {
oneof payload {
JoinLeaderboard join = 1;
ScoreUpdate scoreUpdate = 2;
}
}
message JoinLeaderboard {
string userId = 1;
}
message ScoreUpdate {
string userId = 1;
int64 score = 2;
}
message ServerEvent {
oneof payload {
LeaderboardSnapshot snapshot = 1;
LeaderboardUpdate update = 2;
}
}
message LeaderboardSnapshot {
repeated LeaderboardEntry entries = 1;
}
message LeaderboardUpdate {
LeaderboardEntry entry = 1;
}
message LeaderboardEntry {
string userId = 1;
int64 score = 2;
int32 rank = 3;
}
Real-time Leaderboard Service using gRPC and Redis.
Peak is a backend service that provides a real-time leaderboard using gRPC bidirectional streaming and Redis Sorted Sets.
Clients can:
- Join the leaderboard stream
- Send score updates
- Receive updated leaderboard snapshots and rankings in real-time
The server is stateless with redis acting as the source of truth.
- Java
- Spring Boot
- gRPC (bidirectional streaming)
- Redis (Sorted Sets)
JOIN– Request current leaderboard snapshotSCORE_UPDATE– Update user score
SNAPSHOT– Leaderboard snapshot with ranked entries
- Key:
leaderboard:global - Type: Sorted Set (ZSET)
- Member:
userId(String) - Score:
score(Double)
- Scores are stored in Redis ZSET
- Leaderboard is fetched using
ZREVRANGE - Ranks are assigned sequentially in memory
- Snapshot is streamed back to the client
- Ensure Redis is running locally on default port (6379) by docker.
docker run -d --name redis -p 6379:6379 redis- Run the spring-boot application
./mvnw spring-boot:run- List services:
docker run --rm --network=host \
fullstorydev/grpcurl \
-plaintext localhost:9090 list- Join Leaderboard Stream:
docker run --rm --network=host \
fullstorydev/grpcurl \
-plaintext \
-d '{"join":{}}' \
localhost:9090 \
peak.LeaderboardService/streamLeaderboard- Update Score:
docker run --rm --network=host \
fullstorydev/grpcurl \
-plaintext \
-d '{"scoreUpdate":{"userId":"alice","score":500}}' \
localhost:9090 \
peak.LeaderboardService/streamLeaderboard