stack-service-base 0.0.11 → 0.0.13

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: 5511026a19ca068d501ca1cf33053d66cc3f22925adb51a232f981948aa22f01
4
- data.tar.gz: aa6640e692ef366445b02151eeedd9235ba6d0dc5397a1d30eb419910b203626
3
+ metadata.gz: 4514d564ff933c16486b2b001e7ad75ecfe4ebe4c01294d33007c4da3d25dd33
4
+ data.tar.gz: af9e0e826a5731107674214815f239ebb2b9eead5e8c4c26921eb37c2be80c14
5
5
  SHA512:
6
- metadata.gz: 07eedfbefd18e243a62af1482d960638f4f2b8fe607af7de588f3a67e6b491b4244727163faeb34f8bda69585d8012d4e33e8f1e4116cd4f26b14454faf14002
7
- data.tar.gz: 43c0f4db64e33650a66ab08cd5b24b5763df191a4941914d541c1c7a55c713b2d287f28b4d53dca1cc221e8f6f2654c9de6c6fa8fbff13c07666972505d8dcb2
6
+ metadata.gz: 890c9c92274c9cb9d4905243655627bc9cc7ea089072fe6b15faae6ca4fe81c6475ed3a682eb9945b2c6a70a3e4a69ccbe608598e568120c16fe9192c109d1f8
7
+ data.tar.gz: bd3c39a6041eddb570018bcdf74bf60a3950bb9321a6a3d401f1f6dc3366c21d958359d34ea04a479f9d314e99746c0489df3cb02f61d212fb1f4cebe6771283
@@ -0,0 +1,99 @@
1
+ require 'sequel'
2
+ require 'async/semaphore'
3
+ # $stdout.sync = true
4
+
5
+ class FiberConnectionPool < Sequel::ConnectionPool
6
+ VALIDATION_TIMEOUT = 20
7
+ POOL_SIZE = 10
8
+
9
+ def log(msg)
10
+ return if defined? PERFORMANCE
11
+ $stdout.puts "F:#{Fiber.current.__id__} : T:#{Thread.current.__id__} : A:#{self.__id__} : #{msg}"
12
+ # LOGGER.debug :fiber_pool, msg
13
+ end
14
+
15
+ def initialize(db, opts = OPTS)
16
+ super
17
+ @allocator = ->() {
18
+ make_new(:default).tap { |conn|
19
+ log "new connection (fiber pool) #{conn}"
20
+ }
21
+ }
22
+ @stock = []
23
+ @acquired = {}
24
+ @sp = Async::Semaphore.new opts[:max_connections] || POOL_SIZE
25
+ end
26
+
27
+ def is_valid_connection?(conn)
28
+ sql = valid_connection_sql
29
+ log_connection_execute(conn, sql)
30
+ true
31
+ rescue =>e_
32
+ conn.close rescue
33
+ false
34
+ end
35
+
36
+ def hold(_server = nil)
37
+ return yield @acquired[Fiber.current] if @acquired[Fiber.current] # protect from recursion
38
+ log "hold in (fiber pool: #{__id__}) #{@stock.map{_1.__id__}}"
39
+ fiber = Fiber.current
40
+ try_count = 2
41
+
42
+ @sp.acquire do
43
+ until @acquired[fiber] &&
44
+ ( @acquired[fiber].instance_eval { @last_use_.nil? || (Time.now - @last_use_).to_i < VALIDATION_TIMEOUT } ||
45
+ is_valid_connection?(@acquired[fiber]) )
46
+
47
+ @acquired[fiber] = @stock.shift || @allocator.call
48
+ end
49
+
50
+ @acquired[fiber].instance_eval { @last_use_ = Time.now }
51
+ yield @acquired[fiber]
52
+
53
+ rescue Sequel::DatabaseDisconnectError => e
54
+ log "remove connection (fiber pool) retry(#{try_count})"
55
+ @acquired.delete(fiber)
56
+ (try_count -=1) < 0 ? raise : retry
57
+
58
+ rescue =>e
59
+ $stdout.puts e.message
60
+ $stdout.puts e.backtrace[0..10].join "\n"
61
+ log 'remove connection (fiber pool) give up'
62
+ @acquired.delete(fiber)
63
+ raise
64
+ ensure
65
+ @stock.push @acquired.delete(fiber) if @acquired[fiber]
66
+ log "hold out (fiber pool: #{__id__}) #{@stock.map{_1.__id__}}"
67
+ end
68
+ end
69
+
70
+ def size = @acquired.size
71
+ def max_size = @sp.limit
72
+ # def preconnect(_concurrent = false) = :unimplemented
73
+ def disconnect(symbol)
74
+ until @stock.empty?
75
+ log 'disconnect connection (fiber pool)'
76
+ @stock.shift.close
77
+ end
78
+ end
79
+ # def servers = []
80
+ def pool_type = :fiber # :threaded
81
+ def sync = yield
82
+ end
83
+
84
+ # Override Sequel::Database to use FiberConnectionPool by default.
85
+ Sequel::Database.prepend(Module.new do
86
+ def connection_pool_default_options = { pool_class: FiberConnectionPool }
87
+ end)
88
+
89
+ require 'sequel/adapters/postgres'
90
+
91
+ class Sequel::Postgres::Adapter
92
+ def execute_query(sql, args)
93
+ $stdout.puts "F:#{Fiber.current.__id__} : T:#{Thread.current.__id__} : A:#{self.__id__} : #{sql[0..60]}" unless defined? PERFORMANCE
94
+ @db.log_connection_yield(sql, self, args){args ? async_exec_params(sql, args) : async_exec(sql)}
95
+ rescue => e
96
+ $stdout.puts e.message
97
+ raise
98
+ end
99
+ end
@@ -1,7 +1,7 @@
1
1
  require 'console'
2
2
  require 'fiber'
3
3
 
4
- QUIET = ENV.fetch('CONSOLE_LEVEL', 'false') == 'true'
4
+ QUIET = ENV.fetch('QUIET', 'false') == 'true'
5
5
  PERFORMANCE = ENV.fetch('PERFORMANCE', 'false') == 'true'
6
6
 
7
7
  ENV['CONSOLE_LEVEL'] ||= 'all' unless QUIET || PERFORMANCE
@@ -135,6 +135,11 @@ def otl_current_span
135
135
  yield OpenTelemetry::Trace.current_span
136
136
  end
137
137
 
138
+ # OpenTelemetry::Trace.current_span.tap do |span|
139
+ # span.record_exception(e)
140
+ # span.status = OpenTelemetry::Trace::Status.error("Unhandled exception of type: #{e.class}")
141
+ # end
142
+
138
143
  def otl_traceparent_id
139
144
  return nil unless OTEL_ENABLED
140
145
 
@@ -46,6 +46,16 @@ module RackHelpers
46
46
 
47
47
  #
48
48
 
49
+ Rack.define_middleware :OTELTraceFullRequest do |env, app, opts|
50
+ request_headers = env.select { |k, _| k.start_with? 'HTTP_' }
51
+ request_body = env['rack.input'].read
52
+
53
+ response_code, response_headers, response_body = app.call(env)
54
+ response_body = response_body.read if response_body.respond_to? :read
55
+ otl_span( :Request, {request_headers: , request_body:, response_code:, response_headers: , response_body: }) {}
56
+ [response_code, response_headers, response_body]
57
+ end
58
+
49
59
  Rack.define_middleware :OTELTraceInfo do |env, app, opts|
50
60
  status, headers, body = app.call env
51
61
  if status.to_i >= 500
@@ -23,6 +23,33 @@ module StackServiceBase
23
23
  otel_initialize
24
24
  end
25
25
 
26
+ if defined? Sequel
27
+ Sequel::Database.after_initialize { _1.loggers << LOGGER }
28
+
29
+ attempts= 10
30
+ sleep_interval= 1
31
+
32
+ mod = Module.new do
33
+ define_method(:connect) do |*args, **opts, &blk|
34
+ tries = attempts
35
+ begin
36
+ super(*args, **opts, &blk)
37
+ rescue Sequel::DatabaseConnectionError, Sequel::DatabaseError => e
38
+ if (tries -= 1) > 0
39
+ LOGGER.warn "DB connect failed (#{e.message}), retrying in #{sleep_interval}s… (#{tries} left)"
40
+ sleep sleep_interval
41
+ retry
42
+ end
43
+ raise
44
+ end
45
+ end
46
+ end
47
+
48
+ Sequel.singleton_class.prepend(mod)
49
+ # ---
50
+ #
51
+ require_relative 'stack-service-base/fiber_pool'
52
+ end
26
53
  end
27
54
  end
28
55
  end
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module StackServiceBase
2
2
  class Base
3
- VERSION = '0.0.11'
3
+ VERSION = '0.0.13'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stack-service-base
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.11
4
+ version: 0.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-05-14 00:00:00.000000000 Z
11
+ date: 2025-06-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async
@@ -171,6 +171,7 @@ extensions: []
171
171
  extra_rdoc_files: []
172
172
  files:
173
173
  - lib/stack-service-base.rb
174
+ - lib/stack-service-base/fiber_pool.rb
174
175
  - lib/stack-service-base/logging.rb
175
176
  - lib/stack-service-base/open_telemetry.rb
176
177
  - lib/stack-service-base/prometheus.rb