unleash / unleash-client-ruby Goto Github PK
View Code? Open in Web Editor NEWUnleash client SDK for Ruby
Home Page: https://docs.getunleash.io
License: Apache License 2.0
Unleash client SDK for Ruby
Home Page: https://docs.getunleash.io
License: Apache License 2.0
Hi there, I face some issues when I use puma in clustered mode (following the documentation in readme)
But I would list here a bit more issues:
clients
(multi-process app on a single machine - puma clustered mode for instance) - all processes have the same backup_file https://github.com/Unleash/unleash-client-ruby/blob/main/lib/unleash/configuration.rb#L47, only if you name application differently I can get separated backup_file so I won’t face with no such file or dir
error.system: MacOS BigSur 11.4
setup unleash according to the readme for puma (clustered mode) https://github.com/Unleash/unleash-client-ruby/blob/c745ed4569b5e83498d22e0fbf618e8eb6ce2002/README.md#with-preload_app
No errors should be thrown.
...
[2022-06-07T12:06:38.866649+02:00 Unleash ERROR] : Unable to save backup file. Exception thrown Errno::ENOENT:'No such file or directory @ rb_file_s_rename - (/var/folders/pf/_39szp0x68v6s0vrr0jfz9l80000gn/T/unlea
sh-oh-backend-repo.json.tmp, /var/folders/pf/_39szp0x68v6s0vrr0jfz9l80000gn/T/unleash-oh-backend-repo.json)'
[2022-06-07T12:06:38.867553+02:00 Unleash ERROR] : stacktrace: ["/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/unleash-4.2.1/lib/unleash/toggle_fetcher.rb:76:in `rename'", "/Users/viktorsych/.rb
env/versions/3.0.2/lib/ruby/gems/3.0.0/gems/unleash-4.2.1/lib/unleash/toggle_fetcher.rb:76:in `block in save!'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/unleash-4.2.1/lib/unleash/toggle_fetcher.rb
:72:in `synchronize'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/unleash-4.2.1/lib/unleash/toggle_fetcher.rb:72:in `save!'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/unleash
-4.2.1/lib/unleash/toggle_fetcher.rb:63:in `fetch'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/unleash-4.2.1/lib/unleash/toggle_fetcher.rb:22:in `initialize'", "/Users/viktorsych/.rbenv/versions/3.0
.2/lib/ruby/gems/3.0.0/gems/unleash-4.2.1/lib/unleash/client.rb:21:in `new'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/unleash-4.2.1/lib/unleash/client.rb:21:in `initialize'", "/Users/viktorsych/Co
de/oh/overhaul-backend/lib/unleash.rb:19:in `new'", "/Users/viktorsych/Code/oh/overhaul-backend/lib/unleash.rb:19:in `preload'", "config/puma.rb:56:in `block in _load_from'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ru
by/gems/3.0.0/gems/puma-5.6.4/lib/puma/configuration.rb:297:in `block in run_hooks'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.6.4/lib/puma/configuration.rb:295:in `each'", "/Users/viktorsyc
h/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.6.4/lib/puma/configuration.rb:295:in `run_hooks'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.6.4/lib/puma/cluster/worker.rb:55:in `run'
", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.6.4/lib/puma/cluster.rb:204:in `worker'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.6.4/lib/puma/cluster.rb:97:in `
block in spawn_worker'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/activesupport-6.1.5/lib/active_support/fork_tracker.rb:10:in `block in fork'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/ge
ms/3.0.0/gems/activesupport-6.1.5/lib/active_support/fork_tracker.rb:10:in `block in fork'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/activesupport-6.1.5/lib/active_support/fork_tracker.rb:8:in `fo
rk'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/activesupport-6.1.5/lib/active_support/fork_tracker.rb:8:in `fork'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/activesupport-6
.1.5/lib/active_support/fork_tracker.rb:27:in `fork'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/activesupport-6.1.5/lib/active_support/fork_tracker.rb:8:in `fork'", "/Users/viktorsych/.rbenv/versio
ns/3.0.2/lib/ruby/gems/3.0.0/gems/activesupport-6.1.5/lib/active_support/fork_tracker.rb:27:in `fork'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.6.4/lib/puma/cluster.rb:97:in `spawn_worker'"
, "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.6.4/lib/puma/cluster.rb:79:in `block in spawn_workers'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.6.4/lib/puma/clus
ter.rb:72:in `times'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.6.4/lib/puma/cluster.rb:72:in `spawn_workers'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.6.4/l
ib/puma/cluster.rb:415:in `run'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.6.4/lib/puma/launcher.rb:182:in `run'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.6.
4/lib/puma/cli.rb:81:in `run'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.6.4/bin/puma:10:in `<top (required)>'", "/Users/viktorsych/.rbenv/versions/3.0.2/bin/puma:23:in `load'", "/Users/vikt
orsych/.rbenv/versions/3.0.2/bin/puma:23:in `<top (required)>'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/bundler-2.2.32/lib/bundler/cli/exec.rb:58:in `load'", "/Users/viktorsych/.rbenv/versions/3.
0.2/lib/ruby/gems/3.0.0/gems/bundler-2.2.32/lib/bundler/cli/exec.rb:58:in `kernel_load'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/bundler-2.2.32/lib/bundler/cli/exec.rb:23:in `run'", "/Users/vikto
rsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/bundler-2.2.32/lib/bundler/cli.rb:478:in `exec'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/bundler-2.2.32/lib/bundler/vendor/thor/lib/thor/comma
nd.rb:27:in `run'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/bundler-2.2.32/lib/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/rub
y/gems/3.0.0/gems/bundler-2.2.32/lib/bundler/vendor/thor/lib/thor.rb:392:in `dispatch'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/bundler-2.2.32/lib/bundler/cli.rb:31:in `dispatch'", "/Users/viktor
sych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/bundler-2.2.32/lib/bundler/vendor/thor/lib/thor/base.rb:485:in `start'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/bundler-2.2.32/lib/bundler/cli.
rb:25:in `start'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/bundler-2.2.32/exe/bundle:49:in `block in <top (required)>'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/bundler-2
.2.32/lib/bundler/friendly_errors.rb:103:in `with_friendly_errors'", "/Users/viktorsych/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/bundler-2.2.32/exe/bundle:37:in `<top (required)>'", "/Users/viktorsych/.rbenv/versions
/3.0.2/bin/bundle:23:in `load'", "/Users/viktorsych/.rbenv/versions/3.0.2/bin/bundle:23:in `<main>'"]
No response
No response
4.2.1
No response
No response
ruby, RoR, Sidekiq, Puma
Hello @rarruda,
I have been trying to implement the client to my rails monolith (that is part of Moteefe). One of your enterprise clients.
And I have stumbled upon e big error that does not let me implement it, the request_version
is 2
and the only one supported in the gem is 1
.
This thing does not let me do anything with Unleash.
Can you please tell me what to do or add support for version 2.
Thanks,
Rares and the @moteefe_team
Is there documentation on how to implement a custom strategy?
Looking here: https://unleash.github.io/docs/guides/custom_activation_strategy
It says each client sdk is able to implement their own custom activation strategy. However, I'm not seeing a strategies
argument when initializing.
The Flexible Rollout strategy supports multiple options for stickiness
, used to define how we guarantee consistency for a gradual rollout.
Today it support the following options:
default
(Unleash chooses the first value present on the context in defined order userId, sessionId, random.)userId
sessionId
random
We have now extended the protocol to support any field in the unleash context. In addition It can be any of the predefined context field, in addition to any custom properties the user has defined.
This means that the "stickiness" parameter in the strategy configuration can now be any field on the context, and the SDK should use the specified context field to calculate the "hash" which determine whether the activation strategy should evaluate to true or false.
Edge case:
To guide the implementation we have:
I don't see in the code where retry_limit
is actually used?
Had some issues getting this going in a rails app until I realised that I did not have the URL correct.
In order for it to work for me I have to set the config.url
to http://localhost:4242/api/client
However this causes the metrics gathering to break due a request being made to http://localhost:4242/api/client/client/metrics
Should https://github.com/Unleash/unleash-client-ruby/blob/master/lib/unleash/configuration.rb#L56 include the client
prefix like the other URLS?
The Unleash API client endpoint support query for specific project, to avoid having the SDK download all feature toggles like this:
http https://app.unleash-hosted.com/demo/api/client/features?project=solar
Authorization:56907a2fa53c1d16101d509a10b78e36190b0f918d9f122d
Which will only return feature flags for projectId=solar
.
This should be implemented by adding a new configuration to the unleash SDK to specify the "projectId" and if this option is specified it should be added to the feature-toggle query as a query param when fetching feature toggles from the Unleash API.
Implementation of bootstrapping has lead to some subtle changes to the ScheduledExecutor class. There's some discussion around how this needs to work and what the code needs to look like. Ideally, we need to define the behaviour we'd like to see from this class before we change code.
This is especially important to decide so that we can finalise how async intialisation works (which we partially support at the moment) and will be important if we decide we need to implement the "block until ready" that some of the other SDKs support. Both of these would be surprising, breaking changes from a consumer perspective so deciding on how ScheduledExecutor needs to work is important
Currently README recommends using ~> 4.0.0. Please follow https://semver.org and update README to explicitly state compliance with semver. This will allow a looser dependency specification ~> 4.0
This is kind of similar to #41, but for variants.
The stickiness will be defined as a field on all the variants in the variants array, this to ensure we are not breaking our protocol. In the example we can see the stickiness is configured to "tenantId".
{"variants": [
{
"name": "yellow",
"weight": 166,
"weightType": "variable",
"payload": {
"type": "string",
"value": "yellow submarine"
},
"overrides": [],
"stickiness": "tenantId"
}, {
"name": "salmon",
"weight": 166,
"weightType": "variable",
"payload": {
"type": "string",
"value": "salmon"
},
"overrides": [],
"stickiness": "tenantId"
}
]}
How it looks in Unleash Admin UI:
Edge cases:
If no stickiness is defined the SDK should assume "default" stickiness as today.
To guide the implementation we have:
Unleash has a big shift from the feature environment variants to strategy variants to allow more flexible variants based on constraints and segments.
The strategy variants take precedence over the feature environment variants.
What we do today:
What we want to add on top of existing variants:
There's a video showing new strategy variants in action: Unleash/unleash#1550 (comment)
We already have a specification for strategy variants: https://github.com/Unleash/client-specification/blob/main/specifications/16-strategy-variants.json
We already did the migration for other SDKs:
The gist of the solution is to first check if the activation strategy has a variant/multiple variants attached. If so, select one of them. If none of the strategies have variants we fall back to the feature environment variants as before (for backwards compatibility). So in the new world each strategy resolves not just to enabled/disabled but when it's enabled it has some variant data attached. It makes the order of strategies matter.
In order to prepare the SDKS for strategy constraints we need to introduce two new fields in the Unleash Context
environment
(default value is "default")appName
(already a required config parameter)Both these static context fields should be configured at initialisation of the unleash SDK and be set as default values of the Unleash Context provided to the strategy implementations. The user is allowed to override these values in the context object at runtime (this is required to be backward compatible).
This support should be possible to add without breaking any contract and should be released as a minor version (3.3.x).
I've had a heck of a time tracking down an issue with no logs being written in my rails app.
I have setup the gem with the recommended initialization approach for rails apps but there is some unexpected behavior that seems to be changing the rails logging level to warn
and therefore preventing request logs from being written.
Despite the default rails logging level being debug
setting the Unleash logger to Rails.logger
somehow changes the level to warn
.
Setup
config.log_level
is NOT set in application.rb
or development.rb
, therefore defaulting to debug
My Initializer when following the recommened Unleash config
Unleash.configure do |config|
config.url = Rails.application.credentials.unleash_url
config.instance_id = Rails.application.credentials.unleash_instance_id
config.app_name = Rails.env
config.logger = Rails.logger
end
Starting a rails console and running Rails.logger.level
returns 2
.
Initializer that doesn't change log level
Unleash.configure do |config|
config.url = Rails.application.credentials.unleash_url
config.instance_id = Rails.application.credentials.unleash_instance_id
config.app_name = Rails.env
# config.logger = Rails.logger
end
Starting a rails console and running Rails.logger.level
returns 0
.
Let me know if you need any other information to assist in the diagnosing this issue!
The client doesn't update unleash feature flags information after the application boots using Phusion Passenger as the aplication server. I have tried with WEBrick and Puma, both work!
The README mentions an specific configuration to work on Puma, is there such configuration for Passenger?
Environment:
Gems
Files
# config/initializers/unleash.rb
Unleash.configure do |config|
config.url = 'https://gitlab.com/api/v4/feature_flags/unleash/[SECRET]'
config.instance_id = '[SECRET]'
config.app_name = Rails.application.class.parent.to_s
config.refresh_interval = 5
config.logger = Rails.logger
config.environment = Rails.env
end
UNLEASH = Unleash::Client.new
# Passenger.json
{
"port": "3000",
"max_pool_size":"2",
"min_instances": "2",
"engine": "nginx",
"disable_anonymous_telemetry": true
}
I've set up Unleash's Ruby client on a Rails application that is served by a Puma server, and for such I'm creating the client inside the on_worker_boot
callback and stopping on before_fork
- as described in the README.
It's has been working flawlessly, but since the client is only started as part of the Puma worker lifecycle, unleash is not available when spinning up a Rails console or inside Sidekiq workers.
Any ideas on how I can proceed with this and let Unleash available in others contexts? I tried to do some initialization magic by creating an initializer file that somehow checks if the current process is a Rails console or Sidekiq instance and have had no luck so far.
The Ruby SDK sent metrics even if it is empty. Instead it can hold on to the local metrics longer if there are no metrics to report (e.g. up to 10min).
see
Unleash/client-specification#1 (comment) for details.
Hi,
I am created a new feature flag and with a strategy to only work for users that have a specific property, but for some reason this isn't working for me. What am I doing wrong?
I did the following:
1. I created a feature toggle called 'nogah_test'
2. I added a strategy called companyId which is a list of companyIds
3. I added the strategy to my feature toggle with a specific companyId of 10 (my company Id)
4. My code looks like this:
This is my @unleash_context
when I do:
Unleash::Client.new.is_enabled?('nogah_test', @unleash_context)
it returns true, but when I just add a random companyId to the context or even change properties to be an empty hash, I still get true. It ignores the properties hash for some reason.
It only checks to see if the feature is enabled or disabled.
We know that a similar implementation works for Node.js, but here it doesn't.
Could it be that it just doesn't work in Ruby?
How come??? Thanks.
In Unleash's Ruby client, when feature toggle found disabled, enabled?
method returns default result argument.
I checked the implementation in Go and Node, both just returns false
instead of default result.
While digging further into this issue, I found that in those language's clients, they actually use default value (in form of function callback) for the case that toggle's not found.
Unlike in Ruby's implementation, the default value would be return when toggle not found and toggle not enabled.
I see that there are a few strategies that are showing up in the unleash server related to this client library:
And from the Unleash::STRATEGIES
constant we have:
{:default=>#<Unleash::Strategy::Default:0x00007fe37367f1e8>,
:gradualRolloutRandom=>#<Unleash::Strategy::GradualRolloutRandom:0x00007fe37367f058>,
:unknown=>#<Unleash::Strategy::Unknown:0x00007fe37367ef40>,
:gradualRolloutUserId=>#<Unleash::Strategy::GradualRolloutUserId:0x00007fe37367edb0>,
:applicationHostname=>#<Unleash::Strategy::ApplicationHostname:0x00007fe37367ec20 @hostname="">,
:userWithId=>#<Unleash::Strategy::UserWithId:0x00007fe37367ea68>,
:remoteAddress=>#<Unleash::Strategy::RemoteAddress:0x00007fe37367e8d8>,
:base=>#<Unleash::Strategy::Base:0x00007fe37367e7c0>,
:gradualRolloutSessionId=>#<Unleash::Strategy::GradualRolloutSessionId:0x00007fe37367e630>,
:notImplemented=>#<Unleash::Strategy::NotImplemented: Unleash::Strategy::NotImplemented>}
Would it make sense to filter out the base
class, unknown
and notimplemented
?
Hi, I noticed that I am getting an exception when the scheduler is trying to log an error:
/Users/gingermusketeer/.rubies/ruby-2.5.1/lib/ruby/2.5.0/logger.rb:544:in `error': wrong number of arguments (given 2, expected 0..1) (ArgumentError)
11:25:08 web.1 | from /Users/gingermusketeer/.gem/ruby/2.5.1/gems/unleash-0.1.2/lib/unleash/scheduled_executor.rb:27:in `rescue in block (2 levels) in run'
11:25:08 web.1 | from /Users/gingermusketeer/.gem/ruby/2.5.1/gems/unleash-0.1.2/lib/unleash/scheduled_executor.rb:22:in `block (2 levels) in run'
11:25:08 web.1 | from /Users/gingermusketeer/.gem/ruby/2.5.1/gems/unleash-0.1.2/lib/unleash/scheduled_executor.rb:17:in `loop'
11:25:08 web.1 | from /Users/gingermusketeer/.gem/ruby/2.5.1/gems/unleash-0.1.2/lib/unleash/scheduled_executor.rb:17:in
Is there an issue with my setup or is this part of the gem? It seems to be caused by there being two arguments to the log error call here:
I was able to reproduce this problem by running Unleash.logger.error 'one', 'two'
from the rails console.
When disabling the client but not disabling metrics, the metrics attribute/container is never initialized. Yet, attempting to call enabled?
on the client results in undefined method ... for nil:NilClass
as it attempts to access metrics.
Unleash::Client.new(
disable_client: true,
url: 'https://example.com',
bootstrap_config: Unleash::Bootstrap::Configuration.new(
data: '{
"version": 1,
"features": [
{
"enabled": true,
"name": "featureX"
}
]
}'
)
).enabled?('featureX', nil)
No runtime errors, and correct state of the feature.
[2023-05-01T18:45:55.551253+00:00 Unleash WARN ] : Unleash::Client is disabled! Will only return default (or bootstrapped if available) results!
/shared/unleash/lib/unleash/feature_toggle.rb:30:in `is_enabled?': undefined method `increment' for nil:NilClass (NoMethodError)
Unleash.toggle_metrics.increment(name, choice) unless Unleash.configuration.disable_metrics
^^^^^^^^^^
from /shared/unleash/lib/unleash/client.rb:50:in `is_enabled?'
from unleash_client.rb:77:in `<main>'
No response
No response
Ruby SDK 4.4.2
Enterprise
Hosted by Unleash
Ruby SDK 4.4.2
Just a feature that needs to be implemented - Global Segments. Unleash v4.13 supports enhanced responses for global segments, it would be great if this SDK can make use of this.
Segments are effectively a way for Unleash users to define a list of constraints in such a way that makes them reusable across toggles without manually copying the constraint across to another toggle. Segments have two modes of operation, from the SDK's perspective, the inline mode will have no impact, segments will be remapped on the server side into constraints on the toggle information, no changes need to be . The second mode, global segments, requires that the SDK both opt in and handle the response differently. The handling should effectively result in unpacking the segments referenced in the feature strategies into a set of constraints. The changes required are described below.
Control Header
The SDK needs to pass up a Unleash-Client-Spec
header with a semver value greater or equal to 4.2.0
(i.e. be greater or equal to the version of the unleash client spec tests where global segments are described) when hitting the get toggles endpoint on the Unleash server. This will enable the Unleash server to respond with the enhanced format
Example of the difference between enhanced and standard format:
Standard Format (default)
{
"version": 2,
"features": [
{
"strategies": [
{
"name": "flexibleRollout",
"constraints": [
{
"values": [
"31"
],
"inverted": false,
"operator": "IN",
"contextName": "appName",
"caseInsensitive": false
}
],
"parameters": {
"groupId": "Test1",
"rollout": "100",
"stickiness": "default"
}
}
],
"name": "Test1"
},
{
"strategies": [
{
"name": "flexibleRollout",
"constraints": [
{
"values": [
"31"
],
"inverted": false,
"operator": "IN",
"contextName": "appName",
"caseInsensitive": false
}
],
"parameters": {
"groupId": "Test2",
"rollout": "100",
"stickiness": "default"
}
}
],
"name": "Test2"
}
],
"query": {
"environment": "default"
}
}
Enhanced Format (requires opt in)
{
"version": 2,
"features": [
{
"strategies": [
{
"name": "flexibleRollout",
"constraints": [],
"parameters": {
"groupId": "Test1",
"rollout": "100",
"stickiness": "default"
},
"segments": [
1
]
}
],
"name": "Test1"
},
{
"strategies": [
{
"name": "flexibleRollout",
"constraints": [],
"parameters": {
"groupId": "Test2",
"rollout": "100",
"stickiness": "default"
},
"segments": [
1
]
}
],
"name": "Test2"
}
],
"query": {
"environment": "default"
},
"segments": [
{
"id": 1,
"constraints": [
{
"values": [
"31"
],
"inverted": false,
"operator": "IN",
"contextName": "appName",
"caseInsensitive": false
}
]
}
]
}
The relevant changes between the two formats are that in the enhanced format the segments are defined once as a global list and referenced within the strategy on the toggle by its ID. What's important to note is that the two above packets should be
handled identically, they reference the same toggle state.
Considerations
Hey all, we have the server under a basic authentication and when setting the URL in the initializer like so "http://user:psw@the_url/api" it's not connecting we did a test with plain curl and all worked well and also our NodeJS server works properly with the same URL.
Is there a special thing we need to do to handle this issue?
It seems that the Ruby SDK doesn't evaluate strategies and constraints in the same way as the other SDKs. While the other SDKs check constraints first, Ruby seems to check them second. This can potentially lead to different evaluation results when using this SDK.
It seems this is the line of code responsible for the check:
strategy_enabled?(s, context) && strategy_constraint_matches?(s, context)
On line 65 of feature_toggle.rb
Don't have steps to reproduce this yet, I'll update this ticket when we have a specification test that surfaces the issue
This should evaluate strategies and constraints in the same way as the other SDKs - constraints first
No response
No response
No response
N/A
No response
No response
Affects latest version, v4.3 but probably a whole version range previously
When the client is disabled, attempting to call client.get_variant
always returns the default value, even if the toggle has been defined in the bootstrap config. Seems bootstrapped variant toggles should be loaded and available, just like their standard toggle brethen.
client = Unleash::Client.new(
disable_client: true,
url: 'https://example.com',
bootstrap_config: Unleash::Bootstrap::Configuration.new(
data: '{
"version": 1,
"features": [
{
"enabled": true,
"name": "featureVariantX",
"strategies": [{ "name": "default" }],
"variants": [
{
"name": "default-value",
"payload": {
"type": "string",
"value": "info"
},
"weight": 100,
"weightType": "variable"
}
]
}
]
}'
)
)
pp client.get_variant('featureVariantX', nil)
[2023-04-27T22:35:22.506886+00:00 Unleash WARN ] : Unleash::Client is disabled! Will only return default (or bootstrapped if available) results!
#<Unleash::Variant:0x00007fd37763c930 @enabled=false, @name="disabled", @payload=nil>
Toggle should be enabled and the payload should not be nil.
[2023-04-27T23:03:03.752292+00:00 Unleash WARN ] : Unleash::Client is disabled! Will only return default (or bootstrapped if available) results!
#<Unleash::Variant:0x00007f0622b23ea8 @enabled=true, @name="featureVariantX", @payload={:type=>"string", :value=>"info"}>
No response
No response
No response
Ruby SDK 4.4.2
Enterprise
Hosted by Unleash
Ruby SDK 4.4.2
Hi,
you recently released v4.0.0.
What are the breaking changes a user needs to be aware of when upgrading?
I checked the diff, but it is not obvious.
v3.2.5...v4.0.0
Consider to use the GitHub release feature. A nice way to communicate changes is https://keepachangelog.com
Kind regards
We're just starting to use Unleash and realized that all of our apps need the url, app_name and custom_http_headers set in the code. What we would like to do is set at least DEFAULT_UNLEASH_URL and maybe CUSTOM_HTTP_HEADERS at the platform level that the apps inherit env vars from. This means we don't have to go to each app and provide the url and headers per-app. The app_name would be the only per-app variable, which makes sense because its the only thing that would change between apps. If the URL were to change we could update it in one place and all apps would pick that up upon restart. We can always overwrite the default env vars per-app if needed for testing or modifying headers.
If this change is implemented the below code blocks would be functionally the same:
No parameters uses the default values specified by DEFAULT_UNLEASH_URL
, DEFAULT_UNLEASH_APP_NAME
, DEFAULT_UNLEASH_CUSTOM_HEADERS
require 'unleash'
@unleash = Unleash::Client.new()
Pulling in values from non-default env vars (or just hardcoding values here)
require 'unleash'
@unleash = Unleash::Client.new(
url: ENV['UNLEASH_URL'],
app_name: ENV['UNLEASH_APP_NAME'],
custom_http_headers: ENV['UNLEASH_CUSTOM_HEADERS']
)
Each of our apps has its own configuration but also inherits some environment variables provided by the platform. The platform variables usually are for things that are the same across all apps. Since our apps all use the same Unleash api server, the Unleash URL is the same for every app. It would be ideal to only have to specify this in one place instead of in every app. This also prevents the scenario where a developer forgot to add or update the Unleash api server URL and that one app failed to connect.
I would suggest creating at least 3 new env vars: DEFAULT_UNLEASH_URL, DEFAULT_UNLEASH_APP_NAME, and DEFAULT_UNLEASH_CUSTOM_HTTP_HEADERS
If the corresponding parameter is not provided in the call to Unleash::Client.new()
then check for the correct DEFAULT_ variable and use that if present.
While the app_name is not something shared by all apps it would be good to have a DEFAULT_ for it so that the naming scheme for these variables is consistent. Potentially there could be a DEFAULT_ variable for every parameter that could be given to Client.new()
We should normalize the intervals with other SDKs.
Right now the order of bootstrapping options is codified in source code but not through tests/specs. We should lay down some tests to ensure that the order of bootstrapping selector is defined.
Currently the order of priority is:
This should be fixed in place with a test so that it isn't accidentally changed in future
In order to reduce load on servers, as well as save our users some bandwidth, I'd like for this SDK to react to http status codes, and not just keep requesting at the same frequency if the server is telling it to chill out.
Part of a cross-SDK initiative to make all our SDKs respect http statuses in order to save ourselves and our users for bandwidth/cpu usage that adds no value to either the server or the client.
Unleash/unleash-client-node#537 follows the correct pattern. Use 404, 429 or 50x statuses to reduce polling frequency. On 401 or 403 log and don't keep polling - your user probably needs to update their key before there's any point in continuing to hit the server.
get_variant
method of the client.rb
file.default_variant
.When frozen-string-literal magic comment is used in a file that calls the Unleash#is_enabled? method, FrozenError exception is raised if the constraint has case-insensitive enabled.
No response
No response
No response
No response
No response
v5.3.0
Open source
Self-hosted
Ruby v4.5.0
I am testing the combinations with default result and seems like it does not work as expected when default value is true
. It will always override combined result as true
regardless the combined result of toggle is enabled and strategy result.
https://github.com/Unleash/unleash-client-ruby/blob/master/lib/unleash/feature_toggle.rb#L74..L81
when strategy result already computed as false
and toggle is enabled, it may respect that result instead.
So we I finally had the time to defining a set of client specifications, which defines what the expected result should be for a given set of feature toggles and a given Unleash context. The idea is that they shall ensure correct behaviour of clients across languages.
The specifications lives inside: https://github.com/Unleash/client-specification
The node and java client already implements support for automatically running the test cases as part of the CI validation.
Would be awesome if this client also could leverage the specifications. This will defenetly be used for v4, which will add new capabilities to the Unleash protocol.
The instance of context should be able to be converted to a hash.
The ruby naming convention is a #to_h
method to do so.
Sometimes, one wants to re-use the context set. e.g. to report it to Sentry or any other exception tracker.
Add a to_h
method to Unleash::Context
I have setup the gem with the recommended initialization approach for rails apps but if there is an issue during startup It puts puma into a loop until it is able to connect to the unleash server.
This often has a result of requiring me to restart my computer as puma seems to create processes until this is successful.
Would it make sense for the initial connection to result in a warning instead?
Also is using the on_worker_boot required if using puma? The reason I ask is that it means that Rails.configuration.unleash
is not present in the rails console. Having this available is handy for debugging.
We've started the path to add featureEnabled to the variant response. This is useful when you want to differentiate between a feature flag that is disabled and a feature flag that is enabled but has no variants.
Reference implementation in python:
Specification tests:
If you want to contribute to Unleash, this is a good place to start.
See specification
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.