Scalable Objects Persistence (SOP) V2 for Python. General Public Availability (GPA) Release
Project description
SOP for Python (sop4py)
Scalable Objects Persistence (SOP) is a high-performance, transactional storage engine for Python, powered by a robust Go backend. It combines the raw speed of direct disk I/O with the reliability of ACID transactions and the flexibility of modern AI data management.
Key Features
- Unified Database: Single entry point for managing Vector, Model, and Key-Value stores.
- Transactional B-Tree Store: Unlimited, persistent B-Tree storage for key-value data.
- Complex Keys: Support for composite keys (structs/dataclasses) with custom index specifications (e.g., Region -> Dept -> ID).
- Metadata "Ride-on" Keys: Store metadata directly in the B-Tree key (e.g., timestamps, status flags) to enable high-speed scanning and filtering of millions of records without fetching the heavy value payload. Ideal for "Big Data" management and analytics.
- Vector Database: Built-in vector search (k-NN) for AI embeddings and similarity search.
- Text Search: Transactional, embedded text search engine (BM25).
- AI Model Store: Versioned storage for machine learning models (B-Tree backed).
- ACID Compliance: Full transaction support (Begin, Commit, Rollback) with isolation.
- High Performance: Written in Go with a lightweight Python wrapper (ctypes).
- Caching: Integrated Redis-backed L1/L2 caching for speed.
- Replication: Optional Erasure Coding (EC) for fault-tolerant storage across drives.
- Multi-Tenancy: Native support for Cassandra Keyspaces or Directory-based isolation.
- Flexible Deployment: Supports both Standalone (local) and Clustered (distributed) modes.
Performance & Big Data Management
SOP is designed for high-throughput, low-latency scenarios, making it suitable for "Big Data" management on commodity hardware.
- "Ride-on" Metadata: By embedding metadata (like
IsDeleted,LastUpdated,Category) directly into the Key struct but excluding it from the index (usingIndexSpecification), you can scan millions of keys per second to filter data. This avoids the I/O penalty of fetching the full Value (which might be a large JSON blob or binary file) just to check a status flag. - Direct I/O: SOP bypasses OS page caches where appropriate to offer consistent, raw disk performance.
- Parallelism: The underlying Go engine utilizes highly concurrent goroutines for managing B-Tree nodes and vector indexes.
Documentation
- API Cookbook: Common recipes and patterns (Key-Value, Transactions, AI).
- Examples: Complete runnable scripts.
Installation
Install directly from PyPI:
pip install sop4py
Data Browser (GUI)
SOP comes with a built-in web-based Data Browser to inspect your B-Trees, search keys, and view data.
To launch it, simply run:
sop-browser
- Automatic Setup: The first time you run it, the tool will automatically download the correct binary for your OS/Arch.
- Usage: By default, it opens on
http://localhost:8080. - Arguments: You can pass standard flags, e.g.,
sop-browser -port 9090 -registry ./my_data.
Prerequisites
- Redis: Required for caching and transaction coordination (especially in Clustered mode). Note: Redis is NOT used for data storage, just for coordination & to offer built-in caching.
Running the Examples
SOP comes with a bundled CLI tool sop-demo to easily list and run examples directly from your installation.
List available examples:
sop-demo list
Run a specific example:
sop-demo run vector_demo
Copy examples to your workspace: If you want to inspect the code or modify the examples, you can copy them to your local directory:
sop-demo copy
# Copies to ./sop_examples/
Manual Execution: If you have copied the examples locally, you can also run them using python directly:
python3 sop_examples/concurrent_demo.py
Concurrent Transactions (Standalone): This demo shows how to run concurrent transactions without a Redis dependency. It simulates real-world scenarios by introducing a small random sleep interval (jitter) between batch transactions to mimic network latency and reduce contention.
sop-demo run concurrent_demo_standalone
Concurrent Transactions (Clustered): This demo shows how to run concurrent transactions in a distributed environment (requires Redis). Similar to the standalone demo, it uses jitter to simulate realistic commit timing across different machines in a cluster.
sop-demo run concurrent_demo
Vector Search:
sop-demo run vector_demo
See the examples/ directory for more scripts.
```
- Set PYTHONPATH:
export PYTHONPATH=$PYTHONPATH:$(pwd)/jsondb/python
Quick Start Guide
SOP uses a unified Database object to manage all types of stores (Vector, Model, and B-Tree). All operations are performed within a Transaction.
1. Initialize Database & Context
First, create a Context and open a Database connection.
from sop import Context, TransactionMode, TransactionOptions, Btree, BtreeOptions, Item
from sop.ai import Database, DatabaseType, Item as VectorItem
from sop.database import DatabaseOptions
# Initialize Context
ctx = Context()
# Open Database (Standalone Mode)
# This creates/opens a database at the specified path.
db = Database(DatabaseOptions(stores_folders=["data/my_db"], type=DatabaseType.Standalone))
# Open Database (Clustered Mode with Multi-Tenancy)
# Connects to a specific Cassandra Keyspace ("tenant_1").
# Requires Cassandra and Redis.
# db_clustered = Database(DatabaseOptions(stores_folders=["data/blobs"], keyspace="tenant_1", type=DatabaseType.Clustered))
2. Start a Transaction
All data operations (Create, Read, Update, Delete) must happen within a transaction.
# Begin a transaction (Read-Write)
# You can use 'with' block for auto-commit/rollback, or manage manually.
with db.begin_transaction(ctx) as tx:
# --- 3. Vector Store (AI) ---
# Open a Vector Store named "products"
vector_store = db.open_vector_store(ctx, tx, "products")
# Upsert a Vector Item
vector_store.upsert(ctx, VectorItem(
id="prod_101",
vector=[0.1, 0.5, 0.9],
payload={"name": "Laptop", "price": 999}
))
# --- 4. Model Store (AI) ---
# Open a Model Store named "classifiers"
model_store = db.open_model_store(ctx, tx, "classifiers")
# Save a Model
model_store.save(ctx, "churn", "v1.0", {
"algorithm": "random_forest",
"trees": 100
})
# --- 5. B-Tree Store (Key-Value) ---
# Open a B-Tree named "users"
# Use new_btree to create a new store, or open_btree for existing ones.
# BtreeOptions.name is optional if you pass the name directly to new_btree.
btree = db.new_btree(ctx, "users", tx)
# Add a Key-Value pair
btree.add(ctx, Item(key="user_123", value="John Doe"))
# Find a value
if btree.find(ctx, "user_123"):
# Fetch the value
items = btree.get_values(ctx, Item(key="user_123"))
if items and items[0].value:
print(f"Found User: {items[0].value}")
# --- 6. Complex Keys (Structs) ---
# Define a composite key using a dataclass
from dataclasses import dataclass
from sop.btree import IndexSpecification, IndexFieldSpecification
@dataclass
class EmployeeKey:
region: str
department: str
id: int
# Create B-Tree with custom index (Region -> Dept -> ID)
# This enables fast prefix scans (e.g., "Get all employees in US")
spec = IndexSpecification(index_fields=(
IndexFieldSpecification("region", ascending_sort_order=True),
IndexFieldSpecification("department", ascending_sort_order=True),
IndexFieldSpecification("id", ascending_sort_order=True)
))
# Pass spec as index_spec argument
employees = db.new_btree(ctx, "employees", tx, index_spec=spec)
# Add item with complex key
employees.add(ctx, Item(
key=EmployeeKey("US", "Sales", 101),
value={"name": "Alice"}
))
# --- 7. Simplified Lookup (Dictionary Keys) ---
# You can search for items using a plain dictionary, without needing the original dataclass.
# This is useful for consumer apps that just need to read data.
# Open existing B-Tree (no IndexSpec needed, it's loaded from disk)
employees_read = db.open_btree(ctx, "employees", tx)
# Search using a dict matching the key structure
if employees_read.find(ctx, {"region": "US", "department": "Sales", "id": 101}):
print("Found Alice!")
# --- 8. Text Search ---
# Open a Search Index
idx = db.open_search(ctx, "articles", tx)
idx.add("doc1", "The quick brown fox")
# Transaction commits automatically here.
# If an exception occurs, it rolls back.
6. Querying Data
You can perform queries in a separate transaction (e.g., Read-Only).
# Begin a Read-Only transaction (optional optimization)
with db.begin_transaction(ctx, mode=TransactionMode.ForReading.value) as tx:
# --- Vector Search ---
vs = db.open_vector_store(ctx, tx, "products")
hits = vs.query(ctx, vector=[0.1, 0.5, 0.8], k=5)
for hit in hits:
print(f"Vector Match: {hit.id}, Score: {hit.score}")
# --- Model Retrieval ---
ms = db.open_model_store(ctx, tx, "classifiers")
model = ms.get(ctx, "churn", "v1.0")
print(f"Loaded Model: {model['algorithm']}")
# --- B-Tree Lookup ---
us = db.open_btree(ctx, "user_store", tx)
if us.find(ctx, "user1"):
# Fetch the current item
item = us.get_current_item(ctx)
print(f"User Found: {item.value}")
Performance Tip: For Vector Search workloads that are "Build-Once-Query-Many", use TransactionMode.NoCheck. This bypasses transaction overhead for maximum query throughput.
# High-performance Vector Search (No ACID checks)
with db.begin_transaction(ctx, mode=TransactionMode.NoCheck.value) as tx:
vs = db.open_vector_store(ctx, tx, "products")
hits = vs.query(ctx, vector=[0.1, 0.5, 0.8], k=5)
Advanced Configuration
Logging
You can configure the internal logging of the SOP engine (Go backend) to output to a file or standard error, and control the verbosity.
from sop import Logger, LogLevel
# Configure logging to a file with Debug level
Logger.configure(LogLevel.Debug, "sop_engine.log")
# Or configure logging to stderr (default) with Info level
Logger.configure(LogLevel.Info)
Transaction Options
You can configure timeouts, isolation levels, and more.
from sop import TransactionOptions
opts = TransactionOptions(
max_time=15, # 15 minutes timeout
)
tx = db.begin_transaction(ctx, options=opts)
Clustered Mode
For distributed deployments, switch to DatabaseType.Clustered. This requires Redis for coordination.
from sop.ai import DatabaseType
db = Database(
ctx,
stores_folders=["/mnt/shared_data"],
type=DatabaseType.Clustered
)
Clustered Backend Setup (Cassandra + Redis)
For production environments using Clustered mode, you should initialize both Cassandra (for storage) and Redis (for distributed locking and caching) at application startup.
from sop import Redis
from sop.cassandra import Cassandra
from sop.database import Database, DatabaseOptions, DatabaseType
# 1. Initialize Redis (Required for Locking/Caching in Clustered mode)
# Format: redis://<user>:<password>@<host>:<port>/<db_number>
Redis.initialize("redis://:password@localhost:6379/0")
# 2. Initialize Cassandra (Global Connection)
Cassandra.initialize({
"cluster_hosts": ["127.0.0.1"],
"consistency": 1, # 1 = LocalQuorum
"authenticator": {
"username": "cassandra",
"password": "password"
}
})
# ... Application Logic ...
# Connect to a specific tenant's keyspace
db = Database(DatabaseOptions(
keyspace="tenant_1",
type=DatabaseType.Clustered
))
# ...
# Cleanup on shutdown
Redis.close()
Cassandra.close()
Architecture
SOP uses a split architecture:
- Core Engine (Go): Handles disk I/O, B-Tree algorithms, caching, and transactions. Compiled as a shared library (
.dylib,.so,.dll). - Python Wrapper: Uses
ctypesto interface with the Go engine, providing a Pythonic API (soppackage).
Project Links
- Source Code: GitHub - sharedcode/sop
- PyPI: sop4py
Contributing
Contributions are welcome! Please check the CONTRIBUTING.md file in the repository for guidelines.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sop4py-2.0.31.tar.gz.
File metadata
- Download URL: sop4py-2.0.31.tar.gz
- Upload date:
- Size: 29.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fec96b2935d90391e4b7016d2cdad8bfb2a4c4a358b0a9fe1062996be162e7b1
|
|
| MD5 |
5332cceab5674f790069b09322018def
|
|
| BLAKE2b-256 |
c31f8f2f790839f79f5191db614d0b8458f0051c88bf16bdd8b6d15c04e1c588
|
File details
Details for the file sop4py-2.0.31-py3-none-any.whl.
File metadata
- Download URL: sop4py-2.0.31-py3-none-any.whl
- Upload date:
- Size: 29.5 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fc84992922e7ebbab7e20204828f1bd1604e830b1a432659ac324b44e80f332c
|
|
| MD5 |
9fe8ebeaf9baa33434df36ee88dee1d7
|
|
| BLAKE2b-256 |
883db4b07ebb4853aa04be59c5dd2636b16a274b16933cc5a3c9322b10fe5f95
|