Page MenuHomePhabricator

README.md
No OneTemporary

README.md

# Store Transaction Examples
This directory contains examples demonstrating how to use the store library's transaction system, including how to create custom stores and integrate them with the transaction framework.
## Examples
### 1. `custom_store.rs` - Basic Custom Store
Demonstrates the fundamentals of creating a custom store type with versioning capabilities. This example shows:
- How to implement the `TransactionProvider` trait
- Basic transaction operations within a custom store
- Creating a custom transaction context
**Note**: This example uses placeholder implementations for demonstration purposes and is not intended for production use.
### 2. `extended_custom_store.rs` - Production-Ready Custom Store
A comprehensive example showing how to create a production-ready custom store (AuditStore) that integrates seamlessly with the extended transaction system. This example demonstrates:
- **Custom Store Implementation**: An audit logging store that tracks operations with timestamps
- **Transaction Provider**: Proper implementation of `TransactionProvider` for the custom store
- **Transaction Context**: Creating a custom transaction context for type-safe operations
- **Extension Traits**: How to extend `ExtendedTransactionContext` with custom functionality
- **Atomic Operations**: Coordinating operations across multiple stores (Range, Namespace, and Audit)
- **Error Handling**: Proper transaction rollback and error propagation
- **Real-world Usage**: Complete example with setup, operations, and verification
## Running the Examples
To run the extended custom store example:
```bash
cd collar/crates/store
cargo run --example extended_custom_store
```
To run the tests for the custom store:
```bash
cargo test --example extended_custom_store
```
## Key Concepts
### Transaction Composition
The new transaction system allows you to compose transactions flexibly:
```rust
// Basic transaction with built-in stores
let transaction = Transaction::new()
.with_range_store(&range_store)
.with_namespace_store(&namespace_store)
.with_tree(&metadata_tree);
// Extended transaction with custom stores
let transaction = ExtendedTransaction::new()
.with_range_store(&range_store)
.with_namespace_store(&namespace_store)
.with_custom_store(&audit_store, "audit")
.with_tree(&config_tree);
```
### Custom Store Integration
To create a custom store that works with the transaction system:
1. **Implement `TransactionProvider`**:
```rust
impl TransactionProvider for MyStore {
fn transaction_trees(&self) -> Vec<&Tree> {
vec![&self.tree1, &self.tree2]
}
}
```
2. **Create a Transaction Context**:
```rust
pub struct MyTransactionContext<'a, 'ctx> {
store: &'a MyStore,
tree1: &'ctx sled::transaction::TransactionalTree,
tree2: &'ctx sled::transaction::TransactionalTree,
}
```
3. **Implement Context Methods**:
```rust
impl<'a, 'ctx> MyTransactionContext<'a, 'ctx> {
pub fn my_operation(&self, data: &str) -> Result<u64> {
// Use self.tree1 and self.tree2 for transactional operations
}
}
```
4. **Create Extension Trait**:
```rust
pub trait MyTransactionExtension<'a, 'ctx> {
fn use_my_store(&self, store: &'a MyStore) -> Option<MyTransactionContext<'a, 'ctx>>;
}
impl<'a, 'ctx> MyTransactionExtension<'a, 'ctx> for ExtendedTransactionContext<'a, 'ctx> {
fn use_my_store(&self, store: &'a MyStore) -> Option<MyTransactionContext<'a, 'ctx>> {
let trees = self.custom_store_trees("my_store")?;
Some(MyTransactionContext {
store,
tree1: trees[0],
tree2: trees[1],
})
}
}
```
### Error Handling and Rollback
The transaction system automatically handles rollback when any operation fails:
```rust
let result = transaction.execute(|ctx| {
let success1 = ctx.use_namespace().reserve("users", "alice", "data")?;
let bit = ctx.use_range().assign("ips", "192.168.1.1")?;
// If this fails, everything above gets rolled back
let audit_id = ctx.use_audit(&audit_store)?.log("operation", "details")?;
Ok((success1, bit, audit_id))
});
```
## Best Practices
1. **Store Design**: Each custom store should have a clear, single responsibility
2. **Transaction Safety**: Always use the provided transaction contexts rather than direct tree access
3. **Error Handling**: Use proper error types and let the transaction system handle rollback
4. **Type Safety**: Leverage Rust's type system with custom transaction contexts
5. **Testing**: Write comprehensive tests including rollback scenarios
6. **Documentation**: Document your custom stores and their transaction semantics
## Migration from Legacy API
If you're using the legacy `CombinedTransaction` API, you can migrate to the new system:
```rust
// Old way
let combined = CombinedTransaction::new(&range_store, &namespace_store, vec![&tree]);
let result = combined.execute(|ctx| {
ctx.assign_range("range", "value")?;
ctx.reserve_namespace("ns", "key", "value")?;
Ok(())
});
// New way
let transaction = Transaction::new()
.with_range_store(&range_store)
.with_namespace_store(&namespace_store)
.with_tree(&tree);
let result = transaction.execute(|ctx| {
ctx.use_range().assign("range", "value")?;
ctx.use_namespace().reserve("ns", "key", "value")?;
Ok(())
});
```
The legacy API is still available for backward compatibility, but new code should use the updated API for better composability and type safety.

File Metadata

Mime Type
text/plain
Expires
Mon, Jun 9, 11:29 AM (15 h, 41 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
47641
Default Alt Text
README.md (5 KB)

Event Timeline