ff-ruby-server-sdk 1.4.4 → 1.4.6
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/ff/ruby/server/sdk/api/cf_client.rb +13 -5
- data/lib/ff/ruby/server/sdk/api/evaluator.rb +3 -1
- data/lib/ff/ruby/server/sdk/api/inner_client.rb +10 -5
- data/lib/ff/ruby/server/sdk/api/inner_client_flag_evaluate_callback.rb +3 -3
- data/lib/ff/ruby/server/sdk/api/metrics_processor.rb +23 -34
- data/lib/ff/ruby/server/sdk/version.rb +1 -1
- data/lib/ff/ruby/server/sdk.rb +0 -1
- data/scripts/sdk_specs.sh +1 -1
- metadata +3 -7
- data/lib/ff/ruby/server/sdk/api/metrics_event.rb +0 -53
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 619302fd2168c1df9c8a16375f271e31795465530b880cb51986fbc104577c47
|
4
|
+
data.tar.gz: b778008b460e9db5f86382e6bd62a6bace2ee543a59fb8d4de9df80bd98ff484
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa0fd5bece56084de57aeee050e8dd1ed890981fb868ee50bd44f2219ce3a6b0ac49ffde2c6c8f63131271a57b5f4994d91446f2abfc0bd056ab66a48405c90f
|
7
|
+
data.tar.gz: 4c439844d1780986454e7cf685b236715ae71c2535f7481305f81bc839055fc98691c14d214a1252e5975de7ff894fcceea879a0094ecc06e82a6358fb3ce095
|
@@ -5,13 +5,17 @@ require_relative "inner_client"
|
|
5
5
|
|
6
6
|
class CfClient < Closeable
|
7
7
|
include Singleton
|
8
|
-
|
8
|
+
|
9
|
+
@@instance_mutex = Mutex.new
|
9
10
|
def init(api_key, config, connector = nil)
|
10
11
|
# Only initialize if @client is nil to avoid reinitialization
|
11
|
-
|
12
|
-
|
13
|
-
@client
|
14
|
-
|
12
|
+
|
13
|
+
@@instance_mutex.synchronize do
|
14
|
+
unless @client
|
15
|
+
@config = config || ConfigBuilder.new.build
|
16
|
+
@client = InnerClient.new(api_key, @config, connector)
|
17
|
+
@config.logger.debug "Client initialized with API key: #{api_key}"
|
18
|
+
end
|
15
19
|
end
|
16
20
|
@client
|
17
21
|
end
|
@@ -43,6 +47,10 @@ def wait_for_initialization(timeout_ms: nil)
|
|
43
47
|
@client.json_variation(identifier, target, default_value)
|
44
48
|
end
|
45
49
|
|
50
|
+
def initialized
|
51
|
+
@client.initialized
|
52
|
+
end
|
53
|
+
|
46
54
|
def destroy
|
47
55
|
|
48
56
|
close
|
@@ -117,7 +117,9 @@ class Evaluator < Evaluation
|
|
117
117
|
|
118
118
|
if variation != nil
|
119
119
|
if callback != nil
|
120
|
-
|
120
|
+
feature_name = flag.feature
|
121
|
+
variation_identifier = variation.identifier
|
122
|
+
callback.process_evaluation(feature_name, target, variation_identifier)
|
121
123
|
end
|
122
124
|
return variation
|
123
125
|
end
|
@@ -98,6 +98,10 @@ class InnerClient < ClientCallback
|
|
98
98
|
@evaluator.json_variation(identifier, target, default_value, @evaluator_callback)
|
99
99
|
end
|
100
100
|
|
101
|
+
def initialized
|
102
|
+
@initialized
|
103
|
+
end
|
104
|
+
|
101
105
|
def on_auth_success
|
102
106
|
|
103
107
|
SdkCodes::info_sdk_auth_ok @config.logger
|
@@ -120,9 +124,11 @@ class InnerClient < ClientCallback
|
|
120
124
|
end
|
121
125
|
|
122
126
|
def on_auth_failed
|
123
|
-
|
124
|
-
|
125
|
-
|
127
|
+
@my_mutex.synchronize do
|
128
|
+
SdkCodes::warn_auth_failed_srv_defaults @config.logger
|
129
|
+
@initialized = true
|
130
|
+
@condition.broadcast
|
131
|
+
end
|
126
132
|
end
|
127
133
|
|
128
134
|
def close
|
@@ -243,7 +249,7 @@ class InnerClient < ClientCallback
|
|
243
249
|
|
244
250
|
SdkCodes.info_sdk_init_ok @config.logger
|
245
251
|
|
246
|
-
@condition.
|
252
|
+
@condition.broadcast
|
247
253
|
@initialized = true
|
248
254
|
end
|
249
255
|
end
|
@@ -257,7 +263,6 @@ class InnerClient < ClientCallback
|
|
257
263
|
remaining = timeout ? timeout / 1000.0 : nil # Convert timeout to seconds
|
258
264
|
|
259
265
|
until @initialized
|
260
|
-
|
261
266
|
# Break if timeout has elapsed
|
262
267
|
if remaining && remaining <= 0
|
263
268
|
@config.logger.warn "The SDK has timed out waiting to initialize with supplied timeout #{timeout} ms. The SDK will continue to initialize in the background. Default variations will be served until the SDK initializes."
|
@@ -21,10 +21,10 @@ class InnerClientFlagEvaluateCallback < FlagEvaluateCallback
|
|
21
21
|
@metrics_processor = metrics_processor
|
22
22
|
end
|
23
23
|
|
24
|
-
def process_evaluation(
|
24
|
+
def process_evaluation(feature_name, target, variation_identifier)
|
25
25
|
|
26
|
-
@logger.debug "Processing evaluation: #{
|
26
|
+
@logger.debug "Processing evaluation: #{feature_name || 'nil feature'}, #{variation_identifier || 'nil variation'}, #{target&.identifier || 'nil target'}"
|
27
27
|
|
28
|
-
@metrics_processor.register_evaluation(target,
|
28
|
+
@metrics_processor.register_evaluation(target, feature_name, variation_identifier)
|
29
29
|
end
|
30
30
|
end
|
@@ -6,12 +6,9 @@ require_relative "../dto/target"
|
|
6
6
|
require_relative "../../sdk/version"
|
7
7
|
require_relative "../common/closeable"
|
8
8
|
require_relative "../common/sdk_codes"
|
9
|
-
require_relative "../api/metrics_event"
|
10
9
|
require_relative "../api/summary_metrics"
|
11
10
|
|
12
11
|
class MetricsProcessor < Closeable
|
13
|
-
GLOBAL_TARGET = Target.new(identifier: "__global__cf_target", name: "Global Target").freeze
|
14
|
-
|
15
12
|
def init(connector, config, callback)
|
16
13
|
|
17
14
|
unless connector.kind_of?(Connector)
|
@@ -79,8 +76,8 @@ class MetricsProcessor < Closeable
|
|
79
76
|
@config.logger.debug "Closing metrics processor"
|
80
77
|
end
|
81
78
|
|
82
|
-
def register_evaluation(target,
|
83
|
-
register_evaluation_metric(
|
79
|
+
def register_evaluation(target, feature_name, variation_identifier)
|
80
|
+
register_evaluation_metric(feature_name, variation_identifier)
|
84
81
|
if target
|
85
82
|
register_target_metric(target)
|
86
83
|
end
|
@@ -88,29 +85,24 @@ class MetricsProcessor < Closeable
|
|
88
85
|
|
89
86
|
private
|
90
87
|
|
91
|
-
def register_evaluation_metric(
|
88
|
+
def register_evaluation_metric(feature_name, variation_identifier)
|
92
89
|
# Guard clause to ensure feature_config, @global_target, and variation are valid.
|
93
90
|
# While they should be, this adds protection for an edge case we are seeing where the the ConcurrentMap (now replaced with our own thread safe hash)
|
94
91
|
# seemed to be accessing invalid areas of memory and seg faulting.
|
95
92
|
# Issue being tracked in FFM-12192, and once resolved, can remove these checks in a future release && once the issue is resolved.
|
96
|
-
|
97
|
-
@config.logger.warn("Skipping invalid MetricsEvent: feature_config is missing or incomplete. feature_config=#{
|
93
|
+
unless feature_name && !feature_name.empty?
|
94
|
+
@config.logger.warn("Skipping invalid MetricsEvent: feature_config is missing or incomplete. feature_config=#{feature_name.inspect}")
|
98
95
|
return
|
99
96
|
end
|
100
97
|
|
101
|
-
|
102
|
-
@config.logger.warn("Skipping
|
98
|
+
unless variation_identifier && !variation_identifier.empty?
|
99
|
+
@config.logger.warn("Skipping iInvalid MetricsEvent: variation is missing or incomplete. variation=#{variation_identifier.inspect}")
|
103
100
|
return
|
104
101
|
end
|
105
102
|
|
106
|
-
if variation.nil? || !variation.respond_to?(:identifier) || variation.identifier.nil?
|
107
|
-
@config.logger.warn("Skipping iInvalid MetricsEvent: variation is missing or incomplete. variation=#{variation.inspect}")
|
108
|
-
return
|
109
|
-
end
|
110
|
-
|
111
|
-
event = MetricsEvent.new(feature_config, GLOBAL_TARGET, variation, @config.logger)
|
112
103
|
@metric_maps_mutex.synchronize do
|
113
|
-
|
104
|
+
key = "#{feature_name}\0#{variation_identifier}"
|
105
|
+
@evaluation_metrics[key] = (@evaluation_metrics[key] || 0) + 1
|
114
106
|
end
|
115
107
|
end
|
116
108
|
|
@@ -185,31 +177,28 @@ class MetricsProcessor < Closeable
|
|
185
177
|
|
186
178
|
total_count = 0
|
187
179
|
evaluation_metrics_clone.each do |key, value|
|
188
|
-
|
189
|
-
|
190
|
-
#
|
191
|
-
#
|
192
|
-
|
193
|
-
|
194
|
-
#
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
# If any components are missing, log a detailed warning and skip processing
|
201
|
-
unless missing_components.empty?
|
202
|
-
@config.logger.warn "Skipping invalid metrics event: missing #{missing_components.join(', ')} in key: #{key.inspect}, full details: #{key.inspect}"
|
180
|
+
feature_name, variation_identifier = key.split("\0", 2)
|
181
|
+
|
182
|
+
# Although feature_name and variation_identifier should always be present,
|
183
|
+
# this guard provides protection against an edge case where keys reference
|
184
|
+
# other objects in memory. In versions <= 1.4.4, we were keying on the MetricsEvent
|
185
|
+
# class (now deleted). To remediate this, we have transitioned to using strings as keys.
|
186
|
+
# This issue is being tracked in FFM-12192. Once resolved, these checks can be safely
|
187
|
+
# removed in a future release.
|
188
|
+
# If any required data is missing, log a detailed warning and skip processing.
|
189
|
+
unless feature_name && variation_identifier && value.is_a?(Integer) && value > 0
|
190
|
+
@config.logger.warn "Skipping invalid metrics event: missing or invalid feature_name, variation_identifier, or count. Key: #{key.inspect}, Count: #{value.inspect}"
|
203
191
|
next
|
204
192
|
end
|
205
193
|
|
206
194
|
total_count += value
|
195
|
+
|
207
196
|
metrics_data = OpenapiClient::MetricsData.new({ :attributes => [] })
|
208
197
|
metrics_data.timestamp = (Time.now.to_f * 1000).to_i
|
209
198
|
metrics_data.count = value
|
210
199
|
metrics_data.metrics_type = "FFMETRICS"
|
211
|
-
metrics_data.attributes.push(OpenapiClient::KeyValue.new({ :key => @feature_name_attribute, :value =>
|
212
|
-
metrics_data.attributes.push(OpenapiClient::KeyValue.new({ :key => @variation_identifier_attribute, :value =>
|
200
|
+
metrics_data.attributes.push(OpenapiClient::KeyValue.new({ :key => @feature_name_attribute, :value => feature_name }))
|
201
|
+
metrics_data.attributes.push(OpenapiClient::KeyValue.new({ :key => @variation_identifier_attribute, :value => variation_identifier }))
|
213
202
|
metrics_data.attributes.push(OpenapiClient::KeyValue.new({ :key => @target_attribute, :value => @global_target_identifier }))
|
214
203
|
metrics_data.attributes.push(OpenapiClient::KeyValue.new({ :key => @sdk_type, :value => @server }))
|
215
204
|
metrics_data.attributes.push(OpenapiClient::KeyValue.new({ :key => @sdk_language, :value => "ruby" }))
|
data/lib/ff/ruby/server/sdk.rb
CHANGED
@@ -17,7 +17,6 @@ require_relative "sdk/api/cf_client"
|
|
17
17
|
require_relative "sdk/api/evaluation"
|
18
18
|
require_relative "sdk/api/inner_client"
|
19
19
|
require_relative "sdk/api/auth_service"
|
20
|
-
require_relative "sdk/api/metrics_event"
|
21
20
|
require_relative "sdk/api/default_cache"
|
22
21
|
require_relative "sdk/api/config_builder"
|
23
22
|
require_relative "sdk/api/file_map_store"
|
data/scripts/sdk_specs.sh
CHANGED
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ff-ruby-server-sdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
4
|
+
version: 1.4.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- 'Miloš Vasić, cyr.: Милош Васић'
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-01-20 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: rake
|
@@ -253,7 +252,6 @@ files:
|
|
253
252
|
- lib/ff/ruby/server/sdk/api/inner_client_repository_callback.rb
|
254
253
|
- lib/ff/ruby/server/sdk/api/inner_client_updater.rb
|
255
254
|
- lib/ff/ruby/server/sdk/api/metrics_callback.rb
|
256
|
-
- lib/ff/ruby/server/sdk/api/metrics_event.rb
|
257
255
|
- lib/ff/ruby/server/sdk/api/metrics_processor.rb
|
258
256
|
- lib/ff/ruby/server/sdk/api/operators.rb
|
259
257
|
- lib/ff/ruby/server/sdk/api/polling_processor.rb
|
@@ -289,7 +287,6 @@ metadata:
|
|
289
287
|
homepage_uri: https://www.harness.io/
|
290
288
|
source_code_uri: https://github.com/harness/ff-ruby-server-sdk
|
291
289
|
changelog_uri: https://github.com/harness/ff-ruby-server-sdk/blob/main/CHANGELOG.md
|
292
|
-
post_install_message:
|
293
290
|
rdoc_options: []
|
294
291
|
require_paths:
|
295
292
|
- lib
|
@@ -305,8 +302,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
305
302
|
- !ruby/object:Gem::Version
|
306
303
|
version: '0'
|
307
304
|
requirements: []
|
308
|
-
rubygems_version: 3.
|
309
|
-
signing_key:
|
305
|
+
rubygems_version: 3.6.2
|
310
306
|
specification_version: 4
|
311
307
|
summary: Harness is a feature management platform that helps teams to build better
|
312
308
|
software and to test features quicker.
|
@@ -1,53 +0,0 @@
|
|
1
|
-
class MetricsEvent
|
2
|
-
|
3
|
-
attr_accessor :feature_config, :target, :variation
|
4
|
-
|
5
|
-
def initialize(feature_config, target, variation, logger)
|
6
|
-
|
7
|
-
@target = target
|
8
|
-
@variation = variation
|
9
|
-
@feature_config = feature_config
|
10
|
-
@logger = logger
|
11
|
-
freeze
|
12
|
-
end
|
13
|
-
|
14
|
-
def ==(other)
|
15
|
-
eql?(other)
|
16
|
-
end
|
17
|
-
|
18
|
-
def eql?(other)
|
19
|
-
# Guard clause other is the same type.
|
20
|
-
# While it should be, this adds protection for an edge case we are seeing with very large
|
21
|
-
# project sizes. Issue being tracked in FFM-12192, and once resolved, can feasibly remove
|
22
|
-
# these checks in a future release.
|
23
|
-
unless other.is_a?(MetricsEvent)
|
24
|
-
# We should always have a logger available except when we've deep cloned this class. We don't do any
|
25
|
-
# equality check on clones in metrics code anyway, so this is just a safety check.
|
26
|
-
if @logger
|
27
|
-
@logger.warn("Warning: Attempted to compare MetricsEvent with #{other.class.name}")
|
28
|
-
end
|
29
|
-
return false
|
30
|
-
end
|
31
|
-
|
32
|
-
feature_config.feature == other.feature_config.feature and
|
33
|
-
variation.identifier == other.variation.identifier and
|
34
|
-
target.identifier == other.target.identifier
|
35
|
-
end
|
36
|
-
|
37
|
-
def hash
|
38
|
-
feature_config.feature.hash | variation.identifier.hash | target.identifier.hash
|
39
|
-
end
|
40
|
-
|
41
|
-
|
42
|
-
# Exclude logger from serialization
|
43
|
-
def marshal_dump
|
44
|
-
[@feature_config, @target, @variation]
|
45
|
-
end
|
46
|
-
|
47
|
-
def marshal_load(array)
|
48
|
-
@feature_config, @target, @variation = array
|
49
|
-
@logger = nil
|
50
|
-
end
|
51
|
-
|
52
|
-
|
53
|
-
end
|