Metadata-Version: 2.4
Name: async-cache
Version: 2.0.1
Summary: an asyncio application layer cache and dataloader for python based microservices and applications with thundering herd protection
Author-email: Rajat Singh <iamsinghrajat@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/iamsinghrajat/async-cache
Keywords: asyncio,lru,cache,async,ttl,dataloader,cache warmup,thundering herd protection
Requires-Python: >=3.3
Description-Content-Type: text/x-rst
License-File: LICENSE
Dynamic: license-file

async-cache
===========
:info: In-memory application layer cache

.. image:: https://img.shields.io/pypi/v/async-cache.svg
    :target: https://pypi.python.org/pypi/async-cache
.. image:: https://www.codetriage.com/iamsinghrajat/async-cache/badges/users.svg
    :target: https://pypi.python.org/pypi/async-cache
.. image:: https://static.pepy.tech/personalized-badge/async-cache?period=total&units=international_system&left_color=black&right_color=blue&left_text=Downloads
    :target: https://pepy.tech/project/async-cache





Installation
------------

.. code-block:: shell

    pip install async-cache

See full documentation at https://async-cache.readthedocs.io/

Core Usage: Function API for Microservices
------------------------------------------

Use ``AsyncCache`` for flexible caching:

.. code-block:: python

    from cache import AsyncCache

    cache = AsyncCache(maxsize=1000, default_ttl=300)  # TTL in seconds

    async def get_data(key):
        return await cache.get(
            key,
            loader=lambda: db_query(key),  # auto-caches on miss
        )

    # Warmup hot keys at startup
    await cache.warmup({"hot:key": lambda: preload_hot()})

    # Metrics for observability
    print(cache.get_metrics())  # hits, misses, size, hit_rate

Key Features & Examples
------------------------

**Thundering Herd Protection**
    Prevents duplicate work under concurrent load (e.g., popular keys). Without it, 100 misses = 100 DB hits; with it, = 1.

    .. code-block:: python

        cache = AsyncCache()
        async def loader(): 
            return await db_query()  # expensive
        # 100 concurrent -> 1 loader call
        results = await asyncio.gather(*[cache.get('key', loader=loader) for _ in range(100)])

**DataLoader-Style Batching**
    Groups concurrent gets into one batch call (reduces DB load; configurable window/size).

    .. code-block:: python

        async def batch_loader(keys):
            # one DB query for batch
            return {k: await db_batch_query(k) for k in keys}
        # auto-groups within 5ms window
        await asyncio.gather(
            cache.get(1, batch_loader=batch_loader),
            cache.get(2, batch_loader=batch_loader)
        )

**Cache Warmup**
    Preload at startup to avoid cold misses.

    .. code-block:: python

        await cache.warmup({
            "user:1": lambda: load_user(1),
            "config:global": lambda: load_config(),
        })

**Metrics**
    Observability for hit rate, size, etc. (global or per-function).

    .. code-block:: python

        metrics = cache.get_metrics()  # or func.get_metrics()
        # {'hits': 950, 'misses': 50, 'size': 200, 'hit_rate': 0.95}
        # Use for Prometheus/monitoring

**TTL & Invalidation**
    Per-key control + size-based eviction.

    .. code-block:: python

        await cache.set('key', value, ttl=60)  # override
        await cache.delete('key')  # or func.invalidate_cache(args)
        cache.clear()

Decorator Convenience
---------------------

For simple/readable code (uses core API under the hood):

.. code-block:: python

    from cache import AsyncLRU, AsyncTTL

    @AsyncLRU(maxsize=128)
    async def func(*args):
        ...

    @AsyncTTL(time_to_live=60, skip_args=1)  # e.g. skip 'self'
    async def method(self, arg):
        ...

Testing
-------

A local test dashboard is available for interactive testing:

.. code-block:: shell

    python demo/app.py  # Runs on http://localhost:5001

Use it to verify caching behavior, metrics, and concurrent load handling.

