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​