Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Skip schema dumper when extension is not available #66

Merged
merged 1 commit into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion bin/console
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ def uri_from_test
ENV['PG_URI_TEST']
end

ActiveRecord::Base.establish_connection(ARGV[0] || uri_from_test)
uri = ARGV[0] || uri_from_test
ActiveRecord::Base.establish_connection(uri)
Timescaledb.establish_connection(uri)

Timescaledb::Hypertable.find_each do |hypertable|
class_name = hypertable.hypertable_name.singularize.camelize
Expand Down
9 changes: 9 additions & 0 deletions lib/timescaledb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,20 @@
require_relative 'timescaledb/stats'
require_relative 'timescaledb/stats_report'
require_relative 'timescaledb/migration_helpers'
require_relative 'timescaledb/extension'
require_relative 'timescaledb/version'

module Timescaledb
module_function

def connection
Connection.instance
end

def extension
Extension
end

def chunks
Chunk.all
end
Expand Down
13 changes: 12 additions & 1 deletion lib/timescaledb/connection.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
require 'singleton'

module Timescaledb
# Minimal connection setup for Timescaledb directly with the PG.
# The concept is use a singleton component that can query
# independently of the ActiveRecord::Base connections.
# This is useful for the extension and hypertable metadata.
# It can also #use_connection from active record if needed.
class Connection
include Singleton

Expand Down Expand Up @@ -34,10 +39,16 @@ def connected?
[email protected]?
end

# Override the connection with a raw PG connection.
# @param [PG::Connection] connection The raw PG connection.
def use_connection connection
@connection = connection
end

private

def connection
@connection ||= PG.connect(@config)
end
end
end
end
13 changes: 9 additions & 4 deletions lib/timescaledb/connection_handling.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
module Timescaledb
class ConnectionNotEstablishedError < StandardError; end

# @param [String] config The postgres connection string.
module_function

# @param [String] config with the postgres connection string.
def establish_connection(config)
Connection.instance.config = config
end
module_function :establish_connection

# @param [PG::Connection] to use it directly from a raw connection
def use_connection conn
Connection.instance.use_connection conn
end

def connection
raise ConnectionNotEstablishedError.new unless Connection.instance.connected?

Connection.instance
end
module_function :connection
end
end
23 changes: 23 additions & 0 deletions lib/timescaledb/extension.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module Timescaledb

# Provides metadata around the extension in the database
module Extension
module_function
# @return String version of the timescaledb extension
def version
@version ||= Timescaledb.connection.query_first(<<~SQL)&.version
SELECT extversion as version
FROM pg_extension
WHERE extname = 'timescaledb'
SQL
end

def installed?
version.present?
end

def update!
Timescaledb.connection.execute('ALTER EXTENSION timescaledb UPDATE')
end
end
end
23 changes: 19 additions & 4 deletions lib/timescaledb/schema_dumper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,29 @@ module Timescaledb
# * retention policies
# * continuous aggregates
# * compression settings
# It also ignores Timescale related schemas when dumping the schema.
# It also ignores dumping options as extension is not installed or no hypertables are available.
module SchemaDumper
def tables(stream)
super # This will call #table for each table in the database
return unless Timescaledb::Hypertable.table_exists?

timescale_hypertables(stream)
timescale_retention_policies(stream)
timescale_continuous_aggregates(stream) # Define these before any Scenic views that might use them
if exports_timescaledb_metadata?
timescale_hypertables(stream)
timescale_retention_policies(stream)
timescale_continuous_aggregates(stream) # Define these before any Scenic views that might use them
end
end

# Ignore dumps in case DB is not eligible for TimescaleDB metadata.
# @return [Boolean] true if the extension is installed and hypertables are available, otherwise false.
private def exports_timescaledb_metadata?
# Note it's safe to use the raw connection here because we're only reading from the database
# and not modifying it. We're also on the same connection pool as ActiveRecord::Base.
# The dump process also runs standalone, so we don't need to worry about the connection being
# used elsewhere.
Timescaledb.use_connection @connection.raw_connection

Timescaledb.extension.installed? && Timescaledb.hypertables.any?
end

# Ignores Timescale related schemas when dumping the schema
Expand Down
39 changes: 39 additions & 0 deletions spec/timescaledb/connection_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
RSpec.describe Timescaledb do
describe '.establish_connection' do
it 'returns a PG::Connection object' do
expect do
Timescaledb.establish_connection(ENV['PG_URI_TEST'])
end.to_not raise_error
end
end

describe ::Timescaledb::Connection do
subject(:connection) { Timescaledb::Connection.instance }

it 'returns a Connection object' do
is_expected.to be_a(Timescaledb::Connection)
end

it 'has fast access to the connection' do
expect(connection.send(:connection)).to be_a(PG::Connection)
end

describe '#connected?' do
it { expect(connection.connected?).to be_truthy }
end

describe '#query_first' do
let(:sql) { "select 1 as one" }
subject(:result) { connection.query_first(sql) }

it { expect(result).to be_a(OpenStruct) }
end

describe '#query' do
let(:sql) { "select 1 as one" }
subject(:result) { connection.query(sql) }

it { expect(result).to eq([OpenStruct.new({"one" => "1"})]) }
end
end
end
10 changes: 10 additions & 0 deletions spec/timescaledb_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
expect(Timescaledb::VERSION).not_to be nil
end

describe ".extension" do
describe ".installed?" do
it { expect(Timescaledb.extension.installed?).to be_truthy }
end

describe ".version" do
it { expect(Timescaledb.extension.version).not_to be_empty }
end
end

describe ".chunks" do
subject { Timescaledb.chunks }

Expand Down
Loading