django-cachex 0.4.2__tar.gz → 0.5.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.
Files changed (85) hide show
  1. {django_cachex-0.4.2 → django_cachex-0.5.0}/.gitignore +0 -5
  2. {django_cachex-0.4.2 → django_cachex-0.5.0}/PKG-INFO +4 -15
  3. {django_cachex-0.4.2 → django_cachex-0.5.0}/README.md +3 -12
  4. {django_cachex-0.4.2 → django_cachex-0.5.0}/benchmarks/README.md +24 -37
  5. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/__init__.py +1 -9
  6. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/adapters/__init__.py +2 -22
  7. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/adapters/pipeline.py +2 -4
  8. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/adapters/protocols.py +0 -1
  9. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/adapters/valkey_py.py +2 -2
  10. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/cache/__init__.py +0 -8
  11. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/cache/resp.py +0 -1
  12. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/cache/stream.py +1 -1
  13. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/exceptions.py +1 -1
  14. django_cachex-0.5.0/django_cachex/lock.py +11 -0
  15. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/semaphore.py +1 -1
  16. {django_cachex-0.4.2 → django_cachex-0.5.0}/pyproject.toml +2 -14
  17. django_cachex-0.4.2/crates/django-cachex-redis-rs/LICENSE +0 -21
  18. django_cachex-0.4.2/crates/django-cachex-redis-rs/README.md +0 -23
  19. django_cachex-0.4.2/crates/django-cachex-redis-rs/pyproject.toml +0 -45
  20. django_cachex-0.4.2/django_cachex/adapters/_async_helpers.py +0 -150
  21. django_cachex-0.4.2/django_cachex/adapters/redis_rs.py +0 -196
  22. django_cachex-0.4.2/django_cachex/cache/redis_rs.py +0 -38
  23. django_cachex-0.4.2/django_cachex/lock.py +0 -291
  24. {django_cachex-0.4.2 → django_cachex-0.5.0}/LICENSE +0 -0
  25. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/adapters/_pipeline_parsers.py +0 -0
  26. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/adapters/redis_py.py +0 -0
  27. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/adapters/valkey_glide.py +0 -0
  28. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/__init__.py +0 -0
  29. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/admin.py +0 -0
  30. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/apps.py +0 -0
  31. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/cas.py +0 -0
  32. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/helpers.py +0 -0
  33. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/models.py +0 -0
  34. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/queryset.py +0 -0
  35. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/templates/admin/django_cachex/cache/change_form.html +0 -0
  36. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/templates/admin/django_cachex/cache/change_list.html +0 -0
  37. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/templates/admin/django_cachex/cache/styles.css +0 -0
  38. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/templates/admin/django_cachex/key/_key_detail_actions.html +0 -0
  39. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/templates/admin/django_cachex/key/add_form.html +0 -0
  40. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/templates/admin/django_cachex/key/change_form.html +0 -0
  41. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/templates/admin/django_cachex/key/change_list.html +0 -0
  42. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/templates/admin/django_cachex/key/key_detail_hash.html +0 -0
  43. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/templates/admin/django_cachex/key/key_detail_list.html +0 -0
  44. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/templates/admin/django_cachex/key/key_detail_pagination.html +0 -0
  45. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/templates/admin/django_cachex/key/key_detail_set.html +0 -0
  46. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/templates/admin/django_cachex/key/key_detail_stream.html +0 -0
  47. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/templates/admin/django_cachex/key/key_detail_string.html +0 -0
  48. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/templates/admin/django_cachex/key/key_detail_value_input.html +0 -0
  49. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/templates/admin/django_cachex/key/key_detail_zset.html +0 -0
  50. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/views/__init__.py +0 -0
  51. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/views/base.py +0 -0
  52. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/views/cache_detail.py +0 -0
  53. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/views/key_add.py +0 -0
  54. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/admin/views/key_detail.py +0 -0
  55. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/cache/_semaphore_lua.py +0 -0
  56. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/cache/base.py +0 -0
  57. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/cache/database.py +0 -0
  58. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/cache/locmem.py +0 -0
  59. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/cache/redis_py.py +0 -0
  60. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/cache/tiered.py +0 -0
  61. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/cache/valkey_glide.py +0 -0
  62. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/cache/valkey_py.py +0 -0
  63. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/compressors/__init__.py +0 -0
  64. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/compressors/base.py +0 -0
  65. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/compressors/gzip.py +0 -0
  66. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/compressors/lz4.py +0 -0
  67. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/compressors/lzma.py +0 -0
  68. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/compressors/zlib.py +0 -0
  69. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/compressors/zstd.py +0 -0
  70. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/py.typed +0 -0
  71. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/script.py +0 -0
  72. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/serializers/__init__.py +0 -0
  73. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/serializers/base.py +0 -0
  74. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/serializers/json.py +0 -0
  75. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/serializers/msgpack.py +0 -0
  76. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/serializers/orjson.py +0 -0
  77. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/serializers/ormsgpack.py +0 -0
  78. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/serializers/pickle.py +0 -0
  79. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/stampede.py +0 -0
  80. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/types.py +0 -0
  81. {django_cachex-0.4.2 → django_cachex-0.5.0}/django_cachex/utils.py +0 -0
  82. {django_cachex-0.4.2 → django_cachex-0.5.0}/examples/README.md +0 -0
  83. {django_cachex-0.4.2 → django_cachex-0.5.0}/examples/full/README.md +0 -0
  84. {django_cachex-0.4.2 → django_cachex-0.5.0}/examples/full/pyproject.toml +0 -0
  85. {django_cachex-0.4.2 → django_cachex-0.5.0}/examples/simple/README.md +0 -0
@@ -35,8 +35,3 @@ CLAUDE.md
35
35
  site/
36
36
  *.sqlite3
37
37
  docs/superpowers/
38
-
39
- # Rust build artifacts
40
- target/
41
- django_cachex/adapters/_redis_rs*.so
42
- crates/django-cachex-redis-rs/python/django_cachex/adapters/_redis_rs*.so
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: django-cachex
3
- Version: 0.4.2
3
+ Version: 0.5.0
4
4
  Summary: Full featured Valkey and Redis cache backend for Django
5
5
  Project-URL: Homepage, https://github.com/oliverhaas/django-cachex
6
6
  Project-URL: Documentation, https://oliverhaas.github.io/django-cachex/
@@ -43,8 +43,6 @@ Provides-Extra: ormsgpack
43
43
  Requires-Dist: ormsgpack>=1.10.0; extra == 'ormsgpack'
44
44
  Provides-Extra: redis-py
45
45
  Requires-Dist: redis<9,>=6; extra == 'redis-py'
46
- Provides-Extra: redis-rs
47
- Requires-Dist: django-cachex-redis-rs==0.4.2; extra == 'redis-rs'
48
46
  Provides-Extra: valkey-glide
49
47
  Requires-Dist: valkey-glide-sync<3,>=2.3; extra == 'valkey-glide'
50
48
  Requires-Dist: valkey-glide<3,>=2.3; extra == 'valkey-glide'
@@ -90,7 +88,6 @@ CACHES = {
90
88
  - Cache stampede prevention (TTL-based XFetch).
91
89
  - Two composite backends: `StreamCache` (cross-pod stream-synchronized in-memory cache) and `TieredCache` (L1/L2 with TTL propagation).
92
90
  - Django `LocMemCache` and `DatabaseCache` extensions with the same data-structure ops and admin support.
93
- - Optional Rust I/O driver (PyO3 + tokio + redis-rs) under the same `RespCache` API. Free-threaded CPython (3.14t) supported. Experimental.
94
91
  - Optional `valkey-glide` adapter: Valkey's official Rust-cored client, exposed as `ValkeyGlideCache`. Experimental.
95
92
  - Django admin UI for browsing keys, inspecting values, editing, and flushing. See below.
96
93
 
@@ -132,17 +129,9 @@ Full documentation at [oliverhaas.github.io/django-cachex](https://oliverhaas.gi
132
129
  - Valkey 7.0+ or Redis 6.0+ on the server (the admin's compare-and-swap
133
130
  edits use `SET ... KEEPTTL`, which lands in Redis 6.0)
134
131
 
135
- The Rust I/O driver is optional and experimental: interfaces and
132
+ The `valkey-glide` adapter is optional and experimental: interfaces and
136
133
  behavior may still change, and it has seen less production testing than
137
- the redis-py/valkey-py paths. To opt in, install with the `redis-rs`
138
- extra (`pip install django-cachex[redis-rs]`); this pulls in the
139
- `django-cachex-redis-rs` companion package. Prebuilt wheels are published
140
- for Linux x86_64, Linux aarch64, macOS arm64, and Windows amd64, on
141
- both cp314 and cp314t (free-threaded). Without the extra, the
142
- `RedisRsCache` backends are unavailable but everything else works.
143
-
144
- The `valkey-glide` adapter is also optional and experimental, with the
145
- same caveats. Install with the `valkey-glide` extra
134
+ the redis-py/valkey-py paths. Install with the `valkey-glide` extra
146
135
  (`pip install django-cachex[valkey-glide]`) to enable
147
136
  `ValkeyGlideCache`; it pulls in `valkey-glide-sync` and `valkey-glide`,
148
137
  the official Rust-cored Valkey client. cp314 GIL only; no free-threaded
@@ -154,7 +143,7 @@ client).
154
143
 
155
144
  This project started from [django-redis](https://github.com/jazzband/django-redis) and Django's official [Redis cache backend](https://docs.djangoproject.com/en/stable/topics/cache/#redis). Some serializer and compressor utility code is derived from django-redis, licensed under BSD-3-Clause. The admin UI was inspired by [django-redisboard](https://github.com/ionelmc/django-redisboard).
156
145
 
157
- The Rust I/O driver and async bridge are heavily inspired by, and in places directly adapted from, [django-vcache](https://gitlab.com/glitchtip/django-vcache) (MIT, by David Burke / GlitchTip). The fork-safe tokio runtime, the `RedisRsAwaitable` deferred-loop-binding pattern, and the multiplexed-connection design all originate there.
146
+ The ASGI benchmark follows the shape of [django-vcache](https://gitlab.com/glitchtip/django-vcache)'s `bench_compare.py` (MIT, by David Burke / GlitchTip), so the numbers are directly comparable.
158
147
 
159
148
  I also want to mention [django-valkey](https://github.com/django-commons/django-valkey) and [dj-cache-panel](https://github.com/yassi/dj-cache-panel), which I never really used, but are newer and interesting efforts of similar goals as this package has.
160
149
 
@@ -36,7 +36,6 @@ CACHES = {
36
36
  - Cache stampede prevention (TTL-based XFetch).
37
37
  - Two composite backends: `StreamCache` (cross-pod stream-synchronized in-memory cache) and `TieredCache` (L1/L2 with TTL propagation).
38
38
  - Django `LocMemCache` and `DatabaseCache` extensions with the same data-structure ops and admin support.
39
- - Optional Rust I/O driver (PyO3 + tokio + redis-rs) under the same `RespCache` API. Free-threaded CPython (3.14t) supported. Experimental.
40
39
  - Optional `valkey-glide` adapter: Valkey's official Rust-cored client, exposed as `ValkeyGlideCache`. Experimental.
41
40
  - Django admin UI for browsing keys, inspecting values, editing, and flushing. See below.
42
41
 
@@ -78,17 +77,9 @@ Full documentation at [oliverhaas.github.io/django-cachex](https://oliverhaas.gi
78
77
  - Valkey 7.0+ or Redis 6.0+ on the server (the admin's compare-and-swap
79
78
  edits use `SET ... KEEPTTL`, which lands in Redis 6.0)
80
79
 
81
- The Rust I/O driver is optional and experimental: interfaces and
80
+ The `valkey-glide` adapter is optional and experimental: interfaces and
82
81
  behavior may still change, and it has seen less production testing than
83
- the redis-py/valkey-py paths. To opt in, install with the `redis-rs`
84
- extra (`pip install django-cachex[redis-rs]`); this pulls in the
85
- `django-cachex-redis-rs` companion package. Prebuilt wheels are published
86
- for Linux x86_64, Linux aarch64, macOS arm64, and Windows amd64, on
87
- both cp314 and cp314t (free-threaded). Without the extra, the
88
- `RedisRsCache` backends are unavailable but everything else works.
89
-
90
- The `valkey-glide` adapter is also optional and experimental, with the
91
- same caveats. Install with the `valkey-glide` extra
82
+ the redis-py/valkey-py paths. Install with the `valkey-glide` extra
92
83
  (`pip install django-cachex[valkey-glide]`) to enable
93
84
  `ValkeyGlideCache`; it pulls in `valkey-glide-sync` and `valkey-glide`,
94
85
  the official Rust-cored Valkey client. cp314 GIL only; no free-threaded
@@ -100,7 +91,7 @@ client).
100
91
 
101
92
  This project started from [django-redis](https://github.com/jazzband/django-redis) and Django's official [Redis cache backend](https://docs.djangoproject.com/en/stable/topics/cache/#redis). Some serializer and compressor utility code is derived from django-redis, licensed under BSD-3-Clause. The admin UI was inspired by [django-redisboard](https://github.com/ionelmc/django-redisboard).
102
93
 
103
- The Rust I/O driver and async bridge are heavily inspired by, and in places directly adapted from, [django-vcache](https://gitlab.com/glitchtip/django-vcache) (MIT, by David Burke / GlitchTip). The fork-safe tokio runtime, the `RedisRsAwaitable` deferred-loop-binding pattern, and the multiplexed-connection design all originate there.
94
+ The ASGI benchmark follows the shape of [django-vcache](https://gitlab.com/glitchtip/django-vcache)'s `bench_compare.py` (MIT, by David Burke / GlitchTip), so the numbers are directly comparable.
104
95
 
105
96
  I also want to mention [django-valkey](https://github.com/django-commons/django-valkey) and [dj-cache-panel](https://github.com/yassi/dj-cache-panel), which I never really used, but are newer and interesting efforts of similar goals as this package has.
106
97
 
@@ -15,16 +15,15 @@ depends on letting workloads run).
15
15
  - `redis-py+hiredis`: C parser
16
16
  - `valkey-py`: pure-Python parser
17
17
  - `valkey-py+libvalkey`: C parser
18
- - `redis-rs`: our Rust extension adapter (PyO3 bindings around the
19
- `redis-rs` crate, multiplexed Tokio transport)
20
- - `valkey-glide`: `valkey/valkey-glide` Python wheels (also Rust-cored,
21
- but with a thread-pool transport instead of multiplexed). Only available
22
- on cp314 GIL. No cp314t (free-threaded) wheels yet.
18
+ - `valkey-glide`: `valkey/valkey-glide` Python wheels, Rust-cored with a
19
+ thread-pool transport. Only available on cp314 GIL. No cp314t
20
+ (free-threaded) wheels yet.
23
21
  - `django (builtin)`: Django's official built-in `django.core.cache.backends.redis.RedisCache`
24
22
  (since 4.0). Not the third-party `jazzband/django-redis` package, which is
25
23
  unrelated. Included as an external reference point.
26
24
 
27
- **Serializers** (with `redis-rs` adapter, since adapter overhead is smallest there):
25
+ **Serializers** (with the `valkey-py+libvalkey` adapter, the fastest one that
26
+ is always installed):
28
27
 
29
28
  - `pickle`: stdlib default
30
29
  - `json`: Django's `DjangoJSONEncoder`
@@ -32,7 +31,7 @@ depends on letting workloads run).
32
31
  - `orjson`: Rust-backed JSON
33
32
  - `ormsgpack`: Rust-backed MessagePack
34
33
 
35
- **Compressors** get two views, both with `redis-rs` + `pickle`:
34
+ **Compressors** get two views, both with `valkey-py+libvalkey` + `pickle`:
36
35
 
37
36
  - *Macro* (`test_compressors_macro`): end-to-end Django cache ops on a
38
37
  ~14 KiB queryset-shaped payload. Captures the cost of compress/decompress
@@ -128,7 +127,7 @@ uv run pytest benchmarks/test_throughput.py::test_adapters_async_concurrent -c b
128
127
  uv run pytest benchmarks/test_throughput.py::test_adapters_asgi -c benchmarks/pytest.ini
129
128
 
130
129
  # A single config
131
- uv run pytest 'benchmarks/test_throughput.py::test_adapters_sync[redis-rs]' -c benchmarks/pytest.ini
130
+ uv run pytest 'benchmarks/test_throughput.py::test_adapters_sync[valkey-glide]' -c benchmarks/pytest.ini
132
131
  ```
133
132
 
134
133
  `test_compressors_micro` is the only test that doesn't need Docker, which
@@ -141,7 +140,7 @@ A summary table prints at the end of the session.
141
140
 
142
141
  - **No xdist.** Parallel runs make timings noisy; benchmarks run sequentially.
143
142
  - **Redis vs Valkey.** Each adapter is paired with its natural server
144
- (redis-py → redis, valkey-py / redis-rs / valkey-glide → valkey,
143
+ (redis-py → redis, valkey-py / valkey-glide → valkey,
145
144
  django (builtin) → redis). Cross-pairings are intentionally not in the
146
145
  matrix: both servers speak the same protocol, so the comparison is
147
146
  mostly a wash.
@@ -164,7 +163,6 @@ run; ordering is what matters.
164
163
  | redis-py+hiredis | 2,332 | 2,480 | 2,281 | 1,515 | 1,345 | 2,506 | 1,209 | 51 |
165
164
  | valkey-py | 2,783 | 2,956 | 2,695 | 1,595 | 1,418 | 2,978 | 1,437 | 109 |
166
165
  | valkey-py+libvalkey | 2,821 | 2,973 | 2,741 | 1,695 | 1,508 | 3,040 | 1,465 | 48 |
167
- | **redis-rs** | 9,651 | 13,396 | 11,151 | 2,967 | 4,520 | 13,226 | 5,894 | 29 |
168
166
  | **valkey-glide** | 6,982 | 8,783 | 6,982 | 2,030 | 1,916 | 8,901 | 3,994 | 29 |
169
167
  | django (builtin) | 2,307 | 2,459 | 2,269 | 1,501 | 1,347 | 1,947 | 1,199 | 51 |
170
168
 
@@ -178,7 +176,6 @@ One cache op per request through the full middleware/URL/view path.
178
176
  | redis-py+hiredis | 1,065 | 1,181 | 1,131 | 866 | 807 | 1,168 | 1,173 |
179
177
  | valkey-py | 1,089 | 1,299 | 1,241 | 891 | 836 | 1,276 | 1,282 |
180
178
  | valkey-py+libvalkey | 1,053 | 1,306 | 1,248 | 924 | 861 | 1,290 | 1,300 |
181
- | **redis-rs** | 1,382 | 2,044 | 2,001 | 1,265 | 1,490 | 1,995 | 2,005 |
182
179
  | **valkey-glide** | 1,237 | 1,863 | 1,753 | 1,023 | 994 | 1,816 | 1,838 |
183
180
  | django (builtin) | 861 | 1,177 | 1,133 | 859 | 806 | 1,016 | 1,169 |
184
181
 
@@ -192,7 +189,6 @@ One `await` at a time.
192
189
  | redis-py+hiredis | 1,815 | 1,913 | 1,729 | 1,199 | 849 | 1,911 | 915 |
193
190
  | valkey-py | 2,080 | 2,184 | 2,039 | 1,331 | 854 | 2,190 | 1,055 |
194
191
  | valkey-py+libvalkey | 2,075 | 2,187 | 2,029 | 1,329 | 852 | 2,176 | 1,049 |
195
- | **redis-rs** | 6,862 | 8,238 | 7,517 | 2,474 | 3,560 | 8,265 | 3,952 |
196
192
  | **valkey-glide** | 3,300 | 3,738 | 3,297 | 1,725 | 1,678 | 3,693 | 1,735 |
197
193
  | django (builtin) | 1,948 | 1,995 | 1,929 | 199 | 197 | 989 | 992 |
198
194
 
@@ -207,7 +203,6 @@ actually generate.
207
203
  | redis-py+hiredis | 2,121 | 2,254 | 2,047 | 1,347 | 931 | 2,149 | 979 | 106 |
208
204
  | valkey-py | 2,465 | 2,591 | 2,327 | 1,229 | 920 | 2,569 | 1,130 | 58 |
209
205
  | valkey-py+libvalkey | 2,467 | 2,588 | 2,329 | 1,223 | 917 | 2,564 | 1,129 | 108 |
210
- | **redis-rs** | 22,335 | 35,848 | 28,528 | 2,508 | 6,533 | 39,406 | 5,543 | 108 |
211
206
  | **valkey-glide** | 10,149 | 12,575 | 10,045 | 2,074 | 2,607 | 12,232 | 2,627 | 109 |
212
207
  | django (builtin) | 2,078 | 2,177 | 2,076 | 212 | 210 | 1,051 | 1,018 | 107 |
213
208
 
@@ -222,36 +217,28 @@ six async cache ops, the shape closest to real production load.
222
217
  | redis-py+hiredis | 467 | 213 | 1,509 | 414 | 209 | 209 |
223
218
  | valkey-py | 519 | 192 | 1,375 | 420 | 211 | 211 |
224
219
  | valkey-py+libvalkey | 498 | 200 | 1,599 | 432 | 211 | 211 |
225
- | **redis-rs** | 607 | 164 | 1,152 | 430 | 115 | 115 |
226
220
  | **valkey-glide** | 553 | 180 | 1,395 | 446 | 172 | 172 |
227
221
  | django (builtin) | 156 | 634 | 3,117 | 466 | 337 | 337 |
228
222
 
229
223
  ### Takeaways
230
224
 
231
- - **Sync direct.** `redis-rs` leads on every phase at **~10k get / 11k set
232
- / 13k incr / 6k delete** ops/sec, ~4× the fastest pure-Python adapter.
233
- `valkey-glide` is second on single-key ops (`get`/`set`/`incr` ~7–9k)
234
- but trails `redis-rs` on multi-key and `delete`. Both Rust adapters
235
- use ~4× less Python memory than the pure-Python adapters.
236
- - **Django request cycle.** Both Rust adapters lead the table:
237
- `redis-rs` ~1.4–2.1k ops/sec, `valkey-glide` ~1.2–1.9k. Earlier
238
- versions of these adapters failed this benchmark entirely because
239
- ``close()`` was tearing down the connection on every
240
- ``request_finished`` signal; now they outperform the Python adapters.
241
- - **Async serial.** `redis-rs` runs **~3–4× faster** than the C-parser
242
- Python adapters on every phase (6.9k get, 8.3k incr). `valkey-glide`
243
- is also faster than Python (~1.5–1.8×) but trails `redis-rs`.
244
- - **Async concurrent (50 in flight).** `redis-rs` peaks at **22k get /
245
- 36k get-miss / 39k incr** ops/sec, which is **9–18×** the fastest Python
246
- adapter. `valkey-glide` is half that rate but still **~4×** ahead of
247
- the Python adapters.
225
+ - **Sync direct.** `valkey-glide` leads every phase at **~7k get / 7k set
226
+ / 8.9k incr / 4k delete** ops/sec, ~2.5× the fastest pure-Python adapter,
227
+ and uses ~4× less Python memory.
228
+ - **Django request cycle.** `valkey-glide` leads at ~1.2–1.9k ops/sec.
229
+ An earlier version failed this benchmark entirely because ``close()``
230
+ was tearing down the connection on every ``request_finished`` signal;
231
+ now it outperforms the Python adapters.
232
+ - **Async serial.** `valkey-glide` runs ~1.5–1.8× faster than the C-parser
233
+ Python adapters on every phase (3.3k get, 3.7k incr).
234
+ - **Async concurrent (50 in flight).** `valkey-glide` peaks at **10k get /
235
+ 12.5k get-miss / 12.2k incr** ops/sec, roughly **4–5×** the fastest
236
+ Python adapter.
248
237
  - **ASGI full-stack** (granian × 4 workers, httpx × 100 concurrent).
249
- Both Rust adapters lead on req/s: `redis-rs` 607 (164 ms avg, 1.15 s
250
- p99) and `valkey-glide` 553. They also use **fewer connections** than
251
- every Python adapter (`redis-rs` 115, `valkey-glide` 172, vs Python ~210).
252
- Process-wide client sharing (added after this benchmark first
253
- surfaced 1,111 / 4,577 conn counts) brought the Rust adapters in
254
- line with the multiplexed-transport claim.
238
+ `valkey-glide` leads on req/s at 553 (180 ms avg, 1.4 s p99) and uses
239
+ fewer connections than every Python adapter (172 vs ~210). Process-wide
240
+ client sharing, added after this benchmark first surfaced 4,577 conn
241
+ counts, brought it in line with its transport's claim.
255
242
  - **Connection stability.** Across every shape the cachex path keeps
256
243
  `Δ` at 0 between phases, so there are no per-phase connection leaks on
257
244
  any adapter.
@@ -1,10 +1,4 @@
1
1
  from importlib.metadata import PackageNotFoundError, version
2
- from pkgutil import extend_path
3
-
4
- # Extend the package path so the optional django-cachex-redis-rs binary package
5
- # (which ships ``adapters/_redis_rs*`` files into this namespace) is
6
- # discovered when both packages are installed side by side.
7
- __path__ = extend_path(__path__, __name__)
8
2
 
9
3
  try:
10
4
  __version__ = version("django-cachex")
@@ -19,7 +13,7 @@ from django_cachex.exceptions import (
19
13
  SerializerError,
20
14
  WrongTypeError,
21
15
  )
22
- from django_cachex.lock import AsyncLock, Lock, LockError, LockNotOwnedError
16
+ from django_cachex.lock import LockError, LockNotOwnedError
23
17
  from django_cachex.script import (
24
18
  ScriptHelpers,
25
19
  decode_list_post,
@@ -35,11 +29,9 @@ from django_cachex.semaphore import (
35
29
  from django_cachex.stampede import StampedeConfig
36
30
 
37
31
  __all__ = [
38
- "AsyncLock",
39
32
  "AsyncPipeline",
40
33
  "CachexError",
41
34
  "CompressorError",
42
- "Lock",
43
35
  "LockError",
44
36
  "LockNotOwnedError",
45
37
  "NotSupportedError",
@@ -1,18 +1,10 @@
1
1
  """Adapter layer: low-level Redis/Valkey ops per driver.
2
2
 
3
3
  Each adapter wraps a specific underlying client library (redis-py,
4
- valkey-py, our Rust ``redis-rs`` driver, valkey-glide) and exposes the
5
- operation surface that ``RespCache`` delegates to.
4
+ valkey-py, valkey-glide) and exposes the operation surface that
5
+ ``RespCache`` delegates to.
6
6
  """
7
7
 
8
- from pkgutil import extend_path
9
-
10
- # Extend the sub-package path so the optional ``django-cachex-redis-rs``
11
- # binary package (which ships ``_redis_rs*`` files into this namespace)
12
- # is discovered when the two packages are installed under different
13
- # prefixes.
14
- __path__ = extend_path(__path__, __name__)
15
-
16
8
  from django_cachex.adapters.pipeline import AsyncPipeline, Pipeline
17
9
  from django_cachex.adapters.protocols import (
18
10
  RespAdapterProtocol,
@@ -26,13 +18,6 @@ from django_cachex.adapters.redis_py import (
26
18
  RedisPyPipelineAdapter,
27
19
  RedisPySentinelAdapter,
28
20
  )
29
- from django_cachex.adapters.redis_rs import (
30
- RedisRsAdapter,
31
- RedisRsAsyncPipelineAdapter,
32
- RedisRsClusterAdapter,
33
- RedisRsPipelineAdapter,
34
- RedisRsSentinelAdapter,
35
- )
36
21
  from django_cachex.adapters.valkey_glide import (
37
22
  ValkeyGlideAdapter,
38
23
  ValkeyGlideAsyncPipelineAdapter,
@@ -55,11 +40,6 @@ __all__ = [
55
40
  "RedisPyClusterAdapter",
56
41
  "RedisPyPipelineAdapter",
57
42
  "RedisPySentinelAdapter",
58
- "RedisRsAdapter",
59
- "RedisRsAsyncPipelineAdapter",
60
- "RedisRsClusterAdapter",
61
- "RedisRsPipelineAdapter",
62
- "RedisRsSentinelAdapter",
63
43
  "RespAdapterProtocol",
64
44
  "RespAsyncPipelineProtocol",
65
45
  "RespPipelineProtocol",
@@ -8,8 +8,6 @@ implementation, one per driver:
8
8
  - :class:`~django_cachex.adapters.valkey_py.ValkeyPyPipelineAdapter` /
9
9
  :class:`~django_cachex.adapters.redis_py.RedisPyPipelineAdapter`: wrap a
10
10
  redis-py / valkey-py / cluster ``Pipeline`` object.
11
- - :class:`~django_cachex.adapters.redis_rs.RedisRsPipelineAdapter`:
12
- buffers RESP wire commands for the Rust driver's ``pipeline_exec``.
13
11
  - :class:`~django_cachex.adapters.valkey_glide.ValkeyGlidePipelineAdapter`:
14
12
  drives ``valkey-glide``'s ``Batch``.
15
13
 
@@ -1606,8 +1604,8 @@ class Pipeline:
1606
1604
  class AsyncPipeline(Pipeline):
1607
1605
  """Async sibling of :class:`Pipeline`: same chainable API, awaitable ``execute()``.
1608
1606
 
1609
- Driver async pipelines (``redis.asyncio`` ``Pipeline``, glide async ``Batch``,
1610
- redis-rs ``apipeline_exec``) all expose the same chainable command surface
1607
+ Driver async pipelines (``redis.asyncio`` ``Pipeline``, glide async ``Batch``)
1608
+ all expose the same chainable command surface
1611
1609
  as their sync counterparts; only execution does I/O. So this subclass
1612
1610
  inherits every queueing method from :class:`Pipeline` and only overrides
1613
1611
  the lifecycle (``__aenter__/__aexit__``) and ``execute()`` to be async.
@@ -11,7 +11,6 @@ pipeline layers rely on.
11
11
  :class:`~django_cachex.cache.resp.RespCache` calls on its
12
12
  underlying adapter. Concrete adapters
13
13
  (:class:`~django_cachex.adapters.valkey_py.ValkeyPyAdapter`,
14
- :class:`~django_cachex.adapters.redis_rs.RedisRsAdapter`,
15
14
  :class:`~django_cachex.adapters.valkey_glide.ValkeyGlideAdapter`)
16
15
  inherit from this Protocol so the contract is explicit at the class
17
16
  declaration; the inherited method bodies (``...``) are shadowed by each
@@ -67,7 +67,7 @@ _set = set
67
67
  # process-wide registry every async cache call would open a brand-new
68
68
  # TCP connection, which is slow on the hot path and noisy on the server. Sharing
69
69
  # pools at the module level by event loop + config keeps connections
70
- # reused across tasks, in line with the multiplexed redis-rs adapter.
70
+ # reused across tasks.
71
71
  #
72
72
  # The registry itself lives on each driver-specific adapter class as the
73
73
  # ``_async_pools`` class attribute (see :mod:`~django_cachex.adapters.redis_py`
@@ -375,7 +375,7 @@ class ValkeyPyAdapter(RespAdapterProtocol):
375
375
  """Get an async client connection.
376
376
 
377
377
  ``async def`` for Protocol uniformity: adapters whose async-client
378
- construction is genuinely async (glide, redis-rs) ``await`` here;
378
+ construction is genuinely async (glide) ``await`` here;
379
379
  for redis-py / valkey-py the body is sync (pool lookup +
380
380
  ``Redis(connection_pool=...)``) since the connection opens
381
381
  lazily on the first awaited command.
@@ -10,11 +10,6 @@ from django_cachex.cache.redis_py import (
10
10
  RedisClusterCache,
11
11
  RedisSentinelCache,
12
12
  )
13
- from django_cachex.cache.redis_rs import (
14
- RedisRsCache,
15
- RedisRsClusterCache,
16
- RedisRsSentinelCache,
17
- )
18
13
  from django_cachex.cache.resp import RespCache, RespClusterCache, RespSentinelCache
19
14
  from django_cachex.cache.stream import StreamCache
20
15
  from django_cachex.cache.tiered import TieredCache
@@ -30,9 +25,6 @@ __all__ = [
30
25
  "LocMemCache",
31
26
  "RedisCache",
32
27
  "RedisClusterCache",
33
- "RedisRsCache",
34
- "RedisRsClusterCache",
35
- "RedisRsSentinelCache",
36
28
  "RedisSentinelCache",
37
29
  "RespCache",
38
30
  "RespClusterCache",
@@ -7,7 +7,6 @@ concrete subclasses live in:
7
7
 
8
8
  - :mod:`django_cachex.cache.valkey_py`: ``valkey-py``
9
9
  - :mod:`django_cachex.cache.redis_py`: ``redis-py``
10
- - :mod:`django_cachex.cache.redis_rs`: Rust ``redis-rs`` driver
11
10
  - :mod:`django_cachex.cache.valkey_glide`: ``valkey-glide``
12
11
  """
13
12
 
@@ -12,7 +12,7 @@ Configuration::
12
12
 
13
13
  CACHES = {
14
14
  "redis": {
15
- "BACKEND": "django_cachex.cache.RedisCache", # or RedisRsCache, etc.
15
+ "BACKEND": "django_cachex.cache.RedisCache", # or ValkeyCache, etc.
16
16
  "LOCATION": "redis://127.0.0.1:6379/0",
17
17
  },
18
18
  "default": {
@@ -72,7 +72,7 @@ class WrongTypeError(CachexError, TypeError):
72
72
  of value``. Subclasses both :class:`CachexError` and :class:`TypeError`,
73
73
  so ``except CachexError`` and existing ``except TypeError`` callers both
74
74
  keep working while new code can catch this specifically across backends
75
- (LocMem, redis-py, valkey-py, valkey-glide, redis-rs).
75
+ (LocMem, redis-py, valkey-py, valkey-glide).
76
76
  """
77
77
 
78
78
 
@@ -0,0 +1,11 @@
1
+ """Exceptions every adapter's ``cache.lock()`` object raises, whichever it is."""
2
+
3
+ from django_cachex.exceptions import CachexError
4
+
5
+
6
+ class LockError(CachexError):
7
+ """Raised when a lock operation fails."""
8
+
9
+
10
+ class LockNotOwnedError(LockError):
11
+ """Raised when releasing or extending a lock the caller no longer owns."""
@@ -7,7 +7,7 @@ rest of the project's convention (``foo``/``afoo``):
7
7
  ``_SemaphoreRegistry`` shared by every cache with the same LOCATION;
8
8
  standalone instances share a process-wide registry.
9
9
  - :class:`RespSemaphore`: backed by Lua scripts dispatched through any
10
- ``RespAdapterProtocol`` (redis-py, redis-rs, valkey-py, valkey-glide).
10
+ ``RespAdapterProtocol`` (redis-py, valkey-py, valkey-glide).
11
11
  Constructed by ``cache.semaphore(...)`` / ``cache.asemaphore(...)`` and not
12
12
  exposed at the package root.
13
13
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "django-cachex"
3
- version = "0.4.2"
3
+ version = "0.5.0"
4
4
  description = "Full featured Valkey and Redis cache backend for Django"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -32,9 +32,6 @@ valkey-py = ["valkey>=6.1.0,<7"]
32
32
  libvalkey = ["valkey[libvalkey]>=6.1.0,<7"]
33
33
  redis-py = ["redis>=6,<9"]
34
34
  hiredis = ["redis[hiredis]<9,>=6"]
35
- # Rust I/O driver: separate binary package, prebuilt wheels for Linux x86_64/aarch64,
36
- # macOS arm64, and Windows amd64 (cp314 and cp314t).
37
- redis-rs = ["django-cachex-redis-rs==0.4.2"]
38
35
  # valkey-glide: Rust-cored Valkey client. Sync and async are separate distributions.
39
36
  valkey-glide = ["valkey-glide-sync>=2.3,<3", "valkey-glide>=2.3,<3"]
40
37
  # Compressors
@@ -58,7 +55,6 @@ dev = [
58
55
  "django-stubs==6.1.0",
59
56
  "djlint==1.44.2",
60
57
  "lz4==4.4.5",
61
- "maturin==1.14.1",
62
58
  "msgpack==1.2.1",
63
59
  "ormsgpack==1.12.2",
64
60
  "mypy==2.3.0",
@@ -77,8 +73,6 @@ dev = [
77
73
  # ASGI benchmark only: granian (real ASGI server) plus httpx (load gen).
78
74
  "granian>=2.6,<3",
79
75
  "httpx>=0.28,<0.29",
80
- # Rust I/O driver: workspace member so `uv sync` builds it for tests/benchmarks
81
- "django-cachex-redis-rs",
82
76
  # Full example: workspace member so its deps (notably celery) survive
83
77
  # ``uv sync``, which the pre-commit hook runs on every commit.
84
78
  "django-cachex-full-example",
@@ -96,20 +90,14 @@ build-backend = "hatchling.build"
96
90
 
97
91
  [tool.hatch.build.targets.wheel]
98
92
  packages = ["django_cachex"]
99
- # _redis_rs.pyi is the type stub for the optional Rust extension. It lives
100
- # alongside django_cachex source so type checkers find it during dev, but
101
- # it ships with the django-cachex-redis-rs binary package, not this one.
102
- exclude = ["**/_redis_rs.pyi"]
103
93
 
104
94
  [tool.hatch.build.targets.sdist]
105
95
  include = ["django_cachex/**", "README.md", "LICENSE", "pyproject.toml"]
106
- exclude = ["**/_redis_rs.pyi"]
107
96
 
108
97
  [tool.uv.workspace]
109
- members = ["crates/*", "examples/full"]
98
+ members = ["examples/full"]
110
99
 
111
100
  [tool.uv.sources]
112
- django-cachex-redis-rs = { workspace = true }
113
101
  django-cachex-full-example = { workspace = true }
114
102
 
115
103
  [tool.ruff]
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Oliver Haas <ohaas@e1plus.de>
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,23 +0,0 @@
1
- # django-cachex-redis-rs
2
-
3
- Rust adapter for [django-cachex], built on PyO3 + tokio + [redis-rs].
4
-
5
- This is a binary-only companion package that ships the compiled
6
- `_redis_rs` extension module into the `django_cachex.adapters`
7
- namespace. Install via the `redis-rs` extra on the main package:
8
-
9
- ```console
10
- pip install django-cachex[redis-rs]
11
- ```
12
-
13
- Prebuilt wheels are published for Linux x86_64, Linux aarch64, macOS
14
- arm64, and Windows amd64, on both cp314 and cp314t (free-threaded). On
15
- other platforms there's no wheel, so pip will try to build from source
16
- (requires the Rust toolchain) or fail clearly.
17
-
18
- The pure-Python `django-cachex` package is fully usable on its own; this
19
- binary just unlocks the `RedisRsCache` family of
20
- backends.
21
-
22
- [django-cachex]: https://github.com/oliverhaas/django-cachex
23
- [redis-rs]: https://github.com/redis-rs/redis-rs
@@ -1,45 +0,0 @@
1
- [project]
2
- name = "django-cachex-redis-rs"
3
- version = "0.4.2"
4
- description = "Rust adapter (PyO3 + tokio + redis-rs) for django-cachex"
5
- readme = "README.md"
6
- license = { text = "MIT" }
7
- requires-python = ">=3.14"
8
- authors = [{ name = "Oliver Haas", email = "ohaas@e1plus.de" }]
9
- keywords = ["django", "valkey", "redis", "cache", "rust"]
10
- classifiers = [
11
- "Development Status :: 4 - Beta",
12
- "Environment :: Web Environment",
13
- "Framework :: Django",
14
- "Framework :: Django :: 6.0",
15
- "Intended Audience :: Developers",
16
- "License :: OSI Approved :: MIT License",
17
- "Operating System :: MacOS",
18
- "Operating System :: Microsoft :: Windows",
19
- "Operating System :: POSIX :: Linux",
20
- "Programming Language :: Python",
21
- "Programming Language :: Python :: 3",
22
- "Programming Language :: Python :: 3 :: Only",
23
- "Programming Language :: Python :: 3.14",
24
- "Programming Language :: Python :: Free Threading :: 1 - Unstable",
25
- "Programming Language :: Rust",
26
- "Topic :: Internet :: WWW/HTTP",
27
- "Topic :: Software Development :: Libraries",
28
- "Topic :: Utilities",
29
- "Typing :: Typed",
30
- ]
31
-
32
- [project.urls]
33
- Homepage = "https://github.com/oliverhaas/django-cachex"
34
- Repository = "https://github.com/oliverhaas/django-cachex.git"
35
-
36
- [build-system]
37
- requires = ["maturin>=1.13,<2.0"]
38
- build-backend = "maturin"
39
-
40
- [tool.maturin]
41
- module-name = "django_cachex.adapters._redis_rs"
42
- features = ["pyo3/extension-module"]
43
- python-source = "python"
44
- manifest-path = "Cargo.toml"
45
- include = ["LICENSE"]