faye-redis-ng 1.0.11 → 1.0.13
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
 - data/CHANGELOG.md +85 -13
 - data/README.md +1 -1
 - data/lib/faye/redis/subscription_manager.rb +30 -1
 - data/lib/faye/redis/version.rb +1 -1
 - data/lib/faye/redis.rb +3 -1
 - metadata +1 -1
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 1b5fc029a910d92539d5500a92a851a1b3db0857a5769e6aa9338b8155126cfd
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: cbf1c670cfe442f7511466f278280db0f8543eee8adcddd9dc0e5ba9a0527667
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: ae3fb5749855e269fae14583b830bb3d6c55dda8cd90dd28f0bc2fcd4a91c1ca34b89db0a3ac8d7db9343a337653524f84f8b9d7680c089c14e4d42d172ae5f6
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 4355fe52a0d572f61640bc1bef644563f1df61d0e37549e8d35d76a64b58eafb0a255570f973453cda769f5e3a47b01715995fcb111eaa10bcf9a5292077e369
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -7,18 +7,63 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 
     | 
|
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
            ## [Unreleased]
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
            ## [1.0. 
     | 
| 
      
 10 
     | 
    
         
            +
            ## [1.0.13] - 2025-11-01
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            ### Added - Ping-Based Subscription TTL Refresh
         
     | 
| 
      
 13 
     | 
    
         
            +
            - **Subscription TTLs now refresh on every ping**: Keeps subscriptions alive as long as client is connected
         
     | 
| 
      
 14 
     | 
    
         
            +
              - **New behavior**: Each client ping (every ~25s) now refreshes all subscription-related TTLs
         
     | 
| 
      
 15 
     | 
    
         
            +
              - **Implementation**:
         
     | 
| 
      
 16 
     | 
    
         
            +
                - Added `SubscriptionManager#refresh_client_subscriptions_ttl` method
         
     | 
| 
      
 17 
     | 
    
         
            +
                - Modified `Redis#ping` to call subscription TTL refresh
         
     | 
| 
      
 18 
     | 
    
         
            +
                - Uses pipelined Redis commands for efficient batch TTL updates
         
     | 
| 
      
 19 
     | 
    
         
            +
              - **Impact**:
         
     | 
| 
      
 20 
     | 
    
         
            +
                - ✅ Active clients: Subscriptions never expire while connected
         
     | 
| 
      
 21 
     | 
    
         
            +
                - ✅ Inactive clients: Subscriptions expire 1 hour after last ping (as expected)
         
     | 
| 
      
 22 
     | 
    
         
            +
                - ✅ Long-lived connections: Chat rooms, dashboards work indefinitely
         
     | 
| 
      
 23 
     | 
    
         
            +
                - ✅ Prevents silent subscription expiration for active clients
         
     | 
| 
      
 24 
     | 
    
         
            +
              - **Before**: Subscriptions expired after 1 hour even for active clients
         
     | 
| 
      
 25 
     | 
    
         
            +
              - **After**: Subscriptions only expire 1 hour after client disconnects
         
     | 
| 
      
 26 
     | 
    
         
            +
              - **Performance**: Minimal impact - uses single pipelined Redis call per ping
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            ### Design Decision - Message Queue TTL Not Refreshed
         
     | 
| 
      
 29 
     | 
    
         
            +
            - **Message queue TTL remains fixed at 1 hour**: Does not refresh on ping
         
     | 
| 
      
 30 
     | 
    
         
            +
              - **Rationale**:
         
     | 
| 
      
 31 
     | 
    
         
            +
                - Active clients: Messages are immediately delivered, queue is usually empty
         
     | 
| 
      
 32 
     | 
    
         
            +
                - Empty queues that expire are automatically recreated when needed
         
     | 
| 
      
 33 
     | 
    
         
            +
                - Inactive clients: 1-hour TTL provides sufficient buffer for reconnection
         
     | 
| 
      
 34 
     | 
    
         
            +
                - Memory efficiency: Prevents permanent empty queue keys for long-lived connections
         
     | 
| 
      
 35 
     | 
    
         
            +
              - **Trade-offs**:
         
     | 
| 
      
 36 
     | 
    
         
            +
                - ✅ Better memory efficiency: Empty queues expire after 1 hour
         
     | 
| 
      
 37 
     | 
    
         
            +
                - ✅ No functional impact: Messages still delivered normally to active clients
         
     | 
| 
      
 38 
     | 
    
         
            +
                - ✅ Automatic cleanup: Long-term disconnected clients don't accumulate messages indefinitely
         
     | 
| 
      
 39 
     | 
    
         
            +
              - **Behavior**:
         
     | 
| 
      
 40 
     | 
    
         
            +
                - Active client: Queue may expire, but recreates automatically on next message
         
     | 
| 
      
 41 
     | 
    
         
            +
                - Disconnected < 1 hour: All messages preserved for reconnection
         
     | 
| 
      
 42 
     | 
    
         
            +
                - Disconnected > 1 hour: Messages cleared to prevent unbounded growth
         
     | 
| 
       11 
43 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
            ###  
     | 
| 
       13 
     | 
    
         
            -
            -  
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
      
 44 
     | 
    
         
            +
            ### Notes
         
     | 
| 
      
 45 
     | 
    
         
            +
            - Ping-based refresh ensures long-lived connections (chat, real-time dashboards) work correctly
         
     | 
| 
      
 46 
     | 
    
         
            +
            - Message queue strategy balances reliability with memory efficiency
         
     | 
| 
      
 47 
     | 
    
         
            +
            - The 1-hour TTL provides ample buffer (60 GC cycles) for cleanup after disconnect
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
            ## [1.0.12] - 2025-11-01
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
            ### Changed - Aligned Subscription TTL with Message TTL
         
     | 
| 
      
 52 
     | 
    
         
            +
            - **Adjusted subscription_ttl to 1 hour**: Match message_ttl to prevent message loss window
         
     | 
| 
      
 53 
     | 
    
         
            +
              - **Previous (v1.0.11)**: `subscription_ttl: 300` (5 minutes)
         
     | 
| 
      
 54 
     | 
    
         
            +
              - **New**: `subscription_ttl: 3600` (1 hour, same as message_ttl)
         
     | 
| 
      
 55 
     | 
    
         
            +
              - **Rationale**:
         
     | 
| 
      
 56 
     | 
    
         
            +
                - Subscription and message TTL must be aligned to prevent message loss window
         
     | 
| 
      
 57 
     | 
    
         
            +
                - If subscription expires before messages, new messages won't be enqueued for disconnected clients
         
     | 
| 
      
 58 
     | 
    
         
            +
                - Example: If subscription expires at 5 minutes but messages persist for 1 hour, any new messages after 5 minutes won't reach the client even if they reconnect within 1 hour
         
     | 
| 
      
 59 
     | 
    
         
            +
              - **Impact**:
         
     | 
| 
      
 60 
     | 
    
         
            +
                - Prevents message loss: disconnected clients get all messages within 1-hour window
         
     | 
| 
      
 61 
     | 
    
         
            +
                - No TTL gap: subscriptions and messages expire together
         
     | 
| 
      
 62 
     | 
    
         
            +
                - Conservative approach for reliable message delivery
         
     | 
| 
      
 63 
     | 
    
         
            +
              - **Trade-offs**:
         
     | 
| 
      
 64 
     | 
    
         
            +
                - Uses more memory than 5-minute TTL (but still 96% less than original 24 hours)
         
     | 
| 
      
 65 
     | 
    
         
            +
                - 1-hour reconnection window is sufficient for most mobile/network interruptions
         
     | 
| 
      
 66 
     | 
    
         
            +
                - Maintains 60x safety buffer (60 GC cycles @ 60s interval) for proper cleanup
         
     | 
| 
       22 
67 
     | 
    
         
             
              - **Affected keys**:
         
     | 
| 
       23 
68 
     | 
    
         
             
                - `{namespace}:subscriptions:{client_id}` (SET)
         
     | 
| 
       24 
69 
     | 
    
         
             
                - `{namespace}:channels:{channel}` (SET)
         
     | 
| 
         @@ -26,10 +71,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 
     | 
|
| 
       26 
71 
     | 
    
         
             
                - `{namespace}:patterns` (SET)
         
     | 
| 
       27 
72 
     | 
    
         
             
              - **Backward compatibility**: Users can still override with custom `subscription_ttl` option
         
     | 
| 
       28 
73 
     | 
    
         | 
| 
      
 74 
     | 
    
         
            +
            ### Added - Ping-Based Subscription TTL Refresh
         
     | 
| 
      
 75 
     | 
    
         
            +
            - **Subscription TTLs now refresh on every ping**: Keeps subscriptions alive as long as client is connected
         
     | 
| 
      
 76 
     | 
    
         
            +
              - **New behavior**: Each client ping (every ~25s) now refreshes all subscription-related TTLs
         
     | 
| 
      
 77 
     | 
    
         
            +
              - **Implementation**:
         
     | 
| 
      
 78 
     | 
    
         
            +
                - Added `SubscriptionManager#refresh_client_subscriptions_ttl` method
         
     | 
| 
      
 79 
     | 
    
         
            +
                - Modified `Redis#ping` to call subscription TTL refresh
         
     | 
| 
      
 80 
     | 
    
         
            +
                - Uses pipelined Redis commands for efficient batch TTL updates
         
     | 
| 
      
 81 
     | 
    
         
            +
              - **Impact**:
         
     | 
| 
      
 82 
     | 
    
         
            +
                - ✅ Active clients: Subscriptions never expire while connected
         
     | 
| 
      
 83 
     | 
    
         
            +
                - ✅ Inactive clients: Subscriptions expire 1 hour after last ping (as expected)
         
     | 
| 
      
 84 
     | 
    
         
            +
                - ✅ Long-lived connections: Chat rooms, dashboards work indefinitely
         
     | 
| 
      
 85 
     | 
    
         
            +
                - ✅ Prevents silent subscription expiration for active clients
         
     | 
| 
      
 86 
     | 
    
         
            +
              - **Before**: Subscriptions expired after 1 hour even for active clients
         
     | 
| 
      
 87 
     | 
    
         
            +
              - **After**: Subscriptions only expire 1 hour after client disconnects
         
     | 
| 
      
 88 
     | 
    
         
            +
              - **Performance**: Minimal impact - uses single pipelined Redis call per ping
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
       29 
90 
     | 
    
         
             
            ### Notes
         
     | 
| 
       30 
     | 
    
         
            -
            - This change  
     | 
| 
       31 
     | 
    
         
            -
            - The  
     | 
| 
      
 91 
     | 
    
         
            +
            - This change ensures subscriptions don't expire before their associated messages
         
     | 
| 
      
 92 
     | 
    
         
            +
            - The 1-hour TTL provides ample buffer (60 GC cycles) for cleanup after disconnect
         
     | 
| 
       32 
93 
     | 
    
         
             
            - TTL-safe implementation (v1.0.10) ensures active subscriptions maintain their original TTL
         
     | 
| 
      
 94 
     | 
    
         
            +
            - Conservative approach: prioritizes message delivery over aggressive memory optimization
         
     | 
| 
      
 95 
     | 
    
         
            +
            - Ping-based refresh ensures long-lived connections (chat, real-time dashboards) work correctly
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
            ## [1.0.11] - 2025-10-31
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
            ### Changed - Reduced Subscription TTL (Reverted in v1.0.12)
         
     | 
| 
      
 100 
     | 
    
         
            +
            - **Reduced subscription_ttl from 24 hours to 5 minutes**: More aggressive cleanup of orphaned subscription data
         
     | 
| 
      
 101 
     | 
    
         
            +
              - **Previous**: `subscription_ttl: 86400` (24 hours)
         
     | 
| 
      
 102 
     | 
    
         
            +
              - **New**: `subscription_ttl: 300` (5 minutes = 5x client_timeout)
         
     | 
| 
      
 103 
     | 
    
         
            +
              - **Issue discovered**: This created a message loss window - subscriptions expired in 5 minutes but messages persisted for 1 hour
         
     | 
| 
      
 104 
     | 
    
         
            +
              - **Resolution**: Reverted in v1.0.12 to align with message_ttl (1 hour)
         
     | 
| 
       33 
105 
     | 
    
         | 
| 
       34 
106 
     | 
    
         
             
            ## [1.0.10] - 2025-10-30
         
     | 
| 
       35 
107 
     | 
    
         | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -80,7 +80,7 @@ bayeux = Faye::RackAdapter.new(app, { 
     | 
|
| 
       80 
80 
     | 
    
         
             
              # Data expiration
         
     | 
| 
       81 
81 
     | 
    
         
             
              client_timeout: 60,         # Client session timeout (seconds)
         
     | 
| 
       82 
82 
     | 
    
         
             
              message_ttl: 3600,          # Message TTL (seconds)
         
     | 
| 
       83 
     | 
    
         
            -
              subscription_ttl:  
     | 
| 
      
 83 
     | 
    
         
            +
              subscription_ttl: 3600,     # Subscription keys TTL (seconds, default: 1 hour)
         
     | 
| 
       84 
84 
     | 
    
         | 
| 
       85 
85 
     | 
    
         
             
              # Garbage collection
         
     | 
| 
       86 
86 
     | 
    
         
             
              gc_interval: 60,            # Automatic GC interval (seconds), set to 0 or false to disable
         
     | 
| 
         @@ -12,7 +12,7 @@ module Faye 
     | 
|
| 
       12 
12 
     | 
    
         
             
                  # Subscribe a client to a channel
         
     | 
| 
       13 
13 
     | 
    
         
             
                  def subscribe(client_id, channel, &callback)
         
     | 
| 
       14 
14 
     | 
    
         
             
                    timestamp = Time.now.to_i
         
     | 
| 
       15 
     | 
    
         
            -
                    subscription_ttl = @options[:subscription_ttl] ||  
     | 
| 
      
 15 
     | 
    
         
            +
                    subscription_ttl = @options[:subscription_ttl] || 3600  # 1 hour default (matches message_ttl)
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
17 
     | 
    
         
             
                    client_subs_key = client_subscriptions_key(client_id)
         
     | 
| 
       18 
18 
     | 
    
         
             
                    channel_subs_key = channel_subscribers_key(channel)
         
     | 
| 
         @@ -118,6 +118,35 @@ module Faye 
     | 
|
| 
       118 
118 
     | 
    
         
             
                    EventMachine.next_tick { callback.call(false) } if callback
         
     | 
| 
       119 
119 
     | 
    
         
             
                  end
         
     | 
| 
       120 
120 
     | 
    
         | 
| 
      
 121 
     | 
    
         
            +
                  # Refresh TTL for all subscription keys related to a client
         
     | 
| 
      
 122 
     | 
    
         
            +
                  # Called during ping to keep subscriptions alive as long as client is connected
         
     | 
| 
      
 123 
     | 
    
         
            +
                  def refresh_client_subscriptions_ttl(client_id)
         
     | 
| 
      
 124 
     | 
    
         
            +
                    subscription_ttl = @options[:subscription_ttl] || 3600
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                    @connection.with_redis do |redis|
         
     | 
| 
      
 127 
     | 
    
         
            +
                      # Get all channels this client is subscribed to
         
     | 
| 
      
 128 
     | 
    
         
            +
                      channels = redis.smembers(client_subscriptions_key(client_id))
         
     | 
| 
      
 129 
     | 
    
         
            +
                      return if channels.empty?
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                      # Use pipeline to refresh all TTLs efficiently
         
     | 
| 
      
 132 
     | 
    
         
            +
                      redis.pipelined do |pipeline|
         
     | 
| 
      
 133 
     | 
    
         
            +
                        # Refresh client's subscriptions set
         
     | 
| 
      
 134 
     | 
    
         
            +
                        pipeline.expire(client_subscriptions_key(client_id), subscription_ttl)
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                        # Refresh each subscription metadata and channel subscriber set
         
     | 
| 
      
 137 
     | 
    
         
            +
                        channels.each do |channel|
         
     | 
| 
      
 138 
     | 
    
         
            +
                          pipeline.expire(subscription_key(client_id, channel), subscription_ttl)
         
     | 
| 
      
 139 
     | 
    
         
            +
                          pipeline.expire(channel_subscribers_key(channel), subscription_ttl)
         
     | 
| 
      
 140 
     | 
    
         
            +
                        end
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
                        # Refresh patterns set if it exists
         
     | 
| 
      
 143 
     | 
    
         
            +
                        pipeline.expire(patterns_key, subscription_ttl)
         
     | 
| 
      
 144 
     | 
    
         
            +
                      end
         
     | 
| 
      
 145 
     | 
    
         
            +
                    end
         
     | 
| 
      
 146 
     | 
    
         
            +
                  rescue => e
         
     | 
| 
      
 147 
     | 
    
         
            +
                    log_error("Failed to refresh subscription TTL for client #{client_id}: #{e.message}")
         
     | 
| 
      
 148 
     | 
    
         
            +
                  end
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
       121 
150 
     | 
    
         
             
                  # Get all channels a client is subscribed to
         
     | 
| 
       122 
151 
     | 
    
         
             
                  def get_client_subscriptions(client_id, &callback)
         
     | 
| 
       123 
152 
     | 
    
         
             
                    channels = @connection.with_redis do |redis|
         
     | 
    
        data/lib/faye/redis/version.rb
    CHANGED
    
    
    
        data/lib/faye/redis.rb
    CHANGED
    
    | 
         @@ -25,7 +25,7 @@ module Faye 
     | 
|
| 
       25 
25 
     | 
    
         
             
                  retry_delay: 1,
         
     | 
| 
       26 
26 
     | 
    
         
             
                  client_timeout: 60,
         
     | 
| 
       27 
27 
     | 
    
         
             
                  message_ttl: 3600,
         
     | 
| 
       28 
     | 
    
         
            -
                  subscription_ttl:  
     | 
| 
      
 28 
     | 
    
         
            +
                  subscription_ttl: 3600,  # Subscription keys TTL (1 hour, matches message_ttl), provides safety net if GC fails
         
     | 
| 
       29 
29 
     | 
    
         
             
                  namespace: 'faye',
         
     | 
| 
       30 
30 
     | 
    
         
             
                  gc_interval: 60,  # Automatic garbage collection interval (seconds), set to 0 or false to disable
         
     | 
| 
       31 
31 
     | 
    
         
             
                  cleanup_batch_size: 50  # Number of items per batch during cleanup (min: 1, max: 1000, prevents blocking)
         
     | 
| 
         @@ -88,8 +88,10 @@ module Faye 
     | 
|
| 
       88 
88 
     | 
    
         
             
                end
         
     | 
| 
       89 
89 
     | 
    
         | 
| 
       90 
90 
     | 
    
         
             
                # Ping a client to keep it alive
         
     | 
| 
      
 91 
     | 
    
         
            +
                # Also refreshes subscription TTLs to keep them alive while client is connected
         
     | 
| 
       91 
92 
     | 
    
         
             
                def ping(client_id)
         
     | 
| 
       92 
93 
     | 
    
         
             
                  @client_registry.ping(client_id)
         
     | 
| 
      
 94 
     | 
    
         
            +
                  @subscription_manager.refresh_client_subscriptions_ttl(client_id)
         
     | 
| 
       93 
95 
     | 
    
         
             
                end
         
     | 
| 
       94 
96 
     | 
    
         | 
| 
       95 
97 
     | 
    
         
             
                # Subscribe a client to a channel
         
     |