timescaledb 0.2.4 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5f8ebd4460e965fbf9a35600630c05d476b1a4b674192cd925a3d5f948a64a1
4
- data.tar.gz: d697ab124689a8c4f1ffc5b809a7cecd2ac07bbce84c7b9ca539dc5ae67068c9
3
+ metadata.gz: 06f324ce8793264ad6acb6a0f0f8f107511be08ecf2952e2fab285aeac366d35
4
+ data.tar.gz: d95da2805b53dafd174bb83fce7cf29cc2f13b5e8543bfa144d424e679aba701
5
5
  SHA512:
6
- metadata.gz: 1070bf2f732137006d81790ac1c4b467f733edd3bb724e8773d3c9f6ecb3b5c1dc32c3da638f351589fa944a48c1b8f7a037da6a896beed1557e4fb34e2a8442
7
- data.tar.gz: f5bc47e8c0022d079189e7ad68e2214da6760c4f420bca3c83950137dd7026985fb9239924c241b28f261546a860c744a32db154548bfc0f8871302d35109ff7
6
+ metadata.gz: 14ce31badc6ae1eae1f51161a806354fcaea6b2d39197498766b4cefa2022565410f7fc745adfbaf0d51b68d67454460f3aabab2a94444dd377a93193d0465b2
7
+ data.tar.gz: 573dd6b5ae3bcfa0948176a86561c080a860d72634e1800b750554a694e17a17890a31492e2702b38ced8444b5725586bfcca6cb23109c968748edeb509bcf12
@@ -0,0 +1,72 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+ workflow_dispatch:
7
+ # schedule:
8
+ # - cron: '42 5 * * *'
9
+
10
+ jobs:
11
+ test-in-container:
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ ruby: [ '3.1.2' ]
16
+ database:
17
+ - 'pg14.6-ts2.9.0-patroni-static-primary-latest'
18
+ - 'pg13.9-ts2.9-latest'
19
+
20
+ services:
21
+ database:
22
+ image: timescale/timescaledb-ha:${{matrix.database}}
23
+ env:
24
+ POSTGRES_USER: username
25
+ POSTGRES_PASSWORD: secret
26
+ POSTGRES_DB: testdb
27
+ options: >-
28
+ --health-cmd pg_isready
29
+ --health-interval 10s
30
+ --health-timeout 5s
31
+ --health-retries 5
32
+
33
+
34
+ runs-on: ubuntu-latest
35
+ name: OS Ruby ${{matrix.ruby}} database ${{matrix.database}}
36
+ container: ruby:${{matrix.ruby}}
37
+
38
+ steps:
39
+ - uses: actions/checkout@v3
40
+
41
+ - name: Show Ruby Version
42
+ run: |
43
+ ruby -v
44
+
45
+ - name: Install psql
46
+ run: |
47
+ apt-get update
48
+ apt-get install -y postgresql-client
49
+
50
+ - name: Show PostgreSQL version and time
51
+ env:
52
+ PGPASSWORD: secret
53
+ run: |
54
+ echo "SELECT version()" | psql -h database -U username testdb
55
+ echo "SELECT CURRENT_TIME" | psql -h database -U username testdb
56
+
57
+ - name: Setup
58
+ run: |
59
+ ./bin/setup
60
+
61
+ - name: run tsdb
62
+ run: ./bin/tsdb postgres://username:secret@database:5432/testdb --stats
63
+
64
+ - name: Test setup
65
+ run: |
66
+ echo PG_URI_TEST="postgres://username:secret@database:5432/testdb" > .env
67
+ cat .env
68
+ bundle exec rake test:setup
69
+
70
+ - name: Test
71
+ run: bundle exec rake
72
+
data/.travis.yml CHANGED
@@ -2,8 +2,8 @@
2
2
  language: ruby
3
3
  cache: bundler
4
4
  rvm:
5
- - 2.7.1
6
- before_install: gem install bundler -v 2.1.4
5
+ - 3.1.2
6
+ before_install: gem install bundler
7
7
  gemfile:
8
8
  - Gemfile
9
9
  - Gemfile.scenic
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- timescaledb (0.2.4)
4
+ timescaledb (0.2.6)
5
5
  activerecord
6
6
  activesupport
7
7
  pg (~> 1.2)
@@ -33,7 +33,7 @@ GEM
33
33
  concurrent-ruby (~> 1.0)
34
34
  method_source (1.0.0)
35
35
  minitest (5.14.4)
36
- pg (1.4.4)
36
+ pg (1.4.5)
37
37
  pry (0.14.1)
38
38
  coderay (~> 1.1)
39
39
  method_source (~> 1.0)
data/Gemfile.scenic.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- timescaledb (0.2.3)
4
+ timescaledb (0.2.6)
5
5
  activerecord
6
6
  activesupport
7
7
  pg (~> 1.2)
@@ -56,9 +56,7 @@ GEM
56
56
  nokogiri (1.12.5)
57
57
  mini_portile2 (~> 2.6.1)
58
58
  racc (~> 1.4)
59
- nokogiri (1.12.5-x86_64-darwin)
60
- racc (~> 1.4)
61
- pg (1.4.4)
59
+ pg (1.4.5)
62
60
  pry (0.14.1)
63
61
  coderay (~> 1.1)
64
62
  method_source (~> 1.0)
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Timescaledb
4
+ class ApplicationRecord < ::ActiveRecord::Base
5
+ self.abstract_class = true
6
+ end
7
+ end
@@ -1,5 +1,5 @@
1
1
  module Timescaledb
2
- class Chunk < ActiveRecord::Base
2
+ class Chunk < ::Timescaledb::ApplicationRecord
3
3
  self.table_name = "timescaledb_information.chunks"
4
4
  self.primary_key = "chunk_name"
5
5
 
@@ -1,5 +1,5 @@
1
1
  module Timescaledb
2
- class CompressionSetting < ActiveRecord::Base
2
+ class CompressionSetting < ::Timescaledb::ApplicationRecord
3
3
  self.table_name = "timescaledb_information.compression_settings"
4
4
  belongs_to :hypertable, foreign_key: :hypertable_name
5
5
  end
@@ -1,5 +1,5 @@
1
1
  module Timescaledb
2
- class ContinuousAggregate < ActiveRecord::Base
2
+ class ContinuousAggregate < ::Timescaledb::ApplicationRecord
3
3
  self.table_name = "timescaledb_information.continuous_aggregates"
4
4
  self.primary_key = 'materialization_hypertable_name'
5
5
 
@@ -1,5 +1,5 @@
1
1
  module Timescaledb
2
- class Dimension < ActiveRecord::Base
2
+ class Dimension < ::Timescaledb::ApplicationRecord
3
3
  self.table_name = "timescaledb_information.dimensions"
4
4
  # attribute :time_interval, :interval
5
5
  end
@@ -1,5 +1,5 @@
1
1
  module Timescaledb
2
- class Hypertable < ActiveRecord::Base
2
+ class Hypertable < ::Timescaledb::ApplicationRecord
3
3
  self.table_name = "timescaledb_information.hypertables"
4
4
  self.primary_key = "hypertable_name"
5
5
 
@@ -1,5 +1,5 @@
1
1
  module Timescaledb
2
- class Job < ActiveRecord::Base
2
+ class Job < ::Timescaledb::ApplicationRecord
3
3
  self.table_name = "timescaledb_information.jobs"
4
4
  self.primary_key = "job_id"
5
5
 
@@ -1,5 +1,5 @@
1
1
  module Timescaledb
2
- class JobStat < ActiveRecord::Base
2
+ class JobStat < ::Timescaledb::ApplicationRecord
3
3
  self.table_name = "timescaledb_information.job_stats"
4
4
 
5
5
  belongs_to :job
@@ -4,7 +4,12 @@ require 'active_record/connection_adapters/postgresql_adapter'
4
4
  module Timescaledb
5
5
  # Migration helpers can help you to setup hypertables by default.
6
6
  module MigrationHelpers
7
- # create_table can receive `hypertable` argument
7
+ # `create_table` accepts a `hypertable` argument with options for creating
8
+ # a TimescaleDB hypertable.
9
+ #
10
+ # See https://docs.timescale.com/api/latest/hypertable/create_hypertable/#optional-arguments
11
+ # for additional options supported by the plugin.
12
+ #
8
13
  # @example
9
14
  # options = {
10
15
  # time_column: 'created_at',
@@ -27,15 +32,29 @@ module Timescaledb
27
32
  # Setup hypertable from options
28
33
  # @see create_table with the hypertable options.
29
34
  def create_hypertable(table_name,
30
- time_column: 'created_at',
31
- chunk_time_interval: '1 week',
32
- compress_segmentby: nil,
33
- compress_orderby: 'created_at',
34
- compression_interval: nil
35
- )
35
+ time_column: 'created_at',
36
+ chunk_time_interval: '1 week',
37
+ compress_segmentby: nil,
38
+ compress_orderby: 'created_at',
39
+ compression_interval: nil,
40
+ partition_column: nil,
41
+ number_partitions: nil,
42
+ **hypertable_options)
36
43
 
37
44
  ActiveRecord::Base.logger = Logger.new(STDOUT)
38
- execute "SELECT create_hypertable('#{table_name}', '#{time_column}', chunk_time_interval => INTERVAL '#{chunk_time_interval}')"
45
+
46
+ options = ["chunk_time_interval => INTERVAL '#{chunk_time_interval}'"]
47
+ options += hypertable_options.map { |k, v| "#{k} => #{quote(v)}" }
48
+
49
+ arguments = [
50
+ quote(table_name),
51
+ quote(time_column),
52
+ (quote(partition_column) if partition_column),
53
+ (number_partitions if partition_column),
54
+ *options
55
+ ]
56
+
57
+ execute "SELECT create_hypertable(#{arguments.compact.join(', ')})"
39
58
 
40
59
  if compress_segmentby
41
60
  execute <<~SQL
@@ -68,5 +68,5 @@ module Timescaledb
68
68
  end
69
69
 
70
70
 
71
+ Scenic::Adapters::Postgres.include(Timescaledb::Scenic::Extension)
71
72
  ActiveRecord::ConnectionAdapters::AbstractAdapter.include(Timescaledb::Scenic::MigrationHelpers)
72
- Scenic::Adapters::Postgres.prepend(Timescaledb::Scenic::Extension)
@@ -5,31 +5,24 @@ module Timescaledb
5
5
  module SchemaDumper
6
6
  def tables(stream)
7
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
8
 
10
9
  return unless Timescaledb::Hypertable.table_exists?
11
10
 
12
11
  timescale_hypertables(stream)
13
12
  timescale_retention_policies(stream)
14
- end
15
-
16
- def views(stream)
17
- return unless Timescaledb::ContinuousAggregates.table_exists?
18
-
19
13
  timescale_continuous_aggregates(stream) # Define these before any Scenic views that might use them
20
- super if defined?(super)
21
14
  end
22
15
 
23
16
  def timescale_hypertables(stream)
24
- stream.puts # Insert a blank line above the hypertable definitions, for readability
25
-
26
17
  sorted_hypertables.each do |hypertable|
27
- timescale_hypertable(hypertable, stream)
18
+ timescale_hypertable(hypertable, stream)
28
19
  end
29
20
  end
30
21
 
31
22
  def timescale_retention_policies(stream)
32
- stream.puts # Insert a blank line above the retention policies, for readability
23
+ if sorted_hypertables.any? { |hypertable| hypertable.jobs.exists?(proc_name: "policy_retention") }
24
+ stream.puts # Insert a blank line above the retention policies, for readability
25
+ end
33
26
 
34
27
  sorted_hypertables.each do |hypertable|
35
28
  timescale_retention_policy(hypertable, stream)
@@ -39,13 +32,18 @@ module Timescaledb
39
32
  private
40
33
 
41
34
  def timescale_hypertable(hypertable, stream)
42
- dim = hypertable.main_dimension
43
- extra_settings = {
44
- time_column: "#{dim.column_name}",
45
- chunk_time_interval: "#{dim.time_interval.inspect}"
46
- }.merge(timescale_compression_settings_for(hypertable)).map {|k, v| %Q[#{k}: "#{v}"]}.join(", ")
47
-
48
- stream.puts %Q[ create_hypertable "#{hypertable.hypertable_name}", #{extra_settings}]
35
+ time = hypertable.main_dimension
36
+
37
+ options = {
38
+ time_column: time.column_name,
39
+ chunk_time_interval: time.time_interval.inspect,
40
+ **timescale_compression_settings_for(hypertable),
41
+ **timescale_space_partition_for(hypertable),
42
+ **timescale_index_options_for(hypertable)
43
+ }
44
+
45
+ options = options.map { |k, v| "#{k}: #{v.to_json}" }.join(", ")
46
+ stream.puts %Q[ create_hypertable "#{hypertable.hypertable_name}", #{options}]
49
47
  end
50
48
 
51
49
  def timescale_retention_policy(hypertable, stream)
@@ -56,21 +54,64 @@ module Timescaledb
56
54
 
57
55
  def timescale_compression_settings_for(hypertable)
58
56
  compression_settings = hypertable.compression_settings.each_with_object({}) do |setting, compression_settings|
59
- compression_settings[:compress_segmentby] = setting.attname if setting.segmentby_column_index
57
+ # It's possible to configure compression so that it is segmented by multiple
58
+ # columns. To make sure we capture that correctly, we'll treat them as an array.
59
+ compression_settings[:compress_segmentby] ||= []
60
+ compression_settings[:compress_orderby] ||= []
61
+
62
+ compression_settings[:compress_segmentby] << setting.attname if setting.segmentby_column_index
60
63
 
61
64
  if setting.orderby_column_index
62
- direction = setting.orderby_asc ? "ASC" : "DESC"
63
- compression_settings[:compress_orderby] = "#{setting.attname} #{direction}"
65
+ if setting.orderby_asc
66
+ direction = "ASC"
67
+ if setting.orderby_nullsfirst
68
+ direction += " NULLS FIRST"
69
+ end
70
+ else
71
+ direction = "DESC"
72
+ if !setting.orderby_nullsfirst
73
+ direction += " NULLS LAST"
74
+ end
75
+ end
76
+
77
+ compression_settings[:compress_orderby] << "#{setting.attname} #{direction}"
64
78
  end
65
79
  end
66
80
 
67
81
  hypertable.jobs.compression.each do |job|
68
82
  compression_settings[:compression_interval] = job.config["compress_after"]
69
83
  end
84
+
85
+ # Pack the compression setting arrays into a comma-separated string instead.
86
+ if compression_settings[:compress_segmentby]
87
+ compression_settings[:compress_segmentby] = compression_settings[:compress_segmentby].join(", ")
88
+ end
89
+ if compression_settings[:compress_orderby]
90
+ compression_settings[:compress_orderby] = compression_settings[:compress_orderby].join(", ")
91
+ end
92
+
70
93
  compression_settings
71
94
  end
72
95
 
96
+ def timescale_space_partition_for(hypertable)
97
+ return {} unless hypertable.dimensions.length > 1
98
+
99
+ space = hypertable.dimensions.last
100
+ {partition_column: space.column_name, number_partitions: space.num_partitions}
101
+ end
102
+
103
+ def timescale_index_options_for(hypertable)
104
+ time = hypertable.main_dimension
105
+ if @connection.indexes(hypertable.hypertable_name).any? { |i| i.columns == [time.column_name] }
106
+ {}
107
+ else
108
+ {create_default_indexes: false}
109
+ end
110
+ end
111
+
73
112
  def timescale_continuous_aggregates(stream)
113
+ return unless Timescaledb::ContinuousAggregates.table_exists?
114
+
74
115
  Timescaledb::ContinuousAggregates.all.each do |aggregate|
75
116
  opts = if (refresh_policy = aggregate.jobs.refresh_continuous_aggregate.first)
76
117
  interval = timescale_interval(refresh_policy.schedule_interval)
@@ -95,6 +136,7 @@ module Timescaledb
95
136
 
96
137
  "INTERVAL '#{value}'"
97
138
  end
139
+
98
140
  def sorted_hypertables
99
141
  @sorted_hypertables ||= Timescaledb::Hypertable.order(:hypertable_name).to_a
100
142
  end
@@ -1,3 +1,3 @@
1
1
  module Timescaledb
2
- VERSION = '0.2.4'
2
+ VERSION = '0.2.6'
3
3
  end
data/lib/timescaledb.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'active_record'
2
2
 
3
+ require_relative 'timescaledb/application_record'
3
4
  require_relative 'timescaledb/acts_as_hypertable'
4
5
  require_relative 'timescaledb/acts_as_hypertable/core'
5
6
  require_relative 'timescaledb/toolkit'
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.2.4
4
+ version: 0.2.6
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: 2022-12-16 00:00:00.000000000 Z
11
+ date: 2023-02-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg
@@ -144,6 +144,7 @@ executables:
144
144
  extensions: []
145
145
  extra_rdoc_files: []
146
146
  files:
147
+ - ".github/workflows/ci.yml"
147
148
  - ".gitignore"
148
149
  - ".rspec"
149
150
  - ".ruby-version"
@@ -240,6 +241,7 @@ files:
240
241
  - lib/timescaledb/acts_as_hypertable.rb
241
242
  - lib/timescaledb/acts_as_hypertable/core.rb
242
243
  - lib/timescaledb/acts_as_time_vector.rb
244
+ - lib/timescaledb/application_record.rb
243
245
  - lib/timescaledb/chunk.rb
244
246
  - lib/timescaledb/compression_settings.rb
245
247
  - lib/timescaledb/continuous_aggregates.rb