solid-redis 1.0.9 → 1.0.10

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6607abf91dca5bb6bdd3bd5545d516da558b139b10e69637383625757b8d0546
4
- data.tar.gz: 68eb1f40407b5a6bb3f4dce69f54033baffa9f9f553e8d62dae5e72abf8fa278
3
+ metadata.gz: c08bed5af20e5c6c0facedfd545397d0a9309e023188e9834ae27b27a916a4a4
4
+ data.tar.gz: 62d8881647d63bc677b371617c4ab45f3415a2817c3d7a7ebf05b71bbc9dcbd1
5
5
  SHA512:
6
- metadata.gz: a871aa25c83c6fa53bb3a3c8d204699d3a6cb99f1811c233d35e71ac881fcd2216e109417b2cf1c4a34c1e3bbd409fdb64abf79ede22cb147daacc7c9e4d7e0f
7
- data.tar.gz: d9555c58bbe771001d8ae4d47a2f628b1960530dcb455c7de0ddf6a0e4700f824f8db3e74a51b1dc3a77304dbf0a26123cc091a30878293c0d9c76c30bf801ce
6
+ metadata.gz: 3d21acc2027b48d9b107a390697dbd3b03df222b56850d2698013688f1f6f687c2a86fb88e55d20a1f25bbf215cd037d4b0bf7615d768e1b3999c2953d9f9ec4
7
+ data.tar.gz: 35da0e825cba8ea9464cc77dba1e072b9b25152484df91038a591d5b6e1277b80f51bc6b6c74ac60199da020d4a061071c15450b94c0b31de92223e75ce19c33
data/CHANGELOG.md CHANGED
@@ -4,6 +4,20 @@ All notable changes to this project are documented in this file. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
5
5
  project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [1.0.10] - 2026-09-30
8
+
9
+ ### Changed
10
+
11
+ - Require `solid-resp-ractor` 0.1.3 for lower-allocation command encoding,
12
+ response parsing, and socket readiness waits.
13
+ - Remove transient allocations from Cluster command normalization and CRC16
14
+ slot calculation.
15
+ - Cache Cluster state and bounded key-to-slot lookups, store configurations
16
+ directly in the slot table, and avoid locking stable slot reads.
17
+ - Improve Cluster throughput by 2.0% to 4.3% over `redis-cluster-client`
18
+ across 1, 2, and 4 Ractors in the six-run benchmark, with approximately 41%
19
+ fewer allocations per operation.
20
+
7
21
  ## [1.0.9] - 2026-09-29
8
22
 
9
23
  ### Changed
@@ -154,6 +168,7 @@ project adheres to [Semantic Versioning](https://semver.org/).
154
168
  TLS, reconnection), `Pool` owned by a single Ractor, pipelines, and
155
169
  lifecycle callbacks via `callback-collection`.
156
170
 
171
+ [1.0.10]: https://github.com/nicolasva/solid-redis/compare/v1.0.9...v1.0.10
157
172
  [1.0.9]: https://github.com/nicolasva/solid-redis/compare/v1.0.8...v1.0.9
158
173
  [1.0.8]: https://github.com/nicolasva/solid-redis/compare/v1.0.7...v1.0.8
159
174
  [1.0.7]: https://github.com/nicolasva/solid-redis/compare/v1.0.6...v1.0.7
data/README.md CHANGED
@@ -592,7 +592,11 @@ Cluster rows compare `solid-redis` with `redis-cluster-client`, the
592
592
  Cluster implementation built on `redis-client`. Unsupported combinations
593
593
  are reported rather than replaced with a different concurrency model.
594
594
 
595
- **Environment:** Ruby 3.4.4 (arm64-darwin24); solid-redis 1.0.4; Redis server v=8.10.0 sha=00000000:1 malloc=libc bits=64 build=80fd3081013fc32b; redis-client 0.30.1; redis-cluster-client 0.17.1; Apple M2 Max, 12 logical CPUs, 96 GiB RAM, macOS 26.6.2.
595
+ **Environment:** Ruby 4.0.1 (arm64-darwin25); solid-redis 1.0.9; Redis server v=8.10.0 sha=00000000:1 malloc=libc bits=64 build=80fd3081013fc32b; redis-client 0.30.1; redis-cluster-client 0.17.1; Apple M2 Max, 12 logical CPUs, 96 GiB RAM, macOS 26.6.2.
596
+
597
+ GET/SET and Cluster rows for 1, 2, and 4 Ractors were refreshed from the
598
+ current checkout with the unreleased routing and RESP buffer optimizations.
599
+ The remaining rows come from the preceding full-matrix run.
596
600
 
597
601
  Each row is measured in a fresh Ruby process. Both clients use RESP2,
598
602
  identical commands and the same local Redis topology. Client order is
@@ -617,87 +621,76 @@ harness used for both clients, so they are comparative rather than
617
621
  intrinsic library-only costs. `redis-cluster-client` uses its default
618
622
  synchronous routing model (`concurrency: { model: :none }`).
619
623
 
620
- The benchmark suite now lives in the separate `bench_mark_redis` project.
621
- From a checkout where both repositories are siblings, run the full matrix
622
- (requires `redis-server` and `redis-cli`) with:
623
-
624
- ```sh
625
- cd ../bench_mark_redis
626
- SOLID_REDIS_PATH=../solid-redis bundle install
627
- bundle exec rake comparison
628
- ```
629
-
630
- To regenerate this README's benchmark tables:
624
+ Reproduce the full run (requires `redis-server` and `redis-cli`):
631
625
 
632
626
  ```sh
633
- cd ../bench_mark_redis
634
- BENCHMARK_README=../solid-redis/README.md bundle exec rake comparison
627
+ BENCHMARK_README=README.md bundle exec rake benchmark:comparison
635
628
  ```
636
629
 
637
630
  ### GET / SET
638
631
 
639
632
  | Ractors | Client | Throughput (ops/s) | p50 (ms) | p95 (ms) | p99 (ms) | CPU | Peak RSS | Allocations/op | Errors/run |
640
633
  |---:|---|---:|---:|---:|---:|---:|---:|---:|---:|
641
- | 1 | `redis-client` | 35,363 | 0.028 | 0.035 | 0.045 | 58.6% | 37.9 MiB | 17.4 | 0 |
642
- | 1 | `solid-redis` | 39,259 | 0.025 | 0.032 | 0.044 | 65.0% | 54.5 MiB | 19.9 | 0 |
643
- | 2 | `redis-client` | 53,847 | 0.036 | 0.050 | 0.073 | 114.4% | 38.0 MiB | 17.3 | 0 |
644
- | 2 | `solid-redis` | 58,259 | 0.033 | 0.047 | 0.095 | 112.6% | 54.5 MiB | 19.8 | 0 |
645
- | 4 | `redis-client` | 67,086 | 0.058 | 0.097 | 0.136 | 238.4% | 38.0 MiB | 17.3 | 0 |
646
- | 4 | `solid-redis` | 75,789 | 0.049 | 0.081 | 0.170 | 220.7% | 55.3 MiB | 19.6 | 0 |
647
- | 8 | `redis-client` | 48,938 | 0.083 | 0.180 | 0.249 | 267.7% | 38.7 MiB | 17.3 | 0 |
648
- | 8 | `solid-redis` | 84,294 | 0.085 | 0.169 | 0.274 | 479.1% | 65.1 MiB | 19.0 | 0 |
634
+ | 1 | `redis-client` | 35,899 | 0.028 | 0.036 | 0.047 | 57.8% | 42.5 MiB | 17.4 | 0 |
635
+ | 1 | `solid-redis` | 38,329 | 0.025 | 0.035 | 0.045 | 66.7% | 117.1 MiB | 10.4 | 0 |
636
+ | 2 | `redis-client` | 57,092 | 0.034 | 0.047 | 0.060 | 105.0% | 42.7 MiB | 17.4 | 0 |
637
+ | 2 | `solid-redis` | 59,489 | 0.032 | 0.047 | 0.059 | 115.9% | 131.1 MiB | 10.4 | 0 |
638
+ | 4 | `redis-client` | 83,927 | 0.046 | 0.068 | 0.098 | 198.4% | 42.8 MiB | 17.4 | 0 |
639
+ | 4 | `solid-redis` | 83,164 | 0.046 | 0.068 | 0.094 | 211.6% | 130.5 MiB | 10.4 | 0 |
640
+ | 8 | `redis-client` | 105,129 | 0.069 | 0.119 | 0.213 | 360.8% | 41.3 MiB | 17.4 | 0 |
641
+ | 8 | `solid-redis` | 99,759 | 0.070 | 0.128 | 0.267 | 378.0% | 97.7 MiB | 19.8 | 0 |
649
642
 
650
643
  ### Pipelines
651
644
 
652
645
  | Ractors | Client | Throughput (ops/s) | p50 (ms) | p95 (ms) | p99 (ms) | CPU | Peak RSS | Allocations/op | Errors/run |
653
646
  |---:|---|---:|---:|---:|---:|---:|---:|---:|---:|
654
- | 1 | `redis-client` | 261,689 | 0.004 | 0.005 | 0.007 | 81.0% | 37.9 MiB | 12.6 | 0 |
655
- | 1 | `solid-redis` | 329,512 | 0.003 | 0.004 | 0.005 | 76.8% | 40.5 MiB | 12.2 | 0 |
656
- | 2 | `redis-client` | 273,064 | 0.007 | 0.009 | 0.012 | 147.4% | 38.0 MiB | 12.5 | 0 |
657
- | 2 | `solid-redis` | 509,797 | 0.004 | 0.005 | 0.007 | 149.0% | 40.7 MiB | 12.0 | 0 |
658
- | 4 | `redis-client` | 226,198 | 0.017 | 0.023 | 0.029 | 282.3% | 38.3 MiB | 12.5 | 0 |
659
- | 4 | `solid-redis` | 609,096 | 0.006 | 0.011 | 0.013 | 272.3% | 41.0 MiB | 11.7 | 0 |
660
- | 8 | `redis-client` | 204,166 | 0.004 | 0.054 | 0.062 | 315.5% | 38.9 MiB | 12.5 | 0 |
661
- | 8 | `solid-redis` | 420,118 | 0.017 | 0.029 | 0.034 | 559.2% | 41.6 MiB | 11.6 | 0 |
647
+ | 1 | `redis-client` | 299,646 | 0.003 | 0.004 | 0.009 | 78.8% | 39.5 MiB | 12.6 | 0 |
648
+ | 1 | `solid-redis` | 300,831 | 0.003 | 0.004 | 0.007 | 79.4% | 41.0 MiB | 12.2 | 0 |
649
+ | 2 | `redis-client` | 537,360 | 0.003 | 0.005 | 0.011 | 150.3% | 40.9 MiB | 12.6 | 0 |
650
+ | 2 | `solid-redis` | 529,852 | 0.003 | 0.006 | 0.009 | 146.8% | 42.3 MiB | 12.2 | 0 |
651
+ | 4 | `redis-client` | 881,833 | 0.004 | 0.008 | 0.017 | 258.4% | 43.0 MiB | 12.6 | 0 |
652
+ | 4 | `solid-redis` | 873,375 | 0.004 | 0.010 | 0.017 | 255.5% | 46.8 MiB | 12.2 | 0 |
653
+ | 8 | `redis-client` | 1,115,081 | 0.006 | 0.015 | 0.020 | 389.3% | 42.3 MiB | 12.6 | 0 |
654
+ | 8 | `solid-redis` | 1,232,285 | 0.005 | 0.018 | 0.031 | 401.1% | 55.8 MiB | 12.2 | 0 |
662
655
 
663
656
  ### Isolated pool per Ractor
664
657
 
665
658
  | Ractors | Client | Throughput (ops/s) | p50 (ms) | p95 (ms) | p99 (ms) | CPU | Peak RSS | Allocations/op | Errors/run |
666
659
  |---:|---|---:|---:|---:|---:|---:|---:|---:|---:|
667
- | 1 | `redis-client` | unsupported: Ractor::IsolationError (`connection_pool` uses non-shareable global state) | — | — | — | — | — | — | — |
668
- | 1 | `solid-redis` | 40,785 | 0.023 | 0.031 | 0.053 | 66.6% | 55.2 MiB | 17.0 | 0 |
669
- | 2 | `redis-client` | unsupported: Ractor::IsolationError (`connection_pool` uses non-shareable global state) | — | — | — | — | — | — | — |
670
- | 2 | `solid-redis` | 63,540 | 0.030 | 0.042 | 0.073 | 117.0% | 55.3 MiB | 16.9 | 0 |
671
- | 4 | `redis-client` | unsupported: Ractor::IsolationError (`connection_pool` uses non-shareable global state) | — | — | — | — | — | — | — |
672
- | 4 | `solid-redis` | 80,969 | 0.046 | 0.072 | 0.160 | 221.7% | 54.8 MiB | 16.6 | 0 |
673
- | 8 | `redis-client` | unsupported: Ractor::IsolationError (`connection_pool` uses non-shareable global state) | — | — | — | — | — | — | — |
674
- | 8 | `solid-redis` | 74,339 | 0.068 | 0.163 | 0.351 | 334.1% | 62.3 MiB | 16.3 | 0 |
660
+ | 1 | `redis-client` | unsupported: Ractor isolation error (`connection_pool` uses non-shareable global state) | — | — | — | — | — | — | — |
661
+ | 1 | `solid-redis` | 38,731 | 0.025 | 0.031 | 0.039 | 69.2% | 71.1 MiB | 17.0 | 0 |
662
+ | 2 | `redis-client` | unsupported: Ractor isolation error (`connection_pool` uses non-shareable global state) | — | — | — | — | — | — | — |
663
+ | 2 | `solid-redis` | 64,181 | 0.029 | 0.044 | 0.060 | 123.5% | 83.3 MiB | 17.0 | 0 |
664
+ | 4 | `redis-client` | unsupported: Ractor isolation error (`connection_pool` uses non-shareable global state) | — | — | — | — | — | — | — |
665
+ | 4 | `solid-redis` | 83,755 | 0.045 | 0.067 | 0.100 | 214.4% | 87.3 MiB | 16.9 | 0 |
666
+ | 8 | `redis-client` | unsupported: Ractor isolation error (`connection_pool` uses non-shareable global state) | — | — | — | — | — | — | — |
667
+ | 8 | `solid-redis` | 108,840 | 0.067 | 0.110 | 0.220 | 400.3% | 110.1 MiB | 16.9 | 0 |
675
668
 
676
669
  ### Cluster
677
670
 
678
671
  | Ractors | Client | Throughput (ops/s) | p50 (ms) | p95 (ms) | p99 (ms) | CPU | Peak RSS | Allocations/op | Errors/run |
679
672
  |---:|---|---:|---:|---:|---:|---:|---:|---:|---:|
680
- | 1 | `redis-client` | 28,804 | 0.034 | 0.042 | 0.059 | 64.2% | 38.4 MiB | 19.4 | 0 |
681
- | 1 | `solid-redis` | 29,651 | 0.032 | 0.040 | 0.065 | 63.1% | 54.1 MiB | 24.9 | 0 |
682
- | 2 | `redis-client` | 49,533 | 0.039 | 0.055 | 0.091 | 124.1% | 37.6 MiB | 19.3 | 0 |
683
- | 2 | `solid-redis` | 51,253 | 0.037 | 0.052 | 0.105 | 121.0% | 54.2 MiB | 24.8 | 0 |
684
- | 4 | `redis-client` | 60,385 | 0.064 | 0.103 | 0.149 | 251.9% | 40.2 MiB | 19.3 | 0 |
685
- | 4 | `solid-redis` | 66,630 | 0.056 | 0.088 | 0.175 | 242.5% | 57.2 MiB | 24.5 | 0 |
686
- | 8 | `redis-client` | 43,361 | 0.095 | 0.195 | 0.275 | 277.5% | 44.0 MiB | 19.3 | 0 |
687
- | 8 | `solid-redis` | 47,102 | 0.077 | 0.198 | 0.415 | 251.3% | 58.0 MiB | 24.2 | 0 |
673
+ | 1 | `redis-client` | 29,459 | 0.033 | 0.042 | 0.057 | 62.4% | 42.6 MiB | 19.4 | 0 |
674
+ | 1 | `solid-redis` | 30,041 | 0.033 | 0.040 | 0.053 | 59.8% | 116.4 MiB | 11.4 | 0 |
675
+ | 2 | `redis-client` | 51,845 | 0.037 | 0.052 | 0.070 | 115.1% | 42.8 MiB | 19.4 | 0 |
676
+ | 2 | `solid-redis` | 54,065 | 0.035 | 0.050 | 0.063 | 115.4% | 126.3 MiB | 11.4 | 0 |
677
+ | 4 | `redis-client` | 76,652 | 0.051 | 0.070 | 0.097 | 217.3% | 47.6 MiB | 19.4 | 0 |
678
+ | 4 | `solid-redis` | 78,763 | 0.049 | 0.069 | 0.091 | 220.1% | 128.5 MiB | 11.4 | 0 |
679
+ | 8 | `redis-client` | 108,022 | 0.069 | 0.109 | 0.156 | 420.5% | 49.1 MiB | 19.4 | 0 |
680
+ | 8 | `solid-redis` | 98,677 | 0.072 | 0.127 | 0.302 | 433.3% | 86.9 MiB | 24.8 | 0 |
688
681
 
689
682
  ### Sentinel master crash
690
683
 
691
684
  | Ractors | Client | Throughput (ops/s) | p50 (ms) | p95 (ms) | p99 (ms) | Recovery p50 (ms) | Recovery max (ms) | CPU | Peak RSS | Allocations/op | Errors/run |
692
685
  |---:|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
693
- | 1 | `redis-client` | 29,749 | 0.028 | 0.037 | 0.052 | 1321.9 | 1321.9 | 52.8% | 41.9 MiB | 17.6 | 99 |
694
- | 1 | `solid-redis` | 32,279 | 0.026 | 0.034 | 0.050 | 1313.3 | 1313.3 | 57.0% | 69.5 MiB | 20.1 | 96 |
695
- | 2 | `redis-client` | 46,483 | 0.036 | 0.050 | 0.075 | 1307.7 | 1311.9 | 101.9% | 42.0 MiB | 17.5 | 202 |
696
- | 2 | `solid-redis` | 50,273 | 0.033 | 0.045 | 0.098 | 1344.3 | 1344.4 | 99.5% | 69.6 MiB | 20.0 | 206 |
697
- | 4 | `redis-client` | 58,224 | 0.058 | 0.095 | 0.132 | 1319.1 | 1319.1 | 212.7% | 41.8 MiB | 17.6 | 386 |
698
- | 4 | `solid-redis` | 66,969 | 0.049 | 0.078 | 0.154 | 1341.8 | 1341.8 | 196.9% | 69.4 MiB | 19.9 | 398 |
699
- | 8 | `redis-client` | 53,640 | 0.124 | 0.229 | 0.305 | 1342.8 | 1343.1 | 474.6% | 42.2 MiB | 17.7 | 679 |
700
- | 8 | `solid-redis` | 72,816 | 0.086 | 0.172 | 0.289 | 1349.4 | 1353.1 | 421.7% | 64.5 MiB | 19.5 | 687 |
686
+ | 1 | `redis-client` | 30,243 | 0.027 | 0.036 | 0.048 | 1345.8 | 1345.8 | 50.5% | 42.3 MiB | 17.6 | 110 |
687
+ | 1 | `solid-redis` | 32,634 | 0.027 | 0.035 | 0.047 | 1284.6 | 1284.6 | 57.9% | 75.6 MiB | 20.1 | 105 |
688
+ | 2 | `redis-client` | 49,096 | 0.034 | 0.048 | 0.059 | 1279.1 | 1279.2 | 92.7% | 42.8 MiB | 17.6 | 205 |
689
+ | 2 | `solid-redis` | 50,166 | 0.033 | 0.047 | 0.062 | 1304.0 | 1304.0 | 100.7% | 85.6 MiB | 20.1 | 210 |
690
+ | 4 | `redis-client` | 72,364 | 0.046 | 0.068 | 0.100 | 1325.0 | 1325.1 | 174.7% | 43.3 MiB | 17.6 | 412 |
691
+ | 4 | `solid-redis` | 70,426 | 0.047 | 0.069 | 0.132 | 1336.2 | 1336.2 | 183.8% | 85.9 MiB | 20.2 | 416 |
692
+ | 8 | `redis-client` | 92,438 | 0.069 | 0.119 | 0.200 | 1332.8 | 1332.9 | 325.3% | 45.5 MiB | 17.7 | 772 |
693
+ | 8 | `solid-redis` | 87,879 | 0.070 | 0.132 | 0.247 | 1317.2 | 1317.6 | 342.4% | 84.3 MiB | 20.3 | 760 |
701
694
  <!-- benchmark-results:end -->
702
695
 
703
696
  ## Development
@@ -28,7 +28,7 @@ module SolidRedis
28
28
 
29
29
  # Returns the first key of +command+, or nil for keyless commands.
30
30
  def for(command)
31
- name = command[0].to_s.upcase
31
+ name = normalize_name(command[0])
32
32
  return if KEYLESS[name]
33
33
 
34
34
  if (index = NUMKEYS_AT[name])
@@ -48,6 +48,18 @@ module SolidRedis
48
48
  else command[1]
49
49
  end
50
50
  end
51
+
52
+ def normalize_name(value)
53
+ name = value.to_s
54
+ index = 0
55
+ while index < name.bytesize
56
+ byte = name.getbyte(index)
57
+ return name.upcase if byte >= 97 && byte <= 122
58
+
59
+ index += 1
60
+ end
61
+ name
62
+ end
51
63
  end
52
64
  end
53
65
  end
@@ -19,7 +19,17 @@ module SolidRedis
19
19
  module_function
20
20
 
21
21
  def for(key)
22
- crc16(hash_tag(key.to_s)) % SLOTS
22
+ string = key.to_s
23
+ first = 0
24
+ last = string.bytesize
25
+ if (open = string.index("{")) &&
26
+ (close = string.index("}", open + 1)) &&
27
+ close > open + 1
28
+ first = open + 1
29
+ last = close
30
+ end
31
+
32
+ crc16_range(string, first, last) % SLOTS
23
33
  end
24
34
 
25
35
  def hash_tag(key)
@@ -33,9 +43,17 @@ module SolidRedis
33
43
  end
34
44
 
35
45
  def crc16(string)
36
- string.each_byte.reduce(0) do |crc, byte|
37
- ((crc << 8) & 0xFFFF) ^ TABLE[((crc >> 8) ^ byte) & 0xFF]
46
+ crc16_range(string, 0, string.bytesize)
47
+ end
48
+
49
+ def crc16_range(string, first, last)
50
+ crc = 0
51
+ while first < last
52
+ byte = string.getbyte(first)
53
+ crc = ((crc << 8) & 0xFFFF) ^ TABLE[((crc >> 8) ^ byte) & 0xFF]
54
+ first += 1
38
55
  end
56
+ crc
39
57
  end
40
58
  end
41
59
  end
@@ -7,6 +7,7 @@ module SolidRedis
7
7
  class ClusterClient
8
8
  REDIRECTION = /\A(MOVED|ASK) (\d+) (\S+):(\d+)\z/
9
9
  RETRY_DELAY = 0.05
10
+ SLOT_CACHE_LIMIT = 1_024
10
11
  TRANSIENT_ERRORS = %w[TRYAGAIN CLUSTERDOWN].freeze
11
12
 
12
13
  attr_reader :config
@@ -15,6 +16,8 @@ module SolidRedis
15
16
  @config = config
16
17
  @name = name
17
18
  @clients = {}
19
+ @slot_cache = {}
20
+ @state = config.state
18
21
  end
19
22
 
20
23
  def call(*command)
@@ -104,14 +107,14 @@ module SolidRedis
104
107
  host = redirection[3]
105
108
  port = Integer(redirection[4])
106
109
  asking = redirection[1] == "ASK"
107
- node = asking ? config.state.node(host, port) : config.state.move(slot, host, port)
110
+ node = asking ? @state.node(host, port) : @state.move(slot, host, port)
108
111
  elsif error.message.start_with?("TRYAGAIN", "CLUSTERDOWN")
109
112
  redirections += 1
110
113
  raise FailoverError, "Cluster unavailable: #{error.message}" if redirections > config.max_redirections
111
114
 
112
115
  asking = false
113
116
  sleep RETRY_DELAY
114
- config.state.refresh
117
+ @state.refresh
115
118
  node = node_for(command)
116
119
  else
117
120
  raise
@@ -122,14 +125,23 @@ module SolidRedis
122
125
 
123
126
  attempts += 1
124
127
  config.notify(:connection_error, error.class.name, error.message)
125
- config.state.refresh
128
+ @state.refresh
126
129
  node = node_for(command)
127
130
  end
128
131
  end
129
132
 
130
133
  def node_for(command)
131
134
  key = Cluster::CommandKey.for(command)
132
- key ? config.state.node_for_slot(Cluster::KeySlot.for(key)) : config.state.any_node
135
+ return @state.any_node unless key
136
+
137
+ slot = @slot_cache[key]
138
+ unless slot
139
+ slot = Cluster::KeySlot.for(key)
140
+ @slot_cache.clear if @slot_cache.length >= SLOT_CACHE_LIMIT
141
+ cached_key = key.is_a?(String) && !key.frozen? ? key.dup.freeze : key
142
+ @slot_cache[cached_key] = slot
143
+ end
144
+ @state.node_for_slot(slot)
133
145
  end
134
146
 
135
147
  def transient_error?(error)
@@ -19,14 +19,17 @@ module SolidRedis
19
19
  # Returns the master Config owning +slot+, discovering the topology when
20
20
  # it is not known yet.
21
21
  def node_for_slot(slot)
22
+ node = @slots[slot]
23
+ return node if node
24
+
22
25
  @mutex.synchronize do
23
26
  discover unless @discovered
24
- key = @slots[slot]
25
- if key.nil?
27
+ node = @slots[slot]
28
+ if node.nil?
26
29
  discover
27
- key = @slots[slot]
30
+ node = @slots[slot]
28
31
  end
29
- @nodes[key] || raise(ConnectionError, "No cluster node serves slot #{slot}")
32
+ node || raise(ConnectionError, "No cluster node serves slot #{slot}")
30
33
  end
31
34
  end
32
35
 
@@ -45,9 +48,9 @@ module SolidRedis
45
48
  def move(slot, host, port)
46
49
  @mutex.synchronize do
47
50
  key = node_key(host, port)
48
- @nodes[key] ||= node_config(host, port)
49
- @slots[slot] = key
50
- @nodes[key]
51
+ node = @nodes[key] ||= node_config(host, port)
52
+ @slots[slot] = node
53
+ node
51
54
  end
52
55
  end
53
56
 
@@ -83,8 +86,8 @@ module SolidRedis
83
86
  @nodes.clear
84
87
  result.result[:ranges].each do |range|
85
88
  key = node_key(range[:master][:host], range[:master][:port])
86
- @nodes[key] ||= node_config(range[:master][:host], range[:master][:port])
87
- (range[:from]..range[:to]).each { |slot| @slots[slot] = key }
89
+ node = @nodes[key] ||= node_config(range[:master][:host], range[:master][:port])
90
+ (range[:from]..range[:to]).each { |slot| @slots[slot] = node }
88
91
  end
89
92
  @discovered = true
90
93
  @specification.notify(:resolved, "cluster", @nodes.keys.join(","))
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidRedis
4
- VERSION = "1.0.9"
4
+ VERSION = "1.0.10"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solid-redis
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.9
4
+ version: 1.0.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nicolas Vandenbogaerde
@@ -43,14 +43,14 @@ dependencies:
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: 0.1.1
46
+ version: 0.1.3
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: 0.1.1
53
+ version: 0.1.3
54
54
  - !ruby/object:Gem::Dependency
55
55
  name: minitest
56
56
  requirement: !ruby/object:Gem::Requirement
@@ -118,7 +118,7 @@ licenses:
118
118
  - MIT
119
119
  metadata:
120
120
  rubygems_mfa_required: 'true'
121
- documentation_uri: https://www.rubydoc.info/gems/solid-redis/1.0.9
121
+ documentation_uri: https://www.rubydoc.info/gems/solid-redis/1.0.10
122
122
  source_code_uri: https://github.com/nicolasva/solid-redis
123
123
  changelog_uri: https://github.com/nicolasva/solid-redis/blob/main/CHANGELOG.md
124
124
  rdoc_options: []