amplitude-experiment 1.0.0.beta.2 → 1.0.0.beta.3
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/lib/experiment/client.rb +5 -6
- data/lib/experiment/persistent_http_client.rb +127 -0
- data/lib/experiment/version.rb +1 -1
- data/lib/experiment.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 70f2c8f1fb01b7e1de84104c7d1b875868ea11da376725b172ad062c14789746
|
4
|
+
data.tar.gz: 94849ff0ec78e5ceb05169594203d84d793cbd9d1455e6f60be1383a26788237
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31a6213acb520c87654f4a6120b930ba6cfe17111eca31e986d7eb544aa53164e5caf2723e8f0850bf674411df76c0a5d8c5e4823c34e01af8d61f7244532c36
|
7
|
+
data.tar.gz: 949cffd01dc77def83fd1c64f9d323ffc34f8b2081a850f72530c6d0f19f23fcd3d73edb61b8c8b27d7f135e7bb534db8d3c3bda0e9b2418119021d22c0bd5e4
|
data/lib/experiment/client.rb
CHANGED
@@ -19,6 +19,8 @@ module Experiment
|
|
19
19
|
else
|
20
20
|
Logger::INFO
|
21
21
|
end
|
22
|
+
endpoint = "#{@config.server_url}/sdk/vardata"
|
23
|
+
@uri = URI(endpoint)
|
22
24
|
raise ArgumentError, 'Experiment API key is empty' if @api_key.nil? || @api_key.empty?
|
23
25
|
end
|
24
26
|
|
@@ -91,16 +93,13 @@ module Experiment
|
|
91
93
|
def do_fetch(user, timeout_millis)
|
92
94
|
start_time = Time.now
|
93
95
|
user_context = add_context(user)
|
94
|
-
endpoint = "#{@config.server_url}/sdk/vardata"
|
95
96
|
headers = {
|
96
97
|
'Authorization' => "Api-Key #{@api_key}",
|
97
98
|
'Content-Type' => 'application/json;charset=utf-8'
|
98
99
|
}
|
99
|
-
|
100
|
-
http =
|
101
|
-
|
102
|
-
http.read_timeout = timeout_millis / 1000 if (timeout_millis / 1000) > 0
|
103
|
-
request = Net::HTTP::Post.new(uri, headers)
|
100
|
+
read_timeout = timeout_millis / 1000 if (timeout_millis / 1000) > 0
|
101
|
+
http = PersistentHttpClient.get(@uri, { read_timeout: read_timeout })
|
102
|
+
request = Net::HTTP::Post.new(@uri, headers)
|
104
103
|
request.body = user_context.to_json
|
105
104
|
if request.body.length > 8000
|
106
105
|
@logger.warn("[Experiment] encoded user object length #{request.body.length} cannot be cached by CDN; must be < 8KB")
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Experiment
|
2
|
+
# Persist Http Client to reuse connection and reduce IO
|
3
|
+
class PersistentHttpClient
|
4
|
+
DEFAULT_OPTIONS = { read_timeout: 80 }.freeze
|
5
|
+
|
6
|
+
class << self
|
7
|
+
# url: URI / String
|
8
|
+
# options: any options that Net::HTTP.new accepts
|
9
|
+
def get(url, options = {})
|
10
|
+
uri = url.is_a?(URI) ? url : URI(url)
|
11
|
+
connection_manager.get_client(uri, options)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# each thread gets its own connection manager
|
17
|
+
def connection_manager
|
18
|
+
# before getting a connection manager
|
19
|
+
# we first clear all old ones
|
20
|
+
remove_old_managers
|
21
|
+
Thread.current[:http_connection_manager] ||= new_manager
|
22
|
+
end
|
23
|
+
|
24
|
+
def new_manager
|
25
|
+
# create a new connection manager in a thread safe way
|
26
|
+
mutex.synchronize do
|
27
|
+
manager = ConnectionManager.new
|
28
|
+
connection_managers << manager
|
29
|
+
manager
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def remove_old_managers
|
34
|
+
mutex.synchronize do
|
35
|
+
removed = connection_managers.reject!(&:stale?)
|
36
|
+
(removed || []).each(&:close_connections!)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# mutex isn't needed for CRuby, but might be needed
|
41
|
+
# for other Ruby implementations
|
42
|
+
def mutex
|
43
|
+
@mutex ||= Mutex.new
|
44
|
+
end
|
45
|
+
|
46
|
+
def connection_managers
|
47
|
+
@connection_managers ||= []
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# connection manager represents
|
52
|
+
# a cache of all keep-alive connections
|
53
|
+
# in a current thread
|
54
|
+
class ConnectionManager
|
55
|
+
# if a client wasn't used within this time range
|
56
|
+
# it gets removed from the cache and the connection closed.
|
57
|
+
# This helps to make sure there are no memory leaks.
|
58
|
+
STALE_AFTER = 300 # 5 minutes
|
59
|
+
|
60
|
+
# Seconds to reuse the connection of the previous request. If the idle time is less than this Keep-Alive Timeout,
|
61
|
+
# Net::HTTP reuses the TCP/IP socket used by the previous communication. Source: Ruby docs
|
62
|
+
KEEP_ALIVE_TIMEOUT = 30 # seconds
|
63
|
+
|
64
|
+
# KEEP_ALIVE_TIMEOUT vs STALE_AFTER
|
65
|
+
# STALE_AFTER - how long an Net::HTTP client object is cached in ruby
|
66
|
+
# KEEP_ALIVE_TIMEOUT - how long that client keeps TCP/IP socket open.
|
67
|
+
|
68
|
+
attr_accessor :clients_store, :last_used
|
69
|
+
|
70
|
+
def initialize
|
71
|
+
self.clients_store = {}
|
72
|
+
self.last_used = Time.now
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_client(uri, options)
|
76
|
+
mutex.synchronize do
|
77
|
+
# refresh the last time a client was used,
|
78
|
+
# this prevents the client from becoming stale
|
79
|
+
self.last_used = Time.now
|
80
|
+
|
81
|
+
# we use params as a cache key for clients.
|
82
|
+
# 2 connections to the same host but with different
|
83
|
+
# options are going to use different HTTP clients
|
84
|
+
params = [uri.host, uri.port, options]
|
85
|
+
client = clients_store[params]
|
86
|
+
|
87
|
+
return client if client
|
88
|
+
|
89
|
+
client = Net::HTTP.new(uri.host, uri.port)
|
90
|
+
client.keep_alive_timeout = KEEP_ALIVE_TIMEOUT
|
91
|
+
|
92
|
+
# set SSL to true if a scheme is https
|
93
|
+
client.use_ssl = uri.scheme == 'https'
|
94
|
+
|
95
|
+
# dynamically set Net::HTTP options
|
96
|
+
DEFAULT_OPTIONS.merge(options).each_pair do |key, value|
|
97
|
+
client.public_send("#{key}=", value)
|
98
|
+
end
|
99
|
+
|
100
|
+
# open connection
|
101
|
+
client.start
|
102
|
+
|
103
|
+
# cache the client
|
104
|
+
clients_store[params] = client
|
105
|
+
|
106
|
+
client
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# close connections for each client
|
111
|
+
def close_connections!
|
112
|
+
mutex.synchronize do
|
113
|
+
clients_store.values.each(&:finish)
|
114
|
+
self.clients_store = {}
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def stale?
|
119
|
+
Time.now - last_used > STALE_AFTER
|
120
|
+
end
|
121
|
+
|
122
|
+
def mutex
|
123
|
+
@mutex ||= Mutex.new
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
data/lib/experiment/version.rb
CHANGED
data/lib/experiment.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: amplitude-experiment
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.beta.
|
4
|
+
version: 1.0.0.beta.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Amplitude
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-05-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -124,6 +124,7 @@ files:
|
|
124
124
|
- lib/experiment/config.rb
|
125
125
|
- lib/experiment/cookie.rb
|
126
126
|
- lib/experiment/factory.rb
|
127
|
+
- lib/experiment/persistent_http_client.rb
|
127
128
|
- lib/experiment/user.rb
|
128
129
|
- lib/experiment/variant.rb
|
129
130
|
- lib/experiment/version.rb
|