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

Make sure views are included in the hierarchical order #72

Merged
merged 3 commits into from
Sep 6, 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
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,12 @@ which will correctly handle schema dumping.

### Enable ActsAsHypertable

Create your `config/initializers/timescaledb.rb` file and add the following line:

```ruby
ActiveRecord::Base.extend Timescaledb::ActsAsHypertable
```

You can declare a Rails model as a Hypertable by invoking the `acts_as_hypertable` macro. This macro extends your existing model with timescaledb-related functionality.
model:

Expand All @@ -330,6 +336,39 @@ end

By default, ActsAsHypertable assumes a record's _time_column_ is called `created_at`.

You may isolate your hypertables in another database, so, creating an abstract
layer for your hypertables is a good idea:

```ruby
class Hypertable < ActiveRecord::Base
self.abstract_class = true

extend Timescaledb::ActsAsHypertable

establish_connection :timescaledb
end
```

And then, you can inherit from this model:

```ruby
class Event < Hypertable
acts_as_hypertable time_column: "time"
end
```

Or you can include only when you're going to use them:

```ruby
class Event < ActiveRecord::Base
extend Timescaledb::ActsAsHypertable

establish_connection :timescaledb

acts_as_hypertable time_column: "time"
end
```

### Options

If you are using a different time_column name, you can specify it as follows when invoking the `acts_as_hypertable` macro:
Expand Down Expand Up @@ -435,6 +474,7 @@ define in `spec/rspec_helper.rb`:

```ruby
config.before(:suite) do

hypertable_models = ActiveRecord::Base.descendants.select(&:acts_as_hypertable?)

hypertable_models.each do |klass|
Expand Down
1 change: 0 additions & 1 deletion lib/timescaledb/acts_as_hypertable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,3 @@ def acts_as_hypertable(options = {})
end
end

ActiveRecord::Base.extend Timescaledb::ActsAsHypertable
24 changes: 24 additions & 0 deletions lib/timescaledb/continuous_aggregates.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,30 @@ class ContinuousAggregate < ::Timescaledb::ApplicationRecord
total: count
}
end

scope :hierarchical, -> do
with_recursive = <<~SQL
WITH RECURSIVE caggs AS (
SELECT mat_hypertable_id, parent_mat_hypertable_id, user_view_name
FROM _timescaledb_catalog.continuous_agg
UNION ALL
SELECT continuous_agg.mat_hypertable_id, continuous_agg.parent_mat_hypertable_id, continuous_agg.user_view_name
FROM _timescaledb_catalog.continuous_agg
JOIN caggs ON caggs.parent_mat_hypertable_id = continuous_agg.mat_hypertable_id
)
SELECT * FROM caggs
ORDER BY mat_hypertable_id
SQL
views = unscoped
.select("distinct user_view_name")
.from("(#{with_recursive}) as caggs")
.pluck(:user_view_name)
.uniq

views.map do |view|
find_by(view_name: view)
end
end
end
ContinuousAggregates = ContinuousAggregate
end
2 changes: 1 addition & 1 deletion lib/timescaledb/schema_dumper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ def timescale_index_options_for(hypertable)
def timescale_continuous_aggregates(stream)
return unless Timescaledb::ContinuousAggregates.table_exists?

Timescaledb::ContinuousAggregates.all.find_each do |aggregate|
Timescaledb::ContinuousAggregates.hierarchical.each do |aggregate|
refresh_policies_opts = if (refresh_policy = aggregate.jobs.refresh_continuous_aggregate.first)
interval = timescale_interval(refresh_policy.schedule_interval)
end_offset = timescale_interval(refresh_policy.config["end_offset"])
Expand Down
4 changes: 3 additions & 1 deletion spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
require_relative "support/active_record/models"
require_relative "support/active_record/schema"

Dotenv.load!
Dotenv.load! if File.exists?(".env")

# Establish a connection for testing

ActiveRecord::Base.establish_connection(ENV['PG_URI_TEST'])
Timescaledb.establish_connection(ENV['PG_URI_TEST'])
Expand Down
2 changes: 2 additions & 0 deletions spec/support/active_record/models.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
ActiveRecord::Base.extend Timescaledb::ActsAsHypertable

class Event < ActiveRecord::Base
self.primary_key = "identifier"

Expand Down
11 changes: 11 additions & 0 deletions spec/timescaledb/schema_dumper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
identifier as label,
count(*) as value").group("1,2")
end
let(:query_daily) do
Event
.from("event_counts")
.select("time_bucket('1d', time) as time,
sum(value) as value").group("1")
end

context "schema" do
it "should include the timescaledb extension" do
Expand Down Expand Up @@ -71,8 +77,10 @@
end

it "dumps a create_continuous_aggregate for a view in the database" do
con.execute("DROP MATERIALIZED VIEW IF EXISTS event_daily_counts")
con.execute("DROP MATERIALIZED VIEW IF EXISTS event_counts")
con.create_continuous_aggregate(:event_counts, query, materialized_only: true, finalized: true)
con.create_continuous_aggregate(:event_daily_counts, query_daily, materialized_only: true, finalized: true)

if defined?(Scenic)
Scenic.load # Normally this happens in a railtie, but we aren't loading a full rails env here
Expand All @@ -93,6 +101,9 @@
caggs_creation = dump.index('create_continuous_aggregate("event_counts"')

expect(hypertable_creation).to be < caggs_creation

caggs_dependent_creation = dump.index('create_continuous_aggregate("event_daily_counts"')
expect(caggs_creation).to be < caggs_dependent_creation
end

describe "dumping hypertable options" do
Expand Down
Loading