redis-cluster-activesupport 1.3.0 → 1.4.0

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: 6cac869b68eafe05a50192c4e9551bb0f9cfa2f5d12f914a0a96a150409f624f
4
- data.tar.gz: 43e3d49d20105a2f93a1eecdad0c2a37b21df9780a882f8665f0b8d6faccb802
3
+ metadata.gz: add6d07c71703b7fb32e3500b0045464fb1934f80d3d25b70ea8722a94733775
4
+ data.tar.gz: 82624a0a5d4e631da5812ab4b547b95b92d00a34dcfa3f91cc2e7a2c1b69daad
5
5
  SHA512:
6
- metadata.gz: 16d2b4b7c0db6c1beb26bbbfcef6f4c9d71b379c2dfa8f51f20bf8a3e5ef0c267f9f8f09dfb1758fff26289718b2cfba9511ec4196d227a658c41c7e4b0fe06c
7
- data.tar.gz: e884694bb86da4e3494ffa5c823b184b45302fd5f9bc5ee4b441b46e4ceb6cd96d6628a03078e2370c05113980198f6a4417a54691d904f26f45b7dcb40dd8ce
6
+ metadata.gz: eeb5b301ef0666b2c511868c9e16e43a895240373aa014b5842bf70f04e02ed61ebb2a1addd4cca01baadf125184ede29fab0a1847f271297f22ba4d99fc49b2
7
+ data.tar.gz: 99ea8eb9e80bd102a5571eabfdbb3f3d0129bacfd97abe549df69c77dd3ad4cbcdce145dd463f282e2e00962acd80f373bfc4843bcd596111dfde85f790a1c85
@@ -11,43 +11,69 @@ on:
11
11
  pull_request:
12
12
  branches: [ master ]
13
13
  jobs:
14
+ # Fast unit specs (fakeredis) across every supported Ruby x Rails combination.
15
+ # Supported Rubies: CRuby 3.1-3.4, JRuby 9.4 (Ruby 3.1) and 10.0 (Ruby 3.4).
16
+ # The gemfiles are generated from Appraisals at runtime (they are gitignored).
14
17
  test:
15
18
  runs-on: ubuntu-latest
16
19
  strategy:
20
+ fail-fast: false
17
21
  matrix:
18
- ruby-version: [ '3.2', '3.1', '3.0', '2.7', '2.6', 'jruby-9.3', 'jruby-9.4' ]
19
- gemfile: [ 'rails60', 'rails61', 'rails70', 'rails71', 'rails72' ]
22
+ ruby-version: [ '3.4', '3.3', '3.2', '3.1', 'jruby-9.4', 'jruby-10.0' ]
23
+ appraisal: [ 'rails60', 'rails61', 'rails70', 'rails71', 'rails72', 'rails80', 'rails81' ]
20
24
  exclude:
21
- - ruby-version: '2.6'
22
- gemfile: 'rails70'
23
- - ruby-version: '2.6'
24
- gemfile: 'rails71'
25
- - ruby-version: '2.6'
26
- gemfile: 'rails72'
27
- - ruby-version: '2.7'
28
- gemfile: 'rails72'
29
- - ruby-version: '3.0'
30
- gemfile: 'rails72'
31
- - ruby-version: 'jruby-9.3'
32
- gemfile: 'rails70'
33
- - ruby-version: 'jruby-9.3'
34
- gemfile: 'rails71'
35
- - ruby-version: 'jruby-9.3'
36
- gemfile: 'rails72'
37
- env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
38
- BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile
39
- RAILS_ENV: test
25
+ # Rails 8.0/8.1 require Ruby 3.2+ (excludes 3.1 and JRuby 9.4 which is Ruby 3.1)
26
+ - { ruby-version: '3.1', appraisal: 'rails80' }
27
+ - { ruby-version: '3.1', appraisal: 'rails81' }
28
+ - { ruby-version: 'jruby-9.4', appraisal: 'rails80' }
29
+ - { ruby-version: 'jruby-9.4', appraisal: 'rails81' }
30
+ # Rails 6.0/6.1 don't load on Ruby 3.4 (base64/mutex_m dropped from
31
+ # default gems and undeclared by those Rails versions); JRuby 10.0 is Ruby 3.4.
32
+ - { ruby-version: '3.4', appraisal: 'rails60' }
33
+ - { ruby-version: '3.4', appraisal: 'rails61' }
34
+ - { ruby-version: 'jruby-10.0', appraisal: 'rails60' }
35
+ - { ruby-version: 'jruby-10.0', appraisal: 'rails61' }
40
36
  steps:
41
37
  - name: Checkout code
42
38
  uses: actions/checkout@v4
43
- # Add or replace dependency steps here
44
39
  - name: Install Ruby and gems
45
40
  uses: ruby/setup-ruby@v1
46
41
  with:
47
42
  bundler-cache: true
48
43
  ruby-version: ${{ matrix.ruby-version }}
49
- # Add or replace test runners here
50
- - name: Run tests
51
- run: bundle exec rspec
52
- env:
53
- BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile
44
+ - name: Generate appraisal gemfiles
45
+ run: bundle exec appraisal generate
46
+ - name: Install gems for ${{ matrix.appraisal }}
47
+ run: bundle exec appraisal ${{ matrix.appraisal }} bundle install
48
+ - name: Run unit tests
49
+ run: bundle exec appraisal ${{ matrix.appraisal }} rspec
50
+
51
+ # Full integration specs against a real Redis Cluster (3 masters + 3 replicas).
52
+ integration:
53
+ runs-on: ubuntu-latest
54
+ strategy:
55
+ fail-fast: false
56
+ matrix:
57
+ # 3.2 is the minimum Ruby for Rails 8; 3.4 is the newest supported.
58
+ ruby-version: [ '3.4', '3.2' ]
59
+ appraisal: [ 'rails70', 'rails71', 'rails72', 'rails80', 'rails81' ]
60
+ env:
61
+ REDIS_CLUSTER_NODES: "127.0.0.1:7000,127.0.0.1:7001,127.0.0.1:7002"
62
+ steps:
63
+ - name: Checkout code
64
+ uses: actions/checkout@v4
65
+ - name: Install Redis (server + cli for cluster support)
66
+ run: sudo apt-get update && sudo apt-get install -y redis-server redis-tools
67
+ - name: Install Ruby and gems
68
+ uses: ruby/setup-ruby@v1
69
+ with:
70
+ bundler-cache: true
71
+ ruby-version: ${{ matrix.ruby-version }}
72
+ - name: Generate appraisal gemfiles
73
+ run: bundle exec appraisal generate
74
+ - name: Install gems for ${{ matrix.appraisal }}
75
+ run: bundle exec appraisal ${{ matrix.appraisal }} bundle install
76
+ - name: Boot Redis Cluster
77
+ run: ./bin/redis-cluster
78
+ - name: Run integration tests
79
+ run: bundle exec appraisal ${{ matrix.appraisal }} rake integration
data/.gitignore CHANGED
@@ -10,4 +10,7 @@
10
10
 
11
11
  # rspec failure tracking
12
12
  .rspec_status
13
- gemfiles/*.lock
13
+
14
+ # Gemfiles are generated from Appraisals by `bundle exec appraisal generate`
15
+ gemfiles/*.gemfile
16
+ gemfiles/*.gemfile.lock
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --format documentation
2
2
  --color
3
+ --exclude-pattern "integration/**/*_spec.rb"
data/Appraisals ADDED
@@ -0,0 +1,29 @@
1
+ # Regenerate the gemfiles/ with `bundle exec appraisal generate`.
2
+ # The generated gemfiles are gitignored; CI regenerates them on each run.
3
+ appraise "rails60" do
4
+ gem "activesupport", "~> 6.0.0"
5
+ end
6
+
7
+ appraise "rails61" do
8
+ gem "activesupport", "~> 6.1.0"
9
+ end
10
+
11
+ appraise "rails70" do
12
+ gem "activesupport", "~> 7.0.0"
13
+ end
14
+
15
+ appraise "rails71" do
16
+ gem "activesupport", "~> 7.1.0"
17
+ end
18
+
19
+ appraise "rails72" do
20
+ gem "activesupport", "~> 7.2.0"
21
+ end
22
+
23
+ appraise "rails80" do
24
+ gem "activesupport", "~> 8.0.0"
25
+ end
26
+
27
+ appraise "rails81" do
28
+ gem "activesupport", "~> 8.1.0"
29
+ end
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Redis cluster stores for ActiveSupport [![Build Status](https://travis-ci.org/film42/redis-cluster-activesupport.svg?branch=master)](https://travis-ci.org/film42/redis-cluster-activesupport)
1
+ # Redis cluster stores for ActiveSupport [![CI](https://github.com/film42/redis-cluster-activesupport/actions/workflows/ci.yml/badge.svg)](https://github.com/film42/redis-cluster-activesupport/actions/workflows/ci.yml)
2
2
 
3
3
  This gem was an extension to [redis-activesupport](https://github.com/redis-store/redis-activesupport) that adds support
4
4
  for a few features required to use `redis-store` with redis cluster. Right now there isn't an official redis cluster
@@ -23,6 +23,27 @@ end
23
23
  ```
24
24
 
25
25
 
26
+ ## Limitations
27
+
28
+ Because keys in a redis cluster are partitioned across shards, operations that
29
+ touch **multiple keys at once** can't be executed in a single command and are
30
+ therefore not supported. These methods raise `NotImplementedError` instead of
31
+ silently returning incorrect results:
32
+
33
+ - `read_multi` (would use `MGET`)
34
+ - `write_multi` (would use `MSET`)
35
+ - `fetch_multi` (would use `MULTI`)
36
+ - `delete_matched` (would use `KEYS`/`SCAN`)
37
+
38
+ ```ruby
39
+ store.read_multi("a", "b") # => raises NotImplementedError
40
+ store.write_multi("a" => 1, "b" => 2) # => raises NotImplementedError
41
+ ```
42
+
43
+ Read and write a single key at a time instead. Single-key operations
44
+ (`read`, `write`, `fetch`, `increment`, `decrement`, `delete`, `exist?`) work
45
+ as usual.
46
+
26
47
  ## Installation
27
48
 
28
49
  Add this line to your application's Gemfile:
@@ -39,6 +60,48 @@ Or install it yourself as:
39
60
 
40
61
  $ gem install redis-cluster-activesupport
41
62
 
63
+ ## Compatibility
64
+
65
+ Requires Ruby 3.1+ (CRuby 3.1–3.4, JRuby 9.4–10.0) and is tested against
66
+ ActiveSupport 6.0 through 8.1 (Rails 6.0–8.1). Note that Rails 6.0/6.1 only run
67
+ on Ruby 3.1–3.3, and Rails 8.0/8.1 require Ruby 3.2+. See
68
+ [`.github/workflows/ci.yml`](.github/workflows/ci.yml) for the full Ruby x Rails
69
+ support matrix.
70
+
71
+ ## Development & Testing
72
+
73
+ Fast unit specs run against [fakeredis](https://github.com/guilleiguaran/fakeredis):
74
+
75
+ ```sh
76
+ bundle exec rspec # unit specs only (integration specs are excluded)
77
+ ```
78
+
79
+ Integration specs exercise the store against a **real Redis Cluster**. Spin one
80
+ up locally (3 masters + 3 replicas) and run them with:
81
+
82
+ ```sh
83
+ bin/redis-cluster # boots a cluster on 127.0.0.1:7000-7005
84
+ bundle exec rake integration
85
+ bin/redis-cluster stop # tear it down when finished
86
+ ```
87
+
88
+ Point the specs at an existing cluster with `REDIS_CLUSTER_NODES`
89
+ (comma-separated seed nodes, e.g. `127.0.0.1:7000,127.0.0.1:7001`). On macOS,
90
+ port 7000 is often held by the AirPlay Receiver — use
91
+ `REDIS_CLUSTER_BASE_PORT=7010 bin/redis-cluster` and set `REDIS_CLUSTER_NODES`
92
+ accordingly.
93
+
94
+ To test against a specific Rails version, use [appraisal](https://github.com/thoughtbot/appraisal).
95
+ The supported versions are declared in [`Appraisals`](Appraisals) and the
96
+ `gemfiles/` are generated from it (and gitignored):
97
+
98
+ ```sh
99
+ bundle exec appraisal generate # (re)generate gemfiles/ from Appraisals
100
+ bundle exec appraisal install # generate + bundle install every version
101
+ bundle exec appraisal rails81 rspec # run unit specs against a single version
102
+ bundle exec appraisal rails81 rake integration # integration specs (needs a cluster)
103
+ ```
104
+
42
105
  ## Contributing
43
106
 
44
107
  Bug reports and pull requests are welcome on GitHub at https://github.com/film42/redis-cluster-activesupport.
data/Rakefile CHANGED
@@ -1,6 +1,15 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
3
 
4
+ # Fast unit specs (fakeredis) — this is what runs by default and excludes the
5
+ # integration suite via .rspec's --exclude-pattern.
4
6
  RSpec::Core::RakeTask.new(:spec)
5
7
 
8
+ # Integration specs that run against a real Redis Cluster. Requires a running
9
+ # cluster (see `bin/redis-cluster`) or REDIS_CLUSTER_NODES.
10
+ RSpec::Core::RakeTask.new(:integration) do |t|
11
+ t.pattern = "spec/integration/**/*_spec.rb"
12
+ t.rspec_opts = "--exclude-pattern ''"
13
+ end
14
+
6
15
  task :default => :spec
data/bin/redis-cluster ADDED
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Boots a local Redis Cluster (3 masters + 3 replicas) for the integration specs,
4
+ # then creates the cluster. Used by developers locally and by CI.
5
+ #
6
+ # bin/redis-cluster # start + create the cluster
7
+ # bin/redis-cluster stop # stop the cluster and clean up
8
+ #
9
+ # Configure with:
10
+ # REDIS_CLUSTER_BASE_PORT first node port (default 7000; nodes use base..base+5)
11
+ # REDIS_CLUSTER_HOST bind/announce host (default 127.0.0.1)
12
+ # REDIS_CLUSTER_DIR working dir for node data (default ./tmp/redis-cluster)
13
+ #
14
+ # Note for macOS users: port 7000 is often taken by the AirPlay Receiver
15
+ # (Control Center). Either disable it or run with REDIS_CLUSTER_BASE_PORT=7010.
16
+ set -euo pipefail
17
+
18
+ HOST="${REDIS_CLUSTER_HOST:-127.0.0.1}"
19
+ BASE_PORT="${REDIS_CLUSTER_BASE_PORT:-7000}"
20
+ DIR="${REDIS_CLUSTER_DIR:-$(pwd)/tmp/redis-cluster}"
21
+
22
+ PORTS=()
23
+ for i in 0 1 2 3 4 5; do PORTS+=("$((BASE_PORT + i))"); done
24
+
25
+ stop_cluster() {
26
+ echo "Stopping Redis Cluster nodes on ports ${PORTS[*]}..."
27
+ for p in "${PORTS[@]}"; do
28
+ redis-cli -h "$HOST" -p "$p" shutdown nosave >/dev/null 2>&1 || true
29
+ done
30
+ rm -rf "$DIR"
31
+ echo "Done."
32
+ }
33
+
34
+ if [[ "${1:-start}" == "stop" ]]; then
35
+ stop_cluster
36
+ exit 0
37
+ fi
38
+
39
+ echo "Starting Redis Cluster: ${HOST} ports ${PORTS[*]}"
40
+ rm -rf "$DIR"
41
+ for p in "${PORTS[@]}"; do
42
+ mkdir -p "$DIR/$p"
43
+ redis-server \
44
+ --port "$p" \
45
+ --cluster-enabled yes \
46
+ --cluster-config-file nodes.conf \
47
+ --cluster-node-timeout 5000 \
48
+ --appendonly no \
49
+ --save "" \
50
+ --dir "$DIR/$p" \
51
+ --daemonize yes \
52
+ --logfile "$DIR/$p/server.log"
53
+ done
54
+
55
+ # Wait for every node to accept connections before forming the cluster.
56
+ for p in "${PORTS[@]}"; do
57
+ for _ in $(seq 1 50); do
58
+ if redis-cli -h "$HOST" -p "$p" ping >/dev/null 2>&1; then break; fi
59
+ sleep 0.2
60
+ done
61
+ done
62
+
63
+ ADDRS=()
64
+ for p in "${PORTS[@]}"; do ADDRS+=("$HOST:$p"); done
65
+
66
+ redis-cli --cluster create "${ADDRS[@]}" --cluster-replicas 1 --cluster-yes
67
+
68
+ # `--cluster create` returns once all slots are covered, but there is a brief
69
+ # gossip window before every node reports `cluster_state:ok`. Commands issued
70
+ # during that window can fail, so block until each node considers the cluster
71
+ # ready before handing control back to the caller / test suite.
72
+ echo "Waiting for cluster_state:ok on all nodes..."
73
+ for p in "${PORTS[@]}"; do
74
+ for _ in $(seq 1 50); do
75
+ if redis-cli -h "$HOST" -p "$p" cluster info 2>/dev/null | grep -q "cluster_state:ok"; then
76
+ break
77
+ fi
78
+ sleep 0.2
79
+ done
80
+ done
81
+
82
+ echo
83
+ echo "Redis Cluster ready. Seed nodes:"
84
+ echo " REDIS_CLUSTER_NODES=${HOST}:${PORTS[0]},${HOST}:${PORTS[1]},${HOST}:${PORTS[2]}"
@@ -7,6 +7,14 @@ module ActiveSupport
7
7
  fail ::NotImplementedError, "Deleting keys with a matcher is not supported with redis cluster"
8
8
  end
9
9
 
10
+ def read_multi(*names)
11
+ fail ::NotImplementedError, "Reading multiple keys with MGET is not supported with redis cluster because the keys may live on different shards"
12
+ end
13
+
14
+ def write_multi(hash, options = nil)
15
+ fail ::NotImplementedError, "Writing multiple keys with MSET is not supported with redis cluster because the keys may live on different shards"
16
+ end
17
+
10
18
  def fetch_multi(*names)
11
19
  fail ::NotImplementedError, "The default implementation uses MULTI which isn't supported. This can be changed to use MSET and work."
12
20
  end
@@ -3,7 +3,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
 
4
4
  Gem::Specification.new do |spec|
5
5
  spec.name = "redis-cluster-activesupport"
6
- spec.version = "1.3.0"
6
+ spec.version = "1.4.0"
7
7
  spec.authors = ["Garrett Thornburg"]
8
8
  spec.email = ["film42@gmail.com"]
9
9
 
@@ -19,7 +19,10 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
 
22
- spec.add_dependency "activesupport", ">= 6.0", "< 7.3"
22
+ spec.required_ruby_version = ">= 3.1"
23
+
24
+ spec.add_dependency "activesupport", ">= 6.0", "< 8.2"
25
+ spec.add_development_dependency "appraisal"
23
26
  spec.add_development_dependency "bundler"
24
27
  spec.add_development_dependency "fakeredis"
25
28
  spec.add_development_dependency "pry"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-cluster-activesupport
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garrett Thornburg
@@ -18,7 +18,7 @@ dependencies:
18
18
  version: '6.0'
19
19
  - - "<"
20
20
  - !ruby/object:Gem::Version
21
- version: '7.3'
21
+ version: '8.2'
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -28,7 +28,21 @@ dependencies:
28
28
  version: '6.0'
29
29
  - - "<"
30
30
  - !ruby/object:Gem::Version
31
- version: '7.3'
31
+ version: '8.2'
32
+ - !ruby/object:Gem::Dependency
33
+ name: appraisal
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
32
46
  - !ruby/object:Gem::Dependency
33
47
  name: bundler
34
48
  requirement: !ruby/object:Gem::Requirement
@@ -109,18 +123,14 @@ files:
109
123
  - ".github/workflows/ci.yml"
110
124
  - ".gitignore"
111
125
  - ".rspec"
112
- - ".travis.yml"
126
+ - Appraisals
113
127
  - Gemfile
114
128
  - LICENSE.txt
115
129
  - README.md
116
130
  - Rakefile
117
131
  - bin/console
132
+ - bin/redis-cluster
118
133
  - bin/setup
119
- - gemfiles/rails60.gemfile
120
- - gemfiles/rails61.gemfile
121
- - gemfiles/rails70.gemfile
122
- - gemfiles/rails71.gemfile
123
- - gemfiles/rails72.gemfile
124
134
  - lib/active_support/cache/redis_cluster_store.rb
125
135
  - lib/redis/cluster/activesupport.rb
126
136
  - redis-cluster-activesupport.gemspec
@@ -135,14 +145,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
135
145
  requirements:
136
146
  - - ">="
137
147
  - !ruby/object:Gem::Version
138
- version: '0'
148
+ version: '3.1'
139
149
  required_rubygems_version: !ruby/object:Gem::Requirement
140
150
  requirements:
141
151
  - - ">="
142
152
  - !ruby/object:Gem::Version
143
153
  version: '0'
144
154
  requirements: []
145
- rubygems_version: 3.6.9
155
+ rubygems_version: 4.0.15
146
156
  specification_version: 4
147
157
  summary: Extension to redis-activesupport for working with redis cluster
148
158
  test_files: []
data/.travis.yml DELETED
@@ -1,18 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - jruby-9.4
4
- - jruby-9.3
5
- - 3.2
6
- - 3.1
7
- - 3.0
8
- - 2.7
9
- - 2.6
10
- gemfile:
11
- - gemfiles/rails60.gemfile
12
- - gemfiles/rails61.gemfile
13
- sudo: false
14
- cache: bundler
15
- notifications:
16
- email:
17
- on_success: never
18
- on_failure: always
@@ -1,4 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gem "activesupport", "~> 6.0.0"
4
- gemspec path: "../"
@@ -1,4 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gem "activesupport", "~> 6.1.0"
4
- gemspec path: "../"
@@ -1,4 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gem "activesupport", "~> 7.0.0"
4
- gemspec path: "../"
@@ -1,4 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gem "activesupport", "~> 7.1.0"
4
- gemspec path: "../"
@@ -1,4 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gem "activesupport", "~> 7.2.0"
4
- gemspec path: "../"