Skip to content

Commit

Permalink
Merge branch 'main' of github.com:Yiling-J/theine into main
Browse files Browse the repository at this point in the history
  • Loading branch information
Yiling-J committed Nov 12, 2023
2 parents 85ad72f + ca0e130 commit b8a0856
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 3 deletions.
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,17 @@ cache.close()

# clear cache
cache.clear()

# get current cache stats, please call stats() again if you need updated stats
stats = cache.stats()
print(stats.request_count, stats.hit_count, stats.hit_rate)

# get cache max size
cache.max_size

# get cache current size
len(cache)

```

## Decorator
Expand Down Expand Up @@ -240,6 +251,6 @@ Meta shared anonymized trace captured from large scale production cache services
![hit ratios](benchmarks/fb.png)

## Support
Open an issue, ask question in discussions or join discord channel: https://discord.gg/StrgfPaQqE
Open an issue, ask question in discussions or join discord channel: https://discord.gg/StrgfPaQqE

Theine Go version is also available, which focus on concurrency performance, take a look if you are interested: [Theine Go](https://github.com/Yiling-J/theine-go).
21 changes: 21 additions & 0 deletions tests/test_cache.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import timedelta
from random import randint
from time import sleep
from bounded_zipf import Zipf

import pytest

Expand Down Expand Up @@ -137,3 +138,23 @@ def test_close_cache(policy):
cache.set("foo", "bar", timedelta(seconds=60))
cache.close()
assert cache._maintainer.is_alive() is False


def test_cache_stats(policy):
cache = Cache(policy, 5000)
assert cache.max_size == 5000
assert len(cache) == 0
z = Zipf(1.0001, 10, 20000)
for _ in range(20000):
i = z.get()
key = f"key:{i}"
v = cache.get(key)
if v is None:
cache.set(key, key)
stats = cache.stats()
assert stats.hit_count > 0
assert stats.miss_count > 0
assert stats.request_count == stats.hit_count + stats.miss_count
assert stats.hit_rate > 0.5
assert stats.hit_rate < 1
assert stats.hit_rate == stats.hit_count / stats.request_count
8 changes: 6 additions & 2 deletions theine/models.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
from dataclasses import dataclass
from typing import Any, Optional
class CacheStats:
def __init__(self, total: int, hit: int):
self.request_count = total
self.hit_count = hit
self.miss_count = self.request_count - self.hit_count
self.hit_rate = self.hit_count / self.request_count
9 changes: 9 additions & 0 deletions theine/theine.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from typing_extensions import ParamSpec, Protocol

from theine.exceptions import InvalidTTL
from theine.models import CacheStats

sentinel = object()

Expand Down Expand Up @@ -277,6 +278,9 @@ def __init__(self, policy: str, size: int):
self._closed = False
self._maintainer = Thread(target=self.maintenance, daemon=True)
self._maintainer.start()
self._total = 0
self._hit = 0
self.max_size = size

def __len__(self) -> int:
return self.core.len()
Expand All @@ -288,6 +292,7 @@ def get(self, key: Hashable, default: Any = None) -> Any:
:param key: key hashable, use str/int for best performance.
:param default: returned value if key is not found in cache, default None.
"""
self._total += 1
auto_key = False
key_str = ""
if isinstance(key, str):
Expand All @@ -304,6 +309,7 @@ def get(self, key: Hashable, default: Any = None) -> Any:
self.key_gen.remove(key_str)
return default

self._hit += 1
return self._cache[index]

def _access(self, key: Hashable, ttl: Optional[timedelta] = None):
Expand Down Expand Up @@ -438,3 +444,6 @@ def close(self):
def __del__(self):
self.clear()
self.close()

def stats(self) -> CacheStats:
return CacheStats(self._total, self._hit)

0 comments on commit b8a0856

Please sign in to comment.