Skip to content

Storage

Connexions uses a layered storage architecture that provides service isolation while sharing a single backend connection.

Design Principles

Shared Backend, Isolated Views

All services share a single storage backend (memory or Redis), but each service gets an isolated "view" into that storage through key prefixing. This means:

  • Single connection - One Redis client or memory store for the entire application
  • No cross-service access - Service A cannot read or modify Service B's data
  • Automatic namespacing - Keys are prefixed with the service name transparently

Lazy Resource Creation

Tables and history stores are created on first access, not upfront. This keeps memory usage low when services don't use all features.

TTL Support

Records can have individual expiration times:

  • Per-record TTL - Each Set() call can specify its own TTL
  • Zero means forever - A TTL of 0 means the record never expires
  • Lazy expiration (memory) - Expired records are deleted on access, not via background cleanup
  • Native TTL (Redis) - Uses Redis's built-in key expiration

Architecture

┌─────────────────────────────────────────────────────────┐
│                      Application                         │
├─────────────────────────────────────────────────────────┤
│  Service A           Service B           Service C      │
│  ┌─────────┐         ┌─────────┐         ┌─────────┐   │
│  │   DB    │         │   DB    │         │   DB    │   │
│  ├─────────┤         ├─────────┤         ├─────────┤   │
│  │ History │         │ History │         │ History │   │
│  │ Table() │         │ Table() │         │ Table() │   │
│  └────┬────┘         └────┬────┘         └────┬────┘   │
│       │                   │                   │         │
├───────┴───────────────────┴───────────────────┴─────────┤
│                      Storage                             │
│              (Memory or Redis backend)                   │
│                                                          │
│  Keys: "serviceA:history:GET:/users"                    │
│        "serviceB:cache:token123"                        │
│        "serviceC:history:POST:/orders"                  │
└─────────────────────────────────────────────────────────┘

Components

Storage

The shared backend that manages the actual data. Supports:

  • Memory - In-process storage, data lost on restart
  • Redis - Distributed storage, persists across restarts

DB

A service-scoped wrapper that provides:

  • History() - Typed access to request/response history
  • Table(name) - Generic key-value storage with TTL support
  • CircuitBreakerStore() - Shared circuit breaker state (see below)

Table

Generic key-value store with per-record TTL:

  • Get(ctx, key) - Retrieve a value (returns false if expired or missing)
  • Set(ctx, key, value, ttl) - Store with optional expiration
  • Delete(ctx, key) - Remove a key
  • Data(ctx) - Get all non-expired entries
  • Clear(ctx) - Remove all entries

HistoryTable

Typed wrapper for request/response tracking:

  • Stores request body, response data, status codes
  • Used by caching and upstream middleware
  • Cleared periodically based on historyDuration config

Circuit Breaker Store

Each service has its own circuit breaker, keyed by its upstream URL. The store provides:

  • Memory - Circuit breaker state is local to the process
  • Redis - Circuit breaker state is shared across all application instances

When running multiple instances of connexions behind a load balancer, Redis storage ensures that circuit breaker state (failure counts, open/closed state) is synchronized across all instances. This prevents one instance from continuing to hit a failing upstream while another has already tripped the breaker.

Choosing a Backend

Feature Memory Redis
Setup None Requires Redis server
Persistence No (lost on restart) Yes
Multi-instance No (each instance isolated) Yes (shared state)
Circuit breaker sharing Within process only Across all instances
Performance Fastest Network overhead

Use Memory when: - Running a single instance - Data loss on restart is acceptable - Simplicity is preferred

Use Redis when: - Running multiple instances behind a load balancer - Circuit breaker state must be shared - Request history should persist across restarts

Configuration

See App Configuration for setup options.

# Memory (default)
storage:
  type: memory

# Redis
storage:
  type: redis
  redis:
    address: localhost:6379
    password: ""
    db: 0