Python API¶
MaruHandler¶
The main client interface for Maru. Handles storing, retrieving, and deleting KV cache entries in CXL shared memory via MaruServer.
from maru import MaruConfig, MaruHandler
Usage¶
config = MaruConfig(server_url="tcp://localhost:5555", pool_size=1024 * 1024 * 100)
with MaruHandler(config) as handler:
data = b"hello world"
# Store: alloc → write → register
handle = handler.alloc(size=len(data))
handle.buf[:len(data)] = data
handler.store(key=12345, handle=handle)
# Retrieve (zero-copy memoryview into CXL memory)
result = handler.retrieve(key=12345)
# Check existence
handler.exists(key=12345)
# Delete
handler.delete(key=12345)
Key Behaviors¶
Auto-connect: Connects automatically on init when
auto_connect=True(default)Auto-expand: Automatically allocates new memory regions when current ones are full
Deduplication: Skips store if the key already exists
Zero-copy retrieve: Returns memoryview pointing directly into mmap region
Thread-safe: Read operations (retrieve, exists) are lock-free; write operations (store, delete) are serialized by an internal write lock
Methods¶
- class maru_handler.MaruHandler(config: MaruConfig | None = None)[source]
Bases:
objectInitialize MaruHandler.
- Parameters:
config – Configuration object. If None, uses defaults.
- connect() bool[source]
Connect to the server and request a memory allocation.
- Returns:
True if successful
- close() None[source]
Close the connection and return all allocations.
Sets
_closingevent to reject new operations, then acquires_write_lockto wait for in-flight writes before teardown.
- alloc(size: int) AllocHandle[source]
Allocate a page and return a handle with a writable mmap memoryview.
The caller writes directly to
handle.buf, then passes the handle tostore(key, handle=handle)to register without copying.- Parameters:
size – Required bytes (must be <= chunk_size)
- Returns:
AllocHandle with writable memoryview
- Raises:
RuntimeError – If not connected or closing
ValueError – If size exceeds chunk_size or allocation fails
- free(handle: AllocHandle) None[source]
Free a page previously obtained via alloc().
Can be called before store() (discard) or after (eviction).
- Parameters:
handle – AllocHandle from alloc()
- Raises:
ValueError – If handle is not tracked (already freed or invalid)
- store(key: str, info: MemoryInfo | memoryview | None = None, prefix: bytes | None = None, *, data: memoryview | None = None, handle: AllocHandle | None = None) bool[source]
Store data to the KV cache.
If
handleis provided (zero-copy path), data is already written to the mmap region via alloc() and only register_kv is performed. Otherwise, allocate + memcpy + register are performed in one call.- Parameters:
key – The chunk key string
info – MemoryInfo or memoryview with data
prefix – Optional bytes to prepend (e.g., serialized metadata header)
data – memoryview with data (preferred, keyword-only)
handle – AllocHandle from alloc() for zero-copy store
- Returns:
True if successful
- retrieve(key: str) MemoryInfo | None[source]
Retrieve a zero-copy MemoryInfo from the KV cache.
Returns a MemoryInfo with a memoryview slice of the mmap region. Works for both owned (RW) and shared (RO) regions.
WARNING: The returned memoryview is only valid while the region remains mapped. Do not use after calling close().
- Parameters:
key – The chunk key string
- Returns:
MemoryInfo with memoryview, or None if not found
- exists(key: str) bool[source]
Check if a key exists.
- Parameters:
key – The chunk key string
- Returns:
True if exists
- delete(key: str) bool[source]
Delete a key and free the corresponding page.
- Parameters:
key – The chunk key string
- Returns:
True if deleted
- healthcheck() bool[source]
Check if the handler and MaruServer are healthy.
Verifies local connection state and sends a heartbeat RPC to confirm the MaruServer is responsive.
- Returns:
True if connected and server responded to heartbeat
- get_stats() dict[source]
Get server statistics.
- batch_retrieve(keys: list[str]) list[MemoryInfo | None][source]
Retrieve multiple values as MemoryInfo in batch.
Uses a single batch RPC call for lookup, returns zero-copy memoryview slices for both owned (RW) and shared (RO) regions.
WARNING: Returned memoryviews are only valid while regions remain mapped.
On RPC failure, allocated pages are freed but data already written to those pages is not zeroed. This is safe because the pages are never registered with the server and will be overwritten on reuse.
- Parameters:
keys – List of chunk key strings
- Returns:
List of MemoryInfo (None for keys not found)
- batch_store(keys: list[str], infos: list[MemoryInfo | memoryview], prefixes: list[bytes | None] | None = None) list[bool][source]
Store multiple key-value pairs in batch.
Uses a single batch RPC call for registration.
- Parameters:
keys – List of chunk key strings
infos – List of MemoryInfo or memoryview with data
prefixes – Optional list of prefix bytes per entry
- Returns:
List of booleans indicating success for each key
- batch_exists(keys: list[str]) list[bool][source]
Check if multiple keys exist.
Uses a single batch RPC call instead of N individual calls.
- Parameters:
keys – List of chunk key strings
- Returns:
List of booleans indicating existence for each key
- property pool_handle: _MockModule.MaruHandle | None
Get initial pool handle (backward compat).
- property allocator: PagedMemoryAllocator | None
Get the first region’s allocator (backward compat).
- property owned_region_manager: OwnedRegionManager | None
Get the owned region manager.
- property instance_id: str
Get instance ID.
- property connected: bool
Check if connected.
AllocHandle¶
Handle returned by alloc() for zero-copy writes. The caller writes data directly to buf (a writable memoryview into CXL shared memory), then passes the handle to store() to register the key.
# 1. Allocate a page
handle = handler.alloc(size=1024 * 1024)
# 2. Write directly to CXL memory (zero-copy)
handle.buf[:len(data)] = data
# 3. Register the key (only metadata is sent)
handler.store(key=42, handle=handle)
Property |
Type |
Description |
|---|---|---|
|
|
Writable view into the allocated CXL memory page |
|
|
Requested allocation size in bytes |
|
|
Region ID of the allocated page |
|
|
Page index within the region |
- class maru_handler.memory.AllocHandle(buf: memoryview, _region_id: int, _page_index: int, _size: int)[source]¶
Bases:
objectHandle returned by MaruHandler.alloc() for zero-copy writes.
Caller writes directly to
buf(an mmap memoryview), then passes this handle tostore(key, handle=handle)to register without copy.- property region_id: int¶
Region ID of the allocated page.
- property page_index: int¶
Page index within the region.
- property size: int¶
Requested allocation size in bytes.
MemoryInfo¶
Zero-copy view into CXL shared memory, returned by retrieve(). Wraps a memoryview pointing directly into an mmap’d CXL region.
result = handler.retrieve(key=42)
if result is not None:
# result.view is a memoryview into CXL shared memory
data = bytes(result.view)
length = len(result.view)
Property |
Type |
Description |
|---|---|---|
|
|
Zero-copy view into the CXL memory region (read-only for cross-instance retrieves) |
- class maru_handler.memory.MemoryInfo(view: memoryview)[source]¶
Bases:
objectZero-copy data descriptor using memoryview.
Interface type between LMCache (MemoryObj) and Maru (mmap region). Supports both RW and RO regions via a single memoryview field.
- PUT: connector creates MemoryInfo(view=memory_obj.byte_array)
handler writes via memoryview slice assignment (buf[off:off+n] = view)
- GET: handler returns MemoryInfo(view=memoryview_slice)
connector creates tensor via torch.frombuffer(info.view)
Data size is available via len(view) or view.nbytes.