cached_duckdb
Fast in-memory DataFrame cache using DuckDB with SQL query interface.
Overview
cached_duckdb replaces pandas dict-based caching with DuckDB in-memory connections for:
- Columnar storage - 20-30% less RAM than pandas
- SQL queries - Single-pass filter+aggregate operations
- Concurrency - Safe parallel reads during writes
- Zero disk usage - Pure in-memory like pandas
Key Features
- Generic database/table API - Works with any cache-based system
- Two storage modes:
single_db: One connection per database (enables cross-table JOINs)per_table_db: One connection per (database, table) pair (fully parallel writes)
- Atomic swap - Readers see 100% old or 100% new data, never partial
- TTL-based expiry - Background cleanup with lazy stale flagging
- Scheduler-managed tables - Bypass TTL for scheduled updates
- Thread-safe operations - Per-database or per-table locking
Installation
From PyPI (Recommended)
pip install cached-duckdb
Install Specific Version
pip install cached-duckdb==0.2.1
With Optional Protected Build Extras
pip install "cached-duckdb[all]"
From Local Source
pip install -r requirements.txt
Or install in development mode:
pip install -e .
Verify installed version:
import cached_duckdb
print(cached_duckdb.__version__)
Quick Start
Basic Usage
from cached_duckdb import DuckDbCacheManager
import pandas as pd
# Initialize cache (singleton)
cache = DuckDbCacheManager()
# Store DataFrame
df = pd.DataFrame({
'date': ['2026-01-01', '2026-01-02'],
'amount': [1000, 2000],
'country': ['USA', 'UK']
})
cache.store(database="client_abc", table="sales_data", df=df)
# Query with SQL filtering
result = cache.query(
database="client_abc",
table="sales_data",
sql_where="amount > 1000 AND country = 'USA'",
columns=["date", "amount"]
)
print(result)
Advanced Queries
# Get all data
df = cache.query(database="app_x", table="dataset_1")
# Filter with WHERE
df = cache.query(
database="app_x",
table="dataset_1",
sql_where="age > 25 AND country = 'USA'"
)
# Select specific columns
df = cache.query(
database="app_x",
table="dataset_1",
columns=["name", "age", "salary"]
)
# Limit results
df = cache.query(
database="app_x",
table="dataset_1",
sql_where="date >= '2026-01-01'",
limit=100
)
Cross-Table JOINs (single_db mode)
# Execute raw SQL for complex queries
sql = """
SELECT s.date, s.amount, o.customer_name
FROM sales_data s
JOIN orders o ON s.order_id = o.id
WHERE s.amount > 1000
"""
result = cache.execute_sql(database="client_abc", sql=sql)
Check if Data Exists
if cache.exists(database="client_abc", table="sales_data"):
# Data is ready and fresh
df = cache.query(database="client_abc", table="sales_data")
else:
# Data missing or stale - reload needed
df = load_from_source()
cache.store(database="client_abc", table="sales_data", df=df)
TTL and Scheduler-Managed Tables
# Store with custom TTL
cache.store(
database="client_abc",
table="sales_data",
df=df,
ttl_minutes=60 # Expires after 60 minutes
)
# Scheduler-managed table (no auto-expiry on reads)
cache.store(
database="client_abc",
table="sales_data",
df=df,
scheduler_managed=True # Only scheduler updates it
)
Invalidate Cache
# Invalidate one table
cache.invalidate(database="client_abc", table="sales_data")
# Invalidate all tables for a database
cache.invalidate(database="client_abc")
Get Metadata
# Last updated timestamp
last_updated = cache.get_last_updated(database="client_abc", table="sales_data")
print(f"Last updated: {last_updated}")
# Table info
info = cache.get_table_info(database="client_abc", table="sales_data")
print(f"Rows: {info['row_count']}")
print(f"Columns: {info['columns']}")
print(f"Types: {info['column_types']}")
Documentation
Author
sreeyenan (sreeyenanek@gmail.com)
Version
Current version: 0.2.1
Configuration
Environment Variables
# Storage mode: single_db or per_table_db
CACHED_DUCKDB_DEFAULT_MODE=single_db
# Default TTL in minutes
CACHED_DUCKDB_DEFAULT_TTL_MINUTES=30
# Cleanup thread interval
CACHED_DUCKDB_CLEANUP_INTERVAL_MINUTES=5
# Lock timeout in seconds
CACHED_DUCKDB_LOCK_TIMEOUT_SECONDS=30
# Path to connector config file (optional)
CACHED_DUCKDB_CONFIG_FILE_PATH=/path/to/connector_config.json
# Logger name
CACHED_DUCKDB_LOG_NAME=cached_duckdb
Per-Database Configuration File
Create connector_config.json for per-database settings:
{
"client_abc": {
"duck_cache_mode": "per_table_db",
"default_cache_ttl_minutes": 45,
"sales_data": {
"cache_ttl_minutes": 60,
"scheduler_managed": false
},
"live_feed": {
"cache_ttl_minutes": 0,
"scheduler_managed": true
}
},
"client_xyz": {
"duck_cache_mode": "single_db"
}
}
Priority order:
- Per-table config in JSON file (highest)
- Per-database config in JSON file
- Environment variables
- Hardcoded defaults (lowest)
Load Configuration
from cached_duckdb import DuckDbCacheConfig, DuckDbCacheManager
# From environment
config = DuckDbCacheConfig.from_env()
cache = DuckDbCacheManager(config)
# From dict
config = DuckDbCacheConfig.from_dict({
"default_mode": "single_db",
"default_cache_ttl_minutes": 30,
"config_file_path": "/path/to/connector_config.json"
})
cache = DuckDbCacheManager(config)
Storage Modes
Mode A: single_db (Default)
- One DuckDB connection per
database - Multiple tables share the same connection
- Enables cross-table SQL JOINs
- Write contention: One lock per database
Use when: Database has few tables (< 20) or need cross-table queries
Mode B: per_table_db
- One DuckDB connection per
(database, table)pair - Each table is fully isolated
- Zero write contention between tables
- Fully parallel writes
Use when: Database has many tables (20+) or high write concurrency
Architecture
DuckDbCacheManager (singleton)
├── CacheStore - Atomic writes, table management
├── CacheQuery - SQL queries, metadata
├── TTLRegistry - TTL tracking, cleanup thread
└── CacheConfigResolver - Per-database config resolution
Thread Safety
- store(): Write-locked per database or per table
- query(): Lock-free parallel reads
- invalidate(): Write-locked, waits for active readers
- Background cleanup: Minimal locking, uses stale flags
Use Cases
- Multi-tenant web APIs - Cache per tenant with
database=tenant_id - Analytics dashboards - Fast in-memory OLAP queries
- ETL pipelines - Store intermediate DataFrames
- Session managers - Replace pandas dict caching
- Microservices - Shared cache library across services
API Reference
DuckDbCacheManager
store(database, table, df, ttl_minutes=None, scheduler_managed=False)- Store DataFramequery(database, table, sql_where=None, columns=None, limit=None)- Query with filteringexecute_sql(database, sql)- Execute raw SQLexists(database, table)- Check if exists and freshinvalidate(database, table=None)- Remove from cacheget_last_updated(database, table)- Get timestampget_table_info(database, table)- Get metadataget_raw_connection(database, table=None)- Get DuckDB connectionshutdown()- Close all connections
Exceptions
DuckDbCacheError- Base exceptionDuckDbCacheConfigError- Configuration errorDuckDbCacheLockError- Lock acquisition failedDuckDbCacheNotFoundError- Table not foundDuckDbCacheStaleError- Data is stale
License
MIT License - see LICENSE file
Author
sreeyenan
Metadata
Release files for cached-duckdb 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cached_duckdb-0.2.1.tar.gz | 45.4 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| cached_duckdb-0.2.1-cp311-cp311-win_amd64.whl | CPython 3.11 | CPython 3.11 | Windows x86-64 | Details |
| cached_duckdb-0.2.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl | CPython 3.11 | CPython 3.11 | Linux glibc 2.17+ x86-64, Linux glibc 2.28+ x86-64 | Details |
| cached_duckdb-0.2.1-cp311-cp311-macosx_11_0_arm64.whl | CPython 3.11 | CPython 3.11 | macOS 11.0+ ARM64 | Details |
| cached_duckdb-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl | CPython 3.11 | CPython 3.11 | macOS 10.9+ x86-64 | Details |
Total release size: 3.9 MB
Release files / cached_duckdb-0.2.1.tar.gz
| Download URL | cached_duckdb-0.2.1.tar.gz |
|---|---|
| Size | 45.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2881418e8a4d2db94e65ac010294f16125f1a4327d89a0bf7c2c5d7ff68a810e
|
|
BLAKE2b-256 checksum How to use checksums |
f7f31e4e623220fa190c315f9fb53b31e8ec8036802d22b9a5db9e0e1778b1f8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 1, 2026.
Transparency logRelease files / cached_duckdb-0.2.1-cp311-cp311-win_amd64.whl
| Download URL | cached_duckdb-0.2.1-cp311-cp311-win_amd64.whl |
|---|---|
| Size | 379.3 kB |
| Tags | CPython 3.11 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
73fc3336e63fa51313a186801196eb0e26a877f933a23fb01931da2a69705f4d
|
|
BLAKE2b-256 checksum How to use checksums |
33b57878935a2d3f59988cf1d6475f062e700691658c10a6c10147add2fda489
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 1, 2026.
Transparency logRelease files / cached_duckdb-0.2.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
| Download URL | cached_duckdb-0.2.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 2.6 MB |
| Tags | CPython 3.11 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
87c288e860ce7885ffdd540cb679e3d1592e556b4918458d8f3f52dec62b64ef
|
|
BLAKE2b-256 checksum How to use checksums |
37f8159f3545de248aece390a29860150375784b455b2b33f2bbf011388b4085
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 1, 2026.
Transparency logRelease files / cached_duckdb-0.2.1-cp311-cp311-macosx_11_0_arm64.whl
| Download URL | cached_duckdb-0.2.1-cp311-cp311-macosx_11_0_arm64.whl |
|---|---|
| Size | 417.9 kB |
| Tags | CPython 3.11 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
61d08c51d8d23cb3ab3c54a5d91083971abb3e222e93936ee183ca565e29c036
|
|
BLAKE2b-256 checksum How to use checksums |
d0798c9bb2f535c4c0eb27b4b7c70d74536ba22bdc77e16249e8344843995661
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 1, 2026.
Transparency logRelease files / cached_duckdb-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl
| Download URL | cached_duckdb-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl |
|---|---|
| Size | 428.2 kB |
| Tags | CPython 3.11 macOS 10.9+ x86-64 |
|
SHA-256 checksum How to use checksums |
6ebb22c829b5c0199929c306974063e720a7bf0731bdae7e4606d2595f4d941b
|
|
BLAKE2b-256 checksum How to use checksums |
395c71539703418d11a28123989c7d702c361ea147823c91a9ac6125b9bc30f7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 1, 2026.
Transparency log