Persistent-Map: Effortless Persistent, Async Key-Value Store for Rust
Persistent-Map offers a simple and efficient way to store and retrieve key-value data that needs to survive application restarts. It provides an easy-to-use API with an asynchronous interface and pluggable storage backends.
Key Features
- Simple API: Familiar
insertandgetoperations with an async interface. - Multiple Backends: SQLite, CSV, in-memory, and extensible for more.
- Asynchronous: Designed with
async/awaitfor non-blocking operations. - In-Memory Cache: Uses
DashMapfor fast concurrent access to frequently used data. - Generic: Works with types that implement
SerializeandDeserializeOwned. - Flexible: Choose the storage backend that best fits your needs.
Installation
Add this to your Cargo.toml:
[]
= "0.1.0" # Replace with the latest version
= { = "1", = ["macros", "rt-multi-thread"] } # For the async runtime
# Choose the backends you need
= { = "0.1.0", = ["sqlite", "csv_backend", "in_memory"] }
By default, the crate includes the SQLite and in-memory backends. You can enable other backends as needed.
Basic Usage
Here's a simple example using the SQLite backend:
use ;
async
Available Backends
SQLite Backend
The SQLite backend is ideal for most applications, providing reliable persistence with good performance.
use ;
async
CSV Backend
The CSV backend stores data in a simple CSV file, which can be useful for data that needs to be human-readable.
use ;
async
In-Memory Backend
The in-memory backend doesn't provide persistence but can be useful for testing or temporary storage.
use ;
async
Implementing Custom Backends
One of the key features of persistent-map is its extensibility. You can create your own storage backends by implementing the StorageBackend trait.
The StorageBackend Trait
The StorageBackend trait defines the interface that all storage backends must implement:
Example: JSON File Backend
Here's an example of a custom backend that stores data in a JSON file:
use ;
use HashMap;
use PathBuf;
use fs;
use ;
use Hash;
Best Practices for Custom Backends
When implementing a custom backend, consider the following best practices:
- Error Handling: Convert backend-specific errors to
PersistentError - Concurrency: Ensure your backend is safe for concurrent access
- Performance: Consider caching or batching operations for better performance
- Resilience: Handle edge cases like missing files or corrupted data gracefully
- Testing: Create tests that verify persistence across application restarts
Publishing Your Custom Backend
If you've created a useful backend implementation, consider publishing it as a separate crate that depends on persistent-map. This allows others to benefit from your work while keeping the core crate lightweight.
For example:
persistent-map-redis = { version = "0.1.0", dependencies = { persistent-map = "0.1.0", redis = "0.21.0" } }
Performance Considerations
- The in-memory
DashMapprovides fast concurrent access to data - Persistence operations are asynchronous and don't block the main thread
- For best performance with frequent writes, consider calling
flush()periodically rather than after every write
Future Enhancements
- Additional storage backends (Postgres, Redis, etc.)
- Transactional operations
- Batch operations for improved performance
- Iterator support for more idiomatic Rust usage
Contributing
Contributions are welcome! Here are some ways you can contribute:
- Implement new storage backends
- Improve documentation
- Add tests
- Report bugs
- Suggest new features
Please open an issue or submit a pull request on GitHub.
Versioning
This project follows Semantic Versioning. The current version is 0.1.0, which means it is still in initial development and the API may change.
License
This project is licensed under the MIT License - see the LICENSE file for details.