README

#core/storage

Storage abstraction layer for persistence operations.

#Storage Trait

pub trait Storage {
read(Self, String, StorageKind) -> Bytes raise IOError
write(Self, String, Bytes, StorageKind) -> Unit raise IOError
append(Self, String, Bytes, StorageKind) -> Unit raise IOError
atomic_write(Self, String, Bytes, StorageKind) -> Unit raise IOError
del(Self, String, StorageKind) -> Unit raise IOError
exists(Self, String, StorageKind) -> Bool
list(Self, StorageKind) -> Array[String]
}

#Implementations

  • MemoryStorage: In-memory storage for testing
  • CallbackStorage: Delegates to JS callbacks for custom backends

#StorageKind

Routes data to appropriate backends:
  • Config: Collection configuration files
  • Index: Index structures and manifests
  • Data: Vector data and segments

#
AsyncStorage

pub(open) trait AsyncStorage {
fn async_read(Self, String, StorageKind, (Bytes) -> Unit, (String) -> Unit) -> Unit
fn async_write(Self, String, Bytes, StorageKind, () -> Unit, (String) -> Unit) -> Unit
fn async_atomic_write(Self, String, Bytes, StorageKind, () -> Unit, (String) -> Unit) -> Unit
fn async_del(Self, String, StorageKind, () -> Unit, (String) -> Unit) -> Unit
fn async_exists(Self, String, StorageKind, (Bool) -> Unit, (String) -> Unit) -> Unit
fn async_list(Self, StorageKind, (Array[String]) -> Unit, (String) -> Unit) -> Unit
}

Async storage trait — all targets. All operations return results via MoonBit's async/await mechanism. Implementors must call the provided continuation when I/O completes.

#
Storage

pub trait Storage {
fn read(Self, String, StorageKind) -> Bytes raise IOError
fn write(Self, String, Bytes, StorageKind) -> Unit raise IOError
fn append(Self, String, Bytes, StorageKind) -> Unit raise IOError
fn atomic_write(Self, String, Bytes, StorageKind) -> Unit raise IOError
fn del(Self, String, StorageKind) -> Unit raise IOError
fn exists(Self, String, StorageKind) -> Bool
fn list(Self, StorageKind) -> Array[String] raise IOError
}

Storage trait - abstraction for file-like storage operations kind parameter allows app to route different data types to different backends

#
AsyncIOError

pub(all) suberror AsyncIOError {
AsyncIOFailed(String)
}

Error type for async storage operations.

#
IOError

pub(all) suberror IOError {
NotFound(String)
PermissionDenied(String)
IoFailed(String)
}

IO Error types for storage operations

#
KindAwareStorage

pub struct KindAwareStorage {
config : Map[String, Bytes]
index : Map[String, Bytes]
data : Map[String, Bytes]
}

Kind-aware memory storage — TEST UTILITY.

Unlike MemoryStorage (which ignores kind), this stores data in separate maps per StorageKind, enabling tests to verify that operations route to the correct kind.

Not intended for production use. Prefer MemoryStorage for general in-memory usage or CallbackStorage for external backends.

#
KindAwareStorage::list_kind

fn KindAwareStorage::list_kind(self : KindAwareStorage, kind : StorageKind) -> Array[String]

List files stored under a specific kind

#
KindAwareStorage::new

#
MemoryStorage

pub struct MemoryStorage {
data : Map[String, Bytes]
}

Memory-based storage implementation for testing and in-memory use cases

#
MemoryStorage::append

fn MemoryStorage::append(self : MemoryStorage, path : String, data : Bytes) -> Unit

Append data to file

#
MemoryStorage::atomic_write

fn MemoryStorage::atomic_write(self : MemoryStorage, path : String, data : Bytes) -> Unit

Atomic write (same as write for in-memory implementation)

#
MemoryStorage::clear

fn MemoryStorage::clear(self : MemoryStorage) -> Unit

Clear all files

#
MemoryStorage::del

fn MemoryStorage::del(self : MemoryStorage, path : String) -> Unit raise IOError

Delete a file

#
MemoryStorage::exists

fn MemoryStorage::exists(self : MemoryStorage, path : String) -> Bool

Check if a file exists

#
MemoryStorage::list

fn MemoryStorage::list(self : MemoryStorage) -> Array[String]

List all files

#
MemoryStorage::new

Create a new empty MemoryStorage

#
MemoryStorage::read

fn MemoryStorage::read(self : MemoryStorage, path : String) -> Bytes raise IOError

Read file contents

#
MemoryStorage::write

fn MemoryStorage::write(self : MemoryStorage, path : String, data : Bytes) -> Unit

Write file contents (overwrites)

#
NativeFileStorage

pub struct NativeFileStorage {
root_dir : String
}

#
NativeFileStorage::append

fn NativeFileStorage::append(self : NativeFileStorage, path : String, data : Bytes, kind : StorageKind) -> Unit raise IOError

#
NativeFileStorage::atomic_write

fn NativeFileStorage::atomic_write(self : NativeFileStorage, path : String, data : Bytes, kind : StorageKind) -> Unit raise IOError

#
NativeFileStorage::del

fn NativeFileStorage::del(self : NativeFileStorage, path : String, kind : StorageKind) -> Unit raise IOError

#
NativeFileStorage::exists

fn NativeFileStorage::exists(self : NativeFileStorage, path : String, kind : StorageKind) -> Bool

#
NativeFileStorage::list

fn NativeFileStorage::list(self : NativeFileStorage, kind : StorageKind) -> Array[String] raise IOError

#
NativeFileStorage::new

fn NativeFileStorage::new(root_dir : String) -> NativeFileStorage

#
NativeFileStorage::read

fn NativeFileStorage::read(self : NativeFileStorage, path : String, kind : StorageKind) -> Bytes raise IOError

#
NativeFileStorage::write

fn NativeFileStorage::write(self : NativeFileStorage, path : String, data : Bytes, kind : StorageKind) -> Unit raise IOError

#
ReplicatedStorage

pub struct ReplicatedStorage[T] {
primary : T
replicas : Array[T]
config : ReplicationConfig
}

Replicated storage backend.

T is the underlying AsyncStorage implementation (e.g., JsAsyncCallbackStorage, MemoryStorage, NativeFileStorage).

Invariants:
  • Primary always receives every operation.
  • Replicas receive write/del operations only.
  • Reads go to primary only (read-local semantics).
  • write_quorum <= 1 + replicas.length()

#
ReplicatedStorage::backend_count

fn[T] ReplicatedStorage::backend_count(self : ReplicatedStorage[T]) -> Int

Number of total backends (primary + replicas).

#
ReplicatedStorage::local_only

fn[T] ReplicatedStorage::local_only(storage : T) -> ReplicatedStorage[T]

Create a replicated storage with no replicas (passthrough).

#
ReplicatedStorage::new

fn[T] ReplicatedStorage::new(primary : T, replicas : Array[T], config? : ReplicationConfig) -> ReplicatedStorage[T]

Create a replicated storage with replicas and custom config.

#
ReplicatedStorage::with_all_ack

fn[T] ReplicatedStorage::with_all_ack(primary : T, replicas : Array[T]) -> ReplicatedStorage[T]

Create a replicated storage requiring all backends to ack.

#
ReplicationConfig

pub(all) struct ReplicationConfig {
write_quorum : Int
}

Replication configuration for a single storage backend.

#
ReplicationConfig::all

fn ReplicationConfig::all(replica_count : Int) -> ReplicationConfig

Create a strict replication config requiring all replicas to ack.

#
ReplicationConfig::default

#
ReplicationConfig::majority

fn ReplicationConfig::majority(replica_count : Int) -> ReplicationConfig

Create a majority quorum config.

#
StorageKind

pub(all) enum StorageKind {
Config
Index
Data
}

Storage kind - tells app what type of data is being stored App uses this to route to appropriate backend (e.g., index→DynamoDB, data→S3)

#
async_atomic_write

async fn[S : AsyncStorage] async_atomic_write(storage : S, path : String, data : Bytes, kind : StorageKind) -> Unit

#
async_del

async fn[S : AsyncStorage] async_del(storage : S, path : String, kind : StorageKind) -> Unit

#
async_exists

async fn[S : AsyncStorage] async_exists(storage : S, path : String, kind : StorageKind) -> Bool

#
async_list

async fn[S : AsyncStorage] async_list(storage : S, kind : StorageKind) -> Array[String]

#
async_read

async fn[S : AsyncStorage] async_read(storage : S, path : String, kind : StorageKind) -> Bytes

#
async_write

async fn[S : AsyncStorage] async_write(storage : S, path : String, data : Bytes, kind : StorageKind) -> Unit

#
collection_config_path

fn collection_config_path(collection_name : String) -> String

Path for collection config file

#
collection_data_path

fn collection_data_path(collection_name : String) -> String

Path for collection data (non-distributed)

#
collection_index_path

fn collection_index_path(collection_name : String) -> String

Path for collection index file (distributed)

#
collection_manifest_path

fn collection_manifest_path(collection_name : String) -> String

Path for collection manifest file (distributed)

#
collection_wal_path

fn collection_wal_path(collection_name : String) -> String

Path for collection WAL file

#
ext_config

let ext_config : String

Config file extension (not part of binary format, defined here)

#
ext_data

let ext_data : String

Data file extension (non-distributed, defined here)

#
is_segment_path

fn is_segment_path(path : String, base_name : String) -> Bool

Check if a path is a segment file for a given collection

#
segment_data_path

fn segment_data_path(base : String, pg : Int, part : Int) -> String

Path for data segment file