A redis-like cache based on POSIX shared memory.
Project description
Shmedis
Shmedis is a high-performance, in-memory caching library for Python that behaves like Redis but leverages shared memory to allow for efficient data sharing between processes on the same machine. It provides a simple, dictionary-like interface for storing and retrieving Python objects, making it an ideal solution for inter-process communication and fast-caching scenarios.
Features
- Redis-like API: Offers a familiar and intuitive API with methods like
set,get, anddelete. - Shared Memory Backend: Utilizes
multiprocessing.SharedMemoryfor extremely fast data access across different processes, eliminating the need for socket communication or serialization overhead. - Process-Safe: Implements robust process-safe locking mechanisms using pthread read-write locks (
pthread_rwlock_t), ensuring data integrity during concurrent access. - Efficient Memory Management: Employs a sophisticated memory allocation strategy using a skip list to manage free memory fragments, reducing fragmentation and optimizing memory usage.
- Fast Key Lookups: Uses a custom hash table implementation (
IndexHashTable) withxxhashfor rapid key retrieval. - Built-in Expiration: Supports setting an expiration time for cached items.
How It Works
Shmedis orchestrates several low-level components to create a high-performance cache:
-
Shared Memory Block: A large chunk of memory is allocated using
multiprocessing.SharedMemory, which is accessible by multiple processes. This block is partitioned into several areas:- Metadata Area: Stores essential information about the cache, such as total size, offsets to other data structures, and the heap top.
- Lock Area: Contains a process-shared read-write lock (
RawProcessRwLock) to manage concurrent access. - Index Hash Table: A fixed-size hash table that maps keys to the memory addresses of their corresponding values.
- Free-Memory Skip List: A skip list that keeps track of available memory fragments, enabling efficient allocation and deallocation.
- Data Area: The primary region where the actual key-value data is stored.
-
Data Storage (
SDS): Values are stored using a custom dynamic string-like structure calledSDS(Simple Dynamic String), which is inspired by Redis's own string implementation. EachSDSobject contains metadata like capacity, length, and an optional expiration timestamp. -
Memory Allocation and Deallocation:
- When a new item is added, Shmedis first attempts to find a suitable free memory block from the skip list.
- If no appropriately sized fragment is available, it allocates a new block from the top of the heap.
- When an item is deleted, its memory block is returned to the skip list. The system will also try to merge it with any adjacent free blocks to create larger contiguous free spaces.
-
Concurrency: To prevent race conditions and ensure data consistency, all read and write operations are protected by a
pthread_rwlock_t. This allows multiple processes to read data simultaneously but ensures that write operations have exclusive access.
Installation
You can install Shmedis directly from PyPI:
pip install shmedis
Usage
Using Shmedis is straightforward. Here's a basic example:
import os
from multiprocessing import Process
from shmedis import Shmedis
def writer_process(cache_name):
# Connect to the same shared memory cache
cache = Shmedis(name=cache_name)
# Set some values
cache.set(b'mykey', b'hello world')
cache.set('another_key', 'with a string value'.encode('utf-8'), ex=5) # Expires in 5 seconds
print(f"[Writer PID: {os.getpid()}] Set two keys.")
def reader_process(cache_name):
# Connect to the same shared memory cache
cache = Shmedis(name=cache_name)
# Get a value
value = cache.get(b'mykey')
if value:
print(f"[Reader PID: {os.getpid()}] Got value: {value.decode('utf-8')}")
# Get another value
another_value = cache.get('another_key')
if another_value:
print(f"[Reader PID: {os.getpid()}] Got another_value: {another_value.decode('utf-8')}")
if __name__ == "__main__":
cache_name = "my_shared_cache"
# Create the cache instance in the main process.
# This will create the shared memory block.
main_cache = Shmedis(name=cache_name, size=1024 * 1024) # 1MB cache
# Create and start writer and reader processes
writer = Process(target=writer_process, args=(cache_name,))
reader = Process(target=reader_process, args=(cache_name,))
writer.start()
writer.join()
reader.start()
reader.join()
# Clean up the shared memory
main_cache.shm.unlink()
API Reference
Shmedis(name: str = "default", size: int = 1 << 30)
name: A unique name for the shared memory block.size: The total size in bytes to allocate for the cache.
Methods
-
set(key: bytes | str, value: bytes, ex: int = 0, nx: bool = False): Stores a key-value pair.key: The key for the item (can bestrorbytes).value: The value to store (bytes).ex: Optional expiration time in seconds.nx: Set key if not exist or Fail
Return
Trueif set success. -
get(key: bytes | str) -> Optional[bytes]: Retrieves the value for a given key. ReturnsNoneif the key does not exist or has expired. -
delete(key: bytes | str): Removes a key-value pair from the cache and frees the associated memory.
Contributing
Contributions are welcome! If you find a bug or have a feature request, please open an issue on the GitHub repository.
License
This project is licensed under the APACHE-2.0 License. See the LICENSE-APACHE file for details.
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 shmedis-0.3.8.tar.gz.
File metadata
- Download URL: shmedis-0.3.8.tar.gz
- Upload date:
- Size: 19.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e99d2211b02189e3c08db7f245b5f240bb667737b8a53a1c1ecbc9c1abf373c2
|
|
| MD5 |
7b73c46d0b9e76b16f2692b15950a498
|
|
| BLAKE2b-256 |
05efa65bde2694dc0db9ad4f4e1c5e430721344d0fee0e285388aafc6d34b4d5
|
File details
Details for the file shmedis-0.3.8-py3-none-any.whl.
File metadata
- Download URL: shmedis-0.3.8-py3-none-any.whl
- Upload date:
- Size: 17.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8755ce881440e6e26dba9a33f9ab38a1eb7b325a7fd82d3e1c45182e76aa0075
|
|
| MD5 |
3618e5d3a105c7bd73712eae0daca683
|
|
| BLAKE2b-256 |
d793cdecc6083c83b15a764f109d5b3c5a41d4fdd0b7fa082e903bb8abf7f5b5
|