Skip to content

Latest commit

Β 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

⚑ Idempotent Event Processing System

Production-grade event processing with guaranteed exactly-once delivery

Node.js Express.js PostgreSQL Redis React Docker Prisma Tailwind


A sophisticated distributed event processing system that ensures every event is processed exactly once, even when the same event is received multiple times. Built with Redis Streams for queuing, distributed locks for concurrency safety, and a stunning real-time dashboard.


πŸ—οΈ Architecture

graph TB
    subgraph Client["πŸ–₯️ Frontend (React + Vite)"]
        UI[Dashboard UI]
        SSE_C[SSE Client]
    end

    subgraph API["πŸ”Œ API Server (Express.js)"]
        GW[API Gateway]
        AUTH[JWT Auth]
        RL[Rate Limiter]
        IDEMP[Idempotency Engine]
        CTRL[Controllers]
    end

    subgraph Queue["πŸ“¨ Redis Streams"]
        HIGH[events:high]
        MED[events:medium]
        LOW[events:low]
        DLQ[events:dead-letter]
    end

    subgraph Worker["βš™οΈ Worker Process"]
        CONSUMER[Stream Consumer]
        LOCK[Distributed Lock]
        HANDLER[Event Handlers]
        RETRY[Retry Engine]
    end

    subgraph Data["πŸ’Ύ Data Layer"]
        PG[(PostgreSQL)]
        REDIS[(Redis Cache)]
    end

    UI -->|HTTP/SSE| GW
    GW --> AUTH --> RL --> IDEMP --> CTRL
    CTRL -->|XADD| HIGH & MED & LOW
    CTRL --> PG & REDIS
    CONSUMER -->|XREADGROUP| HIGH & MED & LOW
    CONSUMER --> LOCK --> REDIS
    CONSUMER --> HANDLER
    HANDLER -->|on failure| RETRY
    RETRY -->|max retries| DLQ
    CONSUMER --> PG
    SSE_C -.->|real-time| GW
Loading

✨ Features

Core Engine

  • βœ… Idempotency Guarantee β€” Every event requires an Idempotency-Key header. Duplicates are detected via Redis (fast) + PostgreSQL (permanent audit)
  • βœ… Redis Streams Queue β€” Priority-based queuing (HIGH/MEDIUM/LOW) with consumer groups
  • βœ… Distributed Locking β€” SET NX PX with Lua-script safe release prevents concurrent processing
  • βœ… Exponential Backoff Retry β€” Failed events retry with 2^n second backoff (max 5 retries)
  • βœ… Dead Letter Queue β€” Events exceeding max retries are quarantined with full error history
  • βœ… CRITICAL Priority Bypass β€” Critical events skip the queue and process synchronously

API & Security

  • βœ… JWT Authentication β€” Secure login/register with bcrypt password hashing
  • βœ… API Key Management β€” Generate, list, and revoke API keys with SHA-256 hashing
  • βœ… Sliding Window Rate Limiting β€” Redis sorted set-based rate limiter with Retry-After headers
  • βœ… Request ID Tracing β€” UUID per request attached to all logs and response headers
  • βœ… Swagger/OpenAPI Docs β€” Auto-generated interactive API documentation

Real-Time Dashboard

  • βœ… Live Event Feed β€” Server-Sent Events (SSE) for real-time updates
  • βœ… Webhook Simulator β€” Prove idempotency by sending N duplicate events
  • βœ… Metrics & Charts β€” Throughput, processing times, error rates, queue depth
  • βœ… Event Inspector β€” JSON payload viewer, retry timeline, cURL generator
  • βœ… DLQ Management β€” Replay, bulk replay, or discard dead events

Operations

  • βœ… Docker Compose β€” One command to run the entire stack
  • βœ… Graceful Shutdown β€” Drain queues on SIGTERM
  • βœ… Structured Logging β€” Winston with JSON format and request tracing
  • βœ… Health Checks β€” PostgreSQL + Redis connectivity monitoring
  • βœ… Environment Validation β€” envalid ensures all required config is present

πŸš€ Quick Start

Prerequisites

  • Docker & Docker Compose
  • Node.js 18+ (for local development)

Run with Docker (3 commands)

# 1. Clone & enter the project
git clone <your-repo-url> && cd idempotent-event-processor

# 2. Start all services
docker compose up -d

# 3. Open the dashboard
open http://localhost:5173

Run Locally (Development)

# Start infrastructure
docker compose up -d postgres redis redis-commander

# Backend
cd backend
npm install
npx prisma migrate dev
npm run dev

# Worker (separate terminal)
cd backend
npm run worker:dev

# Frontend (separate terminal)
cd frontend
npm install
npm run dev

πŸ“‘ API Endpoints

Method Endpoint Description
POST /api/v1/auth/register Register a new user
POST /api/v1/auth/login Login with email/password
GET /api/v1/auth/profile Get current user profile
POST /api/v1/events Ingest a new event (requires Idempotency-Key header)
GET /api/v1/events List events with filters & pagination
GET /api/v1/events/:id Get event details with retry history
POST /api/v1/events/:id/replay Replay an event with new idempotency key
GET /api/v1/events/stream Real-time SSE event stream
GET /api/v1/events/export Export events as CSV
GET /api/v1/dlq List dead letter events
POST /api/v1/dlq/:id/replay Replay a dead event
POST /api/v1/dlq/bulk-replay Bulk replay dead events
DELETE /api/v1/dlq/:id Discard a dead event
POST /api/v1/simulate/webhook Simulate duplicate webhooks
GET /api/v1/metrics System metrics & health
GET /api/v1/api-keys List API keys
POST /api/v1/api-keys Create new API key
GET /health Health check
GET /api-docs Swagger UI

πŸ”„ How Idempotency Works

sequenceDiagram
    participant C as Client
    participant A as API Server
    participant R as Redis Cache
    participant P as PostgreSQL
    participant Q as Queue

    C->>A: POST /events (Idempotency-Key: abc-123)
    A->>R: GET idempotency:abc-123
    R-->>A: null (not found)
    A->>P: Check idempotency_store
    P-->>A: null (not found)
    A->>P: INSERT event
    A->>Q: XADD to stream
    A->>R: SETEX idempotency:abc-123 (TTL: 24h)
    A->>P: INSERT idempotency_store
    A-->>C: 201 Created

    Note over C,Q: Same request arrives again...

    C->>A: POST /events (Idempotency-Key: abc-123)
    A->>R: GET idempotency:abc-123
    R-->>A: cached response βœ“
    A-->>C: 200 OK + X-Idempotent-Replayed: true
Loading

πŸ—„οΈ Database Schema

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚    users      β”‚     β”‚     events       β”‚     β”‚  retry_logs   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ id (UUID)     β”‚     β”‚ id (UUID)        │────→│ id (UUID)     β”‚
β”‚ email         β”‚     β”‚ idempotencyKey   β”‚     β”‚ eventId (FK)  β”‚
β”‚ passwordHash  β”‚     β”‚ type             β”‚     β”‚ attemptNumber β”‚
β”‚ name          β”‚     β”‚ payload (JSONB)  β”‚     β”‚ attemptedAt   β”‚
β”‚ createdAt     β”‚     β”‚ metadata (JSONB) β”‚     β”‚ error         β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚ status           β”‚     β”‚ nextRetryAt   β”‚
       β”‚             β”‚ priority         β”‚     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚             β”‚ retryCount       β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”     β”‚ processingTimeMs β”‚     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   api_keys   β”‚     β”‚ errorMessage     β”‚     β”‚    alerts     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€     β”‚ createdAt        β”‚     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ id (UUID)    β”‚     β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚ id (UUID)     β”‚
β”‚ userId (FK)  β”‚              β”‚               β”‚ eventId (FK)  β”‚
β”‚ name         β”‚     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”‚ type          β”‚
β”‚ keyHash      β”‚     β”‚idempotency_store β”‚     β”‚ message       β”‚
β”‚ rateLimit    β”‚     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€     β”‚ severity      β”‚
β”‚ expiresAt    β”‚     β”‚ key (PK)         β”‚     β”‚ createdAt     β”‚
β”‚ isActive     β”‚     β”‚ eventId (FK)     β”‚     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚ responsePayload  β”‚
                     β”‚ expiresAt        β”‚
                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“ Project Structure

β”œβ”€β”€ backend/
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ controllers/     # Request handlers
β”‚   β”‚   β”œβ”€β”€ handlers/        # Event type processors
β”‚   β”‚   β”œβ”€β”€ middleware/       # Auth, rate limit, idempotency
β”‚   β”‚   β”œβ”€β”€ routes/          # Express routes with Swagger docs
β”‚   β”‚   β”œβ”€β”€ services/        # Business logic (queue, lock, metrics)
β”‚   β”‚   β”œβ”€β”€ utils/           # Logger, Redis, Prisma, config
β”‚   β”‚   β”œβ”€β”€ app.js           # Express configuration
β”‚   β”‚   β”œβ”€β”€ server.js        # API server entry
β”‚   β”‚   └── worker.js        # Worker process entry
β”‚   β”œβ”€β”€ prisma/
β”‚   β”‚   └── schema.prisma    # Database schema
β”‚   └── Dockerfile
β”œβ”€β”€ frontend/
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ components/      # Layout, charts, UI
β”‚   β”‚   β”œβ”€β”€ hooks/           # useEvents, useMetrics, useSSE
β”‚   β”‚   β”œβ”€β”€ lib/             # API client, auth context, utils
β”‚   β”‚   └── pages/           # All 9 dashboard pages
β”‚   └── Dockerfile
β”œβ”€β”€ docker-compose.yml       # 6 services orchestration
β”œβ”€β”€ .env                     # Environment configuration
└── README.md

πŸ› οΈ Tech Stack

Layer Technology Purpose
Runtime Node.js 20 Server-side JavaScript
API Express.js 4 HTTP framework
Database PostgreSQL 16 Persistent event storage
Cache/Queue Redis 7 Caching, streams, locks, rate limiting
ORM Prisma Type-safe database access
Queue Redis Streams Priority-based event queuing
Auth JWT + bcrypt Token-based authentication
Validation Zod Runtime schema validation
Logging Winston Structured logging
Docs Swagger/OpenAPI Auto-generated API documentation
Frontend React 18 + Vite Dashboard UI
Styling Tailwind CSS 3 Utility-first CSS
Charts Recharts Data visualization
Animation Framer Motion Smooth UI transitions
Container Docker Compose Multi-service orchestration

πŸ“Š Services

Service Port Description
API Server 3000 Express.js backend
Worker β€” Background event processor
Frontend 5173 React dashboard
PostgreSQL 5432 Primary database
Redis 6379 Cache, queue, locks
Redis Commander 8081 Redis debug GUI

πŸ“„ License

MIT


Built with ❀️ as a portfolio project demonstrating distributed systems, event-driven architecture, and idempotent processing patterns.

About

Production-grade idempotent event processing system with Redis Streams, distributed locking, dead letter queue, and a real-time analytics dashboard. Built to handle duplicate events at scale.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages