Skip to content

Commit 22a86fa

Browse files
committed
Make sure views are included in the hierarchical order
1 parent ac919e5 commit 22a86fa

File tree

5 files changed

+64
-2
lines changed

5 files changed

+64
-2
lines changed

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,12 @@ which will correctly handle schema dumping.
319319

320320
### Enable ActsAsHypertable
321321

322+
Create your `config/initializers/timescaledb.rb` file and add the following line:
323+
324+
```ruby
325+
ActiveRecord::Base.extend Timescaledb::ActsAsHypertable
326+
```
327+
322328
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.
323329
model:
324330

@@ -330,6 +336,27 @@ end
330336

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

339+
You may isolate your hypertables in another database, so, creating an abstract
340+
layer for your hypertables is a good idea:
341+
342+
```ruby
343+
class TimescaledbModel < ActiveRecord::Base
344+
self.abstract_class = true
345+
346+
extend Timescaledb::ActsAsHypertable
347+
348+
establish_connection :timescaledb
349+
end
350+
```
351+
352+
And then, you can inherit from this model:
353+
354+
```ruby
355+
class Event < TimescaledbModel
356+
acts_as_hypertable time_column: "time"
357+
end
358+
```
359+
333360
### Options
334361

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

436463
```ruby
437464
config.before(:suite) do
465+
438466
hypertable_models = ActiveRecord::Base.descendants.select(&:acts_as_hypertable?)
439467

440468
hypertable_models.each do |klass|

lib/timescaledb/acts_as_hypertable.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,3 @@ def acts_as_hypertable(options = {})
6262
end
6363
end
6464

65-
ActiveRecord::Base.extend Timescaledb::ActsAsHypertable

lib/timescaledb/continuous_aggregates.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,30 @@ class ContinuousAggregate < ::Timescaledb::ApplicationRecord
1414
total: count
1515
}
1616
end
17+
18+
scope :hierarchical, -> do
19+
with_recursive = <<~SQL
20+
WITH RECURSIVE caggs AS (
21+
SELECT mat_hypertable_id, parent_mat_hypertable_id, user_view_name
22+
FROM _timescaledb_catalog.continuous_agg
23+
UNION ALL
24+
SELECT continuous_agg.mat_hypertable_id, continuous_agg.parent_mat_hypertable_id, continuous_agg.user_view_name
25+
FROM _timescaledb_catalog.continuous_agg
26+
JOIN caggs ON caggs.parent_mat_hypertable_id = continuous_agg.mat_hypertable_id
27+
)
28+
SELECT * FROM caggs
29+
ORDER BY mat_hypertable_id
30+
SQL
31+
views = unscoped
32+
.select("distinct user_view_name")
33+
.from("(#{with_recursive}) as caggs")
34+
.pluck(:user_view_name)
35+
.uniq
36+
37+
views.map do |view|
38+
find_by(view_name: view)
39+
end
40+
end
1741
end
1842
ContinuousAggregates = ContinuousAggregate
1943
end

lib/timescaledb/schema_dumper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ def timescale_index_options_for(hypertable)
154154
def timescale_continuous_aggregates(stream)
155155
return unless Timescaledb::ContinuousAggregates.table_exists?
156156

157-
Timescaledb::ContinuousAggregates.all.find_each do |aggregate|
157+
Timescaledb::ContinuousAggregates.hierarchical.each do |aggregate|
158158
refresh_policies_opts = if (refresh_policy = aggregate.jobs.refresh_continuous_aggregate.first)
159159
interval = timescale_interval(refresh_policy.schedule_interval)
160160
end_offset = timescale_interval(refresh_policy.config["end_offset"])

spec/timescaledb/schema_dumper_spec.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66
identifier as label,
77
count(*) as value").group("1,2")
88
end
9+
let(:query_daily) do
10+
Event
11+
.from("event_counts")
12+
.select("time_bucket('1d', time) as time,
13+
identifier as label,
14+
sum(value) as value").group("1,2")
15+
end
916

1017
context "schema" do
1118
it "should include the timescaledb extension" do
@@ -73,6 +80,7 @@
7380
it "dumps a create_continuous_aggregate for a view in the database" do
7481
con.execute("DROP MATERIALIZED VIEW IF EXISTS event_counts")
7582
con.create_continuous_aggregate(:event_counts, query, materialized_only: true, finalized: true)
83+
con.create_continuous_aggregate(:event_daily_counts, query_daily, materialized_only: true, finalized: true)
7684

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

95103
expect(hypertable_creation).to be < caggs_creation
104+
105+
caggs_dependent_creation = dump.index('create_continuous_aggregate("event_daily_counts"')
106+
expect(caggs_creation).to be < caggs_dependent_creation
96107
end
97108

98109
describe "dumping hypertable options" do

0 commit comments

Comments
 (0)