timescaledb 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/.tool-versions +1 -0
  4. data/.travis.yml +3 -0
  5. data/Gemfile +4 -0
  6. data/Gemfile.lock +11 -3
  7. data/Gemfile.scenic +7 -0
  8. data/Gemfile.scenic.lock +121 -0
  9. data/README.md +267 -121
  10. data/Rakefile +16 -1
  11. data/bin/console +2 -2
  12. data/bin/setup +2 -0
  13. data/bin/tsdb +30 -6
  14. data/examples/{Gemfile → all_in_one/Gemfile} +1 -1
  15. data/examples/{Gemfile.lock → all_in_one/Gemfile.lock} +2 -2
  16. data/examples/{all_in_one.rb → all_in_one/all_in_one.rb} +3 -6
  17. data/examples/ranking/.gitattributes +7 -0
  18. data/examples/ranking/.gitignore +29 -0
  19. data/examples/ranking/.ruby-version +1 -0
  20. data/examples/ranking/Gemfile +33 -0
  21. data/examples/ranking/Gemfile.lock +189 -0
  22. data/examples/ranking/README.md +166 -0
  23. data/examples/ranking/Rakefile +6 -0
  24. data/examples/ranking/app/controllers/application_controller.rb +2 -0
  25. data/examples/ranking/app/controllers/concerns/.keep +0 -0
  26. data/examples/ranking/app/jobs/application_job.rb +7 -0
  27. data/examples/ranking/app/models/application_record.rb +3 -0
  28. data/examples/ranking/app/models/concerns/.keep +0 -0
  29. data/examples/ranking/app/models/game.rb +2 -0
  30. data/examples/ranking/app/models/play.rb +7 -0
  31. data/examples/ranking/bin/bundle +114 -0
  32. data/examples/ranking/bin/rails +4 -0
  33. data/examples/ranking/bin/rake +4 -0
  34. data/examples/ranking/bin/setup +33 -0
  35. data/examples/ranking/config/application.rb +39 -0
  36. data/examples/ranking/config/boot.rb +4 -0
  37. data/examples/ranking/config/credentials.yml.enc +1 -0
  38. data/examples/ranking/config/database.yml +86 -0
  39. data/examples/ranking/config/environment.rb +5 -0
  40. data/examples/ranking/config/environments/development.rb +60 -0
  41. data/examples/ranking/config/environments/production.rb +75 -0
  42. data/examples/ranking/config/environments/test.rb +53 -0
  43. data/examples/ranking/config/initializers/cors.rb +16 -0
  44. data/examples/ranking/config/initializers/filter_parameter_logging.rb +8 -0
  45. data/examples/ranking/config/initializers/inflections.rb +16 -0
  46. data/examples/ranking/config/initializers/timescale.rb +3 -0
  47. data/examples/ranking/config/locales/en.yml +33 -0
  48. data/examples/ranking/config/puma.rb +43 -0
  49. data/examples/ranking/config/routes.rb +6 -0
  50. data/examples/ranking/config/storage.yml +34 -0
  51. data/examples/ranking/config.ru +6 -0
  52. data/examples/ranking/db/migrate/20220209120747_create_games.rb +10 -0
  53. data/examples/ranking/db/migrate/20220209120910_create_plays.rb +19 -0
  54. data/examples/ranking/db/migrate/20220209143347_create_score_per_hours.rb +5 -0
  55. data/examples/ranking/db/schema.rb +47 -0
  56. data/examples/ranking/db/seeds.rb +7 -0
  57. data/examples/ranking/db/views/score_per_hours_v01.sql +7 -0
  58. data/examples/ranking/lib/tasks/.keep +0 -0
  59. data/examples/ranking/log/.keep +0 -0
  60. data/examples/ranking/public/robots.txt +1 -0
  61. data/examples/ranking/storage/.keep +0 -0
  62. data/examples/ranking/tmp/.keep +0 -0
  63. data/examples/ranking/tmp/pids/.keep +0 -0
  64. data/examples/ranking/tmp/storage/.keep +0 -0
  65. data/examples/ranking/vendor/.keep +0 -0
  66. data/lib/timescaledb/acts_as_hypertable/core.rb +87 -0
  67. data/lib/timescaledb/acts_as_hypertable.rb +62 -0
  68. data/lib/{timescale → timescaledb}/chunk.rb +9 -1
  69. data/lib/{timescale → timescaledb}/compression_settings.rb +3 -2
  70. data/lib/timescaledb/continuous_aggregates.rb +19 -0
  71. data/lib/timescaledb/dimensions.rb +6 -0
  72. data/lib/{timescale → timescaledb}/hypertable.rb +9 -5
  73. data/lib/timescaledb/job.rb +10 -0
  74. data/lib/{timescale → timescaledb}/job_stats.rb +3 -4
  75. data/lib/{timescale → timescaledb}/migration_helpers.rb +35 -5
  76. data/lib/timescaledb/scenic/adapter.rb +55 -0
  77. data/lib/timescaledb/scenic/extension.rb +72 -0
  78. data/lib/timescaledb/schema_dumper.rb +88 -0
  79. data/lib/timescaledb/stats_report.rb +35 -0
  80. data/lib/timescaledb/version.rb +3 -0
  81. data/lib/timescaledb.rb +64 -0
  82. data/{timescale.gemspec → timescaledb.gemspec} +6 -4
  83. metadata +106 -20
  84. data/lib/timescale/acts_as_hypertable.rb +0 -114
  85. data/lib/timescale/continuous_aggregates.rb +0 -9
  86. data/lib/timescale/job.rb +0 -13
  87. data/lib/timescale/stats_report.rb +0 -28
  88. data/lib/timescale/version.rb +0 -3
  89. data/lib/timescale.rb +0 -52
data/README.md CHANGED
@@ -1,48 +1,38 @@
1
- # Timescale
1
+ # TimescaleDB
2
2
 
3
- Welcome to the Timescale gem! To experiment with the code, start cloning the
4
- repository:
3
+ Welcome to the TimescaleDB gem! To experiment with the code, start installing the
4
+ gem:
5
5
 
6
6
  ```bash
7
- git clone https://github.com/jonatas/timescale.git
8
- cd timescale
9
- bundle install
10
- rake install
7
+ gem install timescaledb
11
8
  ```
12
9
 
13
- Then, with `rake install` or installing the gem in your computer, you can run `tsdb` for an interactive prompt.
10
+ ## The `tsdb` CLI
14
11
 
15
- ```bash
16
- tsdb postgres://<user>@localhost:5432/<dbname> --stats --flags
17
- ```
12
+ When you install the gem locally, a new command line application named `tsdb`
13
+ will be linked in your command line.
18
14
 
19
- You can create a `.env` file locally to run tests locally. Make sure to put your
20
- own credentials there!
15
+ It accepts a Postgresql URI and some extra flags that can help you to get more
16
+ info from your TimescaleDB server:
21
17
 
22
18
  ```bash
23
- PG_URI_TEST="postgres://<user>@localhost:5432/<dbname>"
19
+ tsdb <uri> --stats
24
20
  ```
25
21
 
26
- You can put some postgres URI directly as a parameter of
27
- `tsdb`. Here is an example from the console:
22
+ Where the `<uri>` is replaced with params from your connection like:
28
23
 
29
24
  ```bash
30
- tsdb "postgres://jonatasdp@localhost:5432/timescale_test"
25
+ tsdb postgres://<user>@localhost:5432/<dbname> --stats
31
26
  ```
32
27
 
33
- To join the console use `--console`:
34
-
35
- ```bash
36
- tsdb "postgres://jonatasdp@localhost:5432/timescale_test" --console
37
- ```
38
28
 
39
29
  Or just check the stats:
40
30
 
41
31
  ```bash
42
- tsdb "postgres://jonatasdp@localhost:5432/timescale_test" --stats
32
+ tsdb "postgres://jonatasdp@localhost:5432/timescaledb_test" --stats
43
33
  ```
44
34
 
45
- These is a sample output from an almost empty database:
35
+ These is a sample output from database example with almost no data:
46
36
 
47
37
  ```ruby
48
38
  {:hypertables=>
@@ -54,19 +44,153 @@ These is a sample output from an almost empty database:
54
44
  :jobs_stats=>[{:success=>nil, :runs=>nil, :failures=>nil}]}
55
45
  ```
56
46
 
47
+ To start a interactive ruby/[pry](https://github.com/pry/pry) console use `--console`:
57
48
  The console will dynamically create models for all hypertables that it finds
58
49
  in the database.
59
50
 
60
- It will allow you to visit any database and have all models mapped as ActiveRecord
61
- with the [HypertableHelpers](lib/timescale/hypertable_helpers.rb).
51
+ Let's consider the [caggs.sql](https://gist.github.com/jonatas/95573ad8744994094ec9f284150004f9#file-caggs-sql)
52
+ as the example of database.
62
53
 
63
- This library was started on [twitch.tv/timescaledb](https://twitch.tv/timescaledb).
64
- You can watch all episodes here:
65
54
 
66
- 1. [Wrapping Functions to Ruby Helpers](https://www.youtube.com/watch?v=hGPsUxLFAYk).
67
- 2. [Extending ActiveRecord with Timescale Helpers](https://www.youtube.com/watch?v=IEyJIHk1Clk).
68
- 3. [Setup Hypertables for Rails testing environment](https://www.youtube.com/watch?v=wM6hVrZe7xA).
69
- 4. [Packing the code to this repository](https://www.youtube.com/watch?v=CMdGAl_XlL4).
55
+ ```bash
56
+ psql postgres://jonatasdp@localhost:5432/playground -f caggs.sql
57
+ ```
58
+
59
+ Then use `tsdb` in the command line with the same URI and `--stats`:
60
+
61
+ ```bash
62
+ tsdb postgres://jonatasdp@localhost:5432/playground --stats
63
+ {:hypertables=>
64
+ {:count=>1,
65
+ :uncompressed=>1,
66
+ :approximate_row_count=>{"ticks"=>352},
67
+ :chunks=>{:total=>1, :compressed=>0, :uncompressed=>1},
68
+ :size=>{:uncompressed=>"88 KB", :compressed=>"0 Bytes"}},
69
+ :continuous_aggregates=>{:total=>1},
70
+ :jobs_stats=>[{:success=>nil, :runs=>nil, :failures=>nil}]}
71
+ ```
72
+
73
+ To have some interactive playground with the actual database using ruby, just
74
+ try the same command before changing from `--stats` to `--console`:
75
+
76
+ ### tsdb --console
77
+
78
+ The same database from previous example, is used so
79
+ the context has a hypertable named `ticks` and a view named `ohlc_1m`.
80
+
81
+
82
+ ```ruby
83
+ tsdb postgres://jonatasdp@localhost:5432/playground --console
84
+ pry(Timescale)>
85
+ ```
86
+
87
+ The `tsdb` CLI will automatically create ActiveRecord models for hypertables and
88
+ continuous aggregates views.
89
+
90
+ ```ruby
91
+ Tick
92
+ => Timescaledb::Tick(time: datetime, symbol: string, price: decimal, volume: integer)
93
+ ```
94
+
95
+ Note that it's only created for this session and will never be cached in the
96
+ library or any other place.
97
+
98
+ In this case, `Tick` model comes from `ticks` hypertable that was found in the database.
99
+ It contains several extra methods inherited from `acts_as_hypertable` macro.
100
+
101
+ Let's start with the `.hypertable` method.
102
+
103
+ ```ruby
104
+ Tick.hypertable
105
+ => #<Timescaledb::Hypertable:0x00007fe99c258900
106
+ hypertable_schema: "public",
107
+ hypertable_name: "ticks",
108
+ owner: "jonatasdp",
109
+ num_dimensions: 1,
110
+ num_chunks: 1,
111
+ compression_enabled: false,
112
+ is_distributed: false,
113
+ replication_factor: nil,
114
+ data_nodes: nil,
115
+ tablespaces: nil>
116
+ ```
117
+
118
+ The core of the hypertables are the fragmentation of the data into chunks that
119
+ are the child tables that distribute the data. You can check all chunks directly
120
+ from the hypertable relation.
121
+
122
+ ```ruby
123
+ Tick.hypertable.chunks
124
+ unknown OID 2206: failed to recognize type of 'primary_dimension_type'. It will be treated as String.
125
+ => [#<Timescaledb::Chunk:0x00007fe99c31b068
126
+ hypertable_schema: "public",
127
+ hypertable_name: "ticks",
128
+ chunk_schema: "_timescaledb_internal",
129
+ chunk_name: "_hyper_33_17_chunk",
130
+ primary_dimension: "time",
131
+ primary_dimension_type: "timestamp without time zone",
132
+ range_start: 1999-12-30 00:00:00 +0000,
133
+ range_end: 2000-01-06 00:00:00 +0000,
134
+ range_start_integer: nil,
135
+ range_end_integer: nil,
136
+ is_compressed: false,
137
+ chunk_tablespace: nil,
138
+ data_nodes: nil>]
139
+ ```
140
+
141
+ > Chunks are created by partitioning a hypertable's data into one
142
+ > (or potentially multiple) dimensions. All hypertables are partitioned by the
143
+ > values belonging to a time column, which may be in timestamp, date, or
144
+ > various integer forms. If the time partitioning interval is one day,
145
+ > for example, then rows with timestamps that belong to the same day are co-located
146
+ > within the same chunk, while rows belonging to different days belong to different chunks.
147
+ > Learn more [here](https://docs.timescale.com/timescaledb/latest/overview/core-concepts/hypertables-and-chunks/).
148
+
149
+ Another core concept of TimescaleDB is compression. With data partitioned, it
150
+ becomes very convenient to compress and decompress chunks independently.
151
+
152
+ ```ruby
153
+ Tick.hypertable.chunks.first.compress!
154
+ ActiveRecord::StatementInvalid: PG::FeatureNotSupported: ERROR: compression not enabled on "ticks"
155
+ DETAIL: It is not possible to compress chunks on a hypertable that does not have compression enabled.
156
+ HINT: Enable compression using ALTER TABLE with the timescaledb.compress option.
157
+ ```
158
+
159
+ As compression is not enabled, let's do it executing a plain SQL directly from
160
+ the actual context. To borrow a connection, let's use the Tick object.
161
+
162
+ ```ruby
163
+ Tick.connection.execute("ALTER TABLE ticks SET (timescaledb.compress)") # => PG_OK
164
+ ```
165
+
166
+ And now, it's possible to compress and decompress:
167
+
168
+ ```ruby
169
+ Tick.hypertable.chunks.first.compress!
170
+ Tick.hypertable.chunks.first.decompress!
171
+ ```
172
+ Learn more about TimescaleDB compression [here](https://docs.timescale.com/timescaledb/latest/overview/core-concepts/compression/).
173
+
174
+ The `ohlc_1m` view is also available as an ActiveRecord:
175
+
176
+ ```ruby
177
+ Ohlc1m
178
+ => Timescaledb::Ohlc1m(bucket: datetime, symbol: string, open: decimal, high: decimal, low: decimal, close: decimal, volume: integer)
179
+ ```
180
+
181
+ And you can run any query as you do with regular active record queries.
182
+
183
+ ```ruby
184
+ Ohlc1m.order(bucket: :desc).last
185
+ => #<Timescaledb::Ohlc1m:0x00007fe99c2c38e0
186
+ bucket: 2000-01-01 00:00:00 UTC,
187
+ symbol: "SYMBOL",
188
+ open: 0.13e2,
189
+ high: 0.3e2,
190
+ low: 0.1e1,
191
+ close: 0.1e2,
192
+ volume: 27600>
193
+ ```
70
194
 
71
195
  ## Installation
72
196
 
@@ -84,18 +208,26 @@ Or install it yourself as:
84
208
 
85
209
  $ gem install timescaledb
86
210
 
211
+
87
212
  ## Usage
88
213
 
89
- You can check the [all_in_one.rb](examples/all_in_one.rb) that will:
214
+ Check the [examples/ranking](examples/ranking) to get a Rails complete example.
215
+
216
+ You can check the [all_in_one.rb](examples/all_in_one/all_in_one.rb) example that will:
90
217
 
91
218
  1. Create hypertable with compression settings
92
219
  2. Insert data
93
- 3. Run some queries from HypertableHelpers
220
+ 3. Run some queries
94
221
  4. Check chunk size per model
95
222
  5. Compress a chunk
96
223
  6. Check chunk status
97
224
  7. Decompress a chunk
98
225
 
226
+ ### Testing
227
+
228
+ If you need some inspiration for how are you going to test your hypertables,
229
+ please check the [spec/spec_helper.rb](spec/spec_helper.rb) for inspiration.
230
+
99
231
  ### Migrations
100
232
 
101
233
  Create table is now with the `hypertable` keyword allowing to pass a few options
@@ -118,7 +250,7 @@ create_table(:events, id: false, hypertable: hypertable_options) do |t|
118
250
  end
119
251
  ```
120
252
 
121
- #### create_continuous_aggregates
253
+ #### create_continuous_aggregate
122
254
 
123
255
  This example shows a ticks table grouping ticks as OHLCV histograms for every
124
256
  minute.
@@ -140,7 +272,7 @@ end
140
272
  Tick = Class.new(ActiveRecord::Base) do
141
273
  self.table_name = 'ticks'
142
274
  self.primary_key = 'symbol'
143
- include Timescale::HypertableHelpers
275
+ acts_as_hypertable
144
276
  end
145
277
 
146
278
  query = Tick.select(<<~QUERY)
@@ -162,142 +294,156 @@ options = {
162
294
  }
163
295
  }
164
296
 
165
- create_continuous_aggregates('ohlc_1m', query, **options)
297
+ create_continuous_aggregate('ohlc_1m', query, **options)
166
298
  ```
167
299
 
168
- ### Hypertable Helpers
300
+ #### Scenic integration
301
+
302
+ The [Scenic](https://github.com/scenic-views/scenic) gem is an easy way to
303
+ manage database view definitions for a Rails application. TimescaleDB's
304
+ continuous aggregates are more complex than regular PostgreSQL views, and
305
+ the schema dumper included with Scenic can't dump a complete definition.
169
306
 
170
- You can also use `HypertableHelpers` to get access to some basic scopes for your
307
+ This gem automatically configures Scenic to use a `Timescaledb::Scenic::Adapter`
308
+ which will correctly handle schema dumping.
309
+
310
+ ### Enable ActsAsHypertable
311
+
312
+ 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.
171
313
  model:
172
314
 
173
315
  ```ruby
174
316
  class Event < ActiveRecord::Base
175
- self.primary_key = "identifier"
176
-
177
- include Timescale::HypertableHelpers
317
+ acts_as_hypertable
178
318
  end
179
319
  ```
180
320
 
181
- After including the helpers, several methods from timescaledb will be available in the
182
- model.
321
+ By default, ActsAsHypertable assumes a record's _time_column_ is called `created_at`.
183
322
 
184
- ### Chunks
323
+ ### Options
185
324
 
186
- To get chunks from a single hypertable, you can use the `.chunks` directly from
187
- the model name.
325
+ If you are using a different time_column name, you can specify it as follows when invoking the `acts_as_hypertable` macro:
188
326
 
189
327
  ```ruby
190
- Event.chunks
191
- # DEBUG: Timescale::Chunk Load (9.0ms) SELECT "timescaledb_information"."chunks".* FROM "timescaledb_information"."chunks" WHERE "timescaledb_information"."chunks"."hypertable_name" = $1 [["hypertable_name", "events"]]
192
- # => [#<Timescale::Chunk:0x00007f94b0c86008
193
- # hypertable_schema: "public",
194
- # hypertable_name: "events",
195
- # chunk_schema: "_timescaledb_internal",
196
- # chunk_name: "_hyper_180_74_chunk",
197
- # primary_dimension: "created_at",
198
- # primary_dimension_type: "timestamp without time zone",
199
- # range_start: 2021-09-22 21:28:00 +0000,
200
- # range_end: 2021-09-22 21:29:00 +0000,
201
- # range_start_integer: nil,
202
- # range_end_integer: nil,
203
- # is_compressed: false,
204
- # chunk_tablespace: nil,
205
- # data_nodes: nil>
328
+ class Event < ActiveRecord::Base
329
+ acts_as_hypertable time_column: :timestamp
330
+ end
206
331
  ```
207
332
 
208
- To get all hypertables you can use `Timescale.hypertables` method.
333
+ ### Chunks
334
+
335
+ To get all the chunks from a model's hypertable, you can use `.chunks`.
336
+
337
+ ```ruby
338
+ Event.chunks # => [#<Timescaledb::Chunk>, ...]
339
+ ```
209
340
 
210
- ### Hypertable metadata from model
341
+ ### Hypertable metadata
211
342
 
212
- To get all details from hypertable, you can access the `.hypertable` from the
213
- model.
343
+ To get the models' hypertable metadata, you can use `.hypertable`.
214
344
 
215
345
  ```ruby
216
- Event.hypertable
217
- # Timescale::Hypertable Load (4.8ms) SELECT "timescaledb_information"."hypertables".* FROM "timescaledb_information"."hypertables" WHERE "timescaledb_information"."hypertables"."hypertable_name" = $1 LIMIT $2 [["hypertable_name", "events"], ["LIMIT", 1]]
218
- # => #<Timescale::Hypertable:0x00007f94c3151cd8
219
- # hypertable_schema: "public",
220
- # hypertable_name: "events",
221
- # owner: "jonatasdp",
222
- # num_dimensions: 1,
223
- # num_chunks: 1,
224
- # compression_enabled: true,
225
- # is_distributed: false,
226
- # replication_factor: nil,
227
- # data_nodes: nil,
228
- # tablespaces: nil>
346
+ Event.hypertable # => #<Timescaledb::Hypertable>
229
347
  ```
230
348
 
231
- You can also use `Timescale.hypertables` to have access of all hypertables
232
- metadata.
349
+ To get hypertable metadata for all hypertables: `Timescaledb.hypertables`.
233
350
 
234
351
  ### Compression Settings
235
352
 
236
353
  Compression settings are accessible through the hypertable.
237
354
 
238
355
  ```ruby
239
- Event.hypertable.compression_settings
240
- # Timescale::Hypertable Load (1.2ms) SELECT "timescaledb_information"."hypertables".* FROM "timescaledb_information"."hypertables" WHERE "timescaledb_information"."hypertables"."hypertable_name" = $1 LIMIT $2 [["hypertable_name", "events"], ["LIMIT", 1]]
241
- # Timescale::CompressionSettings Load (1.2ms) SELECT "timescaledb_information"."compression_settings".* FROM "timescaledb_information"."compression_settings" WHERE "timescaledb_information"."compression_settings"."hypertable_name" = $1 [["hypertable_name", "events"]]
242
- # => [#<Timescale::CompressionSettings:0x00007f94b0bf7010
243
- # hypertable_schema: "public",
244
- # hypertable_name: "events",
245
- # attname: "identifier",
246
- # segmentby_column_index: 1,
247
- # orderby_column_index: nil,
248
- # orderby_asc: nil,
249
- # orderby_nullsfirst: nil>,
250
- # #<Timescale::CompressionSettings:0x00007f94b0c3e460
251
- # hypertable_schema: "public",
252
- # hypertable_name: "events",
253
- # attname: "created_at",
254
- # segmentby_column_index: nil,
255
- # orderby_column_index: 1,
256
- # orderby_asc: true,
257
- # orderby_nullsfirst: false>]
356
+ Event.hypertable.compression_settings # => [#<Timescaledb::CompressionSettings>, ...]
258
357
  ```
259
358
 
260
- It's also possible to access all data calling `Timescale.compression_settings`.
359
+ To get compression settings for all hypertables: `Timescaledb.compression_settings`.
360
+
361
+ ### Scopes
362
+
363
+ When you enable ActsAsHypertable on your model, we include a couple default scopes. They are:
364
+
365
+ | Scope name | What they return |
366
+ |------------------------|---------------------------------------|
367
+ | `Model.previous_month` | Records created in the previous month |
368
+ | `Model.previous_week` | Records created in the previous week |
369
+ | `Model.this_month` | Records created this month |
370
+ | `Model.this_week` | Records created this week |
371
+ | `Model.yesterday` | Records created yesterday |
372
+ | `Model.today` | Records created today |
373
+ | `Model.last_hour` | Records created in the last hour |
261
374
 
262
- ### RSpec Hooks
375
+ All time-related scopes respect your application's timezone.
376
+
377
+ ## RSpec Hooks
263
378
 
264
379
  In case you want to use TimescaleDB on a Rails environment, you may have some
265
- issues as the schema dump used for tests is not considering hypertables
266
- metadata.
380
+ issues as the schema dump used for tests does not consider hypertables metadata.
267
381
 
268
- If you add the `Timescale::HypertableHelpers` to your model, you can dynamically
269
- create the hypertable adding this hook to your `spec/rspec_helper.rb` file:
382
+ As a work around, you can dynamically create the hypertables yourself for
383
+ testing environments using the following hook which you can
384
+ define in `spec/rspec_helper.rb`:
270
385
 
271
386
  ```ruby
272
- config.before(:suite) do
273
- hypertable_models = ApplicationRecord
274
- .descendants
275
- .select{|clazz| clazz.ancestors.include?( Timescale::HypertableHelpers)}
276
- hypertable_models.each do |clazz|
277
- if clazz.hypertable.exists?
278
- ApplicationRecord.logger.info "skip recreating hypertable for '#{clazz.table_name}'."
279
- next
280
- end
281
- ApplicationRecord.connection.execute <<~SQL
282
- SELECT create_hypertable('#{clazz.table_name}', 'created_at')
283
- SQL
387
+ config.before(:suite) do
388
+ hypertable_models = ActiveRecord::Base.descendants.select(&:acts_as_hypertable?)
389
+
390
+ hypertable_models.each do |klass|
391
+ table_name = klass.table_name
392
+ time_column = klass.hypertable_options[:time_column]
393
+
394
+ if klass.try(:hypertable).present?
395
+ ApplicationRecord.logger.info "hypertable already created for '#{table_name}', skipping."
396
+ next
284
397
  end
398
+
399
+ ApplicationRecord.connection.execute <<~SQL
400
+ SELECT create_hypertable('#{table_name}', '#{time_column.to_s}')
401
+ SQL
285
402
  end
403
+ end
286
404
  ```
287
405
 
288
406
  ## Development
289
407
 
290
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `tsdb` for an interactive prompt that will allow you to experiment.
408
+ 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.
409
+
410
+ You can also run `tsdb` for an interactive prompt that will allow you to experiment.
291
411
 
292
412
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
293
413
 
414
+ You can create a `.env` file locally to run tests locally. Make sure to put your
415
+ own credentials there!
416
+
417
+ ```bash
418
+ PG_URI_TEST="postgres://<user>@localhost:5432/<dbname>"
419
+ ```
420
+
421
+ You can put some postgres URI directly as a parameter of
422
+ `tsdb`. Here is an example from the console:
423
+
424
+ ```bash
425
+ tsdb "postgres://jonatasdp@localhost:5432/timescaledb_test"
426
+ ```
427
+
428
+ ## More resources
429
+
430
+ This library was started on [twitch.tv/timescaledb](https://twitch.tv/timescaledb).
431
+ You can watch all episodes here:
432
+
433
+ 1. [Wrapping Functions to Ruby Helpers](https://www.youtube.com/watch?v=hGPsUxLFAYk).
434
+ 2. [Extending ActiveRecord with Timescale Helpers](https://www.youtube.com/watch?v=IEyJIHk1Clk).
435
+ 3. [Setup Hypertables for Rails testing environment](https://www.youtube.com/watch?v=wM6hVrZe7xA).
436
+ 4. [Packing the code to this repository](https://www.youtube.com/watch?v=CMdGAl_XlL4).
437
+ 4. [the code to this repository](https://www.youtube.com/watch?v=CMdGAl_XlL4).
438
+ 5. [Working with Timescale continuous aggregates](https://youtu.be/co4HnBkHzVw).
439
+ 6. [Creating the command-line application in Ruby to explore the Timescale API](https://www.youtube.com/watch?v=I3vM_q2m7T0).
440
+
294
441
  ### TODO
295
442
 
296
443
  Here is a list of functions that would be great to have:
297
444
 
298
445
  - [ ] Dump and Restore Timescale metadata - Like db/schema.rb but for Timescale configuration.
299
446
  - [ ] Add data nodes support
300
- - [ ] Implement the `timescale` CLI to explore the full API.
301
447
 
302
448
  ## Contributing
303
449
 
data/Rakefile CHANGED
@@ -1,6 +1,21 @@
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"
13
+
14
+ namespace :test do
15
+ task :setup do
16
+ require_relative "spec/spec_helper"
17
+
18
+ teardown_tables
19
+ setup_tables
20
+ end
21
+ end
data/bin/console CHANGED
@@ -11,7 +11,7 @@ end
11
11
 
12
12
  ActiveRecord::Base.establish_connection(ARGV[0] || uri_from_test)
13
13
 
14
- Timescale::Hypertable.find_each do |hypertable|
14
+ Timescaledb::Hypertable.find_each do |hypertable|
15
15
  class_name = hypertable.hypertable_name.singularize.camelize
16
16
 
17
17
  model = Class.new(ActiveRecord::Base) do
@@ -21,7 +21,7 @@ Timescale::Hypertable.find_each do |hypertable|
21
21
  acts_as_hypertable
22
22
  end
23
23
 
24
- Timescale.const_set(class_name, model)
24
+ Timescaledb.const_set(class_name, model)
25
25
  end
26
26
 
27
27
  require "pry"
data/bin/setup CHANGED
@@ -7,5 +7,7 @@ set -vx
7
7
 
8
8
  bundle install
9
9
 
10
+ bundle install --gemfile Gemfile.scenic
11
+
10
12
  # For running tests it's going to use PG_URI_TEST env variable.
11
13
  # Please make sure you set it properly to a TEST database!"
data/bin/tsdb CHANGED
@@ -1,24 +1,48 @@
1
1
  #!/usr/bin/env ruby
2
2
  require "bundler/setup"
3
3
  require "timescale"
4
+ require "pry"
4
5
 
5
6
  ActiveRecord::Base.establish_connection(ARGV[0])
6
7
 
7
- Timescale::Hypertable.find_each do |hypertable|
8
+ Timescaledb::Hypertable.find_each do |hypertable|
8
9
  class_name = hypertable.hypertable_name.singularize.camelize
9
10
  model = Class.new(ActiveRecord::Base) do
10
11
  self.table_name = hypertable.hypertable_name
11
- self.primary_key = self.column_names.first
12
- include Timescale::HypertableHelpers
12
+ acts_as_hypertable
13
13
  end
14
- Timescale.const_set(class_name, model)
14
+ Timescaledb.const_set(class_name, model)
15
+ end
16
+
17
+ Timescaledb::ContinuousAggregates.find_each do |cagg|
18
+ class_name = cagg.view_name.singularize.camelize
19
+ model = Class.new(ActiveRecord::Base) do
20
+ self.table_name = cagg.view_name
21
+ acts_as_hypertable
22
+ end
23
+ Timescaledb.const_set(class_name, model)
24
+ end
25
+
26
+ def show(obj)
27
+ Pry::ColorPrinter.pp(obj)
15
28
  end
16
29
 
17
30
  if ARGV.index("--stats")
18
- pp Timescale.show_stats
31
+ scope = Timescaledb::Hypertable.all
32
+
33
+ if (only = ARGV.index("--only"))
34
+ only_hypertables = ARGV[only+1].split(",")
35
+ scope = scope.where({hypertable_name: only_hypertables})
36
+ end
37
+
38
+ if (except = ARGV.index("--except"))
39
+ except_hypertables = ARGV[except+1].split(",")
40
+ scope = scope.where.not(hypertable_name: except_hypertables)
41
+ end
42
+
43
+ show(Timescaledb.stats(scope))
19
44
  end
20
45
 
21
46
  if ARGV.index("--console")
22
- require "pry"
23
47
  Pry.start(Timescale)
24
48
  end
@@ -1,7 +1,7 @@
1
1
 
2
2
  source 'https://rubygems.org'
3
3
 
4
- gem "timescale", path: "../"
4
+ gem "timescaledb", path: "../"
5
5
  gem "pg"
6
6
  gem "activerecord"
7
7
  gem "composite_primary_keys", "~> 6.0"
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- timescale (0.1.0)
4
+ timescaledb (0.1.2)
5
5
  activerecord
6
6
  pg (~> 1.2)
7
7
 
@@ -45,7 +45,7 @@ DEPENDENCIES
45
45
  dotenv (~> 2.7)
46
46
  pg
47
47
  pry
48
- timescale!
48
+ timescaledb!
49
49
 
50
50
  BUNDLED WITH
51
51
  2.1.4
@@ -2,16 +2,13 @@ require 'bundler/setup'
2
2
  require 'timescale'
3
3
  require 'pp'
4
4
  require 'pry'
5
- require 'dotenv'
6
- Dotenv.load!
7
- # set PG_URI=postgres://user:pass@host:port/db_name
8
- ActiveRecord::Base.establish_connection(ENV['PG_URI_TEST'])
5
+ # ruby all_in_one.rb postgres://user:pass@host:port/db_name
6
+ ActiveRecord::Base.establish_connection( ARGV.last)
9
7
 
10
8
  # Simple example
11
9
  class Event < ActiveRecord::Base
12
10
  self.primary_key = "identifier"
13
-
14
- include Timescale::HypertableHelpers
11
+ acts_as_hypertable
15
12
  end
16
13
 
17
14
  # Setup Hypertable as in a migration
@@ -0,0 +1,7 @@
1
+ # See https://git-scm.com/docs/gitattributes for more about git attribute files.
2
+
3
+ # Mark the database schema as having been generated.
4
+ db/schema.rb linguist-generated
5
+
6
+ # Mark any vendored files as having been vendored.
7
+ vendor/* linguist-vendored