Storage Backend Independence in Orbit-RS

Quick Answer

Yes, cloud storage (S3, Azure, GCP, MinIO) and local storage (LSM-Tree, RocksDB, B-Tree) are completely independent.

Each storage backend is a complete, self-contained persistence solution. You choose ONE backend type per deployment - they don’t layer or combine.

Architecture Overview

┌─────────────────────────────────────────────────────────┐
│                 Orbit-RS Framework                      │
│            (AddressableDirectoryProvider)               │
└┬────────────┬─────────────┬─────────────┬─────────────┬-┘
 │            │             │             │             │
 │   Cloud    │   Local     │   Local     │   Local     │
 │  (REST)    │  (Embedded) │ (Embedded)  │ (Embedded)  │
 │            │             │             │             │
 ▼            ▼             ▼             ▼             ▼
┌──────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌────────┐
│    S3    │ │ LSM-Tree  │ │  RocksDB  │ │ B+Tree+   │ │ Memory │
│ Provider │ │ Provider  │ │ Provider  │ │ WAL       │ │Provider│
│          │ │           │ │           │ │ Provider  │ │        │
│ REST API │ │ SSTables  │ │ Column    │ │ COW Nodes │ │ HashMap│
│ Objects  │ │ Memtables │ │ Families  │ │ WAL Files │ │ In-RAM │
└────┬─────┘ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ └───┬────┘
     │             │             │             │           │
 Internet      Local SSD     Local SSD     Local SSD    RAM
 (AWS S3)       Files         Files         Files      Only

Storage Backend Types

Cloud Backends (Object Storage)

Example: S3 stores actor leases as objects like:

s3://my-bucket/orbit/leases/UserActor/user-123

Local Backends (Embedded Storage)

Example: LSM-Tree stores data in local files:

/data/orbit_lsm_data/
├── memtable_001.sst
├── memtable_002.sst
└── orbit.wal

Code Evidence

S3 Provider - Direct REST API:

impl S3AddressableDirectoryProvider {
    async fn store_lease(&self, lease: &AddressableLease) -> OrbitResult<()> {
        let url = format!("{}/{}/{}", self.config.endpoint, self.config.bucket, key);
        let response = self.client.put(&url).body(data).send().await?;
        // ↑ Direct HTTP call to S3 - no local storage
    }
}

LSM-Tree Provider - Local data structures:

impl LsmTreeAddressableProvider {
    async fn store_lease(&self, lease: &AddressableLease) -> OrbitResult<()> {
        // Write to local WAL file
        self.wal.lock().await.append(&key, &value, "INSERT").await?;
        
        // Write to in-memory table
        self.active_memtable.write().unwrap().insert(key, value);
        
        // Manage local SSTables and compaction
        self.maybe_trigger_compaction().await?;
        // ↑ All operations on local files - no cloud involved
    }
}

Configuration Examples

You configure exactly one backend per Orbit-RS deployment:

Option 1: Pure Cloud (S3)

[server]
persistence_backend = "s3"

[persistence.s3]
endpoint = "https://s3.amazonaws.com"
bucket = "orbit-production-state"

# All data stored in S3 objects

Option 2: Pure Local (LSM-Tree)

[server]
persistence_backend = "lsm_tree"

[persistence.lsm_tree]
data_dir = "/ssd/orbit_data"
memtable_size_limit = 67108864  # 64MB

# All data stored in local LSM files

Option 3: Pure Local (RocksDB)

[server]
persistence_backend = "rocksdb"

[persistence.rocksdb]
data_dir = "/nvme/orbit_rocksdb"
enable_wal = true

# All data stored in RocksDB files

Factory Pattern

The factory.rs module shows this independence clearly:

pub async fn create_addressable_provider(
    config: &PersistenceConfig,
) -> OrbitResult<Arc<dyn AddressableDirectoryProvider>> {
    match config {
        // Each case creates a completely different provider
        PersistenceConfig::S3(s3_config) => {
            Ok(Arc::new(S3AddressableDirectoryProvider::new(s3_config.clone())))
        }
        PersistenceConfig::LsmTree(lsm_config) => {
            Ok(Arc::new(LsmTreeAddressableProvider::new(lsm_config.clone())))
        }
        PersistenceConfig::RocksDB(rocks_config) => {
            Ok(Arc::new(RocksDbAddressableProvider::new(rocks_config.clone())))
        }
        // Each provider is self-contained - no mixing
    }
}

Key Takeaways

Complete Independence: Cloud and local storage backends share no code or dependencies
Single Backend: Choose one backend type per deployment, not a combination
Self-Contained: Each backend handles all persistence operations internally
Different Protocols: Cloud uses HTTP REST, local uses direct file I/O
Different Formats: Cloud stores JSON objects, local uses optimized binary formats

No Layering: You cannot use S3 “on top of” RocksDB or vice versa
No Mixing: You cannot use multiple backends simultaneously
No Fallback: One backend choice per Orbit-RS instance

For detailed information, see the complete Virtual Actor Persistence guide.