timescaledb 0.2.4 → 0.2.6

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 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