timescaledb 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ruby-version +1 -0
- data/.tool-versions +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +5 -2
- data/Gemfile.scenic +7 -0
- data/Gemfile.scenic.lock +121 -0
- data/README.md +14 -4
- data/Rakefile +7 -1
- data/bin/setup +2 -0
- data/lib/timescale/compression_settings.rb +2 -1
- data/lib/timescale/continuous_aggregates.rb +2 -1
- data/lib/timescale/dimensions.rb +2 -3
- data/lib/timescale/hypertable.rb +4 -4
- data/lib/timescale/job.rb +2 -5
- data/lib/timescale/job_stats.rb +2 -3
- data/lib/timescale/migration_helpers.rb +31 -1
- data/lib/timescale/scenic/adapter.rb +55 -0
- data/lib/timescale/schema_dumper.rb +83 -19
- data/lib/timescale/version.rb +1 -1
- data/lib/timescale.rb +12 -0
- data/timescale.gemspec +2 -1
- metadata +22 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7553bac6eee450d1067b66043ef721e77eac03c58dc0fbbfafb42fe1c4330600
|
|
4
|
+
data.tar.gz: d67f848537997b9558af4f174fe2289517f2f6e593b03aafc3ef47097bfac496
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 16792f5b215cdd992ecb006bce4492b939fdefa11dae3e32c2eb3d28645c5386a471b307aae321f1b114ce968fd9550648a4727d288fa9858a59c7a935678c7a
|
|
7
|
+
data.tar.gz: dfb95e8090ce29044889efac22385b2b390825f64f1da7a33a205a1549b89a7b039f076990e9721df1a68dc56d11c30846f391fe0d051ea0837d6bffe8a9eb48
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.7.1
|
data/.tool-versions
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ruby 2.7.1
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
timescaledb (0.1.
|
|
4
|
+
timescaledb (0.1.5)
|
|
5
5
|
activerecord
|
|
6
|
+
activesupport
|
|
6
7
|
pg (~> 1.2)
|
|
7
8
|
|
|
8
9
|
GEM
|
|
@@ -27,6 +28,7 @@ GEM
|
|
|
27
28
|
database_cleaner-core (2.0.1)
|
|
28
29
|
diff-lcs (1.4.4)
|
|
29
30
|
dotenv (2.7.6)
|
|
31
|
+
gemika (0.6.1)
|
|
30
32
|
i18n (1.8.10)
|
|
31
33
|
concurrent-ruby (~> 1.0)
|
|
32
34
|
method_source (1.0.0)
|
|
@@ -62,6 +64,7 @@ PLATFORMS
|
|
|
62
64
|
DEPENDENCIES
|
|
63
65
|
database_cleaner-active_record
|
|
64
66
|
dotenv
|
|
67
|
+
gemika
|
|
65
68
|
pry
|
|
66
69
|
rake (~> 12.0)
|
|
67
70
|
rspec (~> 3.0)
|
|
@@ -69,4 +72,4 @@ DEPENDENCIES
|
|
|
69
72
|
timescaledb!
|
|
70
73
|
|
|
71
74
|
BUNDLED WITH
|
|
72
|
-
2.
|
|
75
|
+
2.2.31
|
data/Gemfile.scenic
ADDED
data/Gemfile.scenic.lock
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
timescaledb (0.1.4)
|
|
5
|
+
activerecord
|
|
6
|
+
activesupport
|
|
7
|
+
pg (~> 1.2)
|
|
8
|
+
|
|
9
|
+
GEM
|
|
10
|
+
remote: https://rubygems.org/
|
|
11
|
+
specs:
|
|
12
|
+
actionpack (6.1.4.1)
|
|
13
|
+
actionview (= 6.1.4.1)
|
|
14
|
+
activesupport (= 6.1.4.1)
|
|
15
|
+
rack (~> 2.0, >= 2.0.9)
|
|
16
|
+
rack-test (>= 0.6.3)
|
|
17
|
+
rails-dom-testing (~> 2.0)
|
|
18
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
|
19
|
+
actionview (6.1.4.1)
|
|
20
|
+
activesupport (= 6.1.4.1)
|
|
21
|
+
builder (~> 3.1)
|
|
22
|
+
erubi (~> 1.4)
|
|
23
|
+
rails-dom-testing (~> 2.0)
|
|
24
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
|
25
|
+
activemodel (6.1.4.1)
|
|
26
|
+
activesupport (= 6.1.4.1)
|
|
27
|
+
activerecord (6.1.4.1)
|
|
28
|
+
activemodel (= 6.1.4.1)
|
|
29
|
+
activesupport (= 6.1.4.1)
|
|
30
|
+
activesupport (6.1.4.1)
|
|
31
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
32
|
+
i18n (>= 1.6, < 2)
|
|
33
|
+
minitest (>= 5.1)
|
|
34
|
+
tzinfo (~> 2.0)
|
|
35
|
+
zeitwerk (~> 2.3)
|
|
36
|
+
builder (3.2.4)
|
|
37
|
+
coderay (1.1.3)
|
|
38
|
+
concurrent-ruby (1.1.9)
|
|
39
|
+
crass (1.0.6)
|
|
40
|
+
database_cleaner-active_record (2.0.1)
|
|
41
|
+
activerecord (>= 5.a)
|
|
42
|
+
database_cleaner-core (~> 2.0.0)
|
|
43
|
+
database_cleaner-core (2.0.1)
|
|
44
|
+
diff-lcs (1.4.4)
|
|
45
|
+
dotenv (2.7.6)
|
|
46
|
+
erubi (1.10.0)
|
|
47
|
+
gemika (0.6.1)
|
|
48
|
+
i18n (1.8.11)
|
|
49
|
+
concurrent-ruby (~> 1.0)
|
|
50
|
+
loofah (2.12.0)
|
|
51
|
+
crass (~> 1.0.2)
|
|
52
|
+
nokogiri (>= 1.5.9)
|
|
53
|
+
method_source (1.0.0)
|
|
54
|
+
mini_portile2 (2.6.1)
|
|
55
|
+
minitest (5.14.4)
|
|
56
|
+
nokogiri (1.12.5)
|
|
57
|
+
mini_portile2 (~> 2.6.1)
|
|
58
|
+
racc (~> 1.4)
|
|
59
|
+
nokogiri (1.12.5-x86_64-darwin)
|
|
60
|
+
racc (~> 1.4)
|
|
61
|
+
pg (1.2.3)
|
|
62
|
+
pry (0.14.1)
|
|
63
|
+
coderay (~> 1.1)
|
|
64
|
+
method_source (~> 1.0)
|
|
65
|
+
racc (1.6.0)
|
|
66
|
+
rack (2.2.3)
|
|
67
|
+
rack-test (1.1.0)
|
|
68
|
+
rack (>= 1.0, < 3)
|
|
69
|
+
rails-dom-testing (2.0.3)
|
|
70
|
+
activesupport (>= 4.2.0)
|
|
71
|
+
nokogiri (>= 1.6)
|
|
72
|
+
rails-html-sanitizer (1.4.2)
|
|
73
|
+
loofah (~> 2.3)
|
|
74
|
+
railties (6.1.4.1)
|
|
75
|
+
actionpack (= 6.1.4.1)
|
|
76
|
+
activesupport (= 6.1.4.1)
|
|
77
|
+
method_source
|
|
78
|
+
rake (>= 0.13)
|
|
79
|
+
thor (~> 1.0)
|
|
80
|
+
rake (12.3.3)
|
|
81
|
+
rspec (3.10.0)
|
|
82
|
+
rspec-core (~> 3.10.0)
|
|
83
|
+
rspec-expectations (~> 3.10.0)
|
|
84
|
+
rspec-mocks (~> 3.10.0)
|
|
85
|
+
rspec-core (3.10.1)
|
|
86
|
+
rspec-support (~> 3.10.0)
|
|
87
|
+
rspec-expectations (3.10.1)
|
|
88
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
89
|
+
rspec-support (~> 3.10.0)
|
|
90
|
+
rspec-its (1.3.0)
|
|
91
|
+
rspec-core (>= 3.0.0)
|
|
92
|
+
rspec-expectations (>= 3.0.0)
|
|
93
|
+
rspec-mocks (3.10.2)
|
|
94
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
95
|
+
rspec-support (~> 3.10.0)
|
|
96
|
+
rspec-support (3.10.3)
|
|
97
|
+
scenic (1.5.4)
|
|
98
|
+
activerecord (>= 4.0.0)
|
|
99
|
+
railties (>= 4.0.0)
|
|
100
|
+
thor (1.1.0)
|
|
101
|
+
tzinfo (2.0.4)
|
|
102
|
+
concurrent-ruby (~> 1.0)
|
|
103
|
+
zeitwerk (2.5.1)
|
|
104
|
+
|
|
105
|
+
PLATFORMS
|
|
106
|
+
ruby
|
|
107
|
+
x86_64-darwin-20
|
|
108
|
+
|
|
109
|
+
DEPENDENCIES
|
|
110
|
+
database_cleaner-active_record
|
|
111
|
+
dotenv
|
|
112
|
+
gemika
|
|
113
|
+
pry
|
|
114
|
+
rake (~> 12.0)
|
|
115
|
+
rspec (~> 3.0)
|
|
116
|
+
rspec-its
|
|
117
|
+
scenic
|
|
118
|
+
timescaledb!
|
|
119
|
+
|
|
120
|
+
BUNDLED WITH
|
|
121
|
+
2.2.33
|
data/README.md
CHANGED
|
@@ -211,7 +211,7 @@ Or install it yourself as:
|
|
|
211
211
|
|
|
212
212
|
## Usage
|
|
213
213
|
|
|
214
|
-
You can check the [all_in_one.rb](examples/all_in_one.rb) that will:
|
|
214
|
+
You can check the [all_in_one.rb](examples/all_in_one.rb) example that will:
|
|
215
215
|
|
|
216
216
|
1. Create hypertable with compression settings
|
|
217
217
|
2. Insert data
|
|
@@ -243,7 +243,7 @@ create_table(:events, id: false, hypertable: hypertable_options) do |t|
|
|
|
243
243
|
end
|
|
244
244
|
```
|
|
245
245
|
|
|
246
|
-
####
|
|
246
|
+
#### create_continuous_aggregate
|
|
247
247
|
|
|
248
248
|
This example shows a ticks table grouping ticks as OHLCV histograms for every
|
|
249
249
|
minute.
|
|
@@ -287,9 +287,19 @@ options = {
|
|
|
287
287
|
}
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
-
|
|
290
|
+
create_continuous_aggregate('ohlc_1m', query, **options)
|
|
291
291
|
```
|
|
292
292
|
|
|
293
|
+
#### Scenic integration
|
|
294
|
+
|
|
295
|
+
The [Scenic](https://github.com/scenic-views/scenic) gem is an easy way to
|
|
296
|
+
manage database view definitions for a Rails application. TimescaleDB's
|
|
297
|
+
continuous aggregates are more complex than regular PostgreSQL views, and
|
|
298
|
+
the schema dumper included with Scenic can't dump a complete definition.
|
|
299
|
+
|
|
300
|
+
This gem automatically configures Scenic to use a `Timescale::Scenic::Adapter`
|
|
301
|
+
which will correctly handle schema dumping.
|
|
302
|
+
|
|
293
303
|
### Enable ActsAsHypertable
|
|
294
304
|
|
|
295
305
|
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.
|
|
@@ -388,7 +398,7 @@ end
|
|
|
388
398
|
|
|
389
399
|
## Development
|
|
390
400
|
|
|
391
|
-
After checking out the repo, run `bin/setup` to install the development dependencies. Then, `bundle exec rake test:setup` to setup the test database and tables. Finally, run `bundle exec
|
|
401
|
+
After checking out the repo, run `bin/setup` to install the development dependencies. Then, `bundle exec rake test:setup` to setup the test database and tables. Finally, run `bundle exec rake` to run the tests.
|
|
392
402
|
|
|
393
403
|
You can also run `tsdb` for an interactive prompt that will allow you to experiment.
|
|
394
404
|
|
data/Rakefile
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
require "bundler/gem_tasks"
|
|
2
2
|
require "rspec/core/rake_task"
|
|
3
3
|
|
|
4
|
+
begin
|
|
5
|
+
require "gemika/tasks"
|
|
6
|
+
rescue LoadError
|
|
7
|
+
puts "Run `gem install gemika` for additional tasks"
|
|
8
|
+
end
|
|
9
|
+
|
|
4
10
|
RSpec::Core::RakeTask.new(:spec)
|
|
5
11
|
|
|
6
|
-
task default: :spec
|
|
12
|
+
task default: "matrix:spec"
|
|
7
13
|
|
|
8
14
|
namespace :test do
|
|
9
15
|
task :setup do
|
data/bin/setup
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
module Timescale
|
|
2
|
-
class
|
|
2
|
+
class CompressionSetting < ActiveRecord::Base
|
|
3
3
|
self.table_name = "timescaledb_information.compression_settings"
|
|
4
4
|
belongs_to :hypertable, foreign_key: :hypertable_name
|
|
5
5
|
end
|
|
6
|
+
CompressionSettings = CompressionSetting
|
|
6
7
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module Timescale
|
|
2
|
-
class
|
|
2
|
+
class ContinuousAggregate < ActiveRecord::Base
|
|
3
3
|
self.table_name = "timescaledb_information.continuous_aggregates"
|
|
4
4
|
self.primary_key = 'materialization_hypertable_name'
|
|
5
5
|
|
|
@@ -15,4 +15,5 @@ module Timescale
|
|
|
15
15
|
}
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
|
+
ContinuousAggregates = ContinuousAggregate
|
|
18
19
|
end
|
data/lib/timescale/dimensions.rb
CHANGED
data/lib/timescale/hypertable.rb
CHANGED
|
@@ -6,17 +6,17 @@ module Timescale
|
|
|
6
6
|
has_many :jobs, foreign_key: "hypertable_name"
|
|
7
7
|
has_many :chunks, foreign_key: "hypertable_name"
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
has_many :compression_settings,
|
|
10
10
|
foreign_key: "hypertable_name",
|
|
11
|
-
class_name: "Timescale::
|
|
11
|
+
class_name: "Timescale::CompressionSetting"
|
|
12
12
|
|
|
13
13
|
has_one :dimensions,
|
|
14
14
|
foreign_key: "hypertable_name",
|
|
15
|
-
class_name: "Timescale::
|
|
15
|
+
class_name: "Timescale::Dimension"
|
|
16
16
|
|
|
17
17
|
has_many :continuous_aggregates,
|
|
18
18
|
foreign_key: "hypertable_name",
|
|
19
|
-
class_name: "Timescale::
|
|
19
|
+
class_name: "Timescale::ContinuousAggregate"
|
|
20
20
|
|
|
21
21
|
def chunks_detailed_size
|
|
22
22
|
struct_from "SELECT * from chunks_detailed_size('#{self.hypertable_name}')"
|
data/lib/timescale/job.rb
CHANGED
|
@@ -3,11 +3,8 @@ module Timescale
|
|
|
3
3
|
self.table_name = "timescaledb_information.jobs"
|
|
4
4
|
self.primary_key = "job_id"
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
attribute :retry_period, :interval
|
|
9
|
-
|
|
10
|
-
scope :compression, -> { where(proc_name: "tsbs_compress_chunks") }
|
|
6
|
+
scope :compression, -> { where(proc_name: [:tsbs_compress_chunks, :policy_compression]) }
|
|
7
|
+
scope :refresh_continuous_aggregate, -> { where(proc_name: :policy_refresh_continuous_aggregate) }
|
|
11
8
|
scope :scheduled, -> { where(scheduled: true) }
|
|
12
9
|
end
|
|
13
10
|
end
|
data/lib/timescale/job_stats.rb
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
module Timescale
|
|
2
|
-
class
|
|
2
|
+
class JobStat < ActiveRecord::Base
|
|
3
3
|
self.table_name = "timescaledb_information.job_stats"
|
|
4
4
|
|
|
5
5
|
belongs_to :job
|
|
6
6
|
|
|
7
|
-
attribute :last_run_duration, :interval
|
|
8
|
-
|
|
9
7
|
scope :success, -> { where(last_run_status: "Success") }
|
|
10
8
|
scope :scheduled, -> { where(job_status: "Scheduled") }
|
|
11
9
|
scope :resume, -> do
|
|
@@ -15,4 +13,5 @@ module Timescale
|
|
|
15
13
|
.to_a.map{|e|e.attributes.transform_keys(&:to_sym) }
|
|
16
14
|
end
|
|
17
15
|
end
|
|
16
|
+
JobStats = JobStat
|
|
18
17
|
end
|
|
@@ -51,7 +51,28 @@ module Timescale
|
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
# Create a new continuous aggregate
|
|
55
|
+
#
|
|
56
|
+
# @param name [String, Symbol] The name of the continuous aggregate.
|
|
57
|
+
# @param query [String] The SQL query for the aggregate view definition.
|
|
58
|
+
# @param with_data [Boolean] Set to true to create the aggregate WITH DATA
|
|
59
|
+
# @param refresh_policies [Hash] Set to create a refresh policy
|
|
60
|
+
# @option refresh_policies [String] start_offset: INTERVAL or integer
|
|
61
|
+
# @option refresh_policies [String] end_offset: INTERVAL or integer
|
|
62
|
+
# @option refresh_policies [String] schedule_interval: INTERVAL
|
|
63
|
+
#
|
|
64
|
+
# @see https://docs.timescale.com/api/latest/continuous-aggregates/add_continuous_aggregate_policy/
|
|
65
|
+
#
|
|
66
|
+
# @example
|
|
67
|
+
# create_continuous_aggregate(:activity_counts, query: <<-SQL, refresh_policies: { schedule_interval: "INTERVAL '1 hour'" })
|
|
68
|
+
# SELECT
|
|
69
|
+
# time_bucket(INTERVAL '1 day', activity.created_at) AS bucket,
|
|
70
|
+
# count(*)
|
|
71
|
+
# FROM activity
|
|
72
|
+
# GROUP BY bucket
|
|
73
|
+
# SQL
|
|
74
|
+
#
|
|
75
|
+
def create_continuous_aggregate(name, query, **options)
|
|
55
76
|
execute <<~SQL
|
|
56
77
|
CREATE MATERIALIZED VIEW #{name}
|
|
57
78
|
WITH (timescaledb.continuous) AS
|
|
@@ -69,6 +90,15 @@ module Timescale
|
|
|
69
90
|
SQL
|
|
70
91
|
end
|
|
71
92
|
end
|
|
93
|
+
alias_method :create_continuous_aggregates, :create_continuous_aggregate
|
|
94
|
+
|
|
95
|
+
def create_retention_policy(table_name, interval:)
|
|
96
|
+
execute "SELECT add_retention_policy('#{table_name}', INTERVAL '#{interval}')"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def remove_retention_policy(table_name)
|
|
100
|
+
execute "SELECT remove_retention_policy('#{table_name}')"
|
|
101
|
+
end
|
|
72
102
|
end
|
|
73
103
|
end
|
|
74
104
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require 'scenic/adapters/postgres'
|
|
2
|
+
require 'scenic/adapters/postgres/views'
|
|
3
|
+
|
|
4
|
+
module Timescale
|
|
5
|
+
module Scenic
|
|
6
|
+
class Views < ::Scenic::Adapters::Postgres::Views
|
|
7
|
+
# All of the views that this connection has defined, excluding any
|
|
8
|
+
# Timescale continuous aggregates. Those should be defined using
|
|
9
|
+
# +create_continuous_aggregate+ rather than +create_view+.
|
|
10
|
+
#
|
|
11
|
+
# @return [Array<Scenic::View>]
|
|
12
|
+
def all
|
|
13
|
+
ts_views = views_from_timescale.map { |v| to_scenic_view(v) }
|
|
14
|
+
pg_views = views_from_postgres.map { |v| to_scenic_view(v) }
|
|
15
|
+
ts_view_names = ts_views.map(&:name)
|
|
16
|
+
# Skip records with matching names (includes the schema name
|
|
17
|
+
# for records not in the public schema)
|
|
18
|
+
pg_views.reject { |v| v.name.in?(ts_view_names) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def views_from_timescale
|
|
24
|
+
connection.execute(<<-SQL.squish)
|
|
25
|
+
SELECT
|
|
26
|
+
view_name as viewname,
|
|
27
|
+
view_definition AS definition,
|
|
28
|
+
'm' AS kind,
|
|
29
|
+
view_schema AS namespace
|
|
30
|
+
FROM timescaledb_information.continuous_aggregates
|
|
31
|
+
SQL
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class Adapter < ::Scenic::Adapters::Postgres
|
|
36
|
+
# Timescale does some funky stuff under the hood with continuous
|
|
37
|
+
# aggregates. A continuous aggregate is made up of:
|
|
38
|
+
#
|
|
39
|
+
# 1. A hypertable to store the materialized data
|
|
40
|
+
# 2. An entry in the jobs table to refresh the data
|
|
41
|
+
# 3. A view definition that union's the hypertable and any recent data
|
|
42
|
+
# not included in the hypertable
|
|
43
|
+
#
|
|
44
|
+
# That doesn't dump well, even to structure.sql (we lose the job
|
|
45
|
+
# definition, since it's not part of the DDL).
|
|
46
|
+
#
|
|
47
|
+
# Our schema dumper implementation will handle dumping the continuous
|
|
48
|
+
# aggregate definintions, but we need to override Scenic's schema dumping
|
|
49
|
+
# to exclude those continuous aggregates.
|
|
50
|
+
def views
|
|
51
|
+
Views.new(connection).all
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -1,24 +1,88 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
|
2
|
+
require 'active_support/core_ext/string/indent'
|
|
3
|
+
|
|
4
|
+
module Timescale
|
|
5
|
+
module SchemaDumper
|
|
6
|
+
def tables(stream)
|
|
7
|
+
super # This will call #table for each table in the database
|
|
8
|
+
views(stream) unless defined?(Scenic) # Don't call this twice if we're using Scenic
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def table(table_name, stream)
|
|
12
|
+
super(table_name, stream)
|
|
13
|
+
if hypertable = Timescale::Hypertable.find_by(hypertable_name: table_name)
|
|
14
|
+
timescale_hypertable(hypertable, stream)
|
|
15
|
+
timescale_retention_policy(hypertable, stream)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def views(stream)
|
|
20
|
+
timescale_continuous_aggregates(stream) # Define these before any Scenic views that might use them
|
|
21
|
+
super if defined?(super)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def timescale_hypertable(hypertable, stream)
|
|
5
27
|
dim = hypertable.dimensions
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
28
|
+
extra_settings = {
|
|
29
|
+
time_column: "#{dim.column_name}",
|
|
30
|
+
chunk_time_interval: "#{dim.time_interval.inspect}"
|
|
31
|
+
}.merge(timescale_compression_settings_for(hypertable)).map {|k, v| %Q[#{k}: "#{v}"]}.join(", ")
|
|
32
|
+
|
|
33
|
+
stream.puts %Q[ create_hypertable "#{hypertable.hypertable_name}", #{extra_settings}]
|
|
34
|
+
stream.puts
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def timescale_retention_policy(hypertable, stream)
|
|
38
|
+
hypertable.jobs.where(proc_name: "policy_retention").each do |job|
|
|
39
|
+
stream.puts %Q[ create_retention_policy "#{job.hypertable_name}", interval: "#{job.config["drop_after"]}"]
|
|
40
|
+
stream.puts
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def timescale_compression_settings_for(hypertable)
|
|
45
|
+
compression_settings = hypertable.compression_settings.each_with_object({}) do |setting, compression_settings|
|
|
46
|
+
compression_settings[:compress_segmentby] = setting.attname if setting.segmentby_column_index
|
|
47
|
+
|
|
48
|
+
if setting.orderby_column_index
|
|
49
|
+
direction = setting.orderby_asc ? "ASC" : "DESC"
|
|
50
|
+
compression_settings[:compress_orderby] = "#{setting.attname} #{direction}"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
hypertable.jobs.compression.each do |job|
|
|
55
|
+
compression_settings[:compression_interval] = job.config["compress_after"]
|
|
56
|
+
end
|
|
57
|
+
compression_settings
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def timescale_continuous_aggregates(stream)
|
|
61
|
+
Timescale::ContinuousAggregates.all.each do |aggregate|
|
|
62
|
+
opts = if (refresh_policy = aggregate.jobs.refresh_continuous_aggregate.first)
|
|
63
|
+
interval = timescale_interval(refresh_policy.schedule_interval)
|
|
64
|
+
end_offset = timescale_interval(refresh_policy.config["end_offset"])
|
|
65
|
+
start_offset = timescale_interval(refresh_policy.config["start_offset"])
|
|
66
|
+
%Q[, refresh_policies: { start_offset: "#{start_offset}", end_offset: "#{end_offset}", schedule_interval: "#{interval}"}]
|
|
67
|
+
else
|
|
68
|
+
""
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
stream.puts <<~AGG.indent(2)
|
|
72
|
+
create_continuous_aggregate("#{aggregate.view_name}", <<-SQL#{opts})
|
|
73
|
+
#{aggregate.view_definition.strip.gsub(/;$/, "")}
|
|
74
|
+
SQL
|
|
75
|
+
AGG
|
|
76
|
+
stream.puts
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def timescale_interval(value)
|
|
81
|
+
return "NULL" if value.nil? || value.to_s.downcase == "null"
|
|
82
|
+
|
|
83
|
+
"INTERVAL '#{value}'"
|
|
13
84
|
end
|
|
14
85
|
end
|
|
15
86
|
end
|
|
16
87
|
|
|
17
|
-
|
|
18
|
-
def build_compression_settings_for(hypertable)
|
|
19
|
-
return if hypertable.compression_settings.nil?
|
|
20
|
-
hypertable.compression_settings.map do |settings|
|
|
21
|
-
", compress_segmentby: #{settings.segmentby_column_index},
|
|
22
|
-
compress_orderby: 'created_at',
|
|
23
|
-
compression_interval: nil)
|
|
24
|
-
=end
|
|
88
|
+
ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaDumper.prepend(Timescale::SchemaDumper)
|
data/lib/timescale/version.rb
CHANGED
data/lib/timescale.rb
CHANGED
|
@@ -9,6 +9,7 @@ require_relative 'timescale/dimensions'
|
|
|
9
9
|
require_relative 'timescale/hypertable'
|
|
10
10
|
require_relative 'timescale/job'
|
|
11
11
|
require_relative 'timescale/job_stats'
|
|
12
|
+
require_relative 'timescale/schema_dumper'
|
|
12
13
|
require_relative 'timescale/stats_report'
|
|
13
14
|
require_relative 'timescale/migration_helpers'
|
|
14
15
|
require_relative 'timescale/version'
|
|
@@ -48,3 +49,14 @@ module Timescale
|
|
|
48
49
|
Timescale::ActsAsHypertable::DEFAULT_OPTIONS
|
|
49
50
|
end
|
|
50
51
|
end
|
|
52
|
+
|
|
53
|
+
begin
|
|
54
|
+
require 'scenic'
|
|
55
|
+
require_relative 'timescale/scenic/adapter'
|
|
56
|
+
|
|
57
|
+
Scenic.configure do |config|
|
|
58
|
+
config.database = Timescale::Scenic::Adapter.new
|
|
59
|
+
end
|
|
60
|
+
rescue LoadError
|
|
61
|
+
# This is expected when the scenic gem is not being used
|
|
62
|
+
end
|
data/timescale.gemspec
CHANGED
|
@@ -27,8 +27,9 @@ Gem::Specification.new do |spec|
|
|
|
27
27
|
spec.executables = spec.files.grep(%r{^bin/tsdb}) { |f| File.basename(f) }
|
|
28
28
|
spec.require_paths = ["lib"]
|
|
29
29
|
|
|
30
|
-
spec.add_dependency
|
|
30
|
+
spec.add_dependency "pg", "~> 1.2"
|
|
31
31
|
spec.add_dependency "activerecord"
|
|
32
|
+
spec.add_dependency "activesupport"
|
|
32
33
|
|
|
33
34
|
spec.add_development_dependency "pry"
|
|
34
35
|
spec.add_development_dependency "rspec-its"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: timescaledb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jônatas Davi Paganini
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-
|
|
11
|
+
date: 2021-12-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: pg
|
|
@@ -38,6 +38,20 @@ dependencies:
|
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: activesupport
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
41
55
|
- !ruby/object:Gem::Dependency
|
|
42
56
|
name: pry
|
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -132,10 +146,14 @@ extra_rdoc_files: []
|
|
|
132
146
|
files:
|
|
133
147
|
- ".gitignore"
|
|
134
148
|
- ".rspec"
|
|
149
|
+
- ".ruby-version"
|
|
150
|
+
- ".tool-versions"
|
|
135
151
|
- ".travis.yml"
|
|
136
152
|
- CODE_OF_CONDUCT.md
|
|
137
153
|
- Gemfile
|
|
138
154
|
- Gemfile.lock
|
|
155
|
+
- Gemfile.scenic
|
|
156
|
+
- Gemfile.scenic.lock
|
|
139
157
|
- LICENSE.txt
|
|
140
158
|
- README.md
|
|
141
159
|
- Rakefile
|
|
@@ -156,6 +174,7 @@ files:
|
|
|
156
174
|
- lib/timescale/job.rb
|
|
157
175
|
- lib/timescale/job_stats.rb
|
|
158
176
|
- lib/timescale/migration_helpers.rb
|
|
177
|
+
- lib/timescale/scenic/adapter.rb
|
|
159
178
|
- lib/timescale/schema_dumper.rb
|
|
160
179
|
- lib/timescale/stats_report.rb
|
|
161
180
|
- lib/timescale/version.rb
|
|
@@ -181,7 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
181
200
|
- !ruby/object:Gem::Version
|
|
182
201
|
version: '0'
|
|
183
202
|
requirements: []
|
|
184
|
-
rubygems_version: 3.
|
|
203
|
+
rubygems_version: 3.1.2
|
|
185
204
|
signing_key:
|
|
186
205
|
specification_version: 4
|
|
187
206
|
summary: TimescaleDB helpers for Ruby ecosystem.
|