File: connection_checkout_event_callback.rb

package info (click to toggle)
ruby-sequel 5.102.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 11,328 kB
  • sloc: ruby: 124,743; makefile: 3
file content (151 lines) | stat: -rw-r--r-- 4,706 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# frozen-string-literal: true
#
# The connection_checkout_event_callback extension modifies a database's
# connection pool to allow for a checkout event callback. This callback is
# called with the following arguments:
#
# :immediately_available :: Connection immediately available and returned
# :not_immediately_available :: Connection not immediately available
# :new_connection :: New connection created and returned
# Float :: Number of seconds waiting to acquire a connection
#
# This is a low-level extension that allows for building telemetry
# information. It doesn't implement any telemetry reporting itself. The
# main reason for recording this information is to use it to determine the
# appropriate size for the connection pool. Having too large a connection
# pool can waste resources, while having too small a connection pool can
# result in substantial time to check out a connection. In general, you
# want to use as small a pool as possible while keeping the time to
# checkout a connection low.
#
# To use the connection checkout event callback, you must first load the
# extension:
#
#   DB.extension(:connection_checkout_event_callback)
#
# By default, an empty proc is used as the callback so that loading the
# support doesn't break anything. If you are using the extension, you
# should set the callback at some point during application startup:
#
#   DB.pool.on_checkout_event = proc do |event|
#     # ...
#   end
#
# When using the sharded connection pool, the callback is also
# passed a second argument, the requested server shard (generally a
# symbol), allowing for collection of per-shard telemetry:
#
#   DB.pool.on_checkout_event = proc do |event, server|
#     # ...
#   end
#
# Note that the callback may be called currently by multiple threads.
# You should use some form of concurrency control inside the callback,
# such as a mutex or queue.
#
# Below is a brief example of usage to determine the percentage of
# connection requests where a connection was immediately available:
#
#   mutex = Mutex.new
#   total = immediates = 0
#
#   DB.pool.on_checkout_event = proc do |event|
#     case event
#     when :immediately_available
#       mutex.synchronize do
#         total += 1
#         immediates += 1
#       end
#     when :not_immediately_available
#       mutex.synchronize do
#         total += 1
#       end
#     end
#   end
#
#   immediate_percentage = lambda do
#     mutex.synchronize do
#       100.0 * immediates / total
#     end
#   end
#   
# Note that this extension only works with the timed_queue
# and sharded_timed_queue connection pools (the default
# connection pools when using Ruby 3.2+).
#
# Related modules: Sequel::ConnectionCheckoutEventCallbacks::TimedQueue,
# Sequel::ConnectionCheckoutEventCallbacks::ShardedTimedQueue

#
module Sequel
  module ConnectionCheckoutEventCallbacks
    module TimedQueue
      # The callback that is called with connection checkout events.
      attr_accessor :on_checkout_event

      private

      def available
        conn = super
        @on_checkout_event.call(conn ? :immediately_available : :not_immediately_available)
        conn
      end

      def try_make_new
        conn = super
        @on_checkout_event.call(:new_connection) if conn
        conn
      end

      def wait_until_available
        timer = Sequel.start_timer
        conn = super
        @on_checkout_event.call(Sequel.elapsed_seconds_since(timer))
        conn
      end
    end

    module ShardedTimedQueue
      # The callback that is called with connection checkout events.
      attr_accessor :on_checkout_event

      private

      def available(queue, server)
        conn = super
        @on_checkout_event.call(conn ? :immediately_available : :not_immediately_available, server)
        conn
      end

      def try_make_new(server)
        conn = super
        @on_checkout_event.call(:new_connection, server) if conn
        conn
      end

      def wait_until_available(queue, server)
        timer = Sequel.start_timer
        conn = super
        @on_checkout_event.call(Sequel.elapsed_seconds_since(timer), server)
        conn
      end
    end
  end

  default_callback = proc{}

  Database.register_extension(:connection_checkout_event_callback) do |db|
    pool = db.pool

    case pool.pool_type
    when :timed_queue
      db.pool.extend(ConnectionCheckoutEventCallbacks::TimedQueue)
    when :sharded_timed_queue
      db.pool.extend(ConnectionCheckoutEventCallbacks::ShardedTimedQueue)
    else
      raise Error, "the connection_checkout_event_callback extension is only supported when using a timed_queue connection pool"
    end

    pool.on_checkout_event ||= default_callback
  end
end