⚡️ FlipCache
Redis-backed hybrid caching for lightning-fast Python data access
🤷♂️ Why FlipCache?
- Seamlessly integrate Redis for accelerated data retrieval in your Python projects.
- Optimize performance with an in-memory cache layer backed by Redis persistence.
- Enjoy ease-of-use and flexible configuration
📥 Installation
pip install flipcache
🚀 Key Features
- Hybrid Caching: Transparent in-memory caching combined with Redis for scalable persistence.
- Expire Times: Set custom expiration times for cached data.
- Configurable: Tailor cache size, data types, and more.
👨💻 Usage Examples
Basic
from flipcache import FlipCache
cache = FlipCache("my_cache")
cache["my_key"] = "my_value"
print(cache["my_key"]) # Outputs: "my_value"
print(cache["unknown"]) # Outputs: None
print("my_key" in cache) # Outputs: True
Pros compared to using simple dictionary:
- Data persistence backed by Redis
- Seamless data conversion from Redis to Python
- Fast data access, compared to pure redis
- Returns None instead of raising an error on key indexing
Expiring Cache
import time
from flipcache import FlipCache
expiring_cache = FlipCache("expiring", local_max=0, expire_time=5)
expiring_cache["data"] = "This will expire"
time.sleep(6)
print(expiring_cache["data"]) # Outputs: None
In order to expiring-feature work with its full potential, we need to set local_max to 0, removing the caching layer.
You lose out on faster data retrieval, in order to get precise expiration results.
We can combine expire_time and local_max, in that case we can access data from cache memory that could have been expired.
JSON Cache
from flipcache import FlipCache, et
user_data = FlipCache(
"user_data",
local_max=100,
expire_time=et.THREE_DAYS,
value_type="json"
)
data = {
"state": 1,
"orders": [1, 2, 3, 4],
"items": {
"foo": 1,
"bar": True,
"baz": []
}
}
# Store data
user_data["some-uuid"] = data
print(user_data["some-uuid"]) # {'state': 1, 'orders': [1, 2, 3, 4], 'items': {'foo': 1, 'bar': True, 'baz': []}}
# Update data
data["state"] = 2
data["items"]["bar"] = False
user_data["some-uuid"] = data
print(user_data["some-uuid"]) # {'state': 2, 'orders': [1, 2, 3, 4], 'items': {'foo': 1, 'bar': False, 'baz': []}}
# Delete data
del user_data["some-uuid"]
print(user_data["some-uuid"]) # None
Custom Encoder/Decoder
from flipcache import FlipCache
from dataclasses import dataclass, field
@dataclass
class Shape:
name: str = "default"
dimensions: list[float] = field(default_factory=list)
edges: int = 0
area: float = 0
def __post_init__(self):
if not self.area and self.dimensions:
self.area = self.dimensions[0] * self.dimensions[1]
def encode_shape(shape: Shape) -> str:
return f"{shape.name}||{shape.dimensions}||{shape.edges}||{shape.area}"
def decode_shape(shape: str) -> Shape:
data = shape.split("||")
return Shape(
name=data[0],
dimensions=[float(num) for num in data[1].strip('[]').split(',') if num],
edges=int(data[2]),
area=float(data[3])
)
my_shape = Shape(name='womp', dimensions=[4.1, 3.4], edges=6, area=16.38)
shape2 = Shape(name='wat', dimensions=[11, 22])
custom = FlipCache(
"custom",
local_max=0,
key_type='int',
value_type='custom',
value_default=Shape(),
value_encoder=encode_shape,
value_decoder=decode_shape
)
custom[123] = my_shape
custom[456] = shape2
print(custom[123]) # Shape(name='womp', dimensions=[4.1, 3.4], edges=6, area=16.38)
print(custom[321]) # Shape(name='default', dimensions=[], edges=0, area=0.0)
print(custom[456]) # Shape(name='wat', dimensions=[11.0, 22.0], edges=0, area=242.0)
FIFODict Example (First-In, First-Out)
from flipcache import FIFODict
cache = FIFODict(max_items=3)
cache["a"] = "Apple"
cache["b"] = "Banana"
cache["c"] = "Cherry"
print(list(cache.keys())) # ['a', 'b', 'c']
cache["d"] = "Date" # Evicts 'a' (oldest)
print(list(cache.keys())) # ['b', 'c', 'd']
✅ Items are evicted in the order they were inserted.
LRUDict Example (Least Recently Used)
from flipcache import LRUDict
cache = LRUDict(max_items=3)
cache["a"] = "Alpha"
cache["b"] = "Beta"
cache["c"] = "Gamma"
_ = cache["a"] # Access 'a', making it most recently used
cache["d"] = "Delta" # Evicts 'b' (least recently used)
print(list(cache.keys())) # ['c', 'a', 'd']
✅ Accessing a key moves it to the end (most recently used).
For more usage examples and details, see examples
⚙️ Configuration Options
FlipCache
local_max: Maximum items in the in-memory cache.expire_time: Redis key expiration time.key_type: Expected key data type.value_type: Expected value data type.value_encoder: Custom function used to encode the value for redisvalue_decoder: Custom function used to decode the value from redisrefresh_expire_time_on_get: Refresh Redis key expiration on accessredis_protocol: custom redis.Redis instance to be passed
FIFODict and LRUDict
max_items: Maximum number of items the dict can hold (defaults 1000)
📊 Benchmarks
Setup
from flipcache import FlipCache
from redis import Redis
KEYS = 1_000
rdp = Redis(decode_responses=True)
cache = FlipCache(name="my_cache", redis_protocol=rdp, local_max=KEYS)
def redis_set():
for i in range(KEYS):
rdp.set(f"my_cache:{i}", i * 2)
def pycache_set():
for i in range(KEYS):
cache[i] = i * 2
def redis_get():
for _ in range(100):
for i in range(KEYS):
v = rdp.get(f"my_cache:{i}")
def pycache_get():
for _ in range(100):
for i in range(KEYS):
v = cache[i]
| Benchmark Name | Mean Time (s) | Standard Deviation |
|---|---|---|
| redis_set | 0.252 | 0.013 |
| flipcache_set | 0.242 | 0.003 |
| redis_get | 22.986 | 0.518 |
| flipcache_get | 0.0172 | 0.000 |
📋 Plans for future releases
- Make it possible to use other redis implementations such as aioredis
- Create readthedocs site for detailed documentation
- Optimize and add new functionality
- Make it threadsafe
- Add tests
Metadata
Release files for FlipCache 1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| flipcache-1.3.tar.gz | 103.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| flipcache-1.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 114.4 kB
Release files / flipcache-1.3.tar.gz
| Download URL | flipcache-1.3.tar.gz |
|---|---|
| Size | 103.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
25dd786863381bb1c7d796bfe3f60b0e8c40a28d9f45dfbc0d1e042dfd46b317
|
|
BLAKE2b-256 checksum How to use checksums |
3984654b7bc6d425b112b282526258a31acc6d6abc725a8e2c2b81b5581ccb3f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.9.21
|
Release files / flipcache-1.3-py3-none-any.whl
| Download URL | flipcache-1.3-py3-none-any.whl |
|---|---|
| Size | 10.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b4ba7ee0963dad58f042b1554297cf2d997a9faa611ac6084bed4f9bc221a7da
|
|
BLAKE2b-256 checksum How to use checksums |
f4c0df58405501eb7ef04fbce0eda0c5d1c5bf6b0d6e48e53b59b849c4c9d46e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.9.21
|