Skip to main content

Commands Overview

SierraDB implements the Redis RESP3 protocol, making it compatible with existing Redis clients while providing specialized commands for event sourcing.

Protocol Compatibility

SierraDB uses the RESP3 (Redis Serialization Protocol version 3) for client communication, which means:

  • Use existing Redis clients - No custom drivers required
  • Familiar Redis patterns - Similar connection and command patterns
  • Protocol efficiency - Optimized binary protocol

Connection

Connect to SierraDB like any Redis instance:

# redis-cli
redis-cli -p 9090

# Specify RESP3 protocol explicitly
redis-cli -p 9090 --resp3

Command Categories

SierraDB commands are organized into several categories:

Core Event Operations

  • EAPPEND - Append event to stream
  • EMAPPEND - Multi-stream transactional append
  • EGET - Get event by ID
  • ESCAN - Scan stream events
  • ESVER - Get stream version

Subscriptions

  • ESUB - Subscribe to stream events

Partition Operations

  • EPSCAN - Scan partition events
  • EPSEQ - Get partition sequence

System Operations

  • HELLO - Protocol handshake
  • PING - Health check
  • INFO - Server statistics

Response Format

SierraDB uses standard RESP3 response types:

Success Responses

# Simple strings
+OK

# Integers
:42

# Bulk strings
$12
Hello world!

# Arrays
*3
$5
event
$7
payload
:1234

Error Responses

# Error format
-ERROR_TYPE Error message details

Common error types:

  • WRONG_EXPECTED_VERSION - Optimistic concurrency conflict
  • STREAM_NOT_FOUND - Stream doesn't exist
  • INVALID_PARTITION - Partition ID out of range
  • SYNTAX_ERROR - Command syntax error

Data Types

Event Structure

Events in SierraDB are returned as RESP3 maps with this structure:

{
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"partition_key": "550e8400-e29b-41d4-a716-446655440001",
"partition_id": 5,
"transaction_id": "550e8400-e29b-41d4-a716-446655440002",
"partition_sequence": 123,
"stream_version": 0,
"timestamp": 1697641800000,
"stream_id": "user-123",
"event_name": "UserRegistered",
"metadata": "{\"source\":\"api\",\"correlation_id\":\"abc-123\"}",
"payload": "{\"email\":\"[email protected]\",\"name\":\"Alice Johnson\"}"
}

Field Descriptions:

  • event_id: Unique identifier for this event (UUID)
  • partition_key: Key used to determine partition placement (UUID)
  • partition_id: Partition where this event is stored (integer)
  • transaction_id: Identifier linking events in the same transaction (UUID)
  • partition_sequence: Monotonic sequence number within the partition (integer, 0-based)
  • stream_version: Version of this event within its stream (integer, 0-based)
  • timestamp: Unix timestamp in milliseconds (integer)
  • stream_id: Identifier for the event stream (string)
  • event_name: Type/name of the event (string)
  • metadata: JSON string containing event metadata
  • payload: JSON string containing event data

Stream ID Format

Stream IDs are UTF-8 strings with these constraints:

  • Maximum length: 64 bytes
  • Cannot be empty
  • Should be meaningful (e.g., user-123, order-456)

Event Names

Event names follow these conventions:

  • Past tense verbs (e.g., UserRegistered, OrderShipped)
  • CamelCase or kebab-case
  • Descriptive and domain-specific

UUIDs

Event IDs and partition keys use UUID format:

  • Version 4 (random) UUIDs recommended
  • Standard format: 550e8400-e29b-41d4-a716-446655440000
  • Case insensitive

Client Examples

Python

import redis
import json
import uuid

# Connect with RESP3
client = redis.Redis(host='localhost', port=9090, protocol=3)

# Append an event
event_id = str(uuid.uuid4())
result = client.execute_command(
'EAPPEND', 'user-123', 'UserRegistered',
'EVENT_ID', event_id,
'PAYLOAD', json.dumps({'email': '[email protected]'}),
'METADATA', json.dumps({'source': 'api'})
)

print(f"Event stored at version {result}")

# Read events
events = client.execute_command('ESCAN', 'user-123', '-', '+')
for event in events['events']: # RESP3 returns a map with 'events' array
event_name = event['event_name']
version = event['stream_version']
payload = json.loads(event['payload']) # payload is a JSON string
print(f"Event: {event_name} at version {version}")

JavaScript (Node.js)

const redis = require('redis');

const client = redis.createClient({
socket: { host: 'localhost', port: 9090 },
RESP: 3
});

await client.connect();

// Append event
const result = await client.sendCommand([
'EAPPEND', 'order-456', 'OrderCreated',
'PAYLOAD', JSON.stringify({total: 99.99, items: ['laptop']})
]);

console.log(`Event stored at version ${result}`);

// Read events
const events = await client.sendCommand(['ESCAN', 'order-456', '-', '+']);
console.log('Events:', events);

await client.disconnect();

Rust

use redis::{Commands, Connection};
use serde_json::json;

fn main() -> redis::RedisResult<()> {
let client = redis::Client::open("redis://127.0.0.1:9090/")?;
let mut con = client.get_connection()?;

// Append event
let version: i64 = redis::cmd("EAPPEND")
.arg("user-123")
.arg("UserRegistered")
.arg("PAYLOAD")
.arg(json!({"email": "[email protected]"}).to_string())
.query(&mut con)?;

println!("Event stored at version {}", version);

// Read events
let events: Vec<Vec<String>> = redis::cmd("ESCAN")
.arg("user-123")
.arg("-")
.arg("+")
.query(&mut con)?;

// Note: Rust redis client handling of RESP3 maps may vary
// This is a simplified example - actual implementation depends on the client
for event in events {
println!("Event data: {:?}", event);
}

Ok(())
}

Go

package main

import (
"context"
"encoding/json"
"fmt"
"github.com/go-redis/redis/v8"
)

func main() {
ctx := context.Background()

rdb := redis.NewClient(&redis.Options{
Addr: "localhost:9090",
Protocol: 3,
})

// Append event
payload, _ := json.Marshal(map[string]interface{}{
"email": "[email protected]",
"name": "John Doe",
})

result := rdb.Do(ctx, "EAPPEND", "user-123", "UserRegistered",
"PAYLOAD", string(payload))
fmt.Printf("Event stored at version %v\n", result.Val())

// Read events
events := rdb.Do(ctx, "ESCAN", "user-123", "-", "+")
fmt.Printf("Events: %v\n", events.Val())
}

Error Handling

Optimistic Concurrency

Handle version conflicts gracefully:

def append_with_retry(stream_id, event_name, payload, max_retries=3):
for attempt in range(max_retries):
try:
# Get current version
current_version = client.execute_command('ESVER', stream_id)

# Append with expected version
result = client.execute_command(
'EAPPEND', stream_id, event_name,
'EXPECTED_VERSION', current_version,
'PAYLOAD', payload
)
return result

except redis.ResponseError as e:
if 'WRONG_EXPECTED_VERSION' in str(e) and attempt < max_retries - 1:
# Retry with updated version
continue
raise

raise Exception("Failed to append after retries")

Connection Handling

import redis
import time

def create_resilient_client():
return redis.Redis(
host='localhost',
port=9090,
protocol=3,
retry_on_timeout=True,
socket_keepalive=True,
socket_keepalive_options={},
health_check_interval=30
)

def execute_with_retry(client, command, *args, max_retries=3):
for attempt in range(max_retries):
try:
return client.execute_command(command, *args)
except redis.ConnectionError:
if attempt < max_retries - 1:
time.sleep(2 ** attempt) # Exponential backoff
continue
raise

Performance Considerations

Batching

For high throughput, consider batching operations:

# Use pipelines for multiple commands
pipe = client.pipeline()
for i in range(1000):
pipe.execute_command('EAPPEND', f'stream-{i}', 'EventType',
'PAYLOAD', f'{{"data": {i}}}')
results = pipe.execute()

Connection Pooling

Use connection pools for concurrent access:

pool = redis.ConnectionPool(host='localhost', port=9090, protocol=3, max_connections=20)
client = redis.Redis(connection_pool=pool)

Command Reference