Skip to content

deep-awasthi/FeatureMesh

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FeatureMesh

A production-grade, cloud-native feature flag management platform.

FeatureMesh lets engineering teams ship code continuously and control what's visible to whom — without deployments. Define flags, target specific user segments, roll out to a percentage of traffic, track every change in an audit log, and push updates to SDK clients in real time over Kafka and SSE.

Java 21 Spring Boot 3.x License: MIT


Why FeatureMesh?

Most feature flag systems are either too simple (environment variables, config files) or too expensive (LaunchDarkly, Split). FeatureMesh is self-hosted, built on a fully reactive stack, and gives you the same core primitives used at scale:

  • Targeting rules — evaluate flags against user attributes (country, userId, plan, any context key)
  • Percentage rollouts — canary-deploy to N% of traffic, increase gradually
  • Multi-environment supportdevelopment, staging, production flag states are independent
  • Real-time push — flag changes propagate to SDK clients via Kafka + SSE in under a second
  • Audit history — every flag mutation is logged with who changed what and when
  • Sub-millisecond evaluation — Redis-cached flag definitions serve hot-path evaluations without touching the database

Architecture

┌──────────────────────────────────────────────────────────┐
│                   FeatureMesh API  :8080                  │
│                                                           │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐  │
│  │  Auth       │  │  Flag Admin │  │  Evaluation     │  │
│  │  /auth/**   │  │  /flags/**  │  │  /evaluate      │  │
│  └─────────────┘  └──────┬──────┘  └────────┬────────┘  │
│                           │                  │            │
│              ┌────────────┘      ┌───────────┘            │
│              ▼                   ▼                        │
│       ┌────────────┐     ┌───────────────┐                │
│       │ PostgreSQL │     │  Redis Cache  │                │
│       │  (R2DBC)   │◄────│  (L1 Store)   │                │
│       └────────────┘     └───────────────┘                │
└────────────────────────────────┬─────────────────────────┘
                                 │ flag.updated event
                                 ▼
                        ┌────────────────┐
                        │  Kafka Broker  │
                        │  (Zookeeper)   │
                        └───────┬────────┘
                                │ broadcast
                    ┌───────────┴──────────┐
                    ▼                      ▼
          ┌─────────────────┐    ┌──────────────────┐
          │  SDK Simulator  │    │  Any SDK Client  │
          │  :8084  (SSE)   │    │  (poll or SSE)   │
          └─────────────────┘    └──────────────────┘

How flag evaluation works

Every evaluation request goes through a two-layer resolution path:

POST /api/v1/evaluate
        │
        ▼
  Redis cache hit?
  ┌────────────────────────────────┐
  │  YES  →  return cached result  │  sub-millisecond
  └────────────────────────────────┘
        │ NO (cache miss or expired)
        ▼
  R2DBC → PostgreSQL (non-blocking read)
        │
        ▼
  Flag enabled for this environment?
        │ NO  →  return default value
        │ YES
        ▼
  Targeting rules match (context attributes)?
        │ NO  →  fall through to rollout
        │ YES →  return targeted value
        ▼
  Percentage rollout check (hash userId → bucket)
        │
        ▼
  Return evaluated result + write-back to Redis

Targeting rules are evaluated in priority order. The first matching rule wins. If no rule matches, the rollout percentage check determines the result.


Technology Stack

Concern Technology
Language Java 21
Framework Spring Boot 3.x, Spring WebFlux
Database PostgreSQL + Spring Data R2DBC
Cache Redis (flag definitions + evaluations)
Messaging Apache Kafka + Zookeeper
Auth JWT (stateless)
API Docs SpringDoc OpenAPI / Swagger UI
Client push SSE (Server-Sent Events) + Polling
Observability Prometheus + Grafana
Containerization Docker + Docker Compose

R2DBC (Reactive Relational Database Connectivity) is used throughout — the entire read/write path to PostgreSQL is non-blocking, allowing the API to handle high evaluation throughput on a small thread pool using Project Reactor.


Project Structure

FeatureMesh/
├── docker/
│   └── docker-compose.yml         # PostgreSQL, Redis, Kafka, Prometheus, Grafana
├── docs/
│   └── architecture.md            # Sequence diagrams, ER diagram, flow details
├── featuremesh/
│   ├── featuremesh-api/           # Core platform API (port 8080)
│   │   └── src/main/java/
│   │       ├── auth/              # JWT registration + login
│   │       ├── flag/              # Flag CRUD, targeting rules, rollout config
│   │       ├── evaluation/        # Flag evaluation engine (Redis → R2DBC)
│   │       ├── audit/             # Immutable audit log
│   │       ├── telemetry/         # Evaluation metrics and telemetry
│   │       └── kafka/             # Flag change event publisher
│   └── featuremesh-sdk-simulator/ # SDK client simulator (port 8084)
│       └── src/main/java/
│           ├── polling/           # Periodic flag state polling
│           └── sse/               # SSE subscriber for real-time push
├── requests.http                  # Full API test suite (IntelliJ / VS Code REST Client)
└── README.md

Quick Start

Prerequisites

  • Docker Desktop running
  • JDK 21 + Maven 3.9+ (for local dev runs)

1. Start infrastructure

Boot PostgreSQL, Redis, Kafka, Prometheus, and Grafana with a single command:

cd docker
docker-compose up -d

Services started:

Service URL / Port
PostgreSQL localhost:5432
Redis localhost:6379
Kafka localhost:9092
Prometheus localhost:9090
Grafana localhost:3000

2. Run the API server

cd featuremesh/featuremesh-api
../../mvnw spring-boot:run

The API starts on http://localhost:8080.

  • Swagger UI: http://localhost:8080/swagger-ui.html
  • OpenAPI JSON: http://localhost:8080/v3/api-docs
  • Health check: http://localhost:8080/actuator/health
  • Prometheus metrics: http://localhost:8080/actuator/prometheus

3. Run the SDK simulator

In a separate terminal:

cd featuremesh/featuremesh-sdk-simulator
../../mvnw spring-boot:run

The simulator starts on http://localhost:8084. It connects to the API via SSE for real-time flag push and demonstrates the polling fallback pattern.


API Reference

Authentication

Register

POST http://localhost:8080/api/v1/auth/register
Content-Type: application/json

{
  "username": "developer1",
  "email": "dev1@featuremesh.com",
  "password": "securepassword123",
  "role": "DEVELOPER"
}

Login

POST http://localhost:8080/api/v1/auth/login
Content-Type: application/json

{
  "username": "developer1",
  "password": "securepassword123"
}

Returns { "accessToken": "eyJ..." }. Pass this as Authorization: Bearer <token> on all flag administration endpoints.


Flag Administration

All flag endpoints require Authorization: Bearer <JWT_ACCESS_TOKEN>.

Create a flag

POST http://localhost:8080/api/v1/flags
Authorization: Bearer <JWT_ACCESS_TOKEN>
Content-Type: application/json

{
  "key": "new-checkout",
  "name": "New Checkout System",
  "description": "Enables the React-based Stripe payment checkout gateway.",
  "type": "BOOLEAN",
  "enabled": true,
  "environmentSlug": "development",
  "targetingRules": [
    {
      "attribute": "country",
      "operator": "EQUALS",
      "value": "IN",
      "priority": 1
    }
  ],
  "rolloutRule": {
    "percentage": 50
  }
}

Field reference:

Field Description
key Unique slug used to evaluate the flag. Immutable after creation.
type BOOLEAN, STRING, NUMBER, or JSON
environmentSlug Isolates flag state per environment (development, staging, production)
targetingRules Ordered list of attribute-based rules. First match wins.
rolloutRule Percentage of unmatched traffic that sees the flag as enabled (0–100)

Targeting rule operators

Operator Meaning
EQUALS Exact match
NOT_EQUALS Inverse match
CONTAINS Substring / set membership
STARTS_WITH String prefix match
IN Value exists in a list

Flag Evaluation

This is the hot-path endpoint called by your application at runtime. No auth required — intended for server-side SDK use.

POST http://localhost:8080/api/v1/evaluate
Content-Type: application/json

{
  "flagKey": "new-checkout",
  "environmentSlug": "development",
  "userId": "user-abc-456",
  "context": {
    "country": "IN"
  }
}

Response:

{
  "flagKey": "new-checkout",
  "value": true,
  "reason": "TARGETING_RULE_MATCH",
  "ruleId": "rule-001"
}

Evaluation reasons:

Reason What happened
TARGETING_RULE_MATCH A targeting rule matched the request context
ROLLOUT_INCLUDED No rule matched; userId hashed into the rollout bucket
ROLLOUT_EXCLUDED No rule matched; userId outside the rollout percentage
FLAG_DISABLED Flag is disabled for this environment
FLAG_NOT_FOUND No flag exists for this key + environment combination

Real-Time Flag Push (Kafka + SSE)

When a flag is created or updated, FeatureMesh publishes a flag.updated event to a Kafka topic. The SDK simulator subscribes and receives updates in real time via Server-Sent Events:

Admin updates flag "new-checkout" rollout from 50% → 80%
         │
         ▼
  API publishes event to Kafka topic: featuremesh.flag.updated
         │
         ▼
  SDK simulator SSE subscriber receives event
         │
         ▼
  Local flag definition cache invalidated + refreshed
         │
         ▼
  Next evaluation call uses updated rollout rule immediately

The simulator also demonstrates a polling fallback — if the SSE connection drops, it falls back to periodic polling at a configured interval, ensuring flag state remains consistent without requiring a persistent connection.


Configuration

All infrastructure connection settings live in featuremesh-api/src/main/resources/application.yml:

spring:
  r2dbc:
    url: r2dbc:postgresql://localhost:5432/featuremeshdb
    username: featuremesh
    password: featuremesh

  data:
    redis:
      host: localhost
      port: 6379

  kafka:
    bootstrap-servers: localhost:9092
    consumer:
      group-id: featuremesh-group

featuremesh:
  cache:
    flag-ttl-seconds: 60       # Redis TTL for cached flag definitions
  evaluation:
    cache-results: true        # Whether to cache individual evaluation results

Observability

Each service exposes metrics via Micrometer at /actuator/prometheus. Pre-built Grafana dashboards are provisioned automatically on startup.

Key metrics tracked:

Metric Description
featuremesh_evaluations_total Total evaluations by flag key and reason
featuremesh_cache_hits_total Redis cache hit rate for flag definitions
featuremesh_flag_updates_total Total flag mutations published to Kafka
featuremesh_evaluation_latency P50/P95/P99 evaluation response time (ms)

Access Grafana at http://localhost:3000 (default credentials: admin / admin).


Running Tests

cd featuremesh
./mvnw test

For a full build:

./mvnw clean compile
./mvnw test

End-to-end API flows are covered in requests.http at the project root. Open it in IntelliJ IDEA or VS Code (REST Client extension) and run the requests sequentially: register → login → create flag → evaluate.


License

MIT

About

Distributed feature flag management platform

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages