Class: HTM::SequelConfig

Inherits:
Object
  • Object
show all
Defined in:
lib/htm/sequel_config.rb

Overview

Sequel database configuration and model loading

Uses HTM::Config for database settings. Configuration can come from:

  • Environment variables (HTM_DATABASE__URL, HTM_DATABASE__HOST, etc.)

  • Programmatic configuration via HTM.configure

Sequel is fiber-safe by design, making it ideal for async/fiber-based concurrency patterns used in HTM’s job processing.

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.dbSequel::Database? (readonly)

Database connection instance

Returns:

  • (Sequel::Database, nil)


19
20
21
# File 'lib/htm/sequel_config.rb', line 19

def db
  @db
end

Class Method Details

.build_connection_string(config) ⇒ String

Build connection string from config hash

Parameters:

  • config (Hash)

    Database configuration

Returns:

  • (String)

    PostgreSQL connection string



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/htm/sequel_config.rb', line 90

def build_connection_string(config)
  # If we have a URL already, use it
  if config[:url]
    return config[:url]
  end

  user = config[:username] || config[:user]
  password = config[:password]
  host = config[:host] || 'localhost'
  port = config[:port] || 5432
  database = config[:database]

  if password && !password.empty?
    "postgres://#{user}:#{password}@#{host}:#{port}/#{database}"
  elsif user
    "postgres://#{user}@#{host}:#{port}/#{database}"
  else
    "postgres://#{host}:#{port}/#{database}"
  end
end

.connected?Boolean

Check if connection is established and active

Returns:

  • (Boolean)


115
116
117
118
119
120
121
# File 'lib/htm/sequel_config.rb', line 115

def connected?
  return false unless @db

  @db.test_connection
rescue Sequel::DatabaseError
  false
end

.connection_statsHash

Get connection pool statistics

Returns:

  • (Hash)

    Pool statistics



160
161
162
163
164
165
166
167
168
169
170
# File 'lib/htm/sequel_config.rb', line 160

def connection_stats
  pool = @db.pool
  {
    size: pool.max_size,
    available: pool.available_connections.size,
    allocated: pool.allocated.size
  }
rescue NoMethodError
  # Fallback for connection pools that don't support these methods
  { size: @db.pool.max_size }
end

.disconnect!void

This method returns an undefined value.

Close all database connections



127
128
129
130
# File 'lib/htm/sequel_config.rb', line 127

def disconnect!
  @db&.disconnect
  @db = nil
end

.ensure_models_loaded!void

This method returns an undefined value.

Ensure models are loaded

Call this after migrations to ensure models are available



73
74
75
# File 'lib/htm/sequel_config.rb', line 73

def ensure_models_loaded!
  require_models unless @models_loaded
end

.establish_connection!(load_models: true) ⇒ Sequel::Database

Establish database connection from HTM::Config

Parameters:

  • load_models (Boolean) (defaults to: true)

    Whether to load models after connection (default: true) Set to false when running migrations on a fresh database

Returns:

  • (Sequel::Database)

    The database connection



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
# File 'lib/htm/sequel_config.rb', line 27

def establish_connection!(load_models: true)
  return @db if @db

  config = load_database_config
  connection_string = build_connection_string(config)

  # Configure Sequel with fiber-safe settings
  @db = Sequel.connect(connection_string, {
    max_connections: config[:pool] || 5,
    pool_timeout: (config[:checkout_timeout] || 5).to_i,
    # Fiber-safe settings - important for async gem compatibility
    preconnect: :concurrently,
    # Use threaded mode which works well with fibers
    single_threaded: false
  })

  # Load PostgreSQL-specific extensions for JSONB and array handling
  @db.extension :pg_json
  @db.extension :pg_array

  # Set search path and statement timeout
  @db.run("SET search_path TO public")
  @db.run("SET statement_timeout = #{config[:statement_timeout] || 30_000}")

  # Load models after connection is established (unless disabled for migrations)
  require_models if load_models && models_loadable?

  @db
end

.execute(sql) ⇒ void

This method returns an undefined value.

Run raw SQL

Parameters:

  • sql (String)

    SQL to execute



177
178
179
# File 'lib/htm/sequel_config.rb', line 177

def execute(sql)
  @db.run(sql)
end

.load_database_configHash

Load database configuration from HTM::Config

Returns:

  • (Hash)

    Database configuration hash



81
82
83
# File 'lib/htm/sequel_config.rb', line 81

def load_database_config
  HTM.config.database_config
end

.models_loadable?Boolean

Check if models can be loaded (tables exist)

Returns:

  • (Boolean)


61
62
63
64
65
66
# File 'lib/htm/sequel_config.rb', line 61

def models_loadable?
  return false unless @db
  @db.table_exists?(:robots)
rescue Sequel::DatabaseError
  false
end

.select_value(sql) ⇒ Object

Select a single value

Parameters:

  • sql (String)

    SQL query

Returns:

  • (Object)

    The value



186
187
188
# File 'lib/htm/sequel_config.rb', line 186

def select_value(sql)
  @db[sql].first&.values&.first
end

.verify_extensions!true

Verify required extensions are available

Returns:

  • (true)

Raises:

  • (RuntimeError)

    if required extensions are missing



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/htm/sequel_config.rb', line 137

def verify_extensions!
  required_extensions = {
    'vector' => 'pgvector extension',
    'pg_trgm' => 'PostgreSQL trigram extension'
  }

  missing = []
  required_extensions.each do |ext, name|
    result = @db["SELECT COUNT(*) AS cnt FROM pg_extension WHERE extname = ?", ext].first
    missing << name if result[:cnt].to_i.zero?
  end

  if missing.any?
    raise "Missing required PostgreSQL extensions: #{missing.join(', ')}"
  end

  true
end