Skip to content

Commit

Permalink
Make sure views are included in the hierarchical order (#72)
Browse files Browse the repository at this point in the history
* Make sure views are included in the hierarchical order
* Remove ActsAsHypertable as a default module on ActiveRecord::Base.
  • Loading branch information
jonatas authored Sep 6, 2024
1 parent ac919e5 commit 6709802
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 3 deletions.
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

0 comments on commit 6709802

Please sign in to comment.