client-query-cache 0.5.0__tar.gz → 0.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/PKG-INFO +12 -3
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/README.md +4 -1
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/pyproject.toml +8 -1
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/pyproject.toml.orig +8 -1
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/entries.py +1 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/manager.py +14 -4
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/asynchronous/collection.py +8 -2
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/synchronous/collection.py +8 -2
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/__init__.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/__init__.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/canonical.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/codec.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/collation.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/collection_metadata.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/count_reads.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/cursor_capture.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/errors.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/find_one_reads.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/find_reads.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/identity_reads.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/keys.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/lifecycle.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/locking.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/lru.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/namespace.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/order_sensitive_keys.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/projection.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/query_filters.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/read_classification.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/read_validation.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/snapshots.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/stream_activation.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/stream_cost.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/stream_events.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/stream_health.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/stream_options.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/traversal.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/unique_keys.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/asynchronous/__init__.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/asynchronous/cursors.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/asynchronous/database.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/asynchronous/manager.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/asynchronous/streams.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/otel.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/py.typed +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/synchronous/__init__.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/synchronous/cursors.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/synchronous/database.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/synchronous/manager.py +0 -0
- {client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/synchronous/streams.py +0 -0
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: client-query-cache
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Client-side caching for PyMongo, kept coherent using MongoDB change streams.
|
|
5
5
|
Author: Alessio Locatelli
|
|
6
6
|
Author-email: Alessio Locatelli <<software.development@secure.mailbox.org>>
|
|
7
|
+
License-Expression: Apache-2.0
|
|
8
|
+
Classifier: Framework :: AsyncIO
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
7
10
|
Classifier: Programming Language :: Python :: 3.14
|
|
8
11
|
Classifier: Programming Language :: Python :: 3.15
|
|
12
|
+
Classifier: Topic :: Database
|
|
13
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
14
|
+
Classifier: Typing :: Typed
|
|
9
15
|
Requires-Dist: pymongo>=4.18.1
|
|
10
16
|
Requires-Dist: opentelemetry-api>=1.45.0 ; extra == 'otel'
|
|
11
17
|
Requires-Python: >=3.14
|
|
@@ -16,7 +22,10 @@ Description-Content-Type: text/markdown
|
|
|
16
22
|
|
|
17
23
|
# Coherent client-side caching for PyMongo
|
|
18
24
|
|
|
19
|
-
[](https://pypi.org/project/client-query-cache/)
|
|
26
|
+
[](https://alessio-locatelli.github.io/client-query-cache/getting-started/installation/)
|
|
27
|
+
[](https://github.com/alessio-locatelli/client-query-cache/actions/workflows/test.yml)
|
|
28
|
+
[](https://pypi.org/project/client-query-cache/)
|
|
20
29
|
|
|
21
30
|
`client-query-cache` uses MongoDB change streams to keep cached reads coherent. It supports synchronous and asyncio applications without requiring a separate cache server.
|
|
22
31
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# Coherent client-side caching for PyMongo
|
|
2
2
|
|
|
3
|
-
[](https://pypi.org/project/client-query-cache/)
|
|
4
|
+
[](https://alessio-locatelli.github.io/client-query-cache/getting-started/installation/)
|
|
5
|
+
[](https://github.com/alessio-locatelli/client-query-cache/actions/workflows/test.yml)
|
|
6
|
+
[](https://pypi.org/project/client-query-cache/)
|
|
4
7
|
|
|
5
8
|
`client-query-cache` uses MongoDB change streams to keep cached reads coherent. It supports synchronous and asyncio applications without requiring a separate cache server.
|
|
6
9
|
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "client-query-cache"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.0"
|
|
4
4
|
description = "Client-side caching for PyMongo, kept coherent using MongoDB change streams."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.14"
|
|
7
|
+
license = "Apache-2.0"
|
|
7
8
|
classifiers = [
|
|
9
|
+
"Framework :: AsyncIO",
|
|
10
|
+
"Intended Audience :: Developers",
|
|
8
11
|
"Programming Language :: Python :: 3.14",
|
|
9
12
|
"Programming Language :: Python :: 3.15",
|
|
13
|
+
"Topic :: Database",
|
|
14
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
15
|
+
"Typing :: Typed",
|
|
10
16
|
]
|
|
11
17
|
dependencies = ["pymongo>=4.18.1"]
|
|
12
18
|
|
|
@@ -46,6 +52,7 @@ dev = [
|
|
|
46
52
|
"opentelemetry-api>=1.45.0",
|
|
47
53
|
"opentelemetry-sdk>=1.45.0",
|
|
48
54
|
"types-psutil>=7.2.2.20260906",
|
|
55
|
+
"psutil>=7.2.2",
|
|
49
56
|
"dnspython>=2.8.0",
|
|
50
57
|
"python-snappy>=0.7.3",
|
|
51
58
|
"pytest-xdist[psutil]>=3.8.0",
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "client-query-cache"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.0"
|
|
4
4
|
description = "Client-side caching for PyMongo, kept coherent using MongoDB change streams."
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Alessio Locatelli", email = "<software.development@secure.mailbox.org>" },
|
|
7
7
|
]
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
requires-python = ">=3.14"
|
|
10
|
+
license = "Apache-2.0"
|
|
10
11
|
classifiers = [
|
|
12
|
+
"Framework :: AsyncIO",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
11
14
|
"Programming Language :: Python :: 3.14",
|
|
12
15
|
"Programming Language :: Python :: 3.15",
|
|
16
|
+
"Topic :: Database",
|
|
17
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
18
|
+
"Typing :: Typed",
|
|
13
19
|
]
|
|
14
20
|
dependencies = ["pymongo>=4.18.1"]
|
|
15
21
|
|
|
@@ -45,6 +51,7 @@ dev = [
|
|
|
45
51
|
"opentelemetry-api>=1.45.0",
|
|
46
52
|
"opentelemetry-sdk>=1.45.0",
|
|
47
53
|
"types-psutil>=7.2.2.20260906",
|
|
54
|
+
"psutil>=7.2.2",
|
|
48
55
|
"dnspython>=2.8.0",
|
|
49
56
|
"python-snappy>=0.7.3",
|
|
50
57
|
"pytest-xdist[psutil]>=3.8.0",
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/manager.py
RENAMED
|
@@ -54,6 +54,8 @@ DEFAULT_MAX_ENTRY_BYTES = 1 * 1024 * 1024
|
|
|
54
54
|
|
|
55
55
|
_NOT_INDEXED = object()
|
|
56
56
|
_DEFAULT_DATABASE_AVAILABILITY = (True, 0)
|
|
57
|
+
_MISS = LookupResult(hit=False)
|
|
58
|
+
_DEFERRED_MISS = LookupResult(hit=False, deferred_miss=True)
|
|
57
59
|
|
|
58
60
|
logger = logging.getLogger(__name__)
|
|
59
61
|
|
|
@@ -313,6 +315,9 @@ class _CacheCoreLifecycle(_CacheCoreBase):
|
|
|
313
315
|
self._database_namespaces.clear()
|
|
314
316
|
logger.info("cache manager closed")
|
|
315
317
|
|
|
318
|
+
def record_miss(self) -> None:
|
|
319
|
+
self._statistics.record_miss()
|
|
320
|
+
|
|
316
321
|
def record_bypass(self, reason: BypassReason = BypassReason.UNSPECIFIED) -> None:
|
|
317
322
|
self._statistics.record_bypass(reason)
|
|
318
323
|
|
|
@@ -908,6 +913,7 @@ class _CacheCoreLookup(_CacheCoreBase):
|
|
|
908
913
|
discriminator: object,
|
|
909
914
|
*,
|
|
910
915
|
codec_options: CodecOptions[Any] | None = None,
|
|
916
|
+
defer_miss: bool = False,
|
|
911
917
|
) -> LookupResult:
|
|
912
918
|
self._ensure_active()
|
|
913
919
|
if not self._is_database_available(namespace.database):
|
|
@@ -917,18 +923,22 @@ class _CacheCoreLookup(_CacheCoreBase):
|
|
|
917
923
|
key = NamespaceCacheKey(namespace, canonical_discriminator)
|
|
918
924
|
entry = self._lru.peek(key)
|
|
919
925
|
if entry is None:
|
|
920
|
-
self.
|
|
921
|
-
return LookupResult(hit=False)
|
|
926
|
+
return self._namespace_miss(defer_miss=defer_miss)
|
|
922
927
|
state = self._namespace(namespace)
|
|
923
928
|
with self._namespace_section(state):
|
|
924
929
|
valid = (state.generation,) == entry.generation_key
|
|
925
930
|
if not valid:
|
|
926
|
-
self.
|
|
927
|
-
return LookupResult(hit=False)
|
|
931
|
+
return self._namespace_miss(defer_miss=defer_miss)
|
|
928
932
|
self._lru.touch(key)
|
|
929
933
|
self._statistics.record_hit()
|
|
930
934
|
return LookupResult(hit=True, value=decode_value(entry.value, codec_options))
|
|
931
935
|
|
|
936
|
+
def _namespace_miss(self, *, defer_miss: bool) -> LookupResult:
|
|
937
|
+
if defer_miss:
|
|
938
|
+
return _DEFERRED_MISS
|
|
939
|
+
self._statistics.record_miss()
|
|
940
|
+
return _MISS
|
|
941
|
+
|
|
932
942
|
def resolve_alias(
|
|
933
943
|
self,
|
|
934
944
|
namespace: NamespaceId,
|
|
@@ -209,15 +209,21 @@ class CachedCollection[DocumentType: Mapping[str, Any]]:
|
|
|
209
209
|
cache.current_index_generation(namespace),
|
|
210
210
|
)
|
|
211
211
|
)
|
|
212
|
+
discovers_unique_keys = identity is NO_IDENTITY and known_keys is None
|
|
212
213
|
lookup_result = cache.lookup_namespace(
|
|
213
|
-
namespace,
|
|
214
|
+
namespace,
|
|
215
|
+
discriminator,
|
|
216
|
+
codec_options=codec_options,
|
|
217
|
+
defer_miss=discovers_unique_keys,
|
|
214
218
|
)
|
|
215
219
|
if lookup_result.hit:
|
|
216
220
|
return cast("DocumentType | None", lookup_result.value)
|
|
217
|
-
if
|
|
221
|
+
if discovers_unique_keys:
|
|
218
222
|
unique_key_match = await self._match_unique_key(
|
|
219
223
|
filter_query, effective_collation
|
|
220
224
|
)
|
|
225
|
+
if unique_key_match is None and lookup_result.deferred_miss:
|
|
226
|
+
cache.record_miss()
|
|
221
227
|
if unique_key_match is not None:
|
|
222
228
|
key_definition, key_values = unique_key_match
|
|
223
229
|
return await self._find_one_by_unique_key(
|
|
@@ -209,15 +209,21 @@ class CachedCollection[DocumentType: Mapping[str, Any]]:
|
|
|
209
209
|
cache.current_index_generation(namespace),
|
|
210
210
|
)
|
|
211
211
|
)
|
|
212
|
+
discovers_unique_keys = identity is NO_IDENTITY and known_keys is None
|
|
212
213
|
lookup_result = cache.lookup_namespace(
|
|
213
|
-
namespace,
|
|
214
|
+
namespace,
|
|
215
|
+
discriminator,
|
|
216
|
+
codec_options=codec_options,
|
|
217
|
+
defer_miss=discovers_unique_keys,
|
|
214
218
|
)
|
|
215
219
|
if lookup_result.hit:
|
|
216
220
|
return cast("DocumentType | None", lookup_result.value)
|
|
217
|
-
if
|
|
221
|
+
if discovers_unique_keys:
|
|
218
222
|
unique_key_match = self._match_unique_key(
|
|
219
223
|
filter_query, effective_collation
|
|
220
224
|
)
|
|
225
|
+
if unique_key_match is None and lookup_result.deferred_miss:
|
|
226
|
+
cache.record_miss()
|
|
221
227
|
if unique_key_match is not None:
|
|
222
228
|
key_definition, key_values = unique_key_match
|
|
223
229
|
return self._find_one_by_unique_key(
|
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/__init__.py
RENAMED
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/canonical.py
RENAMED
|
File without changes
|
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/collation.py
RENAMED
|
File without changes
|
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/count_reads.py
RENAMED
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/cursor_capture.py
RENAMED
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/errors.py
RENAMED
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/find_one_reads.py
RENAMED
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/find_reads.py
RENAMED
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/identity_reads.py
RENAMED
|
File without changes
|
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/lifecycle.py
RENAMED
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/locking.py
RENAMED
|
File without changes
|
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/namespace.py
RENAMED
|
File without changes
|
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/projection.py
RENAMED
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/query_filters.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/snapshots.py
RENAMED
|
File without changes
|
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/stream_cost.py
RENAMED
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/stream_events.py
RENAMED
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/stream_health.py
RENAMED
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/stream_options.py
RENAMED
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/traversal.py
RENAMED
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/_core/unique_keys.py
RENAMED
|
File without changes
|
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/asynchronous/cursors.py
RENAMED
|
File without changes
|
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/asynchronous/manager.py
RENAMED
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/asynchronous/streams.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/synchronous/__init__.py
RENAMED
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/synchronous/cursors.py
RENAMED
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/synchronous/database.py
RENAMED
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/synchronous/manager.py
RENAMED
|
File without changes
|
{client_query_cache-0.5.0 → client_query_cache-0.6.0}/src/client_query_cache/synchronous/streams.py
RENAMED
|
File without changes
|