launchdarkly / ruby-server-sdk

LaunchDarkly Server-side SDK for Ruby
https://docs.launchdarkly.com/sdk/server-side/ruby
Other
34 stars 52 forks source link

Flag Evaluation Issues in Rails Application #297

Open sunnybogawat opened 2 days ago

sunnybogawat commented 2 days ago

Is this a support request? We are encountering an issue with LaunchDarkly feature flag evaluations in our Ruby on Rails application. The problem specifically occurs when the feature flag value is changed. The new value is not recognized by the Rails server until we restart the server.

Detailed Information: Environment: reproduced in any non development environment. For development it works as expected

Problem: Feature flags are not evaluated correctly after their value is changed. This discrepancy persists until the Rails server is restarted. Observation: The issue is isolated to the Rails application context. When evaluating the same feature flags in the Rails console (using rails c), the evaluations work as expected and reflect the correct, updated flag values immediately.

Describe the bug When calling LaunchDarkly.client.all_flags_state(user_obj) followed by flag_value(feature_name) in the Rails app, the flag value does not reflect the current state (e.g., feature is turned ON but returns incorrect value). However, after restarting the Rails server, the correct flag value is returned. Running the same logic in rails c (Rails console) works as expected, without needing to restart anything. The issue is only observed in the Rails app during regular requests, and does not occur in the Rails console.

To reproduce Launch the Rails server (with no prior restart). Toggle a feature flag (turn ON/OFF). Call LaunchDarkly.client.all_flags_state(user_obj) followed by flag_value(feature_name) in the Rails app and observe the incorrect flag value. Restart the Rails server and call the same function again — observe that the correct flag value is now returned. Run the same logic in the Rails console (rails c) and observe the correct behavior without restarting the server. Notes:

We suspect this might be related to caching, threading, or client initialization after the Rails 6.1 upgrade, but we haven't identified the root cause. No caching or threading issues are apparent, and the user object passed to all_flags_state(user_obj) seems consistent across requests. Would appreciate any insights into why this behavior might be happening, or guidance on how to further debug this issue. Expected behavior A clear and concise description of what you expected to happen.

Logs If applicable, add any log output related to your problem.

SDK version LaunchDarkly Ruby Client Version: [5.8.0]

Language version, developer tools Ruby on Rails Version: 6.1

OS/platform For instance, Ubuntu 16.04, Windows 10, or Android 4.0.3. If your code is running in a browser, please also include the browser type and version.

Additional context Add any other context about the problem here.

keelerm84 commented 2 days ago

I'm sorry to hear you are experiencing this issue.

The SDK works by creating multiple threads upon initialization -- one for receiving flag updates, and another for sending event data. Typical Rails deployments use a forking model where the Rails app starts up, then workers are spawned from there. Those workers receive a copy of all the memory the parent process has, but it does NOT get a copy of any spawned threads.

The effect of this is that the client will seemingly never receive updates. Please refer to the Initialize the client while using a Rails application section of our docs for common solutions.

LaunchDarkly Ruby Client Version: [5.8.0]

This version of the SDK reached EOL in January of 2022. We recommend updating to the latest supported version (v8.7.0) to ensure you are receiving all security fixes and new feature releases.

sunnybogawat commented 1 day ago

I'll attempt to improve the situation by following your recommendations and updating the version.

keelerm84 commented 15 hours ago

Please let me know if this resolves your issue, or if there is additional assistance we can provide. Thank you!

sunnybogawat commented 4 hours ago

Our current implementation using version 5.8.0 of the launchdarkly-server-sdk has been analyzed.

Below is my LD client initialisation code

module LaunchDarkly
  def self.client
    config = if Rails.env.test?
               file_source = Rails.root.join('spec', 'fixtures', 'launch-darkly-test-flags.json')
               {
                 data_source: LaunchDarkly::FileDataSource.factory(paths: [file_source], auto_update: true),
                 send_events: false
               }
             else
               {
                 connect_timeout: 10,
                 read_timeout: 10,
                 stream: true
               }
             end

    @client ||= LaunchDarkly::LDClient.new(Settings.launch_darkly[:sdk_key], LaunchDarkly::Config.new(config))
  end

  def self.close_client
    @client&.close
  end
end

The provided code is a wrapper that checks the value of a feature flag. When the flag is turned on, the function feature_permission_enabled? continues to return false until the server is restarted. Upgrading to Rails 6.1 and using Ruby 2.6.6 has led to the observed behavior. Is there anything that needs to be changed or updated to address this problem?

module LaunchDarklyWrapper
  include LaunchDarkly
  def self.feature_permission_enabled?(subdomain, uuid, feature_name)
    feature_value(subdomain, uuid, feature_name).present?
  end

  def self.feature_value(subdomain, uuid, feature_name)
    user_obj = { key: "#{subdomain}:#{uuid}", custom: { selectedEntityId: subdomain } 
    all_states = LaunchDarkly.client.all_flags_state(user_obj)
    return false unless all_states.valid?
    all_states.flag_value(feature_name)
  end
end