Skip to main content

Fast in-memory DataFrame cache using DuckDB with SQL query interface

Project description

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.0

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.0

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:

  1. Per-table config in JSON file (highest)
  2. Per-database config in JSON file
  3. Environment variables
  4. 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 DataFrame
  • query(database, table, sql_where=None, columns=None, limit=None) - Query with filtering
  • execute_sql(database, sql) - Execute raw SQL
  • exists(database, table) - Check if exists and fresh
  • invalidate(database, table=None) - Remove from cache
  • get_last_updated(database, table) - Get timestamp
  • get_table_info(database, table) - Get metadata
  • get_raw_connection(database, table=None) - Get DuckDB connection
  • shutdown() - Close all connections

Exceptions

  • DuckDbCacheError - Base exception
  • DuckDbCacheConfigError - Configuration error
  • DuckDbCacheLockError - Lock acquisition failed
  • DuckDbCacheNotFoundError - Table not found
  • DuckDbCacheStaleError - Data is stale

License

MIT License - see LICENSE file

Author

sreeyenan

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cached_duckdb-0.2.0.tar.gz (45.3 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

cached_duckdb-0.2.0-py3-none-any.whl (51.4 kB view details)

Uploaded Python 3

cached_duckdb-0.2.0-cp311-cp311-win_amd64.whl (379.2 kB view details)

Uploaded CPython 3.11Windows x86-64

cached_duckdb-0.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (2.6 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

cached_duckdb-0.2.0-cp311-cp311-macosx_11_0_arm64.whl (417.8 kB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

cached_duckdb-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl (428.1 kB view details)

Uploaded CPython 3.11macOS 10.9+ x86-64

File details

Details for the file cached_duckdb-0.2.0.tar.gz.

File metadata

  • Download URL: cached_duckdb-0.2.0.tar.gz
  • Upload date:
  • Size: 45.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for cached_duckdb-0.2.0.tar.gz
Algorithm Hash digest
SHA256 a75cbae8356c82630e0c17d80f5b4addff0b4c18c0528b1f404830f9c6bc1bf7
MD5 fd42e84234c8006ba04ed56b353b6fff
BLAKE2b-256 d67046a69ac79113869344f68ab345e7012d4a5ee3211b210b61e9cbfbeb0518

See more details on using hashes here.

Provenance

The following attestation bundles were made for cached_duckdb-0.2.0.tar.gz:

Publisher: publish.yml on sreeyenan/cached_duckdb

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cached_duckdb-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: cached_duckdb-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 51.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for cached_duckdb-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0fbe57e2fc5b0099d09b4b492157115ca4f2c59b54a8ad35eda6cf8a8f9f2820
MD5 5dfb9d31aac2dc4def607f894e70da41
BLAKE2b-256 75ff3343b4f8a289debf49852f32d330e47e4eb006d85e1ae3e1d340dc1b82e1

See more details on using hashes here.

Provenance

The following attestation bundles were made for cached_duckdb-0.2.0-py3-none-any.whl:

Publisher: publish.yml on sreeyenan/cached_duckdb

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cached_duckdb-0.2.0-cp311-cp311-win_amd64.whl.

File metadata

File hashes

Hashes for cached_duckdb-0.2.0-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 8bbc21540dd5890da5762ac1585e1f753f14196febb75690733b3d448abbee8e
MD5 3f0c702973fc858aa4e9d570fb245f69
BLAKE2b-256 341a24cf26d0ba648a23256444c2c739b506260c8dfe0e9d388a97a34f9ac6c2

See more details on using hashes here.

Provenance

The following attestation bundles were made for cached_duckdb-0.2.0-cp311-cp311-win_amd64.whl:

Publisher: publish.yml on sreeyenan/cached_duckdb

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cached_duckdb-0.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for cached_duckdb-0.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 745ca9229bd5b6d83733057030ceb9293c1ea17129fa3993a4d4da49d9533f7d
MD5 4da9678e38fbc40a12c76a0ac4a2d415
BLAKE2b-256 c74ecfc48be49f0b226d70ceddc043de294b380fdf058edceb1a0042a65554cb

See more details on using hashes here.

Provenance

The following attestation bundles were made for cached_duckdb-0.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl:

Publisher: publish.yml on sreeyenan/cached_duckdb

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cached_duckdb-0.2.0-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for cached_duckdb-0.2.0-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 03d773aa2d65a54ae85eea39e62c71767cd431325984cc3929a59fbd3f18c085
MD5 29c8c6c6dc45dbda0e17638c38af54c9
BLAKE2b-256 061d2a00d6a4bb412bc9e8209f567d7a6f788f88430cac2f6116a5ef0f172e0f

See more details on using hashes here.

Provenance

The following attestation bundles were made for cached_duckdb-0.2.0-cp311-cp311-macosx_11_0_arm64.whl:

Publisher: publish.yml on sreeyenan/cached_duckdb

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cached_duckdb-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl.

File metadata

File hashes

Hashes for cached_duckdb-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 101445d9a4cf3781b046184f6cba8a4737fe1a052c23a868a88989ca32c8cbeb
MD5 c69433fa18c4059cc68fd65887440bae
BLAKE2b-256 6a89dd1caa6bf4f9feb51835216b75a0d2fc44d2c6c3b2e05a8b9c14d0696a52

See more details on using hashes here.

Provenance

The following attestation bundles were made for cached_duckdb-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl:

Publisher: publish.yml on sreeyenan/cached_duckdb

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page