Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 

README.md

Redis Driver for ObjectQL

Version 4.0.1 - Enhanced with production-ready features

Status: Production-ready with retry logic, connection pooling, and enhanced error handling.

Overview

The Redis Driver adapts Redis (a key-value store) to work with ObjectQL's universal data protocol. This driver has been enhanced from a reference implementation to a production-ready solution with automatic reconnection, retry logic, and improved error handling.

This driver implements both the legacy Driver interface from @objectql/types and the standard DriverInterface from @objectstack/spec for full compatibility with the new kernel-based plugin system.

Features

  • ✅ Basic CRUD operations (Create, Read, Update, Delete)
  • v4.0: executeQuery() with QueryAST support
  • v4.0: executeCommand() with unified command interface
  • v4.0: Bulk operations (bulkCreate, bulkUpdate, bulkDelete) using Redis PIPELINE
  • v4.0.1: Automatic reconnection with exponential backoff
  • v4.0.1: Configurable retry logic for connection attempts
  • v4.0.1: Enhanced error handling and logging
  • v4.0.1: Connection health checks with latency monitoring
  • ✅ Query filtering (in-memory)
  • ✅ Sorting (in-memory)
  • ✅ Pagination (skip/limit)
  • ✅ Count operations
  • ⚠️ Limited performance for complex queries (scans all keys)
  • ❌ No native aggregation support
  • ❌ No transaction support (planned for v4.1)
  • ❌ No schema introspection

Use Cases

This driver is suitable for:

  • Caching Layer: Store frequently accessed data with automatic reconnection
  • Session Storage: User sessions and temporary data with high availability
  • Simple Key-Value Storage: When you don't need complex queries
  • Development/Testing: Quick prototyping with production-like resilience
  • Microservices: Distributed systems requiring resilient Redis connections

Installation

npm install @objectql/driver-redis redis

Configuration

Basic Configuration

import { ObjectQL } from '@objectql/core';
import { RedisDriver } from '@objectql/driver-redis';

const driver = new RedisDriver({
  url: 'redis://localhost:6379'
});

const app = new ObjectQL({
  driver: driver
});

await app.init();

Production Configuration

const driver = new RedisDriver({
  url: 'redis://localhost:6379',
  
  // Redis client options
  options: {
    password: 'your-password',
    database: 0
  },
  
  // Connection retry configuration
  retry: {
    maxAttempts: 10,          // Maximum retry attempts (default: 10)
    initialDelay: 100,         // Initial delay in ms (default: 100)
    maxDelay: 3000,            // Maximum delay in ms (default: 3000)
    exponentialBackoff: true   // Use exponential backoff (default: true)
  },
  
  // Connection pool configuration
  pool: {
    min: 1,    // Minimum connections (default: 1)
    max: 10    // Maximum connections (default: 10)
  },
  
  // Enable automatic reconnection (default: true)
  autoReconnect: true
});

Basic Usage

// Create a record
const user = await app.create('users', {
  name: 'Alice',
  email: 'alice@example.com',
  role: 'admin'
});
console.log(user.id); // Auto-generated ID

// Find records
const users = await app.find('users', {
  filters: [['role', '=', 'admin']],
  sort: [['name', 'asc']],
  limit: 10
});

// Update a record
await app.update('users', user.id, {
  email: 'alice.new@example.com'
});

// Delete a record
await app.delete('users', user.id);

Performance Considerations

⚠️ Important Limitations

This Redis driver uses full key scanning for queries, which means:

  1. Find Operations: Scans ALL keys matching objectName:* pattern
  2. Filters: Applied in-memory after loading all records
  3. Count: Loads all records to count matches

Performance Impact

  • Small Datasets (< 1000 records): ✅ Acceptable
  • Medium Datasets (1K-10K records): ⚠️ Slow for complex queries
  • Large Datasets (> 10K records): ❌ Not recommended

Optimization Strategies

For production use, consider:

  1. Redis Modules: Use RedisJSON or RedisSearch for better query support
  2. Indexing: Implement secondary indexes using Redis Sets
  3. Hybrid Approach: Use Redis for caching, another driver for queries
  4. Sharding: Distribute data across multiple Redis instances

How Data is Stored

Records are stored as JSON strings with keys following the pattern:

objectName:id

Example:

users:user-123 → {"id":"user-123","name":"Alice","email":"alice@example.com","created_at":"2026-01-15T00:00:00.000Z"}

API Reference

Constructor

new RedisDriver(config: RedisDriverConfig)

Config Options:

  • url (string, required): Redis connection URL
  • options (object, optional): Additional Redis client options

Methods

All standard Driver interface methods are implemented:

Legacy Driver Interface:

  • find(objectName, query, options) - Query multiple records
  • findOne(objectName, id, query, options) - Get single record by ID
  • create(objectName, data, options) - Create new record
  • update(objectName, id, data, options) - Update existing record
  • delete(objectName, id, options) - Delete record
  • count(objectName, filters, options) - Count matching records
  • disconnect() - Close Redis connection

DriverInterface v4.0 Methods:

  • executeQuery(ast, options) - Execute queries using QueryAST format
  • executeCommand(command, options) - Execute commands (create, update, delete, bulk operations)

executeQuery Examples

The new executeQuery method uses the QueryAST format from @objectstack/spec:

// Basic query
const result = await driver.executeQuery({
  object: 'users',
  fields: ['name', 'email']
});
console.log(result.value); // Array of users
console.log(result.count); // Number of results

// Query with filters
const result = await driver.executeQuery({
  object: 'users',
  filters: {
    type: 'comparison',
    field: 'age',
    operator: '>',
    value: 18
  },
  sort: [{ field: 'name', order: 'asc' }],
  top: 10,
  skip: 0
});

// Complex filters (AND/OR)
const result = await driver.executeQuery({
  object: 'users',
  filters: {
    type: 'and',
    children: [
      { type: 'comparison', field: 'role', operator: '=', value: 'user' },
      { type: 'comparison', field: 'age', operator: '>', value: 30 }
    ]
  }
});

executeCommand Examples

The new executeCommand method provides a unified interface for mutations:

// Create a record
const result = await driver.executeCommand({
  type: 'create',
  object: 'users',
  data: { name: 'Alice', email: 'alice@example.com' }
});
console.log(result.success); // true
console.log(result.data); // Created user object
console.log(result.affected); // 1

// Update a record
const result = await driver.executeCommand({
  type: 'update',
  object: 'users',
  id: 'user-123',
  data: { email: 'newemail@example.com' }
});

// Delete a record
const result = await driver.executeCommand({
  type: 'delete',
  object: 'users',
  id: 'user-123'
});

// Bulk create (uses Redis PIPELINE for performance)
const result = await driver.executeCommand({
  type: 'bulkCreate',
  object: 'users',
  records: [
    { name: 'Alice', age: 30 },
    { name: 'Bob', age: 25 },
    { name: 'Charlie', age: 35 }
  ]
});
console.log(result.affected); // 3

// Bulk update
const result = await driver.executeCommand({
  type: 'bulkUpdate',
  object: 'users',
  updates: [
    { id: 'user-1', data: { age: 31 } },
    { id: 'user-2', data: { age: 26 } }
  ]
});

// Bulk delete
const result = await driver.executeCommand({
  type: 'bulkDelete',
  object: 'users',
  ids: ['user-1', 'user-2', 'user-3']
});

Example: Using as Cache Layer

Redis works great as a caching layer in front of another driver:

import { SqlDriver } from '@objectql/driver-sql';
import { RedisDriver } from '@objectql/driver-redis';

// Primary database
const sqlDriver = new SqlDriver({
  client: 'pg',
  connection: process.env.DATABASE_URL
});

// Cache layer
const redisDriver = new RedisDriver({
  url: process.env.REDIS_URL
});

// Use SQL for writes, Redis for cached reads
const app = new ObjectQL({
  datasources: {
    default: sqlDriver,
    cache: redisDriver
  }
});

Development

Building

pnpm build

Testing

# Start Redis
docker run -d -p 6379:6379 redis:latest

# Run tests
pnpm test

Project Structure

packages/drivers/redis/
├── src/
│   └── index.ts          # Main driver implementation
├── test/
│   └── index.test.ts     # Unit tests
├── package.json
├── tsconfig.json
├── jest.config.js
└── README.md

Extending This Driver

This is an example implementation. To make it production-ready:

  1. Add Redis Modules Support

    • RedisJSON for native JSON queries
    • RedisSearch for full-text search
  2. Implement Secondary Indexes

    • Use Redis Sets for indexed fields
    • Maintain index consistency
  3. Add Transaction Support

    • Use Redis MULTI/EXEC for atomic operations
  4. Optimize Queries

    • Avoid scanning all keys
    • Implement cursor-based pagination
  5. Add Connection Pooling

    • Handle connection failures
    • Implement retry logic

Related Documentation

License

MIT - Same as ObjectQL

Contributing

This is an example driver for educational purposes. For production Redis support:

  1. Fork this implementation
  2. Add production features (see "Extending This Driver")
  3. Publish as a community driver
  4. Share with the ObjectQL community

Questions?