cachekit 0.7.0__tar.gz → 0.8.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.
- {cachekit-0.7.0 → cachekit-0.8.0}/Cargo.lock +127 -9
- {cachekit-0.7.0 → cachekit-0.8.0}/PKG-INFO +1 -1
- {cachekit-0.7.0 → cachekit-0.8.0}/pyproject.toml +1 -1
- {cachekit-0.7.0 → cachekit-0.8.0}/rust/Cargo.toml +2 -2
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/__init__.py +1 -1
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/base.py +19 -3
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/cachekitio/backend.py +10 -3
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/provider.py +63 -14
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/redis/provider.py +9 -2
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/decorators/wrapper.py +8 -7
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/reliability/adaptive_timeout.py +10 -8
- {cachekit-0.7.0 → cachekit-0.8.0}/Cargo.toml +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/LICENSE +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/README.md +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/rust/Makefile +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/rust/README.md +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/rust/TEST_EXPANSION_SUMMARY.md +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/rust/src/lib.rs +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/rust/src/python_bindings.rs +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/rust/supply-chain/audits.toml +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/rust/supply-chain/config.toml +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/rust/supply-chain/imports.lock +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/rust/tsan_suppressions.txt +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/__init__.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/base_config.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/cachekitio/__init__.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/cachekitio/client.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/cachekitio/config.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/cachekitio/error_handler.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/cachekitio/session.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/errors.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/file/__init__.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/file/backend.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/file/config.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/memcached/__init__.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/memcached/backend.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/memcached/config.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/memcached/error_handler.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/redis/__init__.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/redis/backend.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/redis/client.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/redis/config.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/backends/redis/error_handler.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/cache_handler.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/config/__init__.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/config/decorator.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/config/nested.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/config/settings.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/config/singleton.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/config/validation.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/decorators/__init__.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/decorators/intent.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/decorators/local_wrapper.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/decorators/main.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/decorators/orchestrator.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/decorators/session.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/decorators/stats_context.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/decorators/tenant_context.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/decorators/utils/__init__.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/di.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/hash_utils.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/health.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/hiredis_compat.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/imports.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/invalidation/__init__.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/invalidation/channel.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/invalidation/event.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/invalidation/redis_channel.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/key_generator.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/l1_cache.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/logging.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/monitoring/__init__.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/monitoring/correlation_tracking.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/monitoring/pool_monitor.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/monitoring/protocols.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/object_cache.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/py.typed +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/reliability/__init__.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/reliability/async_metrics.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/reliability/circuit_breaker.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/reliability/error_classification.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/reliability/load_control.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/reliability/metrics_collection.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/reliability/profiles.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/serializers/__init__.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/serializers/arrow_serializer.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/serializers/auto_serializer.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/serializers/base.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/serializers/encryption_wrapper.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/serializers/orjson_serializer.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/serializers/standard_serializer.py +0 -0
- {cachekit-0.7.0 → cachekit-0.8.0}/src/cachekit/serializers/wrapper.py +0 -0
|
@@ -17,6 +17,43 @@ version = "2.0.1"
|
|
|
17
17
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
18
18
|
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
|
19
19
|
|
|
20
|
+
[[package]]
|
|
21
|
+
name = "aead"
|
|
22
|
+
version = "0.5.2"
|
|
23
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
24
|
+
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
|
|
25
|
+
dependencies = [
|
|
26
|
+
"crypto-common",
|
|
27
|
+
"generic-array",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[[package]]
|
|
31
|
+
name = "aes"
|
|
32
|
+
version = "0.8.4"
|
|
33
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
34
|
+
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
|
35
|
+
dependencies = [
|
|
36
|
+
"cfg-if",
|
|
37
|
+
"cipher",
|
|
38
|
+
"cpufeatures",
|
|
39
|
+
"zeroize",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[[package]]
|
|
43
|
+
name = "aes-gcm"
|
|
44
|
+
version = "0.10.3"
|
|
45
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
46
|
+
checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
|
|
47
|
+
dependencies = [
|
|
48
|
+
"aead",
|
|
49
|
+
"aes",
|
|
50
|
+
"cipher",
|
|
51
|
+
"ctr",
|
|
52
|
+
"ghash",
|
|
53
|
+
"subtle",
|
|
54
|
+
"zeroize",
|
|
55
|
+
]
|
|
56
|
+
|
|
20
57
|
[[package]]
|
|
21
58
|
name = "ahash"
|
|
22
59
|
version = "0.8.12"
|
|
@@ -208,14 +245,17 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
|
|
208
245
|
|
|
209
246
|
[[package]]
|
|
210
247
|
name = "cachekit-core"
|
|
211
|
-
version = "0.
|
|
248
|
+
version = "0.2.0"
|
|
212
249
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
213
|
-
checksum = "
|
|
250
|
+
checksum = "9622b3c3e47fcf7abfde287c557e6171674e37842a2dd26fda03f63720542f17"
|
|
214
251
|
dependencies = [
|
|
252
|
+
"aes",
|
|
253
|
+
"aes-gcm",
|
|
215
254
|
"byteorder",
|
|
216
255
|
"bytes",
|
|
217
256
|
"cbindgen",
|
|
218
257
|
"generic-array",
|
|
258
|
+
"getrandom 0.2.17",
|
|
219
259
|
"hkdf",
|
|
220
260
|
"hmac",
|
|
221
261
|
"lz4_flex",
|
|
@@ -224,14 +264,14 @@ dependencies = [
|
|
|
224
264
|
"serde",
|
|
225
265
|
"serde_bytes",
|
|
226
266
|
"sha2",
|
|
227
|
-
"thiserror
|
|
267
|
+
"thiserror 2.0.18",
|
|
228
268
|
"xxhash-rust",
|
|
229
269
|
"zeroize",
|
|
230
270
|
]
|
|
231
271
|
|
|
232
272
|
[[package]]
|
|
233
273
|
name = "cachekit-rs"
|
|
234
|
-
version = "0.
|
|
274
|
+
version = "0.8.0"
|
|
235
275
|
dependencies = [
|
|
236
276
|
"cachekit-core",
|
|
237
277
|
"criterion",
|
|
@@ -312,6 +352,16 @@ dependencies = [
|
|
|
312
352
|
"half",
|
|
313
353
|
]
|
|
314
354
|
|
|
355
|
+
[[package]]
|
|
356
|
+
name = "cipher"
|
|
357
|
+
version = "0.4.4"
|
|
358
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
359
|
+
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
|
360
|
+
dependencies = [
|
|
361
|
+
"crypto-common",
|
|
362
|
+
"inout",
|
|
363
|
+
]
|
|
364
|
+
|
|
315
365
|
[[package]]
|
|
316
366
|
name = "clap"
|
|
317
367
|
version = "4.6.0"
|
|
@@ -444,6 +494,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
444
494
|
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
|
445
495
|
dependencies = [
|
|
446
496
|
"generic-array",
|
|
497
|
+
"rand_core 0.6.4",
|
|
447
498
|
"typenum",
|
|
448
499
|
]
|
|
449
500
|
|
|
@@ -463,6 +514,15 @@ version = "0.0.6"
|
|
|
463
514
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
464
515
|
checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2"
|
|
465
516
|
|
|
517
|
+
[[package]]
|
|
518
|
+
name = "ctr"
|
|
519
|
+
version = "0.9.2"
|
|
520
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
521
|
+
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
|
|
522
|
+
dependencies = [
|
|
523
|
+
"cipher",
|
|
524
|
+
]
|
|
525
|
+
|
|
466
526
|
[[package]]
|
|
467
527
|
name = "debugid"
|
|
468
528
|
version = "0.8.0"
|
|
@@ -639,8 +699,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
639
699
|
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
|
640
700
|
dependencies = [
|
|
641
701
|
"cfg-if",
|
|
702
|
+
"js-sys",
|
|
642
703
|
"libc",
|
|
643
704
|
"wasi",
|
|
705
|
+
"wasm-bindgen",
|
|
644
706
|
]
|
|
645
707
|
|
|
646
708
|
[[package]]
|
|
@@ -668,6 +730,16 @@ dependencies = [
|
|
|
668
730
|
"wasip3",
|
|
669
731
|
]
|
|
670
732
|
|
|
733
|
+
[[package]]
|
|
734
|
+
name = "ghash"
|
|
735
|
+
version = "0.5.1"
|
|
736
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
737
|
+
checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
|
|
738
|
+
dependencies = [
|
|
739
|
+
"opaque-debug",
|
|
740
|
+
"polyval",
|
|
741
|
+
]
|
|
742
|
+
|
|
671
743
|
[[package]]
|
|
672
744
|
name = "gimli"
|
|
673
745
|
version = "0.32.3"
|
|
@@ -820,6 +892,15 @@ dependencies = [
|
|
|
820
892
|
"str_stack",
|
|
821
893
|
]
|
|
822
894
|
|
|
895
|
+
[[package]]
|
|
896
|
+
name = "inout"
|
|
897
|
+
version = "0.1.4"
|
|
898
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
899
|
+
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
|
900
|
+
dependencies = [
|
|
901
|
+
"generic-array",
|
|
902
|
+
]
|
|
903
|
+
|
|
823
904
|
[[package]]
|
|
824
905
|
name = "is-terminal"
|
|
825
906
|
version = "0.4.17"
|
|
@@ -909,9 +990,9 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
|
|
909
990
|
|
|
910
991
|
[[package]]
|
|
911
992
|
name = "lz4_flex"
|
|
912
|
-
version = "0.
|
|
993
|
+
version = "0.12.2"
|
|
913
994
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
914
|
-
checksum = "
|
|
995
|
+
checksum = "90071f8077f8e40adfc4b7fe9cd495ce316263f19e75c2211eeff3fdf475a3d9"
|
|
915
996
|
dependencies = [
|
|
916
997
|
"twox-hash",
|
|
917
998
|
]
|
|
@@ -1006,6 +1087,12 @@ version = "11.1.5"
|
|
|
1006
1087
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1007
1088
|
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
|
|
1008
1089
|
|
|
1090
|
+
[[package]]
|
|
1091
|
+
name = "opaque-debug"
|
|
1092
|
+
version = "0.3.1"
|
|
1093
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1094
|
+
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
|
1095
|
+
|
|
1009
1096
|
[[package]]
|
|
1010
1097
|
name = "plotters"
|
|
1011
1098
|
version = "0.3.7"
|
|
@@ -1034,6 +1121,18 @@ dependencies = [
|
|
|
1034
1121
|
"plotters-backend",
|
|
1035
1122
|
]
|
|
1036
1123
|
|
|
1124
|
+
[[package]]
|
|
1125
|
+
name = "polyval"
|
|
1126
|
+
version = "0.6.2"
|
|
1127
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1128
|
+
checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
|
|
1129
|
+
dependencies = [
|
|
1130
|
+
"cfg-if",
|
|
1131
|
+
"cpufeatures",
|
|
1132
|
+
"opaque-debug",
|
|
1133
|
+
"universal-hash",
|
|
1134
|
+
]
|
|
1135
|
+
|
|
1037
1136
|
[[package]]
|
|
1038
1137
|
name = "portable-atomic"
|
|
1039
1138
|
version = "1.13.1"
|
|
@@ -1290,7 +1389,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
1290
1389
|
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
|
1291
1390
|
dependencies = [
|
|
1292
1391
|
"rand_chacha",
|
|
1293
|
-
"rand_core",
|
|
1392
|
+
"rand_core 0.9.5",
|
|
1294
1393
|
]
|
|
1295
1394
|
|
|
1296
1395
|
[[package]]
|
|
@@ -1300,7 +1399,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
1300
1399
|
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
|
1301
1400
|
dependencies = [
|
|
1302
1401
|
"ppv-lite86",
|
|
1303
|
-
"rand_core",
|
|
1402
|
+
"rand_core 0.9.5",
|
|
1403
|
+
]
|
|
1404
|
+
|
|
1405
|
+
[[package]]
|
|
1406
|
+
name = "rand_core"
|
|
1407
|
+
version = "0.6.4"
|
|
1408
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1409
|
+
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
|
1410
|
+
dependencies = [
|
|
1411
|
+
"getrandom 0.2.17",
|
|
1304
1412
|
]
|
|
1305
1413
|
|
|
1306
1414
|
[[package]]
|
|
@@ -1318,7 +1426,7 @@ version = "0.4.0"
|
|
|
1318
1426
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1319
1427
|
checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
|
|
1320
1428
|
dependencies = [
|
|
1321
|
-
"rand_core",
|
|
1429
|
+
"rand_core 0.9.5",
|
|
1322
1430
|
]
|
|
1323
1431
|
|
|
1324
1432
|
[[package]]
|
|
@@ -1804,6 +1912,16 @@ version = "0.2.4"
|
|
|
1804
1912
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1805
1913
|
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
|
|
1806
1914
|
|
|
1915
|
+
[[package]]
|
|
1916
|
+
name = "universal-hash"
|
|
1917
|
+
version = "0.5.1"
|
|
1918
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1919
|
+
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
|
|
1920
|
+
dependencies = [
|
|
1921
|
+
"crypto-common",
|
|
1922
|
+
"subtle",
|
|
1923
|
+
]
|
|
1924
|
+
|
|
1807
1925
|
[[package]]
|
|
1808
1926
|
name = "untrusted"
|
|
1809
1927
|
version = "0.9.0"
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cachekit"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.8.0"
|
|
8
8
|
description = "Production-ready Redis caching for Python with intelligent reliability features and Rust-powered performance"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "cachekit-rs"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.8.0"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
authors = ["cachekit Contributors"]
|
|
6
6
|
description = "High-performance storage engine for caching with compression and encryption"
|
|
@@ -20,7 +20,7 @@ crate-type = ["cdylib", "rlib"]
|
|
|
20
20
|
|
|
21
21
|
[dependencies]
|
|
22
22
|
# Compression, checksums, encryption (https://crates.io/crates/cachekit-core)
|
|
23
|
-
cachekit-core = { version = "
|
|
23
|
+
cachekit-core = { version = "0.2.0", features = ["compression", "checksum", "messagepack", "encryption"] }
|
|
24
24
|
|
|
25
25
|
# Python integration - optional for Rust-only builds
|
|
26
26
|
pyo3 = { workspace = true, optional = true }
|
|
@@ -188,10 +188,23 @@ class LockableBackend(Protocol):
|
|
|
188
188
|
- Local-only: SQLite, FileSystem (single-process locking)
|
|
189
189
|
- Not supported: HTTP (stateless), Memcached, S3
|
|
190
190
|
|
|
191
|
+
Contract — bare cache key:
|
|
192
|
+
``acquire_lock`` receives the **bare cache key**, identical to what
|
|
193
|
+
``get``/``set``/``delete`` receive. Callers MUST NOT append suffixes
|
|
194
|
+
like ``:lock`` before passing the key. Each backend owns its own
|
|
195
|
+
lock-namespace derivation:
|
|
196
|
+
|
|
197
|
+
- Redis: derives ``<key>:lock`` internally for the on-wire lock name.
|
|
198
|
+
- CachekitIO (SaaS): the lock endpoint is ``POST /v1/cache/{key}/lock`` —
|
|
199
|
+
no key derivation needed.
|
|
200
|
+
|
|
201
|
+
This convergence keeps the Python SDK byte-for-byte compatible with
|
|
202
|
+
the cachekit-rs and cachekit-ts SDKs at the protocol boundary.
|
|
203
|
+
|
|
191
204
|
Example:
|
|
192
205
|
>>> # Distributed locking pattern (async context):
|
|
193
206
|
>>> # if hasattr(backend, 'acquire_lock'):
|
|
194
|
-
>>> # async with backend.acquire_lock("
|
|
207
|
+
>>> # async with backend.acquire_lock("user:123:profile", timeout=30) as acquired:
|
|
195
208
|
>>> # if acquired:
|
|
196
209
|
>>> # result = expensive_computation()
|
|
197
210
|
"""
|
|
@@ -205,7 +218,10 @@ class LockableBackend(Protocol):
|
|
|
205
218
|
"""Acquire a distributed lock on key.
|
|
206
219
|
|
|
207
220
|
Args:
|
|
208
|
-
key:
|
|
221
|
+
key: Bare cache key (e.g., ``"user:123"``) — the SAME shape passed
|
|
222
|
+
to ``get``/``set``/``delete``. Implementations are responsible
|
|
223
|
+
for any internal lock-namespace derivation; callers MUST NOT
|
|
224
|
+
pre-suffix the key.
|
|
209
225
|
timeout: How long to hold the lock (seconds) before auto-release
|
|
210
226
|
blocking_timeout: Max time to wait for lock acquisition (None = non-blocking)
|
|
211
227
|
|
|
@@ -217,7 +233,7 @@ class LockableBackend(Protocol):
|
|
|
217
233
|
BackendError: If backend operation fails
|
|
218
234
|
|
|
219
235
|
Example:
|
|
220
|
-
>>> async with backend.acquire_lock("
|
|
236
|
+
>>> async with backend.acquire_lock("user:123:profile", timeout=30, blocking_timeout=5) as acquired: # doctest: +SKIP
|
|
221
237
|
... if acquired: # doctest: +SKIP
|
|
222
238
|
... # Lock held, safe to proceed
|
|
223
239
|
... pass # doctest: +SKIP
|
|
@@ -553,14 +553,21 @@ class CachekitIOBackend:
|
|
|
553
553
|
async def _try_acquire_lock(self, lock_key: str, timeout: float) -> str | None:
|
|
554
554
|
"""Single attempt at the SaaS lock endpoint. Returns lock_id, or None if held.
|
|
555
555
|
|
|
556
|
+
``lock_key`` is the bare cache key (LockableBackend contract). The SaaS lock
|
|
557
|
+
endpoint is ``POST /v1/cache/{key}/lock`` — no internal derivation is needed
|
|
558
|
+
because lock semantics live in the URL path, not the key namespace. Appending
|
|
559
|
+
``:lock`` here would push the SaaS validator past its 7-segment canonical key
|
|
560
|
+
budget and trigger a 400 at the edge.
|
|
561
|
+
|
|
556
562
|
Non-finite (NaN / ±inf) or non-positive ``timeout`` is clamped to 1ms — without
|
|
557
563
|
the guard, ``int(NaN)`` / ``int(inf)`` would raise outside ``BackendError`` and
|
|
558
564
|
escape the wrapper's degrade-to-no-lock branch.
|
|
559
565
|
|
|
560
566
|
Raises:
|
|
561
|
-
BackendError: For AUTHENTICATION and PERMANENT failures (bad key, bad
|
|
562
|
-
format) — polling won't recover and the wrapper
|
|
563
|
-
TRANSIENT/TIMEOUT/UNKNOWN are swallowed as
|
|
567
|
+
BackendError: For AUTHENTICATION and PERMANENT failures (bad key, bad key
|
|
568
|
+
format rejected by SaaS validator) — polling won't recover and the wrapper
|
|
569
|
+
degrades to no-lock execution. TRANSIENT/TIMEOUT/UNKNOWN are swallowed as
|
|
570
|
+
None so the polling loop can retry.
|
|
564
571
|
"""
|
|
565
572
|
# Clamp non-positive / non-finite timeouts before the int conversion.
|
|
566
573
|
# int(NaN) / int(inf) raise ValueError/OverflowError that aren't BackendError, so
|
|
@@ -104,36 +104,85 @@ class DefaultCacheClientProvider(CacheClientProvider):
|
|
|
104
104
|
class DefaultBackendProvider(BackendProviderInterface):
|
|
105
105
|
"""Default backend provider with env-based auto-detection.
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
1. CACHEKIT_API_KEY
|
|
109
|
-
2. CACHEKIT_REDIS_URL
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
107
|
+
Selection is by a single, unambiguous environment signal. Priority order:
|
|
108
|
+
1. CACHEKIT_API_KEY → CachekitIOBackend (SaaS)
|
|
109
|
+
2. CACHEKIT_REDIS_URL → RedisBackend
|
|
110
|
+
3. CACHEKIT_MEMCACHED_SERVERS → MemcachedBackend
|
|
111
|
+
4. CACHEKIT_FILE_CACHE_DIR → FileBackend
|
|
112
|
+
5. REDIS_URL, or nothing set → RedisBackend (12-factor / localhost default)
|
|
113
|
+
|
|
114
|
+
Setting more than one of the four prefixed selectors (1-4) raises
|
|
115
|
+
``ConfigurationError`` — auto-detection must be unambiguous; pass
|
|
116
|
+
``backend=`` explicitly to override. The non-prefixed ``REDIS_URL`` is only a
|
|
117
|
+
fallback and never counts as a conflict (12-factor convention).
|
|
118
|
+
|
|
119
|
+
CachekitIO/Memcached/File backends are stateless singletons (cached). Redis
|
|
120
|
+
backends are per-request tenant-scoped wrappers (not cached —
|
|
121
|
+
RedisBackendProvider.get_backend() reads the tenant_context ContextVar). For
|
|
122
|
+
single-tenant deployments (default), tenant_context is set to "default".
|
|
113
123
|
"""
|
|
114
124
|
|
|
125
|
+
# Prefixed selectors in priority order. REDIS_URL is the implicit fallback
|
|
126
|
+
# and intentionally excluded so it never triggers a conflict.
|
|
127
|
+
_SELECTORS = (
|
|
128
|
+
("CACHEKIT_API_KEY", "cachekitio"),
|
|
129
|
+
("CACHEKIT_REDIS_URL", "redis"),
|
|
130
|
+
("CACHEKIT_MEMCACHED_SERVERS", "memcached"),
|
|
131
|
+
("CACHEKIT_FILE_CACHE_DIR", "file"),
|
|
132
|
+
)
|
|
133
|
+
|
|
115
134
|
def __init__(self):
|
|
116
135
|
self._cachekitio_backend = None
|
|
117
136
|
self._redis_provider = None
|
|
137
|
+
self._memcached_backend = None
|
|
138
|
+
self._file_backend = None
|
|
118
139
|
|
|
119
|
-
def
|
|
120
|
-
"""
|
|
140
|
+
def _detect(self):
|
|
141
|
+
"""Return the chosen backend key, or None to use the Redis fallback.
|
|
121
142
|
|
|
122
|
-
|
|
123
|
-
Redis backends are per-request tenant-scoped wrappers (not cached —
|
|
124
|
-
RedisBackendProvider.get_backend() reads tenant_context ContextVar).
|
|
143
|
+
Raises ConfigurationError if more than one prefixed selector is set.
|
|
125
144
|
"""
|
|
126
145
|
import os
|
|
127
146
|
|
|
128
|
-
|
|
129
|
-
if
|
|
147
|
+
matched = [(env_var, key) for env_var, key in self._SELECTORS if os.environ.get(env_var)]
|
|
148
|
+
if len(matched) > 1:
|
|
149
|
+
from cachekit.config.validation import ConfigurationError
|
|
150
|
+
|
|
151
|
+
names = ", ".join(env_var for env_var, _ in matched)
|
|
152
|
+
raise ConfigurationError(
|
|
153
|
+
f"Ambiguous backend auto-detection: multiple selectors set ({names}). "
|
|
154
|
+
"Set exactly one of CACHEKIT_API_KEY / CACHEKIT_REDIS_URL / "
|
|
155
|
+
"CACHEKIT_MEMCACHED_SERVERS / CACHEKIT_FILE_CACHE_DIR, or pass backend= explicitly."
|
|
156
|
+
)
|
|
157
|
+
return matched[0][1] if matched else None
|
|
158
|
+
|
|
159
|
+
def get_backend(self):
|
|
160
|
+
"""Get backend instance, auto-detected from environment on first call."""
|
|
161
|
+
choice = self._detect()
|
|
162
|
+
|
|
163
|
+
if choice == "cachekitio":
|
|
130
164
|
if self._cachekitio_backend is None:
|
|
131
165
|
from cachekit.backends.cachekitio import CachekitIOBackend
|
|
132
166
|
|
|
133
167
|
self._cachekitio_backend = CachekitIOBackend()
|
|
134
168
|
return self._cachekitio_backend
|
|
135
169
|
|
|
136
|
-
|
|
170
|
+
if choice == "memcached":
|
|
171
|
+
if self._memcached_backend is None:
|
|
172
|
+
from cachekit.backends.memcached import MemcachedBackend, MemcachedBackendConfig
|
|
173
|
+
|
|
174
|
+
self._memcached_backend = MemcachedBackend(MemcachedBackendConfig.from_env())
|
|
175
|
+
return self._memcached_backend
|
|
176
|
+
|
|
177
|
+
if choice == "file":
|
|
178
|
+
if self._file_backend is None:
|
|
179
|
+
from cachekit.backends.file import FileBackend, FileBackendConfig
|
|
180
|
+
|
|
181
|
+
self._file_backend = FileBackend(FileBackendConfig.from_env())
|
|
182
|
+
return self._file_backend
|
|
183
|
+
|
|
184
|
+
# choice == "redis" (explicit CACHEKIT_REDIS_URL) or None (REDIS_URL / localhost fallback).
|
|
185
|
+
# Tenant-scoped: call the provider each time so it re-reads tenant_context.
|
|
137
186
|
if self._redis_provider is None:
|
|
138
187
|
from cachekit.backends.redis.config import RedisBackendConfig
|
|
139
188
|
from cachekit.backends.redis.provider import RedisBackendProvider, tenant_context
|
|
@@ -327,7 +327,10 @@ class PerRequestRedisBackend:
|
|
|
327
327
|
"""Acquire distributed lock (LockableBackend protocol).
|
|
328
328
|
|
|
329
329
|
Args:
|
|
330
|
-
key:
|
|
330
|
+
key: Bare cache key (will be tenant-scoped and ``:lock``-suffixed internally).
|
|
331
|
+
The LockableBackend protocol passes the same key as ``get``/``set``/``delete``;
|
|
332
|
+
the ``:lock`` namespace is a Redis-backend implementation detail kept
|
|
333
|
+
on-wire for zero-migration compatibility with existing deployments.
|
|
331
334
|
timeout: How long to hold lock (seconds) before auto-release
|
|
332
335
|
blocking_timeout: Max time to wait for lock (None = non-blocking)
|
|
333
336
|
|
|
@@ -343,7 +346,11 @@ class PerRequestRedisBackend:
|
|
|
343
346
|
"""
|
|
344
347
|
import asyncio
|
|
345
348
|
|
|
346
|
-
|
|
349
|
+
# Derive the on-wire Redis lock name from the bare cache key: ``<scoped_key>:lock``.
|
|
350
|
+
# Keeping this suffix on the wire preserves compatibility with existing Redis
|
|
351
|
+
# deployments — the lock identity didn't change, only the protocol boundary
|
|
352
|
+
# (the wrapper no longer pollutes the cache_key passed in).
|
|
353
|
+
scoped_key = f"{self._scoped_key(key)}:lock"
|
|
347
354
|
lock = None
|
|
348
355
|
try:
|
|
349
356
|
from redis.lock import Lock
|
|
@@ -1060,8 +1060,11 @@ def create_cache_wrapper(
|
|
|
1060
1060
|
)
|
|
1061
1061
|
|
|
1062
1062
|
# CACHE MISS - Use distributed lock to prevent thundering herd
|
|
1063
|
-
# This ensures only one request executes the function while others wait
|
|
1064
|
-
|
|
1063
|
+
# This ensures only one request executes the function while others wait.
|
|
1064
|
+
# LockableBackend protocol contract: pass the bare cache_key. Each backend
|
|
1065
|
+
# owns its internal lock-namespace derivation (Redis: ``<key>:lock``; SaaS:
|
|
1066
|
+
# ``POST /v1/cache/{key}/lock``). Appending here would pollute the SaaS
|
|
1067
|
+
# 7-segment canonical key and 400 at the edge.
|
|
1065
1068
|
lock_timeout = 30.0 # Lock expires after 30 seconds to prevent deadlock
|
|
1066
1069
|
blocking_timeout = 5.0 # Wait up to 5 seconds to acquire lock
|
|
1067
1070
|
|
|
@@ -1070,7 +1073,7 @@ def create_cache_wrapper(
|
|
|
1070
1073
|
try:
|
|
1071
1074
|
# Use backend's async lock protocol
|
|
1072
1075
|
async with _backend.acquire_lock(
|
|
1073
|
-
|
|
1076
|
+
cache_key,
|
|
1074
1077
|
timeout=lock_timeout,
|
|
1075
1078
|
blocking_timeout=blocking_timeout,
|
|
1076
1079
|
) as lock_acquired:
|
|
@@ -1102,9 +1105,7 @@ def create_cache_wrapper(
|
|
|
1102
1105
|
else:
|
|
1103
1106
|
# Lock timeout - double-check cache before giving up
|
|
1104
1107
|
# Another request may have populated it while we waited
|
|
1105
|
-
logger().warning(
|
|
1106
|
-
f"Failed to acquire lock for {cache_key} (lock_key={lock_key}) after {blocking_timeout}s, checking cache"
|
|
1107
|
-
)
|
|
1108
|
+
logger().warning(f"Failed to acquire lock for {cache_key} after {blocking_timeout}s, checking cache")
|
|
1108
1109
|
try:
|
|
1109
1110
|
cached_data = await operation_handler.cache_handler.get_async(cache_key) # type: ignore[attr-defined]
|
|
1110
1111
|
if cached_data is not None:
|
|
@@ -1202,7 +1203,7 @@ def create_cache_wrapper(
|
|
|
1202
1203
|
raise e.original_exception from e
|
|
1203
1204
|
|
|
1204
1205
|
# Lock operation failed - execute without lock
|
|
1205
|
-
logger().warning(f"Lock operation failed for {cache_key}
|
|
1206
|
+
logger().warning(f"Lock operation failed for {cache_key}, executing without lock: {e}")
|
|
1206
1207
|
# Fall through to execute without locking
|
|
1207
1208
|
|
|
1208
1209
|
# Execute without locking (either backend doesn't support it or lock failed)
|
|
@@ -161,11 +161,12 @@ class AdaptiveTimeoutManager:
|
|
|
161
161
|
# Get adaptive timeouts for lock operations
|
|
162
162
|
lock_timeout, blocking_timeout = timeout_manager.get_lock_timeouts()
|
|
163
163
|
|
|
164
|
-
|
|
165
|
-
|
|
164
|
+
async with backend.acquire_lock(
|
|
165
|
+
cache_key,
|
|
166
166
|
timeout=lock_timeout,
|
|
167
|
-
blocking_timeout=blocking_timeout
|
|
168
|
-
)
|
|
167
|
+
blocking_timeout=blocking_timeout,
|
|
168
|
+
) as acquired:
|
|
169
|
+
...
|
|
169
170
|
|
|
170
171
|
Examples:
|
|
171
172
|
Create manager with defaults:
|
|
@@ -360,11 +361,12 @@ class AdaptiveTimeoutManager:
|
|
|
360
361
|
|
|
361
362
|
Example:
|
|
362
363
|
lock_timeout, blocking_timeout = manager.get_lock_timeouts()
|
|
363
|
-
|
|
364
|
-
|
|
364
|
+
async with backend.acquire_lock(
|
|
365
|
+
cache_key,
|
|
365
366
|
timeout=lock_timeout,
|
|
366
|
-
blocking_timeout=blocking_timeout
|
|
367
|
-
)
|
|
367
|
+
blocking_timeout=blocking_timeout,
|
|
368
|
+
) as acquired:
|
|
369
|
+
...
|
|
368
370
|
"""
|
|
369
371
|
with self._lock:
|
|
370
372
|
return (self._current_lock_timeout, self._current_blocking_timeout)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|