dalli-elasticache 2.0.0 → 2.0.2

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: 66afce9165ca020c9c46d25f12901919968d33ec9de15be287a9f589497a042e
4
- data.tar.gz: e67a94f65b45ecaf034ba6bfd67c574c3275100d43b8f893d52181606dfdeb1e
3
+ metadata.gz: c0afff12dcbecd681d00446e7a792cb82f356f28e02bfaac78a1962180e7973e
4
+ data.tar.gz: 2918dfeefec59bf0c663c15ecc2d981a1b16a51d8b653d2df710290f379945f8
5
5
  SHA512:
6
- metadata.gz: 92399afd4065a1bdb8f387eb6e04d47152c3e4e2e25fe8449713258409d84986014c2c71ad67a0aa90d8a2bd8a3df9721aca6862ac456c908d1fee4901b4465f
7
- data.tar.gz: 379dce4897d563f6811133aaef02c724da76a0bc6f31973b324241d913c5dae2bb53aa15f0536518de6f7e42d1526ff3f3efbfc83f13fc7d3610cff5277d44b0
6
+ metadata.gz: 0acf68cd09b9bf0bda5e1e77a4c8aff7925edf1e1a51e26adf8773e7a86df0e69fdf9d285d6ee965b24648d0feeb6406cb9bfca6a1187f7ffb652ab117b275fc
7
+ data.tar.gz: 05702c86d429beea0759bde3682068986b7b0da1f54c55155e3b9ce2f228e95d9b979c8c8ffdf8a35e1a2b752a3e3523624aa8940454f073fd2c6cb79311f1b5
data/README.md CHANGED
@@ -89,6 +89,57 @@ elasticache.refresh
89
89
  elasticache.refresh.client
90
90
  ```
91
91
 
92
+ ### TLS
93
+
94
+ Pass an `OpenSSL::SSL::SSLContext` via `ssl_context:` to enable TLS for the auto-discovery connection. This uses the same interface as Dalli's own TLS support.
95
+
96
+ ```ruby
97
+ ssl_context = OpenSSL::SSL::SSLContext.new
98
+ ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
99
+
100
+ elasticache = Dalli::ElastiCache.new(
101
+ "my-cluster.abc123.cfg.use1.cache.amazonaws.com:11211",
102
+ { expires_in: 1.day },
103
+ ssl_context: ssl_context
104
+ )
105
+ ```
106
+
107
+ Note that `ssl_context:` controls TLS for the auto-discovery endpoint only. To also encrypt traffic to the cache nodes themselves, pass `ssl_context:` in the Dalli options as well:
108
+
109
+ ```ruby
110
+ elasticache = Dalli::ElastiCache.new(endpoint, { ssl_context: ssl_context }, ssl_context: ssl_context)
111
+ ```
112
+
113
+ ### Refreshing the Node List
114
+
115
+ The node list is fetched once and cached. Call `#refresh` to re-query the endpoint, which creates a new `Dalli::Client` with the current node list.
116
+
117
+ Two common patterns for keeping the node list up to date:
118
+
119
+ **On failure** — rescue `Dalli::RingError` (raised when no nodes are reachable) and refresh before retrying:
120
+
121
+ ```ruby
122
+ begin
123
+ cache.get("key")
124
+ rescue Dalli::RingError
125
+ elasticache.refresh
126
+ retry
127
+ end
128
+ ```
129
+
130
+ **On a schedule** — refresh periodically in a background thread or job. AWS does not publish exact timing for node replacement, but the process typically takes a few minutes, so refreshing every 60 seconds is a reasonable default:
131
+
132
+ ```ruby
133
+ Thread.new do
134
+ loop do
135
+ sleep 60
136
+ elasticache.refresh
137
+ end
138
+ end
139
+ ```
140
+
141
+ A combination of both approaches is reasonable for production use.
142
+
92
143
  License
93
144
  -------
94
145
 
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'openssl'
4
+ require 'timeout'
5
+
3
6
  module Dalli
4
7
  module Elasticache
5
8
  module AutoDiscovery
@@ -8,46 +11,61 @@ module Dalli
8
11
  # command. Contains the network logic.
9
12
  ##
10
13
  class BaseCommand
11
- attr_reader :host, :port, :timeout
14
+ attr_reader :host, :port, :timeout, :ssl_context
12
15
 
13
- def initialize(host, port, timeout = nil)
16
+ def initialize(host, port, timeout = nil, ssl_context: nil)
14
17
  @host = host
15
18
  @port = port
16
19
  @timeout = timeout
20
+ @ssl_context = ssl_context
17
21
  end
18
22
 
19
23
  # Send an ASCII command to the endpoint
20
24
  #
21
25
  # Returns the raw response as a String
22
26
  def send_command
23
- socket = ::Socket.tcp(@host, @port, connect_timeout: timeout)
27
+ tcp_socket = ::Socket.tcp(@host, @port, connect_timeout: timeout)
24
28
  begin
29
+ socket = ssl_context ? wrap_with_ssl(tcp_socket) : tcp_socket
25
30
  socket.puts command
26
31
  response_from_socket(socket)
27
32
  ensure
28
- socket.close
33
+ (socket || tcp_socket).close
29
34
  end
30
35
  end
31
36
 
37
+ # `timeout` bounds the entire multi-line read as a single wall-clock deadline,
38
+ # not each individual `readline`. A per-line readiness check (e.g. `wait_readable`)
39
+ # can't tell buffered-but-unread bytes apart from an empty socket once a buffering
40
+ # layer (Ruby's IO buffer, or OpenSSL::Buffering for TLS) sits between the fd and
41
+ # `readline`, so it can't be used to gate individual lines reliably.
32
42
  def response_from_socket(socket)
43
+ return read_lines(socket) unless timeout
44
+
45
+ Timeout.timeout(timeout, Timeout::Error, "Auto discovery read timed out after #{timeout}s") do
46
+ read_lines(socket)
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def read_lines(socket)
33
53
  data = +''
34
54
  loop do
35
- wait_for_data(socket)
36
55
  line = socket.readline
37
56
  break if line.include?('END')
38
57
 
39
58
  data << line
40
59
  end
41
-
42
60
  data
43
61
  end
44
62
 
45
- private
46
-
47
- def wait_for_data(socket)
48
- return unless timeout
49
-
50
- raise Timeout::Error, "Auto discovery read timed out after #{timeout}s" unless socket.wait_readable(timeout)
63
+ def wrap_with_ssl(tcp_socket)
64
+ ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, ssl_context)
65
+ ssl_socket.hostname = @host
66
+ ssl_socket.sync_close = true
67
+ ssl_socket.connect
68
+ ssl_socket
51
69
  end
52
70
  end
53
71
  end
@@ -15,9 +15,10 @@ module Dalli
15
15
  # Matches DNS/IPv4 like "my-host.cache.aws.com:11211" or bracketed IPv6 like "[::1]:11211"
16
16
  ENDPOINT_REGEX = /^([-_.a-zA-Z0-9]+|\[[a-fA-F0-9:]+\])(?::(\d+))?$/
17
17
 
18
- def initialize(addr, timeout: nil)
18
+ def initialize(addr, timeout: nil, ssl_context: nil)
19
19
  @host, @port = parse_endpoint_address(addr)
20
20
  @timeout = timeout
21
+ @ssl_context = ssl_context
21
22
  end
22
23
 
23
24
  DEFAULT_PORT = 11_211
@@ -30,12 +31,12 @@ module Dalli
30
31
 
31
32
  # A cached ElastiCache::StatsResponse
32
33
  def stats
33
- @stats ||= StatsCommand.new(@host, @port, @timeout).response
34
+ @stats ||= StatsCommand.new(@host, @port, @timeout, ssl_context: @ssl_context).response
34
35
  end
35
36
 
36
37
  # A cached ElastiCache::ConfigResponse
37
38
  def config
38
- @config ||= ConfigCommand.new(@host, @port, @timeout).response
39
+ @config ||= ConfigCommand.new(@host, @port, @timeout, ssl_context: @ssl_context).response
39
40
  end
40
41
 
41
42
  # The memcached engine version
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Dalli
4
4
  class ElastiCache
5
- VERSION = '2.0.0'
5
+ VERSION = '2.0.2'
6
6
  end
7
7
  end
@@ -38,9 +38,10 @@ module Dalli
38
38
  # timeout: - connect and read timeout in seconds for auto-discovery TCP calls.
39
39
  # Defaults to 5 seconds.
40
40
  ##
41
- def initialize(config_endpoint, dalli_options = {}, timeout: DEFAULT_TIMEOUT)
41
+ def initialize(config_endpoint, dalli_options = {}, timeout: DEFAULT_TIMEOUT, ssl_context: nil)
42
42
  @timeout = timeout
43
- @endpoint = Dalli::Elasticache::AutoDiscovery::Endpoint.new(config_endpoint, timeout:)
43
+ @ssl_context = ssl_context
44
+ @endpoint = Dalli::Elasticache::AutoDiscovery::Endpoint.new(config_endpoint, timeout:, ssl_context:)
44
45
  @options = dalli_options
45
46
  end
46
47
 
@@ -72,8 +73,8 @@ module Dalli
72
73
  # Clear all cached data from the cluster endpoint
73
74
  def refresh
74
75
  config_endpoint = "#{endpoint.host}:#{endpoint.port}"
75
- @endpoint = Dalli::Elasticache::AutoDiscovery::Endpoint.new(config_endpoint, timeout: @timeout)
76
-
76
+ @endpoint = Dalli::Elasticache::AutoDiscovery::Endpoint.new(config_endpoint, timeout: @timeout,
77
+ ssl_context: @ssl_context)
77
78
  self
78
79
  end
79
80
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dalli-elasticache
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Suggs
@@ -69,7 +69,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
69
69
  - !ruby/object:Gem::Version
70
70
  version: 1.3.5
71
71
  requirements: []
72
- rubygems_version: 4.0.6
72
+ rubygems_version: 4.0.18
73
73
  specification_version: 4
74
74
  summary: Configure Dalli clients with ElastiCache's AutoDiscovery
75
75
  test_files: []