Skip to content

Commit a7498e3

Browse files
authored
FFM-12088 Retry on HTTP status code 0 / add optional timeout to wait_for_initialization (#44)
* FFM-12088 Retry on http code 0 * FFM-12088 Return http code if it is there * FFM-12088 Bump version * FFM-12088 Add wait_for_init timeout + sdk code * FFM-12088 Update docs * FFM-12088 Update log * FFM-12088 Change timeout param name * FFM-12088 Remove commented out code * FFM-12088 Bump minor version
1 parent 009b477 commit a7498e3

File tree

8 files changed

+84
-11
lines changed

8 files changed

+84
-11
lines changed

docs/further_reading.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,50 @@ client.init(apiKey, ConfigBuilder
2424
| enableStream | analytics_enabled(false) | Enable streaming mode. | true |
2525
| enableAnalytics | stream_enabled(true) | Enable analytics. Metrics data is posted every 60s | true |
2626

27+
## Client Initialization Options
28+
The Harness Feature Flags SDK for Ruby provides flexible initialization strategies to accommodate various application requirements. You can choose between an asynchronous (non-blocking) or synchronous (blocking) approach to initialize the SDK.
29+
30+
### Asynchronous (Non-Blocking) Initialization
31+
The SDK can be initialized asynchronously without blocking the main thread or requiring a callback. In this case, defaults will be served until the SDK completes the initialization process.
32+
33+
```ruby
34+
client = CfClient.instance
35+
client.init(api_key, config)
36+
37+
# Will serve default until the SDK completes initialization
38+
result = client.bool_variation("bool_flag", target, false)
39+
```
40+
41+
### Synchronous (Blocking) Initialization
42+
43+
In cases where it's critical to ensure the SDK is initialized before evaluating flags, the SDK offers a synchronous initialization method. This approach blocks the current thread until the SDK is fully initialized or the optional specified timeout (in milliseconds) period elapses.
44+
45+
The synchronous method is useful for environments where feature flag decisions are needed before continuing, such as during application startup.
46+
47+
You can use the `wait_for_initialization` method, optionally providing a timeout in milliseconds to prevent waiting indefinitely in case of unrecoverable isues, e.g. incorrect API key used.
48+
49+
**Usage with a timeout**
50+
51+
```ruby
52+
client = CfClient.instance
53+
client.init(api_key, config)
54+
55+
client.wait_for_initialization
56+
57+
result = client.bool_variation("bool_flag", target, false)
58+
```
59+
60+
**Usage without a timeout**
61+
62+
```ruby
63+
client = CfClient.instance
64+
client.init(api_key, config)
65+
66+
client.wait_for_initialization(timeout_ms: 3000)
67+
68+
result = client.bool_variation("bool_flag", target, false)
69+
```
70+
2771
## Logging Configuration
2872
You can provide your own logger to the SDK i.e. using the moneta logger we can do this
2973

lib/ff/ruby/server/sdk/api/auth_service.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,9 @@ def should_retry_http_code(code)
8989
# 503 service unavailable
9090
# 504 gateway timeout
9191
# -1 OpenAPI error (timeout etc)
92+
# 0 Un-categorised typhoeus error
9293
case code
93-
when 408,425,429,500,502,503,504,-1
94+
when 408,425,429,500,502,503,504,-1, 0
9495
return true
9596
else
9697
return false

lib/ff/ruby/server/sdk/api/cf_client.rb

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,9 @@ def init(api_key = nil, config = nil, connector = nil)
5050
end
5151
end
5252

53-
def wait_for_initialization
54-
53+
def wait_for_initialization(timeout_ms: nil)
5554
if @client != nil
56-
57-
@client.wait_for_initialization
55+
@client.wait_for_initialization(timeout: timeout_ms)
5856
end
5957
end
6058

lib/ff/ruby/server/sdk/api/inner_client.rb

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,28 +231,41 @@ def on_processor_ready(processor)
231231
@initialized = true
232232
end
233233

234-
def wait_for_initialization
235-
234+
def wait_for_initialization(timeout: nil)
236235
synchronize do
236+
SdkCodes::info_sdk_waiting_to_initialize(@config.logger, timeout)
237237

238-
@config.logger.debug "Waiting for initialization to finish"
238+
start_time = Time.now
239239

240240
until @initialized
241+
# Check if a timeout is specified and has been exceeded
242+
if timeout && (Time.now - start_time) > (timeout / 1000.0)
243+
@config.logger.warn "The SDK has timed out waiting to initialize with supplied timeout #{timeout} ms"
244+
handle_initialization_failure
245+
end
241246

242247
sleep(1)
243248
end
244249

245250
if @failure
246-
247251
raise "Initialization failed"
248252
end
249253

250254
@config.logger.debug "Waiting for initialization has completed"
251255
end
252256
end
253257

258+
254259
protected
255260

261+
def handle_initialization_failure
262+
@auth_service.close
263+
@poll_processor.stop
264+
@update_processor.stop
265+
@metrics_processor.stop
266+
on_auth_failed
267+
end
268+
256269
def setup
257270

258271
@repository = StorageRepository.new(@config.cache, @repository_callback, @config.store, @config.logger)

lib/ff/ruby/server/sdk/common/sdk_codes.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ def self.info_sdk_init_ok(logger)
1414
logger.info SdkCodes.sdk_err_msg(1000)
1515
end
1616

17+
def self.info_sdk_waiting_to_initialize(logger, timeout)
18+
if timeout
19+
message = "with timeout: #{timeout} ms"
20+
else
21+
22+
message = "with no timeout"
23+
24+
end
25+
logger.info SdkCodes.sdk_err_msg(1003, message)
26+
end
27+
1728
def self.info_sdk_auth_ok(logger)
1829
logger.info SdkCodes.sdk_err_msg(2000)
1930
end
@@ -76,6 +87,7 @@ def self.warn_bucket_by_attr_not_found(logger, attr_name, new_value)
7687
1000 => "The SDK has successfully initialized",
7788
1001 => "The SDK has failed to initialize due to the following authentication error:",
7889
1002 => "The SDK has failed to initialize due to a missing or empty API key",
90+
1003 => "The SDK is waiting for initialzation to complete",
7991
# SDK_AUTH_2xxx
8092
2000 => "Authenticated ok",
8193
2001 => "Authentication failed with a non-recoverable error - defaults will be served",

lib/ff/ruby/server/sdk/connector/harness_connector.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ def authenticate
4646
# determine if a timeout has occurred. This is fixed in 6.3.0 but requires Ruby version to be increased to 2.7
4747
# https://github.com/OpenAPITools/openapi-generator/releases/tag/v6.3.0
4848
@config.logger.warn "OpenapiClient::ApiError [\n\n#{e}\n]"
49+
50+
if e.code
51+
return e.code
52+
end
53+
4954
return -1
5055
end
5156

lib/ff/ruby/server/sdk/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module Ruby
55
module Server
66
module Sdk
77

8-
VERSION = "1.3.2"
8+
VERSION = "1.4.0"
99
end
1010
end
1111
end

scripts/sdk_specs.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
#!/bin/bash
22

33
export ff_ruby_sdk="ff-ruby-server-sdk"
4-
export ff_ruby_sdk_version="1.3.2"
4+
export ff_ruby_sdk_version="1.4.0"

0 commit comments

Comments
 (0)