pgbus 0.8.2 → 0.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 479168f8521b550bb2b786766ac630374cb80de0afb9e2e26afe87a9585b466c
4
- data.tar.gz: a6c28faae035eebd4eb925d66b2914e50112728b648eaefc79473a5ff8c1513c
3
+ metadata.gz: 2fb58fdb0a36d1e0b4f4834cca266aa8771a2fa8c9419bb5aaa7e2a2e83c4725
4
+ data.tar.gz: 4b9fe002b753689046de0833c8d1112da0cf03c43a6bf2a050df989a76653bb3
5
5
  SHA512:
6
- metadata.gz: 8c8dc58764091199f12b11befacf8f218038e2f09226350b52c625ea953751a25d39d3c28e74088fb9df19fe0f7b87a055b16cd380a5dae7618198ab35dda430
7
- data.tar.gz: 5a76fa6870bd3b207da626b4a8e5317d38470e4d2447cfbb20fa01faafe88eb23156d5a3ecb0ef309b758f41a8222ee8c80fe38ec66599f5239c951b4de82325
6
+ metadata.gz: 397c99fe5da35f6e75720c27c9b1e002ce744720cadba773166674a8bf86eb88ca2151c63bfa0c042eff7ff9f2f5d7026c9d344e655f63f9a93f268f47f1225a
7
+ data.tar.gz: 33f98b8663e708d44ae5936a37fbeb3ca7966b80f358f9713d86b71ac95390a4eca0ab88c5faf2c008a203d8a88a43cdc0f73809bf2b95baadacfdc36e0a11a3
@@ -106,7 +106,8 @@ module Pgbus
106
106
  :streams_write_deadline_ms, :streams_falcon_streaming_body,
107
107
  :streams_stats_enabled, :streams_test_mode,
108
108
  :streams_orphan_sweep_interval, :streams_orphan_threshold,
109
- :streams_durable_patterns
109
+ :streams_durable_patterns,
110
+ :streams_host, :streams_port, :streams_database_url
110
111
  attr_reader :streams_default_broadcast_mode # rubocop:disable Style/AccessorGrouping
111
112
 
112
113
  # AppSignal integration (auto-loaded when ::Appsignal is defined and this is true).
@@ -193,6 +194,22 @@ module Pgbus
193
194
  @streams_enabled = true
194
195
  @streams_path = nil
195
196
  @streams_queue_prefix = "pgbus_stream"
197
+ # Streamer-only connection overrides. The Streamer's Listener owns a
198
+ # dedicated long-lived `wait_for_notify` PG connection that can't go
199
+ # through a PgBouncer in transaction mode (LISTEN/NOTIFY don't survive
200
+ # transaction-pool COMMIT boundaries — see PlanetScale's docs). Setting
201
+ # any of these overrides only the Streamer's connection options; the
202
+ # worker, dispatcher, and client publish paths keep using the regular
203
+ # `database_url` / `connection_params` (typically pooled).
204
+ #
205
+ # streams_host — override host only
206
+ # streams_port — override port only (most common case:
207
+ # pooler is on 6432, direct is 5432)
208
+ # streams_database_url — full URL override; takes precedence over
209
+ # the host/port surgicals when set
210
+ @streams_host = nil
211
+ @streams_port = nil
212
+ @streams_database_url = nil
196
213
  @streams_signed_name_secret = nil
197
214
  @streams_default_retention = 5 * 60 # 5 minutes
198
215
  @streams_retention = {}
@@ -608,6 +625,43 @@ module Pgbus
608
625
  end
609
626
  end
610
627
 
628
+ # Connection options the Streamer's dedicated LISTEN/NOTIFY PG connection
629
+ # should use. Defaults to `connection_options` (same as workers and the
630
+ # publish path). If any of `streams_database_url`, `streams_host`, or
631
+ # `streams_port` is set, the Streamer's connection is reconfigured —
632
+ # everything else keeps using the base options.
633
+ #
634
+ # The typical use is "workers go through PgBouncer, streamer goes direct":
635
+ #
636
+ # c.connects_to = { database: { writing: :pgbus } } # pooler
637
+ # c.streams_port = 5432 # direct
638
+ #
639
+ # Precedence: streams_database_url > streams_host/port override > base.
640
+ def streams_connection_options
641
+ return streams_database_url if streams_database_url
642
+
643
+ base = connection_options
644
+ return base unless streams_host || streams_port
645
+
646
+ case base
647
+ when Hash
648
+ result = base.dup
649
+ result[:host] = streams_host if streams_host
650
+ result[:port] = streams_port if streams_port
651
+ result
652
+ when String
653
+ # libpq's conninfo parser takes later key=value pairs as overrides
654
+ # for earlier ones, so we just append. Handles both URI form
655
+ # (postgres://...) and key=value form.
656
+ parts = [base]
657
+ parts << "host=#{streams_host}" if streams_host
658
+ parts << "port=#{streams_port}" if streams_port
659
+ parts.join(" ")
660
+ else
661
+ base
662
+ end
663
+ end
664
+
611
665
  private
612
666
 
613
667
  # Coerce a duration setting value to a positive Numeric.
data/lib/pgbus/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pgbus
4
- VERSION = "0.8.2"
4
+ VERSION = "0.8.4"
5
5
  end
@@ -145,7 +145,7 @@ module Pgbus
145
145
 
146
146
  def build_pg_connection
147
147
  require "pg" unless defined?(::PG::Connection)
148
- opts = @config.connection_options
148
+ opts = @config.streams_connection_options
149
149
  case opts
150
150
  when String then ::PG.connect(opts)
151
151
  when Hash then ::PG.connect(**opts)
@@ -131,21 +131,16 @@ module Pgbus
131
131
  msg = @queue.pop
132
132
  break if msg == :__stop__
133
133
 
134
- # Wake coalescing: if a WakeMessage arrives, opportunistically
135
- # drain consecutive same-stream wakes from the queue. Without
136
- # this, N broadcasts in rapid succession produce N
137
- # WakeMessages, each running its own read_after roundtrip
138
- # even though one read_after with the lowest cursor would
139
- # have pulled all N messages. The drain is bounded by the
140
- # queue's current contents — once we hit a non-Wake or a
141
- # different stream, we stop and let the regular path handle
142
- # the rest.
143
- if msg.is_a?(WakeMessage) && msg.payload.nil?
144
- wakes, trailing = drain_wakes_for(msg)
145
- wakes.each { |w| handle(w) }
146
- handle(trailing) if trailing
147
- else
148
- handle(msg)
134
+ begin
135
+ if msg.is_a?(WakeMessage) && msg.payload.nil?
136
+ wakes, trailing = drain_wakes_for(msg)
137
+ wakes.each { |w| handle(w) }
138
+ handle(trailing) if trailing
139
+ else
140
+ handle(msg)
141
+ end
142
+ ensure
143
+ release_ar_connections
149
144
  end
150
145
  end
151
146
  rescue StandardError => e
@@ -483,6 +478,19 @@ module Pgbus
483
478
  # if operators actually look at it. All failures are
484
479
  # swallowed by StreamStat.record! itself so a stats-table
485
480
  # outage cannot block the dispatcher.
481
+ # Release any AR connections the dispatcher fiber acquired during
482
+ # this iteration (typically from StreamStat.record! via BusRecord).
483
+ # Without this, the connection stays leased while the fiber parks
484
+ # on @queue.pop, blocking clear_reloadable_connections! on the
485
+ # next Rails code reload (10s wedge under rack-timeout).
486
+ def release_ar_connections
487
+ return unless defined?(::ActiveRecord::Base)
488
+
489
+ Pgbus::BusRecord.connection_handler.clear_active_connections!
490
+ rescue StandardError => e
491
+ @logger.debug { "[Pgbus::Streamer::StreamEventDispatcher] AR connection release failed: #{e.class}: #{e.message}" }
492
+ end
493
+
486
494
  def record_stat(stream_name:, event_type:, started_at:, fanout: nil, ephemeral: false)
487
495
  return unless ephemeral || @config.streams_stats_enabled
488
496
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgbus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 0.8.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikael Henriksson