From e78f7a7c481d60ce52754a16a2750157ac51bf60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B4natas=20Davi=20Paganini?= Date: Tue, 4 Mar 2025 13:27:26 +0100 Subject: [PATCH 1/4] Add `rake timescaledb:update_extension` task. --- README.md | 15 ++ examples/ranking/.ruby-version | 2 +- examples/ranking/Gemfile | 10 +- examples/ranking/Gemfile.lock | 244 +++++++++--------- examples/ranking/Rakefile | 1 + .../ranking/config/initializers/timescale.rb | 1 - examples/ranking/db/schema.rb | 216 ++-------------- lib/tasks/timescaledb.rake | 18 ++ lib/timescaledb/extension.rb | 2 +- lib/timescaledb/scenic/extension.rb | 2 +- 10 files changed, 192 insertions(+), 319 deletions(-) create mode 100644 lib/tasks/timescaledb.rake diff --git a/README.md b/README.md index 92deca3..27755c8 100644 --- a/README.md +++ b/README.md @@ -449,6 +449,21 @@ config.before(:suite) do end ``` +## Updating the timescaledb extension + +The ActiveRecord migrations doesn't support Postgresql extension updates. You can load the timescaledb tasks to make it available on your environment. +First, require it on the Rakefile: + +```ruby +require 'timescaledb/tasks` +``` + +And then you can run the update_extension task: + +```bash + bundle exec rake timescaledb:update_extension +``` + ## More resources If you want to learn more about TimescaleDB with Ruby code, you can check the [examples](examples) folder and videos below: diff --git a/examples/ranking/.ruby-version b/examples/ranking/.ruby-version index be94e6f..9c25013 100644 --- a/examples/ranking/.ruby-version +++ b/examples/ranking/.ruby-version @@ -1 +1 @@ -3.2.2 +3.3.6 diff --git a/examples/ranking/Gemfile b/examples/ranking/Gemfile index 2d36ff2..f269777 100644 --- a/examples/ranking/Gemfile +++ b/examples/ranking/Gemfile @@ -1,14 +1,14 @@ source "https://rubygems.org" git_source(:github) { |repo| "https://github.com/#{repo}.git" } -ruby "3.2.2" +ruby "3.3.6" # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" -gem "rails", "~> 7.1.3.2" +gem "rails" gem "timescaledb", path: "../../" -gem "pg", "~> 1.1" -gem "puma", "~> 6.0" +gem "pg" +gem "puma" gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ] # Reduces boot times through caching; required in config/boot.rb @@ -30,4 +30,4 @@ group :development do end -gem "scenic", "~> 1.5" +gem "scenic" diff --git a/examples/ranking/Gemfile.lock b/examples/ranking/Gemfile.lock index 11a17fb..e94e97b 100644 --- a/examples/ranking/Gemfile.lock +++ b/examples/ranking/Gemfile.lock @@ -1,109 +1,110 @@ PATH remote: ../.. specs: - timescaledb (0.2.7) + timescaledb (0.3.2) activerecord activesupport + ostruct pg (~> 1.2) GEM remote: https://rubygems.org/ specs: - actioncable (7.1.3.2) - actionpack (= 7.1.3.2) - activesupport (= 7.1.3.2) + actioncable (8.0.1) + actionpack (= 8.0.1) + activesupport (= 8.0.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.1.3.2) - actionpack (= 7.1.3.2) - activejob (= 7.1.3.2) - activerecord (= 7.1.3.2) - activestorage (= 7.1.3.2) - activesupport (= 7.1.3.2) - mail (>= 2.7.1) - net-imap - net-pop - net-smtp - actionmailer (7.1.3.2) - actionpack (= 7.1.3.2) - actionview (= 7.1.3.2) - activejob (= 7.1.3.2) - activesupport (= 7.1.3.2) - mail (~> 2.5, >= 2.5.4) - net-imap - net-pop - net-smtp + actionmailbox (8.0.1) + actionpack (= 8.0.1) + activejob (= 8.0.1) + activerecord (= 8.0.1) + activestorage (= 8.0.1) + activesupport (= 8.0.1) + mail (>= 2.8.0) + actionmailer (8.0.1) + actionpack (= 8.0.1) + actionview (= 8.0.1) + activejob (= 8.0.1) + activesupport (= 8.0.1) + mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (7.1.3.2) - actionview (= 7.1.3.2) - activesupport (= 7.1.3.2) + actionpack (8.0.1) + actionview (= 8.0.1) + activesupport (= 8.0.1) nokogiri (>= 1.8.5) - racc rack (>= 2.2.4) rack-session (>= 1.0.1) rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - actiontext (7.1.3.2) - actionpack (= 7.1.3.2) - activerecord (= 7.1.3.2) - activestorage (= 7.1.3.2) - activesupport (= 7.1.3.2) + useragent (~> 0.16) + actiontext (8.0.1) + actionpack (= 8.0.1) + activerecord (= 8.0.1) + activestorage (= 8.0.1) + activesupport (= 8.0.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.1.3.2) - activesupport (= 7.1.3.2) + actionview (8.0.1) + activesupport (= 8.0.1) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (7.1.3.2) - activesupport (= 7.1.3.2) + activejob (8.0.1) + activesupport (= 8.0.1) globalid (>= 0.3.6) - activemodel (7.1.3.2) - activesupport (= 7.1.3.2) - activerecord (7.1.3.2) - activemodel (= 7.1.3.2) - activesupport (= 7.1.3.2) + activemodel (8.0.1) + activesupport (= 8.0.1) + activerecord (8.0.1) + activemodel (= 8.0.1) + activesupport (= 8.0.1) timeout (>= 0.4.0) - activestorage (7.1.3.2) - actionpack (= 7.1.3.2) - activejob (= 7.1.3.2) - activerecord (= 7.1.3.2) - activesupport (= 7.1.3.2) + activestorage (8.0.1) + actionpack (= 8.0.1) + activejob (= 8.0.1) + activerecord (= 8.0.1) + activesupport (= 8.0.1) marcel (~> 1.0) - activesupport (7.1.3.2) + activesupport (8.0.1) base64 + benchmark (>= 0.3) bigdecimal - concurrent-ruby (~> 1.0, >= 1.0.2) + concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) - mutex_m - tzinfo (~> 2.0) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) base64 (0.2.0) - bigdecimal (3.1.7) - bootsnap (1.18.3) + benchmark (0.4.0) + bigdecimal (3.1.9) + bootsnap (1.18.4) msgpack (~> 1.2) - builder (3.2.4) + builder (3.3.0) coderay (1.1.3) - concurrent-ruby (1.2.3) - connection_pool (2.4.1) + concurrent-ruby (1.3.5) + connection_pool (2.5.0) crass (1.0.6) - date (3.3.4) + date (3.4.1) drb (2.2.1) - erubi (1.12.0) + erubi (1.13.1) globalid (1.2.1) activesupport (>= 6.1) - i18n (1.14.4) + i18n (1.14.7) concurrent-ruby (~> 1.0) - io-console (0.7.2) - irb (1.12.0) - rdoc + io-console (0.8.0) + irb (1.15.1) + pp (>= 0.6.0) + rdoc (>= 4.0.0) reline (>= 0.4.2) - loofah (2.22.0) + logger (1.6.6) + loofah (2.24.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.8.1) @@ -112,107 +113,114 @@ GEM net-pop net-smtp marcel (1.0.4) - method_source (1.0.0) + method_source (1.1.0) mini_mime (1.1.5) - mini_portile2 (2.8.5) - minitest (5.22.3) - msgpack (1.7.2) - mutex_m (0.2.0) - net-imap (0.4.10) + mini_portile2 (2.8.8) + minitest (5.25.4) + msgpack (1.8.0) + net-imap (0.5.6) date net-protocol net-pop (0.1.2) net-protocol net-protocol (0.2.2) timeout - net-smtp (0.5.0) + net-smtp (0.5.1) net-protocol - nio4r (2.7.1) - nokogiri (1.16.3) + nio4r (2.7.4) + nokogiri (1.18.3) mini_portile2 (~> 2.8.2) racc (~> 1.4) - pg (1.5.6) - pry (0.14.2) + ostruct (0.6.1) + pg (1.5.9) + pp (0.6.2) + prettyprint + prettyprint (0.2.0) + pry (0.15.2) coderay (~> 1.1) method_source (~> 1.0) - pry-rails (0.3.9) - pry (>= 0.10.4) - psych (5.1.2) + pry-rails (0.3.11) + pry (>= 0.13.0) + psych (5.2.3) + date stringio - puma (6.4.2) + puma (6.6.0) nio4r (~> 2.0) - racc (1.7.3) - rack (3.0.10) - rack-session (2.0.0) + racc (1.8.1) + rack (3.1.11) + rack-session (2.1.0) + base64 (>= 0.1.0) rack (>= 3.0.0) - rack-test (2.1.0) + rack-test (2.2.0) rack (>= 1.3) - rackup (2.1.0) + rackup (2.2.1) rack (>= 3) - webrick (~> 1.8) - rails (7.1.3.2) - actioncable (= 7.1.3.2) - actionmailbox (= 7.1.3.2) - actionmailer (= 7.1.3.2) - actionpack (= 7.1.3.2) - actiontext (= 7.1.3.2) - actionview (= 7.1.3.2) - activejob (= 7.1.3.2) - activemodel (= 7.1.3.2) - activerecord (= 7.1.3.2) - activestorage (= 7.1.3.2) - activesupport (= 7.1.3.2) + rails (8.0.1) + actioncable (= 8.0.1) + actionmailbox (= 8.0.1) + actionmailer (= 8.0.1) + actionpack (= 8.0.1) + actiontext (= 8.0.1) + actionview (= 8.0.1) + activejob (= 8.0.1) + activemodel (= 8.0.1) + activerecord (= 8.0.1) + activestorage (= 8.0.1) + activesupport (= 8.0.1) bundler (>= 1.15.0) - railties (= 7.1.3.2) + railties (= 8.0.1) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.0) + rails-html-sanitizer (1.6.2) loofah (~> 2.21) - nokogiri (~> 1.14) - railties (7.1.3.2) - actionpack (= 7.1.3.2) - activesupport (= 7.1.3.2) - irb + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + railties (8.0.1) + actionpack (= 8.0.1) + activesupport (= 8.0.1) + irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) thor (~> 1.0, >= 1.2.2) zeitwerk (~> 2.6) - rake (13.1.0) - rdoc (6.6.3.1) + rake (13.2.1) + rdoc (6.12.0) psych (>= 4.0.0) - reline (0.5.0) + reline (0.6.0) io-console (~> 0.5) - scenic (1.7.0) + scenic (1.8.0) activerecord (>= 4.0.0) railties (>= 4.0.0) - stringio (3.1.0) - thor (1.3.1) - timeout (0.4.1) + securerandom (0.4.1) + stringio (3.1.5) + thor (1.3.2) + timeout (0.4.3) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - webrick (1.8.1) - websocket-driver (0.7.6) + uri (1.0.3) + useragent (0.16.11) + websocket-driver (0.7.7) + base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) - zeitwerk (2.6.13) + zeitwerk (2.7.2) PLATFORMS ruby DEPENDENCIES bootsnap - pg (~> 1.1) + pg pry-rails - puma (~> 6.0) - rails (~> 7.1.3.2) - scenic (~> 1.5) + puma + rails + scenic timescaledb! tzinfo-data RUBY VERSION - ruby 3.2.2p53 + ruby 3.3.6p108 BUNDLED WITH - 2.1.4 + 2.6.5 diff --git a/examples/ranking/Rakefile b/examples/ranking/Rakefile index 9a5ea73..ffdb2bb 100644 --- a/examples/ranking/Rakefile +++ b/examples/ranking/Rakefile @@ -2,5 +2,6 @@ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. require_relative "config/application" +require 'timescaledb/tasks' Rails.application.load_tasks diff --git a/examples/ranking/config/initializers/timescale.rb b/examples/ranking/config/initializers/timescale.rb index dcfdfc3..e2488e4 100644 --- a/examples/ranking/config/initializers/timescale.rb +++ b/examples/ranking/config/initializers/timescale.rb @@ -2,4 +2,3 @@ require 'scenic' ActiveSupport.on_load(:active_record) { extend Timescaledb::ActsAsHypertable } - diff --git a/examples/ranking/db/schema.rb b/examples/ranking/db/schema.rb index c019a79..c97bda3 100644 --- a/examples/ranking/db/schema.rb +++ b/examples/ranking/db/schema.rb @@ -10,205 +10,37 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 0) do - create_schema "_timescaledb_cache" - create_schema "_timescaledb_debug" - +ActiveRecord::Schema[8.0].define(version: 2025_03_04_094036) do # These are extensions that must be enabled in order to support this database - enable_extension "pg_stat_statements" - enable_extension "pg_trgm" - enable_extension "plpgsql" + enable_extension "pg_catalog.plpgsql" enable_extension "timescaledb" enable_extension "timescaledb_toolkit" - create_table "network_device_data", id: false, force: :cascade do |t| - t.timestamptz "time", null: false - t.integer "device", null: false - t.integer "id", null: false - t.bigint "counter32bit" - t.bigint "counter64bit" - t.index ["time"], name: "network_device_data_time_idx", order: :desc - end - - create_table "pages", id: false, force: :cascade do |t| - t.timestamptz "time", null: false - t.text "url", null: false - t.float "time_to_fetch" - t.text "title", null: false - t.text "headers", default: [], array: true - t.jsonb "links" - t.text "body", default: [], array: true - t.text "codeblocks", default: [], array: true - t.integer "html_size" - t.tsvector "search_vector" - t.index ["search_vector"], name: "pages_search_vector_idx", using: :gin - t.index ["time"], name: "pages_time_idx", order: :desc + create_table "games", force: :cascade do |t| + t.string "name" + t.string "description" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end - create_table "sample", id: false, force: :cascade do |t| - t.timestamptz "time", null: false - t.text "device_id", null: false - t.float "value", null: false - t.index ["time"], name: "sample_time_idx", order: :desc + create_table "plays", id: false, force: :cascade do |t| + t.bigint "game_id", null: false + t.integer "score" + t.decimal "total_time" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["created_at"], name: "plays_created_at_idx", order: :desc + t.index ["game_id"], name: "index_plays_on_game_id" end - - create_table "ticks", id: false, force: :cascade do |t| - t.timestamptz "time", null: false - t.text "symbol", null: false - t.decimal "price", null: false - t.decimal "volume", null: false - t.index ["time"], name: "ticks_time_idx", order: :desc - end - - - create_view "network_data_final", sql_definition: <<-SQL - SELECT id, - bucket, - interpolated_rate(counter32bit_agg, bucket, 'PT1M'::interval, lag(counter32bit_agg) OVER (PARTITION BY id ORDER BY bucket), lead(counter32bit_agg) OVER (PARTITION BY id ORDER BY bucket)) AS counter32bitrate, - interpolated_rate(counter64bit_agg, bucket, 'PT1M'::interval, lag(counter64bit_agg) OVER (PARTITION BY id ORDER BY bucket), lead(counter64bit_agg) OVER (PARTITION BY id ORDER BY bucket)) AS counter64bitrate - FROM network_data_agg_1min - ORDER BY id, bucket; - SQL - create_view "network_data_final_with_resets", sql_definition: <<-SQL - WITH counter_data AS ( - SELECT network_device_data."time", - network_device_data.device, - network_device_data.id, - network_device_data.counter64bit, - lag(network_device_data.counter64bit) OVER (PARTITION BY network_device_data.device, network_device_data.id ORDER BY network_device_data."time") AS prev_counter64bit - FROM network_device_data - ), resets_detected AS ( - SELECT counter_data."time", - counter_data.device, - counter_data.id, - counter_data.counter64bit, - counter_data.prev_counter64bit, - CASE - WHEN (counter_data.counter64bit < counter_data.prev_counter64bit) THEN 1 - ELSE 0 - END AS reset_detected - FROM counter_data - ), rate_calculation AS ( - SELECT resets_detected."time", - resets_detected.device, - resets_detected.id, - resets_detected.counter64bit, - resets_detected.prev_counter64bit, - resets_detected.reset_detected, - CASE - WHEN (resets_detected.reset_detected = 1) THEN (((resets_detected.counter64bit)::numeric + ('18446744073709551615'::numeric - (COALESCE(resets_detected.prev_counter64bit, (0)::bigint))::numeric)) / EXTRACT(epoch FROM (resets_detected."time" - lag(resets_detected."time") OVER (PARTITION BY resets_detected.device, resets_detected.id ORDER BY resets_detected."time")))) - ELSE (((resets_detected.counter64bit - COALESCE(resets_detected.prev_counter64bit, resets_detected.counter64bit)))::numeric / EXTRACT(epoch FROM (resets_detected."time" - lag(resets_detected."time") OVER (PARTITION BY resets_detected.device, resets_detected.id ORDER BY resets_detected."time")))) - END AS rate - FROM resets_detected - ) - SELECT "time", - device, - id, - rate - FROM rate_calculation - ORDER BY "time", device, id; - SQL - create_view "ohlcv_1m", sql_definition: <<-SQL - SELECT bucket, - symbol, - open(candlestick) AS open, - high(candlestick) AS high, - low(candlestick) AS low, - close(candlestick) AS close, - volume(candlestick) AS volume, - vwap(candlestick) AS vwap - FROM _ohlcv_1m; - SQL - create_view "ohlcv_1h", sql_definition: <<-SQL - SELECT bucket, - symbol, - open(candlestick) AS open, - high(candlestick) AS high, - low(candlestick) AS low, - close(candlestick) AS close, - volume(candlestick) AS volume, - vwap(candlestick) AS vwap - FROM _ohlcv_1h; - SQL - create_view "ohlcv_1d", sql_definition: <<-SQL - SELECT bucket, - symbol, - open(candlestick) AS open, - high(candlestick) AS high, - low(candlestick) AS low, - close(candlestick) AS close, - volume(candlestick) AS volume, - vwap(candlestick) AS vwap - FROM _ohlcv_1d; - SQL - create_hypertable "network_device_data", time_column: "time", chunk_time_interval: "7 days" - create_hypertable "pages", time_column: "time", chunk_time_interval: "1 day" - create_hypertable "sample", time_column: "time", chunk_time_interval: "7 days" - create_hypertable "ticks", time_column: "time", chunk_time_interval: "1 day", compress_segmentby: "symbol", compress_orderby: "time ASC", compress_after: "7 days" - create_continuous_aggregate("network_data_agg_1min", <<-SQL, , materialized_only: true, finalized: true) - SELECT time_bucket('PT1M'::interval, "time") AS bucket, - device, - id, - counter_agg("time", (counter32bit)::double precision) AS counter32bit_agg, - counter_agg("time", (counter64bit)::double precision) AS counter64bit_agg - FROM network_device_data - GROUP BY (time_bucket('PT1M'::interval, "time")), device, id - SQL - - create_continuous_aggregate("_ohlcv_1m", <<-SQL, , materialized_only: false, finalized: true) - SELECT time_bucket('PT1M'::interval, "time") AS bucket, - symbol, - candlestick_agg("time", (price)::double precision, (volume)::double precision) AS candlestick - FROM ticks - GROUP BY (time_bucket('PT1M'::interval, "time")), symbol - SQL - - create_continuous_aggregate("_ohlcv_1h", <<-SQL, , materialized_only: true, finalized: true) - SELECT time_bucket('PT1H'::interval, bucket) AS bucket, - symbol, - rollup(candlestick) AS candlestick - FROM _ohlcv_1m - GROUP BY (time_bucket('PT1H'::interval, bucket)), symbol - SQL - - create_continuous_aggregate("_ohlcv_1d", <<-SQL, , materialized_only: true, finalized: true) - SELECT time_bucket('P1D'::interval, bucket) AS bucket, - symbol, - rollup(candlestick) AS candlestick - FROM _ohlcv_1h - GROUP BY (time_bucket('P1D'::interval, bucket)), symbol - SQL - - create_continuous_aggregate("stats_agg_1m_sample", <<-SQL, refresh_policies: { start_offset: "INTERVAL '00:05:00'", end_offset: "INTERVAL '00:01:00'", schedule_interval: "INTERVAL '60'"}, materialized_only: false, finalized: true) - SELECT time_bucket('PT1M'::interval, "time") AS bucket, - device_id, - stats_agg(value) AS stats_agg - FROM sample - GROUP BY (time_bucket('PT1M'::interval, "time")), device_id - SQL - - create_continuous_aggregate("stats_agg_1h_sample", <<-SQL, refresh_policies: { start_offset: "INTERVAL '03:00:00'", end_offset: "INTERVAL '01:00:00'", schedule_interval: "INTERVAL '5'"}, materialized_only: false, finalized: true) - SELECT time_bucket('PT1H'::interval, bucket) AS bucket, - device_id, - rollup(stats_agg) AS stats_agg - FROM stats_agg_1m_sample - GROUP BY (time_bucket('PT1H'::interval, bucket)), device_id - SQL - - create_continuous_aggregate("stats_agg_1d_sample", <<-SQL, refresh_policies: { start_offset: "INTERVAL '3 days'", end_offset: "INTERVAL '01:00:00'", schedule_interval: "INTERVAL '5'"}, materialized_only: false, finalized: true) - SELECT time_bucket('P1D'::interval, bucket) AS bucket, - device_id, - rollup(stats_agg) AS stats_agg - FROM stats_agg_1h_sample - GROUP BY (time_bucket('P1D'::interval, bucket)), device_id - SQL - - create_continuous_aggregate("stats_agg_monthly_sample", <<-SQL, refresh_policies: { start_offset: "INTERVAL '3 mons'", end_offset: "INTERVAL '01:00:00'", schedule_interval: "INTERVAL '5'"}, materialized_only: false, finalized: true) - SELECT time_bucket('P1M'::interval, bucket) AS bucket, - device_id, - rollup(stats_agg) AS stats_agg - FROM stats_agg_1d_sample - GROUP BY (time_bucket('P1M'::interval, bucket)), device_id + create_hypertable "plays", time_column: "created_at", chunk_time_interval: "1 day", compress_segmentby: "game_id", compress_orderby: "created_at ASC", compress_after: "P7D" + create_continuous_aggregate("score_per_hours", <<-SQL, materialized_only: true, finalized: true) + SELECT game_id, + time_bucket('PT1H'::interval, created_at) AS bucket, + avg(score) AS avg, + max(score) AS max, + min(score) AS min + FROM plays + GROUP BY game_id, (time_bucket('PT1H'::interval, created_at)) SQL end diff --git a/lib/tasks/timescaledb.rake b/lib/tasks/timescaledb.rake new file mode 100644 index 0000000..4089997 --- /dev/null +++ b/lib/tasks/timescaledb.rake @@ -0,0 +1,18 @@ +# Utility rake task to update the timescaledb extension +# This is necessary to run before running migrations. +# Load this rake task in your Rakefile +# require 'timescaledb/tasks' + +namespace :timescaledb do + desc "Update TimescaleDB extension (must be run as the first command in a fresh session)" + task :update_extension => :environment do + Timescaledb.establish_connection ENV["DATABASE_URL"] + Timescaledb::Extension.update! + end + + desc "Show TimescaleDB extension version" + task :version => :environment do + Timescaledb.establish_connection ENV["DATABASE_URL"] + puts "TimescaleDB extension version: #{Timescaledb::Extension.version}" + end +end diff --git a/lib/timescaledb/extension.rb b/lib/timescaledb/extension.rb index 32d9f84..f4d06d8 100644 --- a/lib/timescaledb/extension.rb +++ b/lib/timescaledb/extension.rb @@ -17,7 +17,7 @@ def installed? end def update! - Timescaledb.connection.execute('ALTER EXTENSION timescaledb UPDATE') + Timescaledb.connection.query_first('ALTER EXTENSION timescaledb UPDATE') end end end diff --git a/lib/timescaledb/scenic/extension.rb b/lib/timescaledb/scenic/extension.rb index e1efac7..98352d0 100644 --- a/lib/timescaledb/scenic/extension.rb +++ b/lib/timescaledb/scenic/extension.rb @@ -67,5 +67,5 @@ def create_scenic_continuous_aggregate(name) end -Scenic::Adapters::Postgres.include(Timescaledb::Scenic::Extension) +Scenic::Adapters::Postgres.prepend(Timescaledb::Scenic::Extension) ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(Timescaledb::Scenic::MigrationHelpers) From 31aa0016d620e92406a62313a78ed00fedd8fbbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B4natas=20Davi=20Paganini?= Date: Tue, 4 Mar 2025 14:39:43 +0100 Subject: [PATCH 2/4] Scenic include instead of prepend --- .github/workflows/test.yml | 2 +- lib/timescaledb.rb | 1 + lib/timescaledb/scenic/extension.rb | 7 ++++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 290837a..0088ad3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ '3.1.2' ] + ruby: [ '3.3.3' ] gemfile: [ 'Gemfile', 'Gemfile.scenic' ] database: - 'pg17-ts2.17-all' diff --git a/lib/timescaledb.rb b/lib/timescaledb.rb index 60cde6c..2a9811e 100644 --- a/lib/timescaledb.rb +++ b/lib/timescaledb.rb @@ -1,4 +1,5 @@ require 'active_record' +require 'uri' require_relative 'timescaledb/application_record' require_relative 'timescaledb/acts_as_hypertable' diff --git a/lib/timescaledb/scenic/extension.rb b/lib/timescaledb/scenic/extension.rb index 98352d0..7d1a6d1 100644 --- a/lib/timescaledb/scenic/extension.rb +++ b/lib/timescaledb/scenic/extension.rb @@ -26,6 +26,7 @@ def create_materialized_view(name, sql_definition, with: nil, no_data: false) # @override Scenic::Adapters::Postgres#create_view # to add the `with: ` keyword that can be used for such option. def create_view(name, version: nil, with: nil, sql_definition: nil, materialized: false, no_data: false) + if version.present? && sql_definition.present? raise( ArgumentError, @@ -47,7 +48,7 @@ def create_view(name, version: nil, with: nil, sql_definition: nil, materialized with: with ) else - ::Scenic.database.create_view(name, sql_definition, with: with) + ::Scenic.database.create_view(name, sql_definition: sql_definition, with: with) end end @@ -67,5 +68,5 @@ def create_scenic_continuous_aggregate(name) end -Scenic::Adapters::Postgres.prepend(Timescaledb::Scenic::Extension) -ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(Timescaledb::Scenic::MigrationHelpers) +Scenic::Adapters::Postgres.include(Timescaledb::Scenic::Extension) +ActiveRecord::ConnectionAdapters::AbstractAdapter.include(Timescaledb::Scenic::MigrationHelpers) From 71db13d2ccd2ba856f4831fad0775b7b81440b1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B4natas=20Davi=20Paganini?= Date: Tue, 4 Mar 2025 16:30:25 +0100 Subject: [PATCH 3/4] Update Rails version --- Gemfile.lock | 4 ++-- Gemfile.scenic.lock | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 22cac8e..432986a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - timescaledb (0.3.1) + timescaledb (0.3.2) activerecord activesupport ostruct @@ -34,7 +34,7 @@ GEM method_source (1.0.0) minitest (5.18.0) ostruct (0.6.1) - pg (1.5.8) + pg (1.5.9) pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) diff --git a/Gemfile.scenic.lock b/Gemfile.scenic.lock index 3760260..51306c3 100644 --- a/Gemfile.scenic.lock +++ b/Gemfile.scenic.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - timescaledb (0.3.1) + timescaledb (0.3.2) activerecord activesupport ostruct From a02c860f1e80917ce51bc9628868c2d2bd065d70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B4natas=20Davi=20Paganini?= Date: Thu, 27 Mar 2025 12:13:33 +0100 Subject: [PATCH 4/4] Update scenic dependencies --- Gemfile.scenic.lock | 187 ++++++++++++++++++++++++++++---------------- 1 file changed, 118 insertions(+), 69 deletions(-) diff --git a/Gemfile.scenic.lock b/Gemfile.scenic.lock index 51306c3..5e0e0ce 100644 --- a/Gemfile.scenic.lock +++ b/Gemfile.scenic.lock @@ -10,97 +10,146 @@ PATH GEM remote: https://rubygems.org/ specs: - actionpack (6.1.4.1) - actionview (= 6.1.4.1) - activesupport (= 6.1.4.1) - rack (~> 2.0, >= 2.0.9) + actionpack (8.0.2) + actionview (= 8.0.2) + activesupport (= 8.0.2) + nokogiri (>= 1.8.5) + rack (>= 2.2.4) + rack-session (>= 1.0.1) rack-test (>= 0.6.3) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.2.0) - actionview (6.1.4.1) - activesupport (= 6.1.4.1) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + useragent (~> 0.16) + actionview (8.0.2) + activesupport (= 8.0.2) builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.1, >= 1.2.0) - activemodel (6.1.4.1) - activesupport (= 6.1.4.1) - activerecord (6.1.4.1) - activemodel (= 6.1.4.1) - activesupport (= 6.1.4.1) - activesupport (6.1.4.1) - concurrent-ruby (~> 1.0, >= 1.0.2) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activemodel (8.0.2) + activesupport (= 8.0.2) + activerecord (8.0.2) + activemodel (= 8.0.2) + activesupport (= 8.0.2) + timeout (>= 0.4.0) + activesupport (8.0.2) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) - tzinfo (~> 2.0) - zeitwerk (~> 2.3) - builder (3.2.4) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) + base64 (0.2.0) + benchmark (0.4.0) + bigdecimal (3.1.9) + builder (3.3.0) coderay (1.1.3) - concurrent-ruby (1.1.9) + concurrent-ruby (1.3.5) + connection_pool (2.5.0) crass (1.0.6) - database_cleaner-active_record (2.0.1) + database_cleaner-active_record (2.2.0) activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) - diff-lcs (1.4.4) - dotenv (2.7.6) - erubi (1.10.0) - gemika (0.6.1) - i18n (1.8.11) + date (3.4.1) + diff-lcs (1.6.1) + dotenv (3.1.7) + drb (2.2.1) + erubi (1.13.1) + gemika (0.8.4) + i18n (1.14.7) concurrent-ruby (~> 1.0) - loofah (2.12.0) + io-console (0.8.0) + irb (1.15.1) + pp (>= 0.6.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + logger (1.7.0) + loofah (2.24.0) crass (~> 1.0.2) - nokogiri (>= 1.5.9) - method_source (1.0.0) - mini_portile2 (2.6.1) - minitest (5.14.4) - nokogiri (1.12.5) - mini_portile2 (~> 2.6.1) + nokogiri (>= 1.12.0) + method_source (1.1.0) + mini_portile2 (2.8.8) + minitest (5.25.5) + nokogiri (1.18.6) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) + nokogiri (1.18.6-x86_64-darwin) racc (~> 1.4) ostruct (0.6.1) pg (1.5.9) - pry (0.14.1) + pp (0.6.2) + prettyprint + prettyprint (0.2.0) + pry (0.15.2) coderay (~> 1.1) method_source (~> 1.0) - racc (1.6.0) - rack (2.2.3) - rack-test (1.1.0) - rack (>= 1.0, < 3) - rails-dom-testing (2.0.3) - activesupport (>= 4.2.0) + psych (5.2.3) + date + stringio + racc (1.8.1) + rack (3.1.12) + rack-session (2.1.0) + base64 (>= 0.1.0) + rack (>= 3.0.0) + rack-test (2.2.0) + rack (>= 1.3) + rackup (2.2.1) + rack (>= 3) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.4.2) - loofah (~> 2.3) - railties (6.1.4.1) - actionpack (= 6.1.4.1) - activesupport (= 6.1.4.1) - method_source - rake (>= 0.13) - thor (~> 1.0) + rails-html-sanitizer (1.6.2) + loofah (~> 2.21) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + railties (8.0.2) + actionpack (= 8.0.2) + activesupport (= 8.0.2) + irb (~> 1.13) + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) rake (12.3.3) - rspec (3.10.0) - rspec-core (~> 3.10.0) - rspec-expectations (~> 3.10.0) - rspec-mocks (~> 3.10.0) - rspec-core (3.10.1) - rspec-support (~> 3.10.0) - rspec-expectations (3.10.1) + rdoc (6.13.0) + psych (>= 4.0.0) + reline (0.6.0) + io-console (~> 0.5) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.3) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.3) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-its (1.3.0) - rspec-core (>= 3.0.0) - rspec-expectations (>= 3.0.0) - rspec-mocks (3.10.2) + rspec-support (~> 3.13.0) + rspec-its (2.0.0) + rspec-core (>= 3.13.0) + rspec-expectations (>= 3.13.0) + rspec-mocks (3.13.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-support (3.10.3) - scenic (1.5.4) + rspec-support (~> 3.13.0) + rspec-support (3.13.2) + scenic (1.8.0) activerecord (>= 4.0.0) railties (>= 4.0.0) - thor (1.1.0) - tzinfo (2.0.4) + securerandom (0.4.1) + stringio (3.1.6) + thor (1.3.2) + timeout (0.4.3) + tzinfo (2.0.6) concurrent-ruby (~> 1.0) - zeitwerk (2.5.1) + uri (1.0.3) + useragent (0.16.11) + zeitwerk (2.7.2) PLATFORMS ruby @@ -118,4 +167,4 @@ DEPENDENCIES timescaledb! BUNDLED WITH - 2.3.7 + 2.6.5