async-redis 0.12.0 → 0.13.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/context/client-architecture.md +124 -0
- data/context/data-structures.md +486 -0
- data/context/getting-started.md +37 -41
- data/context/index.yaml +20 -0
- data/context/scripting.md +243 -0
- data/context/streams.md +317 -0
- data/context/subscriptions.md +35 -24
- data/context/transactions-and-pipelines.md +197 -0
- data/lib/async/redis/context/pipeline.rb +1 -1
- data/lib/async/redis/endpoint.rb +26 -4
- data/lib/async/redis/range_map.rb +3 -0
- data/lib/async/redis/version.rb +1 -1
- data/license.md +1 -1
- data/readme.md +15 -0
- data/releases.md +5 -0
- data.tar.gz.sig +0 -0
- metadata +8 -3
- metadata.gz.sig +0 -0
    
        data/context/subscriptions.md
    CHANGED
    
    | @@ -2,24 +2,35 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            This guide explains how to use Redis pub/sub functionality with `async-redis` to publish and subscribe to messages.
         | 
| 4 4 |  | 
| 5 | 
            +
            Redis pub/sub enables real-time communication between different parts of your application or between different applications. It's perfect for broadcasting notifications, coordinating distributed systems, or building real-time features, provided you don't need reliable messaging.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Common use cases:
         | 
| 8 | 
            +
            - **Real-time notifications**: Alert users about new messages, updates, or events.
         | 
| 9 | 
            +
            - **System coordination**: Notify services about configuration changes or cache invalidation.
         | 
| 10 | 
            +
            - **Live updates**: Push data changes to web interfaces or mobile apps.
         | 
| 11 | 
            +
            - **Event-driven architecture**: Decouple services through asynchronous messaging.
         | 
| 12 | 
            +
             | 
| 5 13 | 
             
            ## Overview
         | 
| 6 14 |  | 
| 7 | 
            -
            Redis  | 
| 15 | 
            +
            Redis provides 3 pub/sub mechanisms:
         | 
| 16 | 
            +
            - **SUBSCRIBE**: Subscribe to specific channels by name.
         | 
| 17 | 
            +
            - **PSUBSCRIBE**: Subscribe to channels matching patterns (e.g., `user.*`).
         | 
| 18 | 
            +
            - **SSUBSCRIBE**: Sharded subscriptions for cluster environments (better performance).
         | 
| 8 19 |  | 
| 9 20 | 
             
            ## Subscribe
         | 
| 10 21 |  | 
| 11 | 
            -
            The `SUBSCRIBE` command  | 
| 22 | 
            +
            The `SUBSCRIBE` command lets you listen for messages on specific channels. This creates a persistent connection that receives messages as they're published.
         | 
| 12 23 |  | 
| 13 | 
            -
             | 
| 24 | 
            +
            Here's a simple notification system - first, create a listener:
         | 
| 14 25 |  | 
| 15 26 | 
             
            ``` ruby
         | 
| 16 | 
            -
            require  | 
| 17 | 
            -
            require  | 
| 27 | 
            +
            require "async"
         | 
| 28 | 
            +
            require "async/redis"
         | 
| 18 29 |  | 
| 19 30 | 
             
            client = Async::Redis::Client.new
         | 
| 20 31 |  | 
| 21 32 | 
             
            Async do
         | 
| 22 | 
            -
            	client.subscribe  | 
| 33 | 
            +
            	client.subscribe "status.frontend" do |context|
         | 
| 23 34 | 
             
            		puts "Listening for messages on 'status.frontend'..."
         | 
| 24 35 |  | 
| 25 36 | 
             
            		type, name, message = context.listen
         | 
| @@ -29,17 +40,17 @@ Async do | |
| 29 40 | 
             
            end
         | 
| 30 41 | 
             
            ```
         | 
| 31 42 |  | 
| 32 | 
            -
             | 
| 43 | 
            +
            In another part of your application, publish notifications:
         | 
| 33 44 |  | 
| 34 45 | 
             
            ``` ruby
         | 
| 35 | 
            -
            require  | 
| 36 | 
            -
            require  | 
| 46 | 
            +
            require "async"
         | 
| 47 | 
            +
            require "async/redis"
         | 
| 37 48 |  | 
| 38 49 | 
             
            client = Async::Redis::Client.new
         | 
| 39 50 |  | 
| 40 51 | 
             
            Async do
         | 
| 41 52 | 
             
            	puts "Publishing message..."
         | 
| 42 | 
            -
            	client.publish  | 
| 53 | 
            +
            	client.publish "status.frontend", "good"
         | 
| 43 54 | 
             
            	puts "Message sent!"
         | 
| 44 55 | 
             
            end
         | 
| 45 56 | 
             
            ```
         | 
| @@ -57,13 +68,13 @@ Received: good | |
| 57 68 | 
             
            Subscriptions are at-most-once delivery. In addition, subscriptions are stateful, meaning that they maintain their own internal state and can be affected by network issues or server restarts. In order to improve resilience, it's important to implement error handling and reconnection logic.
         | 
| 58 69 |  | 
| 59 70 | 
             
            ```ruby
         | 
| 60 | 
            -
            require  | 
| 61 | 
            -
            require  | 
| 71 | 
            +
            require "async"
         | 
| 72 | 
            +
            require "async/redis"
         | 
| 62 73 |  | 
| 63 74 | 
             
            client = Async::Redis::Client.new
         | 
| 64 75 |  | 
| 65 76 | 
             
            Async do
         | 
| 66 | 
            -
            	client.subscribe  | 
| 77 | 
            +
            	client.subscribe "status.frontend" do |context|
         | 
| 67 78 | 
             
            		puts "Listening for messages on 'status.frontend'..."
         | 
| 68 79 |  | 
| 69 80 | 
             
            		context.each do |type, name, message|
         | 
| @@ -79,19 +90,19 @@ end | |
| 79 90 |  | 
| 80 91 | 
             
            ## Pattern Subscribe
         | 
| 81 92 |  | 
| 82 | 
            -
             | 
| 93 | 
            +
            When you need to listen to multiple related channels, patterns save you from subscribing to each channel individually. This is perfect for monitoring all user activities or all events from a specific service.
         | 
| 83 94 |  | 
| 84 | 
            -
             | 
| 95 | 
            +
            For example, to monitor all user-related events (`user.login`, `user.logout`, `user.signup`):
         | 
| 85 96 |  | 
| 86 97 | 
             
            ``` ruby
         | 
| 87 | 
            -
            require  | 
| 88 | 
            -
            require  | 
| 98 | 
            +
            require "async"
         | 
| 99 | 
            +
            require "async/redis"
         | 
| 89 100 |  | 
| 90 101 | 
             
            endpoint = Async::Redis.local_endpoint
         | 
| 91 102 | 
             
            client = Async::Redis::Client.new(endpoint)
         | 
| 92 103 |  | 
| 93 104 | 
             
            Async do
         | 
| 94 | 
            -
            	client.psubscribe  | 
| 105 | 
            +
            	client.psubscribe "status.*" do |context|
         | 
| 95 106 | 
             
            		puts "Listening for messages on 'status.*'..."
         | 
| 96 107 |  | 
| 97 108 | 
             
            		type, pattern, name, message = context.listen
         | 
| @@ -110,14 +121,14 @@ If you are working with a clustered environment, you can improve performance by | |
| 110 121 | 
             
            To use sharded subscriptions, use a cluster client which supports sharded pub/sub:
         | 
| 111 122 |  | 
| 112 123 | 
             
            ``` ruby
         | 
| 113 | 
            -
            require  | 
| 114 | 
            -
            require  | 
| 124 | 
            +
            require "async"
         | 
| 125 | 
            +
            require "async/redis"
         | 
| 115 126 |  | 
| 116 127 | 
             
            # endpoints = ...
         | 
| 117 128 | 
             
            cluster_client = Async::Redis::ClusterClient.new(endpoints)
         | 
| 118 129 |  | 
| 119 130 | 
             
            Async do
         | 
| 120 | 
            -
            	cluster_client.subscribe  | 
| 131 | 
            +
            	cluster_client.subscribe "status.frontend" do |context|
         | 
| 121 132 | 
             
            		puts "Listening for messages on 'status.frontend'..."
         | 
| 122 133 |  | 
| 123 134 | 
             
            		type, name, message = context.listen
         | 
| @@ -128,15 +139,15 @@ end | |
| 128 139 | 
             
            ```
         | 
| 129 140 |  | 
| 130 141 | 
             
            ``` ruby
         | 
| 131 | 
            -
            require  | 
| 132 | 
            -
            require  | 
| 142 | 
            +
            require "async"
         | 
| 143 | 
            +
            require "async/redis"
         | 
| 133 144 |  | 
| 134 145 | 
             
            # endpoints = ...
         | 
| 135 146 | 
             
            cluster_client = Async::Redis::ClusterClient.new(endpoints)
         | 
| 136 147 |  | 
| 137 148 | 
             
            Async do
         | 
| 138 149 | 
             
            	puts "Publishing message..."
         | 
| 139 | 
            -
            	cluster_client.publish( | 
| 150 | 
            +
            	cluster_client.publish("status.frontend", "good")
         | 
| 140 151 | 
             
            	puts "Message sent!"
         | 
| 141 152 | 
             
            end
         | 
| 142 153 | 
             
            ```
         | 
| @@ -0,0 +1,197 @@ | |
| 1 | 
            +
            # Transactions and Pipelines
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            This guide explains how to use Redis transactions and pipelines with `async-redis` for atomic operations and improved performance.
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            By default, each command (e.g. `GET key`) acquires a connection from the client, runs the command, returns the result and releases the connection back to the client's connection pool. This may be inefficient for some use cases, and so there are several ways to group together operations that run on the same connection.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            ## Transactions (MULTI/EXEC)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            Transactions ensure that multiple Redis commands execute atomically - either all commands succeed, or none of them do. This is crucial when you need to maintain data consistency, such as transferring money between accounts or updating related fields together.
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            Use transactions when you need:
         | 
| 12 | 
            +
            - **Atomic updates**: Multiple operations that must all succeed or all fail.
         | 
| 13 | 
            +
            - **Data consistency**: Keeping related data in sync across multiple keys.
         | 
| 14 | 
            +
            - **Preventing partial updates**: Avoiding situations where only some of your changes are applied.
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            Redis transactions queue commands and execute them all at once:
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            ``` ruby
         | 
| 19 | 
            +
            require "async/redis"
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            endpoint = Async::Redis.local_endpoint
         | 
| 22 | 
            +
            client = Async::Redis::Client.new(endpoint)
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            Async do
         | 
| 25 | 
            +
            	begin
         | 
| 26 | 
            +
            		# Execute commands atomically:
         | 
| 27 | 
            +
            		results = client.transaction do |context|
         | 
| 28 | 
            +
            			context.multi
         | 
| 29 | 
            +
            			
         | 
| 30 | 
            +
            			# Queue commands for atomic execution:
         | 
| 31 | 
            +
            			context.set("user:1:name", "Alice")
         | 
| 32 | 
            +
            			context.set("user:1:email", "alice@example.com")
         | 
| 33 | 
            +
            			context.incr("user:count")
         | 
| 34 | 
            +
            			
         | 
| 35 | 
            +
            			# Execute all queued commands:
         | 
| 36 | 
            +
            			context.execute
         | 
| 37 | 
            +
            		end
         | 
| 38 | 
            +
            		
         | 
| 39 | 
            +
            		puts "Transaction results: #{results}"
         | 
| 40 | 
            +
            		
         | 
| 41 | 
            +
            	ensure
         | 
| 42 | 
            +
            		client.close
         | 
| 43 | 
            +
            	end
         | 
| 44 | 
            +
            end
         | 
| 45 | 
            +
            ```
         | 
| 46 | 
            +
             | 
| 47 | 
            +
            ### Watch/Unwatch for Optimistic Locking
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            When multiple clients might modify the same data simultaneously, you need to handle race conditions. Redis WATCH provides optimistic locking - the transaction only executes if watched keys haven't changed since you started watching them.
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            This is essential for scenarios like:
         | 
| 52 | 
            +
            - Updating counters or balances where the current value matters.
         | 
| 53 | 
            +
            - Implementing atomic increment operations with business logic.
         | 
| 54 | 
            +
            - Preventing lost updates in concurrent environments.
         | 
| 55 | 
            +
             | 
| 56 | 
            +
            Here's how to implement safe concurrent updates:
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            ``` ruby
         | 
| 59 | 
            +
            require "async/redis"
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            endpoint = Async::Redis.local_endpoint
         | 
| 62 | 
            +
            client = Async::Redis::Client.new(endpoint)
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            Async do
         | 
| 65 | 
            +
            	begin
         | 
| 66 | 
            +
            		# Initialize counter.
         | 
| 67 | 
            +
            		client.set("counter", 0)
         | 
| 68 | 
            +
            		
         | 
| 69 | 
            +
            		# Optimistic locking example:
         | 
| 70 | 
            +
            		5.times do |i|
         | 
| 71 | 
            +
            			success = false
         | 
| 72 | 
            +
            			attempts = 0
         | 
| 73 | 
            +
            			
         | 
| 74 | 
            +
            			while !success && attempts < 3
         | 
| 75 | 
            +
            				attempts += 1
         | 
| 76 | 
            +
            				
         | 
| 77 | 
            +
            				result = client.transaction do |context|
         | 
| 78 | 
            +
            					# Watch the counter for changes (executes immediately):
         | 
| 79 | 
            +
            					context.watch("counter")
         | 
| 80 | 
            +
            					
         | 
| 81 | 
            +
            					# Read current value (executes immediately):
         | 
| 82 | 
            +
            					current_value = client.get("counter").to_i
         | 
| 83 | 
            +
            					new_value = current_value + 1
         | 
| 84 | 
            +
            					
         | 
| 85 | 
            +
            					# Start transaction - commands after this are queued, not executed:
         | 
| 86 | 
            +
            					context.multi
         | 
| 87 | 
            +
            					
         | 
| 88 | 
            +
            					# Queue commands (these return "QUEUED", don't execute yet):
         | 
| 89 | 
            +
            					context.set("counter", new_value)
         | 
| 90 | 
            +
            					context.set("last_update", Time.now.to_f)
         | 
| 91 | 
            +
            					
         | 
| 92 | 
            +
            					# Execute all queued commands atomically.
         | 
| 93 | 
            +
            					# Returns nil if watched key was modified by another client:
         | 
| 94 | 
            +
            					context.execute
         | 
| 95 | 
            +
            				end
         | 
| 96 | 
            +
            				
         | 
| 97 | 
            +
            				if result
         | 
| 98 | 
            +
            					puts "Increment #{i + 1} succeeded: #{result}"
         | 
| 99 | 
            +
            					success = true
         | 
| 100 | 
            +
            				else
         | 
| 101 | 
            +
            					puts "Increment #{i + 1} failed, retrying (attempt #{attempts})"
         | 
| 102 | 
            +
            					sleep 0.01
         | 
| 103 | 
            +
            				end
         | 
| 104 | 
            +
            			end
         | 
| 105 | 
            +
            		end
         | 
| 106 | 
            +
            		
         | 
| 107 | 
            +
            		final_value = client.get("counter")
         | 
| 108 | 
            +
            		puts "Final counter value: #{final_value}"
         | 
| 109 | 
            +
            		
         | 
| 110 | 
            +
            	ensure
         | 
| 111 | 
            +
            		client.close
         | 
| 112 | 
            +
            	end
         | 
| 113 | 
            +
            end
         | 
| 114 | 
            +
            ```
         | 
| 115 | 
            +
             | 
| 116 | 
            +
            ## Pipelines
         | 
| 117 | 
            +
             | 
| 118 | 
            +
            When you need to execute many Redis commands quickly, sending them one-by-one creates network latency bottlenecks. Pipelines solve this by batching multiple commands together, dramatically reducing round-trip time.
         | 
| 119 | 
            +
             | 
| 120 | 
            +
            Use pipelines when you need:
         | 
| 121 | 
            +
            - **Better performance**: Reduce network round trips for bulk operations.
         | 
| 122 | 
            +
            - **High throughput**: Process hundreds or thousands of commands efficiently.
         | 
| 123 | 
            +
            - **Independent operations**: Commands that don't depend on each other's results.
         | 
| 124 | 
            +
             | 
| 125 | 
            +
            Unlike transactions, pipeline commands are not atomic - some may succeed while others fail:
         | 
| 126 | 
            +
             | 
| 127 | 
            +
            ``` ruby
         | 
| 128 | 
            +
            require "async/redis"
         | 
| 129 | 
            +
             | 
| 130 | 
            +
            endpoint = Async::Redis.local_endpoint
         | 
| 131 | 
            +
            client = Async::Redis::Client.new(endpoint)
         | 
| 132 | 
            +
             | 
| 133 | 
            +
            Async do
         | 
| 134 | 
            +
            	begin
         | 
| 135 | 
            +
            		# Execute commands in a pipeline:
         | 
| 136 | 
            +
            		results = client.pipeline do |context|
         | 
| 137 | 
            +
            			# Commands are buffered and flushed if needed:
         | 
| 138 | 
            +
            			context.set("pipeline:1", "value1")
         | 
| 139 | 
            +
            			context.set("pipeline:2", "value2")
         | 
| 140 | 
            +
            			context.set("pipeline:3", "value3")
         | 
| 141 | 
            +
            			
         | 
| 142 | 
            +
            			context.get("pipeline:1")
         | 
| 143 | 
            +
            			context.get("pipeline:2")
         | 
| 144 | 
            +
            			context.get("pipeline:3")
         | 
| 145 | 
            +
            			
         | 
| 146 | 
            +
            			# Flush and collect the results from all previous commands:
         | 
| 147 | 
            +
            			context.collect
         | 
| 148 | 
            +
            		end
         | 
| 149 | 
            +
            		
         | 
| 150 | 
            +
            		puts "Pipeline results: #{results}"
         | 
| 151 | 
            +
            		
         | 
| 152 | 
            +
            	ensure
         | 
| 153 | 
            +
            		client.close
         | 
| 154 | 
            +
            	end
         | 
| 155 | 
            +
            end
         | 
| 156 | 
            +
            ```
         | 
| 157 | 
            +
             | 
| 158 | 
            +
            ### Synchronous Pipeline Operations
         | 
| 159 | 
            +
             | 
| 160 | 
            +
            When you need immediate results from individual commands within a pipeline, use `context.sync`:
         | 
| 161 | 
            +
             | 
| 162 | 
            +
            ``` ruby
         | 
| 163 | 
            +
            require "async/redis"
         | 
| 164 | 
            +
             | 
| 165 | 
            +
            endpoint = Async::Redis.local_endpoint
         | 
| 166 | 
            +
            client = Async::Redis::Client.new(endpoint)
         | 
| 167 | 
            +
             | 
| 168 | 
            +
            Async do
         | 
| 169 | 
            +
            	begin
         | 
| 170 | 
            +
            		client.pipeline do |context|
         | 
| 171 | 
            +
            			# Set values using pipeline - no immediate response:
         | 
| 172 | 
            +
            			context.set("async_key_1", "value1")
         | 
| 173 | 
            +
            			context.set("async_key_2", "value2")
         | 
| 174 | 
            +
            			
         | 
| 175 | 
            +
            			# Get immediate response using sync:
         | 
| 176 | 
            +
            			immediate_result = context.sync.get("async_key_1")
         | 
| 177 | 
            +
            			puts "Immediate result: #{immediate_result}"
         | 
| 178 | 
            +
            			
         | 
| 179 | 
            +
            			# Continue with pipelined operations:
         | 
| 180 | 
            +
            			context.get("async_key_2")
         | 
| 181 | 
            +
            			
         | 
| 182 | 
            +
            			# Collect remaining pipelined results:
         | 
| 183 | 
            +
            			context.collect
         | 
| 184 | 
            +
            		end
         | 
| 185 | 
            +
            		
         | 
| 186 | 
            +
            	ensure
         | 
| 187 | 
            +
            		client.close
         | 
| 188 | 
            +
            	end
         | 
| 189 | 
            +
            end
         | 
| 190 | 
            +
            ```
         | 
| 191 | 
            +
             | 
| 192 | 
            +
            Use `context.sync` when you need to:
         | 
| 193 | 
            +
            - **Check values mid-pipeline**: Verify data before continuing with more operations.
         | 
| 194 | 
            +
            - **Conditional logic**: Make decisions based on current Redis state.
         | 
| 195 | 
            +
            - **Debugging**: Get immediate feedback during pipeline development.
         | 
| 196 | 
            +
             | 
| 197 | 
            +
            Note that `sync` operations execute immediately and flush pending responses, so use them strategically to maintain pipeline performance benefits.
         | 
    
        data/lib/async/redis/endpoint.rb
    CHANGED
    
    | @@ -2,10 +2,12 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            # Released under the MIT License.
         | 
| 4 4 | 
             
            # Copyright, 2024-2025, by Samuel Williams.
         | 
| 5 | 
            +
            # Copyright, 2025, by Joan Lledó.
         | 
| 5 6 |  | 
| 6 7 | 
             
            require "io/endpoint"
         | 
| 7 8 | 
             
            require "io/endpoint/host_endpoint"
         | 
| 8 9 | 
             
            require "io/endpoint/ssl_endpoint"
         | 
| 10 | 
            +
            require "io/endpoint/unix_endpoint"
         | 
| 9 11 |  | 
| 10 12 | 
             
            require_relative "protocol/resp2"
         | 
| 11 13 | 
             
            require_relative "protocol/authenticated"
         | 
| @@ -41,6 +43,15 @@ module Async | |
| 41 43 | 
             
            				self.new(URI::Generic.build(scheme: "redis", host: host, port: port), **options)
         | 
| 42 44 | 
             
            			end
         | 
| 43 45 |  | 
| 46 | 
            +
            			# Create a local Redis endpoint from a UNIX socket path.
         | 
| 47 | 
            +
            			# @parameter path [String] The path to the UNIX socket.
         | 
| 48 | 
            +
            			# @parameter options [Hash] Additional options for the endpoint.
         | 
| 49 | 
            +
            			# @returns [Endpoint] A local Redis endpoint.
         | 
| 50 | 
            +
            			def self.unix(path, **options)
         | 
| 51 | 
            +
            				endpoint = ::IO::Endpoint.unix(path)
         | 
| 52 | 
            +
            				self.new(URI::Generic.build(scheme: "redis", path:), endpoint, **options)
         | 
| 53 | 
            +
            			end
         | 
| 54 | 
            +
            			
         | 
| 44 55 | 
             
            			SCHEMES = {
         | 
| 45 56 | 
             
            				"redis" => URI::Generic,
         | 
| 46 57 | 
             
            				"rediss" => URI::Generic,
         | 
| @@ -63,13 +74,13 @@ module Async | |
| 63 74 | 
             
            			# @parameter scheme [String, nil] The scheme to use, e.g. "redis" or "rediss". If nil, will auto-detect.
         | 
| 64 75 | 
             
            			# @parameter hostname [String] The hostname to connect to (or bind to).
         | 
| 65 76 | 
             
            			# @parameter options [Hash] Additional options, passed to {#initialize}.
         | 
| 66 | 
            -
            			def self.for(scheme, host,  | 
| 77 | 
            +
            			def self.for(scheme, host, port: nil, database: nil, **options)
         | 
| 67 78 | 
             
            				# Auto-detect scheme if not provided:
         | 
| 68 79 | 
             
            				if default_scheme = options.delete(:scheme)
         | 
| 69 80 | 
             
            					scheme ||= default_scheme
         | 
| 70 81 | 
             
            				end
         | 
| 71 82 |  | 
| 72 | 
            -
            				scheme ||= options | 
| 83 | 
            +
            				scheme ||= options[:ssl_context] ? "rediss" : "redis"
         | 
| 73 84 |  | 
| 74 85 | 
             
            				uri_klass = SCHEMES.fetch(scheme.downcase) do
         | 
| 75 86 | 
             
            					raise ArgumentError, "Unsupported scheme: #{scheme.inspect}"
         | 
| @@ -82,7 +93,6 @@ module Async | |
| 82 93 | 
             
            				self.new(
         | 
| 83 94 | 
             
            					uri_klass.build(
         | 
| 84 95 | 
             
            						scheme: scheme,
         | 
| 85 | 
            -
            						userinfo: credentials&.join(":"),
         | 
| 86 96 | 
             
            						host: host,
         | 
| 87 97 | 
             
            						port: port,
         | 
| 88 98 | 
             
            						path: path,
         | 
| @@ -263,7 +273,7 @@ module Async | |
| 263 273 | 
             
            			# @parameter endpoint [IO::Endpoint] Optional base endpoint to wrap.
         | 
| 264 274 | 
             
            			# @returns [IO::Endpoint] The built endpoint, potentially wrapped with SSL.
         | 
| 265 275 | 
             
            			def build_endpoint(endpoint = nil)
         | 
| 266 | 
            -
            				endpoint ||=  | 
| 276 | 
            +
            				endpoint ||= make_endpoint
         | 
| 267 277 |  | 
| 268 278 | 
             
            				if secure?
         | 
| 269 279 | 
             
            					# Wrap it in SSL:
         | 
| @@ -345,9 +355,21 @@ module Async | |
| 345 355 | 
             
            				return options
         | 
| 346 356 | 
             
            			end
         | 
| 347 357 |  | 
| 358 | 
            +
            			def unix_endpoint
         | 
| 359 | 
            +
            				::IO::Endpoint.unix(@url.path)
         | 
| 360 | 
            +
            			end
         | 
| 361 | 
            +
            			
         | 
| 348 362 | 
             
            			def tcp_endpoint
         | 
| 349 363 | 
             
            				::IO::Endpoint.tcp(self.hostname, port, **tcp_options)
         | 
| 350 364 | 
             
            			end
         | 
| 365 | 
            +
            			
         | 
| 366 | 
            +
            			def make_endpoint
         | 
| 367 | 
            +
            				if @url.host
         | 
| 368 | 
            +
            					tcp_endpoint
         | 
| 369 | 
            +
            				else
         | 
| 370 | 
            +
            					unix_endpoint
         | 
| 371 | 
            +
            				end
         | 
| 372 | 
            +
            			end
         | 
| 351 373 | 
             
            		end
         | 
| 352 374 | 
             
            	end
         | 
| 353 375 | 
             
            end
         | 
    
        data/lib/async/redis/version.rb
    CHANGED
    
    
    
        data/license.md
    CHANGED
    
    | @@ -12,7 +12,7 @@ Copyright, 2021, by Olle Jonsson. | |
| 12 12 | 
             
            Copyright, 2021, by Troex Nevelin.  
         | 
| 13 13 | 
             
            Copyright, 2022, by Tim Willard.  
         | 
| 14 14 | 
             
            Copyright, 2022, by Gleb Sinyavskiy.  
         | 
| 15 | 
            -
            Copyright, 2024, by Joan Lledó.  
         | 
| 15 | 
            +
            Copyright, 2024-2025, by Joan Lledó.  
         | 
| 16 16 | 
             
            Copyright, 2025, by Travis Bell.  
         | 
| 17 17 |  | 
| 18 18 | 
             
            Permission is hereby granted, free of charge, to any person obtaining a copy
         | 
    
        data/readme.md
    CHANGED
    
    | @@ -14,12 +14,27 @@ Please see the [project documentation](https://socketry.github.io/async-redis/) | |
| 14 14 |  | 
| 15 15 | 
             
              - [Getting Started](https://socketry.github.io/async-redis/guides/getting-started/index) - This guide explains how to use the `async-redis` gem to connect to a Redis server and perform basic operations.
         | 
| 16 16 |  | 
| 17 | 
            +
              - [Transactions and Pipelines](https://socketry.github.io/async-redis/guides/transactions-and-pipelines/index) - This guide explains how to use Redis transactions and pipelines with `async-redis` for atomic operations and improved performance.
         | 
| 18 | 
            +
             | 
| 17 19 | 
             
              - [Subscriptions](https://socketry.github.io/async-redis/guides/subscriptions/index) - This guide explains how to use Redis pub/sub functionality with `async-redis` to publish and subscribe to messages.
         | 
| 18 20 |  | 
| 21 | 
            +
              - [Data Structures and Operations](https://socketry.github.io/async-redis/guides/data-structures/index) - This guide explains how to work with Redis data types and operations using `async-redis`.
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              - [Streams](https://socketry.github.io/async-redis/guides/streams/index) - This guide explains how to use Redis streams with `async-redis` for reliable message processing and event sourcing.
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              - [Scripting](https://socketry.github.io/async-redis/guides/scripting/index) - This guide explains how to use Redis Lua scripting with `async-redis` for atomic operations and advanced data processing.
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              - [Client Architecture](https://socketry.github.io/async-redis/guides/client-architecture/index) - This guide explains the different client types available in `async-redis` and when to use each one.
         | 
| 28 | 
            +
             | 
| 19 29 | 
             
            ## Releases
         | 
| 20 30 |  | 
| 21 31 | 
             
            Please see the [project releases](https://socketry.github.io/async-redis/releases/index) for all releases.
         | 
| 22 32 |  | 
| 33 | 
            +
            ### v0.13.0
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              - Fix password with special characters when using sentinels.
         | 
| 36 | 
            +
              - Fix support for UNIX domain socket endpoints. You can now create Unix socket endpoints using `Async::Redis::Endpoint.unix("/path/to/socket.sock")` or parse them from URLs like `redis:/path/to/socket.sock`.
         | 
| 37 | 
            +
             | 
| 23 38 | 
             
            ### v0.12.0
         | 
| 24 39 |  | 
| 25 40 | 
             
              - Add agent context.
         | 
    
        data/releases.md
    CHANGED
    
    | @@ -1,5 +1,10 @@ | |
| 1 1 | 
             
            # Releases
         | 
| 2 2 |  | 
| 3 | 
            +
            ## v0.13.0
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              - Fix password with special characters when using sentinels.
         | 
| 6 | 
            +
              - Fix support for UNIX domain socket endpoints. You can now create Unix socket endpoints using `Async::Redis::Endpoint.unix("/path/to/socket.sock")` or parse them from URLs like `redis:/path/to/socket.sock`.
         | 
| 7 | 
            +
             | 
| 3 8 | 
             
            ## v0.12.0
         | 
| 4 9 |  | 
| 5 10 | 
             
              - Add agent context.
         | 
    
        data.tar.gz.sig
    CHANGED
    
    | Binary file | 
    
        metadata
    CHANGED
    
    | @@ -1,19 +1,19 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: async-redis
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.13.1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Samuel Williams
         | 
| 8 8 | 
             
            - Huba Nagy
         | 
| 9 9 | 
             
            - David Ortiz
         | 
| 10 | 
            +
            - Joan Lledó
         | 
| 10 11 | 
             
            - Gleb Sinyavskiy
         | 
| 11 12 | 
             
            - Mikael Henriksson
         | 
| 12 13 | 
             
            - Travis Bell
         | 
| 13 14 | 
             
            - Troex Nevelin
         | 
| 14 15 | 
             
            - Alex Matchneer
         | 
| 15 16 | 
             
            - Jeremy Jung
         | 
| 16 | 
            -
            - Joan Lledó
         | 
| 17 17 | 
             
            - Olle Jonsson
         | 
| 18 18 | 
             
            - Pierre Montelle
         | 
| 19 19 | 
             
            - Salim Semaoune
         | 
| @@ -125,9 +125,14 @@ executables: [] | |
| 125 125 | 
             
            extensions: []
         | 
| 126 126 | 
             
            extra_rdoc_files: []
         | 
| 127 127 | 
             
            files:
         | 
| 128 | 
            +
            - context/client-architecture.md
         | 
| 129 | 
            +
            - context/data-structures.md
         | 
| 128 130 | 
             
            - context/getting-started.md
         | 
| 129 131 | 
             
            - context/index.yaml
         | 
| 132 | 
            +
            - context/scripting.md
         | 
| 133 | 
            +
            - context/streams.md
         | 
| 130 134 | 
             
            - context/subscriptions.md
         | 
| 135 | 
            +
            - context/transactions-and-pipelines.md
         | 
| 131 136 | 
             
            - lib/async/redis.rb
         | 
| 132 137 | 
             
            - lib/async/redis/client.rb
         | 
| 133 138 | 
             
            - lib/async/redis/cluster_client.rb
         | 
| @@ -167,7 +172,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 167 172 | 
             
                - !ruby/object:Gem::Version
         | 
| 168 173 | 
             
                  version: '0'
         | 
| 169 174 | 
             
            requirements: []
         | 
| 170 | 
            -
            rubygems_version: 3. | 
| 175 | 
            +
            rubygems_version: 3.7.2
         | 
| 171 176 | 
             
            specification_version: 4
         | 
| 172 177 | 
             
            summary: A Redis client library.
         | 
| 173 178 | 
             
            test_files: []
         | 
    
        metadata.gz.sig
    CHANGED
    
    | Binary file |