redis-client-namespace 0.1.0 → 0.1.1

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: 5b42761a140eeca3fe1fc46feaf42636faafde11bec1aa8144f055c314686806
4
- data.tar.gz: 795aa14bf6b0616886b6231fedffd2f23c60901d452f204824a0d1523587a4b1
3
+ metadata.gz: 7e13d5a883c2e2dd430843640140758704e128296ddd3ff65c7eddae15a6b98e
4
+ data.tar.gz: 22f7e62d0ccc6d1d2c4b0c888f0985a4e431ede0eb4730f356f7a1a788a1796f
5
5
  SHA512:
6
- metadata.gz: 02cc98329107c65ac11d1e851b277eea83cb520741c82e8d90f8275d4b943163fdc5a7b58806d73f7f4c68151b1b32437050b32b57f8432566a24ad339d95802
7
- data.tar.gz: a59cdff0ae554c69cd060a4bfab2062c0e088352b6ffe0afff082c2f8df1ad85ddd93be233588f9c1b61052bbdfd44e6ae0d0596e7652ad5f99a04b9e30a0821
6
+ metadata.gz: d46cda192df4c1623c8eeb92ec9b2ade00a59a9f26908a115083d29066129d6a80f96e8f6f814d60973ef419e570d0ce68f5772659e1ff133548ed16acd29fc3
7
+ data.tar.gz: 626cea93d4bc9e245ced597235cab8b833a05ad2bdcc3c25aae06dce41e5131df59d55b76186b3fced8cf40167ed95cee0c0b75fbe54fa3f6ba52d43e88deb71
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.1] - 2025-07-29
4
+
5
+ - Add `RedisClient::Namespace.command_builder` class method for conditional namespacing based on environment variables
6
+
3
7
  ## [0.1.0] - 2025-07-26
4
8
 
5
9
  - Initial release
data/README.md CHANGED
@@ -78,6 +78,34 @@ client = RedisClient.config(command_builder: child).new_client
78
78
  client.call("SET", "queue", "important") # Actually sets "jobs:myapp:queue"
79
79
  ```
80
80
 
81
+ ### with `Redis` ([`redis-rb`](https://github.com/redis/redis-rb))
82
+
83
+ This gem also works with the [redis](https://github.com/redis/redis-rb) gem through its `command_builder` option:
84
+
85
+ ```ruby
86
+ require 'redis'
87
+ require 'redis-client-namespace'
88
+
89
+ # Create a namespaced command builder
90
+ namespace = RedisClient::Namespace.new("myapp")
91
+
92
+ # Use with redis-rb
93
+ redis = Redis.new(host: 'localhost', port: 6379, command_builder: namespace)
94
+
95
+ # All commands will be automatically namespaced
96
+ redis.set("user:123", "john") # Actually sets "myapp:user:123"
97
+ redis.get("user:123") # Actually gets "myapp:user:123"
98
+ redis.del("user:123", "user:456") # Actually deletes "myapp:user:123", "myapp:user:456"
99
+
100
+ # Works with all Redis commands
101
+ redis.lpush("queue", ["job1", "job2"]) # Actually pushes to "myapp:queue"
102
+ redis.hset("config", "timeout", "30") # Actually sets in "myapp:config"
103
+ redis.sadd("tags", "ruby", "rails") # Actually adds to "myapp:tags"
104
+
105
+ # Pattern matching
106
+ redis.keys("user:*") # Actually searches for "myapp:user:*"
107
+ ```
108
+
81
109
  ### Sidekiq Integration
82
110
 
83
111
  This gem is particularly useful for Sidekiq applications that need namespace isolation:
@@ -212,4 +240,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
212
240
 
213
241
  ## Code of Conduct
214
242
 
215
- Everyone interacting in the RedisClient::Namespace project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/ken39arg/redis-client-namespace/blob/main/CODE_OF_CONDUCT.md).
243
+ Everyone interacting in the RedisClient::Namespace project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/ken39arg/redis-client-namespace/blob/main/CODE_OF_CONDUCT.md).
@@ -0,0 +1,47 @@
1
+ # Benchmarks
2
+
3
+ This directory contains performance benchmarks comparing `redis-client-namespace` with the traditional `redis-namespace` gem.
4
+
5
+ ## Running Benchmarks
6
+
7
+ ### Prerequisites
8
+
9
+ First, install the benchmark dependencies:
10
+
11
+ ```bash
12
+ bundle install --with benchmark
13
+ ```
14
+
15
+ ### Redis Namespace Comparison
16
+
17
+ To run the benchmark comparing `redis-namespace` vs `redis-client-namespace`:
18
+
19
+ ```bash
20
+ # Using default Redis (localhost:6379)
21
+ ruby benchmark/redis_namespace_comparison.rb
22
+
23
+ # Using custom Redis server
24
+ REDIS_HOST=localhost REDIS_PORT=16379 ruby benchmark/redis_namespace_comparison.rb
25
+ ```
26
+
27
+ This benchmark tests various Redis operations:
28
+ - Single key operations (SET, GET)
29
+ - Multiple key operations (MGET)
30
+ - List operations (LPUSH, LRANGE)
31
+ - Hash operations (HSET, HGETALL)
32
+ - Set operations (SADD, SMEMBERS)
33
+ - Pattern matching (KEYS)
34
+ - Complex operations (ZRANGE)
35
+ - Transactions (MULTI/EXEC)
36
+
37
+ ## Expected Results
38
+
39
+ The `redis-client-namespace` gem is designed to have minimal overhead compared to `redis-namespace`. The benchmarks help verify that the performance characteristics are comparable or better across different types of Redis operations.
40
+
41
+ ## Adding New Benchmarks
42
+
43
+ To add new benchmarks:
44
+ 1. Create a new Ruby file in this directory
45
+ 2. Use `benchmark-ips` for consistent measurement
46
+ 3. Include warm-up phases to ensure fair comparison
47
+ 4. Test a variety of Redis operations relevant to your use case
@@ -0,0 +1,233 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "benchmark/ips"
6
+ require "redis"
7
+ require "redis-namespace"
8
+ require "redis-client-namespace"
9
+
10
+ # Setup Redis connections
11
+ REDIS_HOST = ENV.fetch("REDIS_HOST", "127.0.0.1")
12
+ REDIS_PORT = ENV.fetch("REDIS_PORT", "6379")
13
+ REDIS_DB = ENV.fetch("REDIS_DB", "0")
14
+
15
+ puts "Redis Namespace Benchmark: redis-namespace vs redis-client-namespace"
16
+ puts "=" * 70
17
+ puts "Testing with redis-rb (Redis.new)"
18
+ puts "Redis: #{REDIS_HOST}:#{REDIS_PORT}/#{REDIS_DB}"
19
+ puts "=" * 70
20
+
21
+ # Create Redis clients
22
+ plain_redis = Redis.new(host: REDIS_HOST, port: REDIS_PORT, db: REDIS_DB)
23
+
24
+ # redis-namespace (traditional approach)
25
+ redis_namespace = Redis::Namespace.new("bench_old", redis: plain_redis)
26
+
27
+ # redis-client-namespace (new approach)
28
+ namespace_builder = RedisClient::Namespace.new("bench_new")
29
+ redis_client_namespace = Redis.new(host: REDIS_HOST, port: REDIS_PORT, db: REDIS_DB, command_builder: namespace_builder)
30
+
31
+ # Clean up before benchmark
32
+ plain_redis.flushdb
33
+
34
+ # Warm up
35
+ redis_namespace.set("warmup", "value")
36
+ redis_client_namespace.set("warmup", "value")
37
+ redis_namespace.get("warmup")
38
+ redis_client_namespace.get("warmup")
39
+
40
+ puts "\n## Single Key Operations"
41
+ puts
42
+
43
+ Benchmark.ips do |x|
44
+ x.report("redis-namespace SET") do
45
+ redis_namespace.set("key", "value")
46
+ end
47
+
48
+ x.report("redis-client-namespace SET") do
49
+ redis_client_namespace.set("key", "value")
50
+ end
51
+
52
+ x.compare!
53
+ end
54
+
55
+ Benchmark.ips do |x|
56
+ x.report("redis-namespace GET") do
57
+ redis_namespace.get("key")
58
+ end
59
+
60
+ x.report("redis-client-namespace GET") do
61
+ redis_client_namespace.get("key")
62
+ end
63
+
64
+ x.compare!
65
+ end
66
+
67
+ puts "\n## Multiple Key Operations"
68
+ puts
69
+
70
+ # Prepare data for MGET
71
+ 10.times { |i| redis_namespace.set("mkey#{i}", "value#{i}") }
72
+ 10.times { |i| redis_client_namespace.set("mkey#{i}", "value#{i}") }
73
+
74
+ keys = (0...10).map { |i| "mkey#{i}" }
75
+
76
+ Benchmark.ips do |x|
77
+ x.report("redis-namespace MGET") do
78
+ redis_namespace.mget(*keys)
79
+ end
80
+
81
+ x.report("redis-client-namespace MGET") do
82
+ redis_client_namespace.mget(*keys)
83
+ end
84
+
85
+ x.compare!
86
+ end
87
+
88
+ puts "\n## List Operations"
89
+ puts
90
+
91
+ Benchmark.ips do |x|
92
+ x.report("redis-namespace LPUSH") do
93
+ redis_namespace.lpush("list", %w[item1 item2])
94
+ end
95
+
96
+ x.report("redis-client-namespace LPUSH") do
97
+ redis_client_namespace.lpush("list", %w[item1 item2])
98
+ end
99
+
100
+ x.compare!
101
+ end
102
+
103
+ Benchmark.ips do |x|
104
+ x.report("redis-namespace LRANGE") do
105
+ redis_namespace.lrange("list", 0, -1)
106
+ end
107
+
108
+ x.report("redis-client-namespace LRANGE") do
109
+ redis_client_namespace.lrange("list", 0, -1)
110
+ end
111
+
112
+ x.compare!
113
+ end
114
+
115
+ puts "\n## Hash Operations"
116
+ puts
117
+
118
+ Benchmark.ips do |x|
119
+ x.report("redis-namespace HSET") do
120
+ redis_namespace.hset("hash", "field1", "value1", "field2", "value2")
121
+ end
122
+
123
+ x.report("redis-client-namespace HSET") do
124
+ redis_client_namespace.hset("hash", "field1", "value1", "field2", "value2")
125
+ end
126
+
127
+ x.compare!
128
+ end
129
+
130
+ Benchmark.ips do |x|
131
+ x.report("redis-namespace HGETALL") do
132
+ redis_namespace.hgetall("hash")
133
+ end
134
+
135
+ x.report("redis-client-namespace HGETALL") do
136
+ redis_client_namespace.hgetall("hash")
137
+ end
138
+
139
+ x.compare!
140
+ end
141
+
142
+ puts "\n## Set Operations"
143
+ puts
144
+
145
+ Benchmark.ips do |x|
146
+ x.report("redis-namespace SADD") do
147
+ redis_namespace.sadd("set", %w[member1 member2])
148
+ end
149
+
150
+ x.report("redis-client-namespace SADD") do
151
+ redis_client_namespace.sadd("set", %w[member1 member2])
152
+ end
153
+
154
+ x.compare!
155
+ end
156
+
157
+ Benchmark.ips do |x|
158
+ x.report("redis-namespace SMEMBERS") do
159
+ redis_namespace.smembers("set")
160
+ end
161
+
162
+ x.report("redis-client-namespace SMEMBERS") do
163
+ redis_client_namespace.smembers("set")
164
+ end
165
+
166
+ x.compare!
167
+ end
168
+
169
+ puts "\n## Pattern Matching"
170
+ puts
171
+
172
+ # Create some keys for pattern matching
173
+ 10.times { |i| redis_namespace.set("user:#{i}", "value#{i}") }
174
+ 10.times { |i| redis_client_namespace.set("user:#{i}", "value#{i}") }
175
+
176
+ Benchmark.ips do |x|
177
+ x.report("redis-namespace KEYS") do
178
+ redis_namespace.keys("user:*")
179
+ end
180
+
181
+ x.report("redis-client-namespace KEYS") do
182
+ redis_client_namespace.keys("user:*")
183
+ end
184
+
185
+ x.compare!
186
+ end
187
+
188
+ puts "\n## Complex Operations"
189
+ puts
190
+
191
+ # Prepare sorted set data
192
+ redis_namespace.zadd("zset", [[1, "member1"], [2, "member2"], [3, "member3"]])
193
+ redis_client_namespace.zadd("zset", [[1, "member1"], [2, "member2"], [3, "member3"]])
194
+
195
+ Benchmark.ips do |x|
196
+ x.report("redis-namespace ZRANGE") do
197
+ redis_namespace.zrange("zset", 0, -1)
198
+ end
199
+
200
+ x.report("redis-client-namespace ZRANGE") do
201
+ redis_client_namespace.zrange("zset", 0, -1)
202
+ end
203
+
204
+ x.compare!
205
+ end
206
+
207
+ # Transactions
208
+ puts "\n## Transactions"
209
+ puts
210
+
211
+ Benchmark.ips do |x|
212
+ x.report("redis-namespace MULTI") do
213
+ redis_namespace.multi do |r|
214
+ r.set("tx_key1", "value1")
215
+ r.set("tx_key2", "value2")
216
+ end
217
+ end
218
+
219
+ x.report("redis-client-namespace MULTI") do
220
+ redis_client_namespace.multi do |r|
221
+ r.set("tx_key1", "value1")
222
+ r.set("tx_key2", "value2")
223
+ end
224
+ end
225
+
226
+ x.compare!
227
+ end
228
+
229
+ # Clean up
230
+ plain_redis.flushdb
231
+
232
+ puts "\n#{"=" * 70}"
233
+ puts "Benchmark completed!"
@@ -0,0 +1,86 @@
1
+ # Benchmark Results: redis-namespace vs redis-client-namespace
2
+
3
+ Date: 2025-07-29
4
+
5
+ ## Test Environment
6
+
7
+ - Ruby: 3.4.5 (2025-07-16 revision 20cda200d3) +PRISM [arm64-darwin24]
8
+ - Redis: 127.0.0.1:16379
9
+ - Library versions:
10
+ - redis-namespace: 1.11.0
11
+ - redis-client-namespace: (current development version)
12
+
13
+ ## Summary
14
+
15
+ The benchmarks show that `redis-client-namespace` performs comparably to `redis-namespace` across all tested operations. In most cases, the performance difference falls within the margin of error, indicating that the new implementation introduces minimal overhead.
16
+
17
+ ## Detailed Results
18
+
19
+ ### Single Key Operations
20
+
21
+ | Operation | redis-namespace | redis-client-namespace | Comparison |
22
+ |-----------|----------------|----------------------|------------|
23
+ | SET | 2,045.7 i/s | 2,093.9 i/s | Same-ish (within error) |
24
+ | GET | 2,516.3 i/s | 2,473.3 i/s | Same-ish (within error) |
25
+
26
+ ### Multiple Key Operations
27
+
28
+ | Operation | redis-namespace | redis-client-namespace | Comparison |
29
+ |-----------|----------------|----------------------|------------|
30
+ | MGET (10 keys) | 2,386.6 i/s | 2,440.0 i/s | Same-ish (within error) |
31
+
32
+ ### List Operations
33
+
34
+ | Operation | redis-namespace | redis-client-namespace | Comparison |
35
+ |-----------|----------------|----------------------|------------|
36
+ | LPUSH | 2,264.6 i/s | 2,198.1 i/s | Same-ish (within error) |
37
+ | LRANGE | 64.4 i/s | 67.7 i/s | Same-ish (within error) |
38
+
39
+ ### Hash Operations
40
+
41
+ | Operation | redis-namespace | redis-client-namespace | Comparison |
42
+ |-----------|----------------|----------------------|------------|
43
+ | HSET | 2,660.7 i/s | 2,326.5 i/s | Same-ish (within error) |
44
+ | HGETALL | 2,299.9 i/s | 2,886.9 i/s | Same-ish (within error) |
45
+
46
+ ### Set Operations
47
+
48
+ | Operation | redis-namespace | redis-client-namespace | Comparison |
49
+ |-----------|----------------|----------------------|------------|
50
+ | SADD | 1,421.5 i/s | 2,536.3 i/s | Same-ish (within error) |
51
+ | SMEMBERS | 2,860.3 i/s | 2,945.3 i/s | Same-ish (within error) |
52
+
53
+ ### Pattern Matching
54
+
55
+ | Operation | redis-namespace | redis-client-namespace | Comparison |
56
+ |-----------|----------------|----------------------|------------|
57
+ | KEYS | 2,137.0 i/s | 2,502.2 i/s | Same-ish (within error) |
58
+
59
+ ### Complex Operations
60
+
61
+ | Operation | redis-namespace | redis-client-namespace | Comparison |
62
+ |-----------|----------------|----------------------|------------|
63
+ | ZRANGE | 2,908.9 i/s | 2,506.6 i/s | Same-ish (within error) |
64
+
65
+ ### Transactions
66
+
67
+ | Operation | redis-namespace | redis-client-namespace | Comparison |
68
+ |-----------|----------------|----------------------|------------|
69
+ | MULTI/EXEC | 2,039.9 i/s | 1,685.0 i/s | Same-ish (within error) |
70
+
71
+ ## Key Observations
72
+
73
+ 1. **Performance Parity**: `redis-client-namespace` maintains performance parity with `redis-namespace` across all tested operations.
74
+
75
+ 2. **No Significant Overhead**: The new architecture based on `RedisClient::CommandBuilder` doesn't introduce significant overhead compared to the traditional approach.
76
+
77
+ 3. **Consistent Performance**: Both libraries show consistent performance characteristics across different types of Redis operations.
78
+
79
+ 4. **Production Ready**: The benchmark results indicate that `redis-client-namespace` is suitable for production use as a drop-in replacement for `redis-namespace` when using `redis-rb`.
80
+
81
+ ## Notes
82
+
83
+ - All comparisons marked as "same-ish" indicate that the performance difference falls within the statistical margin of error
84
+ - The benchmarks used `benchmark-ips` for accurate measurements
85
+ - Each operation was warmed up before measurement to ensure fair comparison
86
+ - LRANGE operations show lower throughput due to the large amount of data being transferred
@@ -2,6 +2,6 @@
2
2
 
3
3
  class RedisClient
4
4
  class Namespace
5
- VERSION = "0.1.0"
5
+ VERSION = "0.1.1"
6
6
  end
7
7
  end
@@ -32,5 +32,42 @@ class RedisClient
32
32
  @separator = separator
33
33
  @parent_command_builder = parent_command_builder
34
34
  end
35
+
36
+ # Creates a command builder that conditionally applies namespacing.
37
+ #
38
+ # If the namespace is nil or empty, returns the parent_command_builder directly,
39
+ # effectively disabling namespacing. Otherwise, creates a new Namespace instance.
40
+ #
41
+ # This is particularly useful for environment-based configuration where you want
42
+ # to enable/disable namespacing based on environment variables.
43
+ #
44
+ # @param namespace [String, nil] The namespace to use. If nil or empty, namespacing is disabled
45
+ # @param separator [String] The separator between namespace and key (default: ":")
46
+ # @param parent_command_builder [Object] The parent command builder to use (default: RedisClient::CommandBuilder)
47
+ # @return [Object] Either a Namespace instance or the parent_command_builder
48
+ #
49
+ # @example Environment-based namespacing
50
+ # # Enable namespacing only when REDIS_NAMESPACE is set
51
+ # builder = RedisClient::Namespace.command_builder(ENV.fetch("REDIS_NAMESPACE", ""))
52
+ # client = RedisClient.new(command_builder: builder)
53
+ #
54
+ # # With REDIS_NAMESPACE=myapp: keys will be prefixed with "myapp:"
55
+ # # With REDIS_NAMESPACE="" or unset: no namespacing applied
56
+ #
57
+ # @example Sidekiq configuration
58
+ # Sidekiq.configure_server do |config|
59
+ # config.redis = {
60
+ # url: 'redis://localhost:6379/1',
61
+ # command_builder: RedisClient::Namespace.command_builder(ENV.fetch("REDIS_NAMESPACE", ""))
62
+ # }
63
+ # end
64
+ def self.command_builder(namespace = "", separator: ":", parent_command_builder: RedisClient::CommandBuilder)
65
+ if namespace.nil? || namespace.empty?
66
+ parent_command_builder
67
+ else
68
+ new(namespace, separator: separator,
69
+ parent_command_builder: parent_command_builder)
70
+ end
71
+ end
35
72
  end
36
73
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-client-namespace
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kensaku Araga
@@ -38,6 +38,9 @@ files:
38
38
  - LICENSE.txt
39
39
  - README.md
40
40
  - Rakefile
41
+ - benchmark/README.md
42
+ - benchmark/redis_namespace_comparison.rb
43
+ - benchmark/results.md
41
44
  - compose.yml
42
45
  - lib/redis-client-namespace.rb
43
46
  - lib/redis_client/namespace.rb