Skip to content

Cache Strategies Guide ​

NanaSQLite provides multiple caching strategies to achieve optimal performance for your use case.

Cache Types Overview ​

StrategyCacheTypeCharacteristicsRecommended For
UnboundedUNBOUNDEDKeeps all data in memorySmall–medium DBs (default)
LRULRUEvicts least-recently-used entriesLarge DBs with memory constraints
TTLTTLAuto-expires data after a set durationSessions and temporary data

Unbounded Cache (UNBOUNDED) ​

The default caching strategy. Keeps all data in memory for the fastest possible access.

python
from nanasqlite import NanaSQLite

# Default: unbounded cache
db = NanaSQLite("app.db")

# Explicitly specifying
db = NanaSQLite("app.db", cache_strategy="unbounded")

Characteristics ​

  • O(1) memory reads
  • Every database entry is kept in memory
  • Memory usage grows proportionally with data size
  • Use bulk_load=True to load all data into cache at startup
python
# Bulk-load all data into cache at startup
db = NanaSQLite("app.db", bulk_load=True)

# Load all data into cache later
db.load_all()

Considerations ​

  • High memory consumption with large datasets
  • Consider switching to LRU when exceeding 100,000 entries

LRU Cache ​

Least Recently Used cache that automatically evicts the oldest-accessed entries.

python
from nanasqlite import NanaSQLite, CacheType

# LRU cache: keep at most 1000 entries
db = NanaSQLite(
    "app.db",
    cache_strategy=CacheType.LRU,
    cache_size=1000,
)

How It Works ​

  1. If data is in cache, return immediately (cache hit)
  2. If not in cache, read from database (cache miss)
  3. When cache reaches cache_size, the least-recently-accessed entry is evicted
python
# LRU cache usage example
db = NanaSQLite("app.db", cache_strategy="lru", cache_size=500)

db["key1"] = "value1"  # Added to cache
db["key2"] = "value2"  # Added to cache

val = db["key1"]       # Cache hit (fast)
val = db.get_fresh("key1")  # Bypass cache, read directly from DB

Fast LRU (lru-dict) ​

When the lru-dict package is installed, a C-extension-based fast LRU is automatically used.

bash
pip install nanasqlite[speed]  # Installs lru-dict + orjson

Speed comparison:

ImplementationRead Speed
Standard LRU (OrderedDict)1x
FastLRU (lru-dict)~2–3x
Data SizeRecommended cache_size
Up to 10K entries1,000
10K–100K entries5,000
100K+ entries10,000

TTL Cache ​

Time-To-Live cache that automatically expires data after a specified duration.

python
from nanasqlite import NanaSQLite, CacheType

# TTL cache: expire after 60 seconds
db = NanaSQLite(
    "app.db",
    cache_strategy=CacheType.TTL,
    cache_ttl=60.0,         # in seconds
)

How It Works ​

  1. When data is added to cache, a timestamp is recorded
  2. After TTL elapses, the entry is expired on next access (lazy expiration)
  3. Expired data is re-read from the database
python
import time

db = NanaSQLite("app.db", cache_strategy="ttl", cache_ttl=30.0)

db["session"] = {"user": "alice", "token": "abc123"}

# Within 30 seconds: returned from cache
val = db["session"]  # Fast

time.sleep(31)

# After 30 seconds: reloaded from DB
val = db["session"]  # DB access occurs

TTL + Size Limit ​

You can also add a size limit to TTL caches:

python
db = NanaSQLite(
    "app.db",
    cache_strategy="ttl",
    cache_ttl=300.0,     # Expire after 5 minutes
    cache_size=2000,     # Maximum 2000 entries
)
ScenarioRecommended cache_ttl
Session store300–3,600 seconds
API response cache30–300 seconds
Config file cache3,600+ seconds
Real-time data1–10 seconds

Cache Persistence TTL ​

Set cache_persistence_ttl=True to trigger a write-back to the database when a TTL entry expires.

python
db = NanaSQLite(
    "app.db",
    cache_strategy="ttl",
    cache_ttl=60.0,
    cache_persistence_ttl=True,  # Sync to DB on TTL expiration
)

Cache Management Methods ​

Inspect and Control ​

python
# Check whether a key is in the cache
db.is_cached("key1")  # True / False

# Clear the entire cache
db.clear_cache()

# Load all data into cache
db.load_all()

# Refresh a specific key's cache entry
db.refresh("key1")

# Bypass cache and read directly from DB
value = db.get_fresh("key1")

Batch Operations and Cache ​

Batch operations automatically update the cache as well:

python
# Batch update (cache updated simultaneously)
db.batch_update({"k1": "v1", "k2": "v2", "k3": "v3"})

# Batch get (maximizes cache hits)
results = db.batch_get(["k1", "k2", "k3"])

# Batch delete (removes from cache too)
db.batch_delete(["k1", "k2"])

Cache Usage with Async ​

The same cache strategies are available with AsyncNanaSQLite:

python
from nanasqlite import AsyncNanaSQLite, CacheType

async def main():
    db = AsyncNanaSQLite(
        "app.db",
        cache_strategy=CacheType.LRU,
        cache_size=1000,
    )

    await db.aset("key", "value")
    val = await db.aget("key")  # Cache hit

    await db.aclear_cache()
    await db.aload_all()

    db.close()

Strategy Selection Flowchart ​

Number of entries < 10K?
├─ Yes → UNBOUNDED (default)
└─ No
   ├─ Data needs expiration?
   │  ├─ Yes → TTL
   │  └─ No → LRU
   └─ Plenty of memory available?
      ├─ Yes → UNBOUNDED + bulk_load
      └─ No → LRU (tune cache_size)

Performance Comparison ​

OperationUNBOUNDEDLRUTTL
Read (hit)O(1)O(1)O(1)
Read (miss)O(DB)O(DB)O(DB)
WriteO(1)O(1)O(1)
Memory usageHighBoundedBounded
Initial loadbulk_loadLazyLazy