Class: ClickhouseNative::Pool

Inherits:
Object
  • Object
show all
Defined in:
lib/clickhouse_native/pool.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(host:, port:, database: "default", user: "default", password: "", compression: :none, logger: nil, settings: {}, pool_size: 5, pool_timeout: 5, ping_before_query: true, tcp_keepalive: true, retry_timeout: 1) ⇒ Pool

Returns a new instance of Pool.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/clickhouse_native/pool.rb', line 9

def initialize(host:, port:, database: "default", user: "default", password: "",
               compression: :none, logger: nil, settings: {},
               pool_size: 5, pool_timeout: 5,
               ping_before_query: true, tcp_keepalive: true, retry_timeout: 1)
  @host = host
  @port = port
  @database = database
  client_kwargs = {
    host:, port:, database:, user:, password:, compression:, logger:, settings:,
    ping_before_query:, tcp_keepalive:, retry_timeout:
  }
  @pool = ConnectionPool.new(size: pool_size, timeout: pool_timeout) do
    Client.new(**client_kwargs)
  end
end

Instance Attribute Details

#databaseObject (readonly)

Returns the value of attribute database.



7
8
9
# File 'lib/clickhouse_native/pool.rb', line 7

def database
  @database
end

#hostObject (readonly)

Returns the value of attribute host.



7
8
9
# File 'lib/clickhouse_native/pool.rb', line 7

def host
  @host
end

#portObject (readonly)

Returns the value of attribute port.



7
8
9
# File 'lib/clickhouse_native/pool.rb', line 7

def port
  @port
end

Instance Method Details

#describe_table(table, db_name: nil) ⇒ Object



91
92
93
# File 'lib/clickhouse_native/pool.rb', line 91

def describe_table(table, db_name: nil)
  with { |c| c.describe_table(table, db_name:) }
end

#execute(sql, **opts) ⇒ Object



63
64
65
# File 'lib/clickhouse_native/pool.rb', line 63

def execute(sql, **opts)
  with { |c| c.execute(sql, **opts) }
end

#insert(table, rows, **opts) ⇒ Object



79
80
81
# File 'lib/clickhouse_native/pool.rb', line 79

def insert(table, rows, **opts)
  with { |c| c.insert(table, rows, **opts) }
end

#pingObject



83
84
85
# File 'lib/clickhouse_native/pool.rb', line 83

def ping
  with(&:ping)
end

#query(sql, **opts) ⇒ Object



67
68
69
# File 'lib/clickhouse_native/pool.rb', line 67

def query(sql, **opts)
  with { |c| c.query(sql, **opts) }
end

#query_each(sql, **opts, &block) ⇒ Object



71
72
73
# File 'lib/clickhouse_native/pool.rb', line 71

def query_each(sql, **opts, &block)
  with { |c| c.query_each(sql, **opts, &block) }
end

#query_value(sql, **opts) ⇒ Object



75
76
77
# File 'lib/clickhouse_native/pool.rb', line 75

def query_value(sql, **opts)
  with { |c| c.query_value(sql, **opts) }
end

#server_versionObject



87
88
89
# File 'lib/clickhouse_native/pool.rb', line 87

def server_version
  with(&:server_version)
end

#withObject

On exception, discard the client rather than reuse it: an error path leaves the socket in an unknown state. The C++ binding issues ResetConnection, but a subsequent send can still surface buffered protocol errors from the prior aborted operation — those get attributed to whatever SQL we tried next, producing misleading log lines and re-raises in unrelated code. A fresh socket + handshake is cheap relative to debugging that.

ConnectionError gets one automatic retry: pooled connections that have been idle long enough for the server / an LB to FIN them surface as "closed" on the very next recv (errno is whatever stale value was left in the thread — "closed: Success", "closed: Operation now in progress", etc. all mean the same recv()==0). Discarding and re-checking out lands a fresh socket and the operation succeeds. The retry only triggers when the dead-connection error fired before any data was sent, so write operations don't risk double-execution.

This is the backstop: with ping_before_query on (the default) the driver already pings and transparently reconnects a dead socket before running the query, so most stale connections never surface as a ConnectionError here at all. This retry still covers the residual race (socket dies between the ping and the query).



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/clickhouse_native/pool.rb', line 47

def with
  attempts = 0
  begin
    @pool.with do |client|
      yield client
    rescue
      @pool.discard_current_connection(&:close)
      raise
    end
  rescue ConnectionError
    attempts += 1
    retry if attempts == 1
    raise
  end
end