fix(ruby): observability plugin compatibility — require server-sdk >= 8.11.0 and fix Rails Railtie load order#575
Open
ccschmitz-launchdarkly wants to merge 5 commits into
Conversation
The observability plugin includes LaunchDarkly::Interfaces::Plugins::Plugin at load time, but that interface was only added in launchdarkly-server-sdk 8.11.0. The gemspec allowed >= 8.0, so installing against an older SDK (e.g. 8.8.3) raised a bare 'uninitialized constant LaunchDarkly::Interfaces::Plugins' on require 'launchdarkly_observability'. - Bump the gemspec dependency to >= 8.11.0 so bundler resolves a compatible SDK - Add a load-time guard that raises an actionable LoadError naming the installed SDK version and the upgrade command, instead of the cryptic constant error - Update the README dependency note
Vadman97
approved these changes
May 29, 2026
…uire crash When the gem is required lazily after Rails has finished booting (e.g. from an autoloaded model during a request), ActiveSupport's :after_initialize load hook has already fired, so the Railtie's config.after_initialize block runs synchronously while rails.rb is still being evaluated. The block referenced ControllerHelpers and the private attach_otel_log_bridge method, both defined *after* the Railtie in the file, so the require crashed with: uninitialized constant LaunchDarklyObservability::ControllerHelpers In a normal boot (gem required before init completes) the block is deferred until the file finishes loading, which is why standard setups never hit it. - Move ControllerHelpers/ViewHelpers above the Railtie - Move the Railtie's private class methods above config.after_initialize - Add a regression test simulating the already-booted (immediate hook) condition
… state The regression test stubs a booted Rails environment, including a Rails.logger. The LD SDK's Config.default_logger returns Rails.logger whenever Rails responds to :logger, so a leaked stub logger was picked up by later tests (IntegrationTest) and crashed client.close with 'undefined method info'. This only surfaced in CI because of test ordering (random seed). - Use a real ::Logger.new(File::NULL) instead of a bare Object - Track and fully remove every constant and the :logger singleton method in teardown - Extract Rails/ActionController/ActionView stubs into helpers
The Rails e2e apps depend on launchdarkly-observability as a path gem. Bumping the
gemspec's launchdarkly-server-sdk constraint to >= 8.11.0 left their committed
Gemfile.lock files out of date, so CI's frozen bundle install failed with exit 16
('gemspecs for path gems changed, but the lockfile can't be updated'). Re-resolved
both lockfiles; the only change is the recorded dependency constraint (resolved
server-sdk 8.13.0 already satisfies it).
Rubocop's Lint/DuplicateMethods flagged the repeated inline
`Class.new { def self.include(_mod); end }` stubs as duplicate definitions on
the enclosing scope (a static-analysis limitation with anonymous classes), failing
CI on the rubocop version it resolves. Extract a single noop_includable_class helper
so the no-op include is defined once.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two compatibility fixes for the Ruby observability plugin, both surfaced by a customer trying to get
launchdarkly-observabilityworking in a Rails app (Slack thread).1. Require
launchdarkly-server-sdk >= 8.11.0The plugin includes
LaunchDarkly::Interfaces::Plugins::Pluginat load time, but that interface was only added in server-sdk 8.11.0 ("Add experimental plugin support"). The gemspec allowed>= 8.0, so bundler could resolve an older, incompatible SDK andrequire 'launchdarkly_observability'would blow up with:>= 8.0to>= 8.11.0.LoadError(naming the installed SDK version and thebundle updatecommand) instead of the bare constant error, for anyone already sitting on an old SDK.2. Fix Rails Railtie load-order crash on lazy require
After upgrading the SDK, the customer hit a second error:
The Railtie's
config.after_initializeblock registers via ActiveSupport's lazy load hooks, which run immediately if the:after_initializeevent has already fired. The customer required the gem lazily from an autoloaded model (app/models/feature.rb) during a request — i.e. after Rails had booted — so the block executed synchronously whilerails.rbwas still loading. It referencedControllerHelpersand the privateattach_otel_log_bridge, both defined below the Railtie, so the require crashed. In a normal boot the block is deferred until the file finishes loading, which is why standard setups (and our existing tests) never hit it.ControllerHelpers/ViewHelpersabove theRailtie.config.after_initialize.How did you test this change?
ControllerHelperscrash with a regression test that simulates the already-booted condition (immediateafter_initialize); it fails on the old ordering and passes on the new one.requiresucceeds on server-sdk 8.12.2/8.13.0; a simulated 8.8.3 (noPluginsinterface) raises the clearLoadError.project_idand auto-extracted SDK key) work on server-sdk 8.13.0.bundle exec rake test→ 114 runs, 0 failures, 0 errors.bundle exec rubocopon changed files → no offenses.Are there any deployment considerations?
None — no migrations or data changes. The next
launchdarkly-observabilityrelease will require server-sdk>= 8.11.0; users on older SDKs were already broken at require time and now get a clear upgrade message. The Railtie reordering is behavior-preserving for normal boots and only fixes the lazy-require case.Note
Low Risk
Targeted dependency floor, load-time guard, and file reordering with regression coverage; normal Rails boots are behavior-preserving.
Overview
This PR fixes two customer-facing compatibility issues in
launchdarkly-observability.Server SDK minimum version: The gemspec, README, and Rails e2e lockfiles now require
launchdarkly-server-sdk>= 8.11.0 (plugin APIs landed in 8.11.0).plugin.rbraises a clearLoadErrorwith version and upgrade guidance if an older SDK is loaded at require time.Rails lazy-load crash:
rails.rbis reordered soControllerHelpers,ViewHelpers, and the Railtie’s privateattach_otel_log_bridgehelpers are defined before theRailtieand itsconfig.after_initializehook. When the gem is required after boot, that hook can run synchronously during the file load; the old order causeduninitialized constant LaunchDarklyObservability::ControllerHelpers. A newrails_railtie_test.rbregression test simulates post-boot require with immediateafter_initialize.Reviewed by Cursor Bugbot for commit 6bac386. Bugbot is set up for automated code reviews on this repo. Configure here.