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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a2266042c42929dc9203cf1160bbfd64d429c87cdb076b3369cd55bbf77032e
4
- data.tar.gz: 51509e6fb5fb775bfbdcd4db0dada3dfa26f6bee5f5729d2b3fa165b5b417644
3
+ metadata.gz: 1b5fc029a910d92539d5500a92a851a1b3db0857a5769e6aa9338b8155126cfd
4
+ data.tar.gz: cbf1c670cfe442f7511466f278280db0f8543eee8adcddd9dc0e5ba9a0527667
5
5
  SHA512:
6
- metadata.gz: 79c6d4bee6b55572a772a4977fd5b5a5857746c415bac1f6f0a802ad69b16e137642b90d20ec02ec21abb95f69e5fab9ab830f018c6aaa974ad56b2c3202f269
7
- data.tar.gz: 38c29ca6cd4330c5a862951b2810e8e095e4dec2939691bd8379ef714f947ad05a0e875bcf55a53de42d35913357d2f05c38f08b0ce16da12e553e2c7a22f1b1
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.11] - 2025-10-31
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
- ### Changed - Optimized Subscription TTL
13
- - **Reduced subscription_ttl from 24 hours to 5 minutes**: More aggressive cleanup of orphaned subscription data
14
- - **Previous**: `subscription_ttl: 86400` (24 hours)
15
- - **New**: `subscription_ttl: 300` (5 minutes = 5x client_timeout)
16
- - **Rationale**: Client keys expire after 60 seconds, keeping subscription data for 24 hours was excessive
17
- - **Impact**: Faster memory reclamation for disconnected clients while maintaining safety net
18
- - **Benefits**:
19
- - Reduces memory footprint by cleaning up orphaned subscriptions faster
20
- - Maintains 5-minute safety window (5 GC cycles) for proper cleanup
21
- - Allows short-lived reconnections within 5 minutes to reuse subscription data
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 is safe because automatic GC runs every 60 seconds by default
31
- - The 5-minute TTL provides sufficient buffer (5 GC cycles) for cleanup
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: 300, # Subscription keys TTL (seconds, default: 5 minutes)
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] || 300 # 5 minutes default (5x client_timeout)
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|
@@ -1,5 +1,5 @@
1
1
  module Faye
2
2
  class Redis
3
- VERSION = '1.0.11'
3
+ VERSION = '1.0.13'
4
4
  end
5
5
  end
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: 300, # Subscription keys TTL (5 minutes = 5x client_timeout), provides safety net if GC fails
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faye-redis-ng
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.11
4
+ version: 1.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zac