Module: RailsOtelContext::Adapters::Clickhouse
- Defined in:
- lib/rails_otel_context/adapters/clickhouse.rb
Constant Summary collapse
- CANDIDATE_METHODS =
click_house gem v1.x used :query/:select; v2.x uses :select_all/:select_one/:select_value. We list all known variants so install! picks whichever the loaded gem version defines.
%i[ select_all select_one select_value insert insert_compact insert_rows execute command query select ].freeze
- REENTRANCY_KEY =
:_rails_otel_ctx_clickhouse_instrumenting- METHOD_OP_ALIAS =
Maps compound gem method names to their SQL verb for span naming. select_all/select_one/select_value → SELECT; insert_* → INSERT.
{ 'SELECT_ALL' => 'SELECT', 'SELECT_ONE' => 'SELECT', 'SELECT_VALUE' => 'SELECT', 'INSERT_COMPACT' => 'INSERT', 'INSERT_ROWS' => 'INSERT' }.freeze
Class Method Summary collapse
- .install!(app_root:) ⇒ Object
-
.parse_table(statement) ⇒ Object
Returns [db_name, table_name] extracted from the SQL statement.
- .patch_module_for(klass, methods) ⇒ Object
-
.span_name_for(statement, method_op, table_name: nil) ⇒ Object
Derives a human-readable span name from the SQL statement.
- .target_clients ⇒ Object
Class Method Details
.install!(app_root:) ⇒ Object
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/rails_otel_context/adapters/clickhouse.rb', line 18 def install!(app_root:) begin require 'click_house' rescue LoadError # ClickHouse client gem is optional for consumers. end target_clients.each do |klass| methods = CANDIDATE_METHODS.select { |method_name| klass.method_defined?(method_name) } next if methods.empty? patch_module = patch_module_for(klass, methods) patch_module.configure(app_root: app_root) next if klass.ancestors.include?(patch_module) klass.prepend(patch_module) end end |
.parse_table(statement) ⇒ Object
Returns [db_name, table_name] extracted from the SQL statement. db_name is the schema prefix (e.g. “mailgun_analytics”), nil when absent. table_name is the bare table (e.g. “mailgun_events”), nil when not found.
89 90 91 92 93 94 95 96 97 |
# File 'lib/rails_otel_context/adapters/clickhouse.rb', line 89 def parse_table(statement) return [nil, nil] unless statement.is_a?(String) qualified = statement.match(/(?:\bFROM\b|\bINTO\b|\bUPDATE\b)\s+([\w.]+)/i)&.captures&.first return [nil, nil] unless qualified parts = qualified.split('.') parts.size > 1 ? [parts[0..-2].join('.'), parts.last] : [nil, parts.first] end |
.patch_module_for(klass, methods) ⇒ Object
47 48 49 50 51 |
# File 'lib/rails_otel_context/adapters/clickhouse.rb', line 47 def patch_module_for(klass, methods) @patch_modules ||= {} key = [klass.name, methods.sort].join(':') @patch_modules[key] ||= build_patch_module(methods) end |
.span_name_for(statement, method_op, table_name: nil) ⇒ Object
Derives a human-readable span name from the SQL statement. Follows OTel DB convention: “sql_verb table”. Falls back to “method_op clickhouse” when the statement is absent or cannot be parsed (e.g. raw ClickHouse commands with no FROM clause).
Accepts an optional pre-parsed table_name to avoid a second regex scan when the caller already holds the result of parse_table.
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/rails_otel_context/adapters/clickhouse.rb', line 70 def span_name_for(statement, method_op, table_name: nil) effective_op = METHOD_OP_ALIAS.fetch(method_op, method_op) return "#{effective_op} clickhouse" unless statement.is_a?(String) sql_verb = statement.lstrip.split(/\s/, 2).first&.upcase table_name = parse_table(statement).last if table_name.nil? if sql_verb && table_name "#{sql_verb} #{table_name}" elsif sql_verb "#{sql_verb} clickhouse" else "#{effective_op} clickhouse" end end |
.target_clients ⇒ Object
37 38 39 40 41 42 43 44 45 |
# File 'lib/rails_otel_context/adapters/clickhouse.rb', line 37 def target_clients clients = [] clients << ::ClickHouse::Client if defined?(::ClickHouse::Client) clients << ::ClickHouse::Connection if defined?(::ClickHouse::Connection) clients << ::Clickhouse::Client if defined?(::Clickhouse::Client) clients.uniq end |