From 1619e60ca51e0de1b28d855d6d30c2cc4bed1371 Mon Sep 17 00:00:00 2001 From: Sean Mallia Date: Tue, 13 Oct 2026 15:51:30 -0400 Subject: [PATCH 1/2] feat(rails): expose flag_definition_cache_provider in PostHog.init block The Rails InitConfig is an explicit allowlist of setters and was never extended when the distributed flag definition cache landed, so Rails apps could only pass the provider via the hash form of PostHog.init. Add the setter, document it in the generated initializer, and cover the block form with specs. Co-Authored-By: Claude Fable 5.1 --- .../rails-flag-definition-cache-provider.md | 5 +++ posthog-rails/examples/posthog.rb | 10 ++++++ .../generators/posthog/templates/posthog.rb | 10 ++++++ posthog-rails/lib/posthog/rails/railtie.rb | 10 ++++++ public_api_snapshot.txt | 1 + spec/posthog/rails/railtie_spec.rb | 31 +++++++++++++++++++ 6 files changed, 67 insertions(+) create mode 100644 .changeset/rails-flag-definition-cache-provider.md diff --git a/.changeset/rails-flag-definition-cache-provider.md b/.changeset/rails-flag-definition-cache-provider.md new file mode 100644 index 0000000..ffd834c --- /dev/null +++ b/.changeset/rails-flag-definition-cache-provider.md @@ -0,0 +1,5 @@ +--- +"posthog-rails": minor +--- + +Add `config.flag_definition_cache_provider` to the `PostHog.init` block so Rails apps can share feature flag definitions across processes via an external cache (for example Redis). The generated initializer now documents the option. diff --git a/posthog-rails/examples/posthog.rb b/posthog-rails/examples/posthog.rb index 15ff337..7b832d0 100644 --- a/posthog-rails/examples/posthog.rb +++ b/posthog-rails/examples/posthog.rb @@ -132,6 +132,16 @@ # Feature flag request timeout in seconds (default: 3) config.feature_flag_request_timeout_seconds = 3 + # Distributed flag definition cache (optional, for local evaluation) + # Share flag definitions across processes (Puma workers, Sidekiq, pods) via an + # external cache so only one process polls PostHog. Pass any object implementing + # the PostHog::FlagDefinitionCacheProvider interface. A Redis implementation is + # in the posthog-ruby repo under examples/redis_flag_cache.rb. + # config.flag_definition_cache_provider = RedisFlagCache.new( + # Redis.new(url: ENV.fetch('REDIS_URL')), + # service_key: "#{Rails.application.class.module_parent_name.underscore}-#{Rails.env}" + # ) + # Error callback - called when PostHog encounters an error # config.on_error = proc { |status, message| # Rails.logger.error("[PostHog] Error #{status}: #{message}") diff --git a/posthog-rails/lib/generators/posthog/templates/posthog.rb b/posthog-rails/lib/generators/posthog/templates/posthog.rb index 86e0969..b188734 100644 --- a/posthog-rails/lib/generators/posthog/templates/posthog.rb +++ b/posthog-rails/lib/generators/posthog/templates/posthog.rb @@ -134,6 +134,16 @@ # Feature flag request timeout in seconds (default: 3) config.feature_flag_request_timeout_seconds = 3 + # Distributed flag definition cache (optional, for local evaluation) + # Share flag definitions across processes (Puma workers, Sidekiq, pods) via an + # external cache so only one process polls PostHog. Pass any object implementing + # the PostHog::FlagDefinitionCacheProvider interface. A Redis implementation is + # in the posthog-ruby repo under examples/redis_flag_cache.rb. + # config.flag_definition_cache_provider = RedisFlagCache.new( + # Redis.new(url: ENV.fetch('REDIS_URL')), + # service_key: "#{Rails.application.class.module_parent_name.underscore}-#{Rails.env}" + # ) + # Error callback - called when PostHog encounters an error # config.on_error = proc { |status, message| # Rails.logger.error("[PostHog] Error #{status}: #{message}") diff --git a/posthog-rails/lib/posthog/rails/railtie.rb b/posthog-rails/lib/posthog/rails/railtie.rb index dfa4bcd..4dcadc7 100644 --- a/posthog-rails/lib/posthog/rails/railtie.rb +++ b/posthog-rails/lib/posthog/rails/railtie.rb @@ -347,6 +347,16 @@ def before_send=(value) @base_options[:before_send] = value end + # An object implementing the {PostHog::FlagDefinitionCacheProvider} interface so multiple + # Rails processes (Puma workers, Sidekiq, Kubernetes pods) can share flag definitions + # through an external cache instead of each polling PostHog independently. + # + # @param value [Object, nil] + # @return [Object, nil] + def flag_definition_cache_provider=(value) + @base_options[:flag_definition_cache_provider] = value + end + # @return [Hash] Core SDK options suitable for {PostHog::Client.new}. def to_client_options @base_options diff --git a/public_api_snapshot.txt b/public_api_snapshot.txt index 278bd8b..4930d38 100644 --- a/public_api_snapshot.txt +++ b/public_api_snapshot.txt @@ -334,6 +334,7 @@ instance_method PostHog::Rails::InitConfig#before_send=(value) instance_method PostHog::Rails::InitConfig#compress_request=(value) instance_method PostHog::Rails::InitConfig#feature_flag_request_timeout_seconds=(value) instance_method PostHog::Rails::InitConfig#feature_flags_polling_interval=(value) +instance_method PostHog::Rails::InitConfig#flag_definition_cache_provider=(value) instance_method PostHog::Rails::InitConfig#host=(value) instance_method PostHog::Rails::InitConfig#max_queue_size=(value) instance_method PostHog::Rails::InitConfig#on_error=(value) diff --git a/spec/posthog/rails/railtie_spec.rb b/spec/posthog/rails/railtie_spec.rb index c216db9..67242cc 100644 --- a/spec/posthog/rails/railtie_spec.rb +++ b/spec/posthog/rails/railtie_spec.rb @@ -267,6 +267,37 @@ ) end + it 'passes a flag definition cache provider from the init block to the client' do + stub_request(:get, %r{https://us\.i\.posthog\.com/flags/definitions}) + .to_return(status: 200, body: { flags: [], group_type_mapping: {}, cohorts: {} }.to_json) + provider = double( + 'FlagCache', + flag_definitions: nil, + should_fetch_flag_definitions?: true, + on_flag_definitions_received: nil, + shutdown: nil + ) + + PostHog.init do |config| + config.api_key = 'phc_test' + config.secret_key = 'phs_test' + config.flag_definition_cache_provider = provider + config.test_mode = true + end + + expect(provider).to have_received(:on_flag_definitions_received).with(hash_including(:flags)) + end + + it 'validates the flag definition cache provider interface' do + expect do + PostHog.init do |config| + config.api_key = 'phc_test' + config.flag_definition_cache_provider = Object.new + config.test_mode = true + end + end.to raise_error(ArgumentError, /flag_definitions/) + end + it 'uses the Rails user agent for event and feature flag requests' do batch_request = stub_request(:post, 'https://us.i.posthog.com/batch/').to_return(status: 200, body: '{}') flags_request = stub_request(:post, 'https://us.i.posthog.com/flags/?v=2') From ed58e1027ad2537fa12cb5065f55b380948ee224 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Tue, 13 Oct 2026 14:24:16 -0700 Subject: [PATCH 2/2] test(rails): shut down cache provider poller after init spec --- spec/posthog/rails/railtie_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/posthog/rails/railtie_spec.rb b/spec/posthog/rails/railtie_spec.rb index 67242cc..9be00c6 100644 --- a/spec/posthog/rails/railtie_spec.rb +++ b/spec/posthog/rails/railtie_spec.rb @@ -286,6 +286,8 @@ end expect(provider).to have_received(:on_flag_definitions_received).with(hash_including(:flags)) + ensure + PostHog.client&.shutdown end it 'validates the flag definition cache provider interface' do