TimescaleDB Ruby Gem¶
The Timescale SDK for Ruby
A Ruby gem for working with TimescaleDB - an open-source time-series database built on PostgreSQL. This gem provides ActiveRecord integration and helpful tools for managing time-series data.
What is TimescaleDB?¶
TimescaleDB extends PostgreSQL with specialized features for time-series data:
- Hypertables: Automatically partitioned tables optimized for time-series data
- Hypercores: Hypercore is a dynamic storage engine that allows you to store data in a way that is optimized for time-series data
- Chunks: Transparent table partitions that improve query performance
- Continuous Aggregates: Materialized views that automatically update
- Data Compression: Automatic compression of older data
- Data Retention: Policies for managing data lifecycle
Installation¶
Add to your Gemfile:
Or install directly:
Quick Start¶
1. Create Hypertables in Migrations¶
class CreateEvents < ActiveRecord::Migration[7.0]
def up
hypertable_options = {
time_column: 'created_at',
chunk_time_interval: '1 day',
compress_segmentby: 'identifier',
compress_orderby: 'created_at DESC',
compress_after: '7 days',
drop_after: '3 months',
partition_column: 'user_id', # Optional: Add space partitioning
number_partitions: 4 # Required when using partition_column
}
create_table(:events, id: false, hypertable: hypertable_options) do |t|
t.timestamptz :created_at
t.string :identifier, null: false
t.jsonb :payload
t.integer :user_id
end
end
end
2. Enable TimescaleDB in Your Models¶
Global Configuration¶
# config/initializers/timescaledb.rb
ActiveSupport.on_load(:active_record) { extend Timescaledb::ActsAsHypertable }
# app/models/event.rb
class Event < ActiveRecord::Base
acts_as_hypertable time_column: "time",
segment_by: "identifier",
value_column: "cast(payload->>'value' as float)"
end
Per-Model Configuration¶
class Event < ActiveRecord::Base
extend Timescaledb::ActsAsHypertable
acts_as_hypertable time_column: "time"
end
Abstract Model Configuration¶
class Hypertable < ActiveRecord::Base
self.abstract_class = true
extend Timescaledb::ActsAsHypertable
end
class Event < Hypertable
acts_as_hypertable time_column: "time"
end
Advanced Features¶
We're always looking for ways to improve the gem and make it easier to use. Feel free to open an issue or a PR if you have any ideas or suggestions.
Scenic Integration¶
The gem integrates with the Scenic gem for managing database views:
class CreateAnalyticsView < ActiveRecord::Migration[7.0]
def change
create_view :daily_analytics,
materialized: true,
version: 1,
with: "timescaledb.continuous"
end
end
Compression Settings¶
Access and configure compression settings:
# Check compression settings
Event.hypertable.compression_settings
# Get compression stats
Event.hypertable.compression_stats
# Compress specific chunks
Event.chunks.uncompressed.where("end_time < ?", 1.week.ago).each(&:compress!)
Job Management¶
Monitor and manage background jobs:
# List all jobs
Timescaledb::Job.all
# Check compression jobs
Timescaledb::Job.compression.scheduled
# View job statistics
Timescaledb::JobStats.success.resume
Dimensions and Partitioning¶
Access information about table dimensions:
# Get main time dimension
Event.hypertable.main_dimension
# Check all dimensions
Event.hypertable.dimensions
Extension Management¶
Manage the TimescaleDB extension:
# Check version
Timescaledb::Extension.version
# Check if installed
Timescaledb::Extension.installed?
# Update extension
Timescaledb::Extension.update!
Schema Dumper¶
The gem enhances Rails' schema dumper to handle TimescaleDB-specific features:
- Hypertable configurations
- Compression settings
- Continuous aggregates
- Retention policies
- Space partitioning
Continuous Aggregates Macro¶
The gem provides a macro for creating continuous aggregates:
class Achievement < ActiveRecord::Base
extend Timescaledb::ActsAsHypertable
acts_as_hypertable time_column: "time", segment_by: "user_id", value_column: "points"
scope :count_by_user, -> { group(:user_id).count }
scope :points_by_user, -> { group(:user_id).sum(:points) }
continuous_aggregate scopes: [:count_by_user, :points_by_user],
timeframes: [:hour, :day, :month]
end
class CreateAchievements < ActiveRecord::Migration[7.0]
def up
hypertable = {
time_column: "created_at",
segment_by: "user_id",
value_column: "points"
}
create_table :achievements, id: false, hypertable: hypertable do |t|
t.timestamptz :created_at, default: -> { "now()" }
t.integer :user_id, null: false
t.integer :points, null: false, default: 1
end
Achievement.create_continuous_aggregate
end
def down
Achievement.drop_continuous_aggregate
drop_table :achievements
end
end
Check the blog post for more details: building a better Ruby ORM for time series data.
Testing Support¶
For testing environments, you can use this RSpec configuration:
# spec/spec_helper.rb
RSpec.configure do |config|
config.before(:suite) do
hypertable_models = ActiveRecord::Base.descendants.select(&:acts_as_hypertable?)
hypertable_models.each do |klass|
next if klass.try(:hypertable).present?
ApplicationRecord.connection.create_hypertable(
klass.table_name,
time_column: klass.hypertable_options[:time_column],
chunk_time_interval: '1 day'
)
end
end
end
Contributing¶
Bug reports and pull requests are welcome on GitHub at https://github.com/timescale/timescaledb-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
You can also connect to the #ruby channel on the TimescaleDB Community Slack.
License¶
The gem is available as open source under the terms of the MIT License.
Code of Conduct¶
Everyone interacting in the Timescale project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.