redis-time-series 0.1.1 → 0.5.1

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: 23852f9c6baca09369bbcce49642070c13d1212ba2ca9e7eec93dd691702eb72
4
- data.tar.gz: 9edac086b6fc8119382bac0777160d96279df597cdf7cb4f84d27a36532118d2
3
+ metadata.gz: 32031c61eb040add6088dc94a31be33c08108d9127da7e006bb377a247340d03
4
+ data.tar.gz: 8d2cccb22bd414a9fefbcbcdda8b52cce58d6d55e4c3ae67750e699e8aca8d60
5
5
  SHA512:
6
- metadata.gz: c8efbc6e6426d316715fc134f71d5d2d7bc334ac2d088340eba39d050185fdbd5cbf2a8d7c6cd80274e9bb3b53766905c30124d551b0d5b863180cb12c89eec0
7
- data.tar.gz: 5a1d988f13fbace8a269b7156fea1858072a15d030439fbad38e2dfcf720503c71182267d424c5557f8bacf61305975c75c35ad2050c8e307c3b46fb8dc8b5f8
6
+ metadata.gz: f6c008474c8e8d3a297f0a2f7953a784ee5a530423c3f6be6f4d9953277bb182ed1bdafe2edb52964afdfac9c4d2cd66be32a87e1f3ac7290e011a0059b94464
7
+ data.tar.gz: 043e562af28011213dfeb6692e61b803a84dbb87cff0d49fad7f9fc46e2650f19b44f5637efeb5810ecefaff1683ee4f14bd41cc0a88bb90808480f9f7f9a616
@@ -5,6 +5,8 @@ on:
5
5
  branches: [ master ]
6
6
  pull_request:
7
7
  branches: [ master ]
8
+ schedule:
9
+ - cron: '0 0 * * *'
8
10
 
9
11
  jobs:
10
12
  spec:
@@ -14,13 +16,28 @@ jobs:
14
16
  image: redislabs/redistimeseries:latest
15
17
  ports:
16
18
  - 6379:6379/tcp
19
+ env:
20
+ CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
21
+ GIT_COMMIT_SHA: ${{ github.sha }}
22
+ GIT_BRANCH: ${{ github.head_ref }}
17
23
  steps:
18
24
  - uses: actions/checkout@v2
19
25
  - name: Set up Ruby
20
26
  uses: ruby/setup-ruby@v1
21
27
  with:
22
28
  ruby-version: 2.6
29
+ - name: Set up CodeClimate
30
+ run: |
31
+ curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
32
+ chmod +x ./cc-test-reporter
33
+ ./cc-test-reporter before-build
23
34
  - name: Install dependencies
24
35
  run: bundle install
25
36
  - name: Run specs
26
37
  run: bundle exec rake spec
38
+ - name: Upload coverage report
39
+ run: ./cc-test-reporter after-build -t simplecov coverage/.resultset.json
40
+ - uses: actions/upload-artifact@v2
41
+ with:
42
+ name: coverage
43
+ path: coverage/
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 0.5.1
6
+ * Update Info struct for RTS 1.4 compatibility (#45)
7
+
8
+ ## 0.5.0
9
+ * Fix aggregations for TS.RANGE command (#34)
10
+ * Extract client handling into Client module (#32)
11
+ * Add `uncompressed` param to TS.ADD, TS.INCRBY, TS.DECRBY (#35)
12
+ * Add `Redis::TimeSeries::Rule` object (#38)
13
+ * Add [YARD documentation](https://rubydoc.info/gems/redis-time-series) (#40)
14
+
15
+ ## 0.4.0
16
+ * Added [hash-based filter DSL](https://github.com/dzunk/redis-time-series/tree/7173c73588da50614c02f9c89bf2ecef77766a78#filter-dsl)
17
+ * Removed `Time#ts_msec` monkey-patch
18
+ * Renamed `TimeSeries.queryindex` to `.query_index`
19
+ * Added `TS.CREATERULE` and `TS.DELETERULE` commands
20
+ * Renamed `InvalidFilters` to `FilterError`
21
+
22
+ ## 0.3.0
23
+ * Added `TS.QUERYINDEX` command
24
+
25
+ ## 0.2.0
26
+ * Converted `#info` to a struct instead of a hash.
27
+ * Added methods on time series for getting info attributes.
28
+
3
29
  ## 0.1.1
4
30
  Fix setting labels on `TS.CREATE` and `TS.ALTER`
5
31
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- redis-time-series (0.1.0)
4
+ redis-time-series (0.5.1)
5
5
  redis (~> 4.0)
6
6
 
7
7
  GEM
@@ -16,15 +16,17 @@ GEM
16
16
  coderay (1.1.3)
17
17
  concurrent-ruby (1.1.6)
18
18
  diff-lcs (1.3)
19
+ docile (1.3.2)
19
20
  i18n (1.8.3)
20
21
  concurrent-ruby (~> 1.0)
22
+ json (2.3.1)
21
23
  method_source (1.0.0)
22
24
  minitest (5.14.1)
23
25
  pry (0.13.1)
24
26
  coderay (~> 1.1)
25
27
  method_source (~> 1.0)
26
28
  rake (13.0.1)
27
- redis (4.1.4)
29
+ redis (4.2.2)
28
30
  rspec (3.9.0)
29
31
  rspec-core (~> 3.9.0)
30
32
  rspec-expectations (~> 3.9.0)
@@ -38,6 +40,11 @@ GEM
38
40
  diff-lcs (>= 1.2.0, < 2.0)
39
41
  rspec-support (~> 3.9.0)
40
42
  rspec-support (3.9.3)
43
+ simplecov (0.17.1)
44
+ docile (~> 1.1)
45
+ json (>= 1.8, < 3)
46
+ simplecov-html (~> 0.10.0)
47
+ simplecov-html (0.10.2)
41
48
  thread_safe (0.3.6)
42
49
  tzinfo (1.2.7)
43
50
  thread_safe (~> 0.1)
@@ -53,6 +60,7 @@ DEPENDENCIES
53
60
  rake (~> 13.0)
54
61
  redis-time-series!
55
62
  rspec (~> 3.0)
63
+ simplecov (< 0.18)
56
64
 
57
65
  BUNDLED WITH
58
66
  1.17.2
data/README.md CHANGED
@@ -1,4 +1,8 @@
1
- ![](https://github.com/dzunk/redis-time-series/workflows/RSpec/badge.svg)
1
+ [![RSpec](https://github.com/dzunk/redis-time-series/workflows/RSpec/badge.svg)](https://github.com/dzunk/redis-time-series/actions?query=workflow%3ARSpec+branch%3Amaster)
2
+ [![Gem Version](https://badge.fury.io/rb/redis-time-series.svg)](https://badge.fury.io/rb/redis-time-series)
3
+ [![Documentation](https://img.shields.io/badge/docs-rubydoc.info-brightgreen)](https://rubydoc.info/gems/redis-time-series)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/19a5925c20318508b4a4/maintainability)](https://codeclimate.com/github/dzunk/redis-time-series/maintainability)
5
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/19a5925c20318508b4a4/test_coverage)](https://codeclimate.com/github/dzunk/redis-time-series/test_coverage)
2
6
 
3
7
  # RedisTimeSeries
4
8
 
@@ -72,6 +76,10 @@ Add a single value with a timestamp
72
76
  ```ruby
73
77
  ts.add 1234, 3.minutes.ago # Used ActiveSupport here, but any Time object works fine
74
78
  => #<Redis::TimeSeries::Sample:0x00007fa6ce05f3f8 @time=2020-06-25 23:39:54 -0700, @value=0.1234e4>
79
+
80
+ # Optionally store data uncompressed
81
+ ts.add 5678, uncompressed: true
82
+ => #<Redis::TimeSeries::Sample:0x00007f93f43cdf68 @time=2020-07-18 23:15:29 -0700, @value=0.5678e4>
75
83
  ```
76
84
  Add multiple values with timestamps
77
85
  ```ruby
@@ -88,6 +96,10 @@ ts.increment # alias of incrby
88
96
  => 1593154255069
89
97
  ts.decrement # alias of decrby
90
98
  => 1593154257344
99
+
100
+ # Optionally store data uncompressed
101
+ ts.incrby 4, uncompressed: true
102
+ => 1595139299769
91
103
  ```
92
104
  ```ruby
93
105
  ts.get
@@ -119,61 +131,203 @@ ts.get
119
131
  ```
120
132
  Get a range of values
121
133
  ```ruby
122
- ts.range 10.minutes.ago..Time.current # Time range as an argument
134
+ # Time range as an argument
135
+ ts.range(10.minutes.ago..Time.current)
123
136
  => [#<Redis::TimeSeries::Sample:0x00007fa25f13fc28 @time=2020-06-25 23:50:51 -0700, @value=0.57e2>,
124
- #<Redis::TimeSeries::Sample:0x00007fa25f13db58 @time=2020-06-25 23:50:55 -0700, @value=0.58e2>,
125
- #<Redis::TimeSeries::Sample:0x00007fa25f13d900 @time=2020-06-25 23:50:57 -0700, @value=0.57e2>,
126
- #<Redis::TimeSeries::Sample:0x00007fa25f13d680 @time=2020-06-25 23:51:30 -0700, @value=0.58e2>]
127
- ts.range from: 10.minutes.ago, to: Time.current # Time range as keyword args
137
+ #<Redis::TimeSeries::Sample:0x00007fa25f13db58 @time=2020-06-25 23:50:55 -0700, @value=0.58e2>,
138
+ #<Redis::TimeSeries::Sample:0x00007fa25f13d900 @time=2020-06-25 23:50:57 -0700, @value=0.57e2>,
139
+ #<Redis::TimeSeries::Sample:0x00007fa25f13d680 @time=2020-06-25 23:51:30 -0700, @value=0.58e2>]
140
+
141
+ # Time range as keyword args
142
+ ts.range(from: 10.minutes.ago, to: Time.current)
128
143
  => [#<Redis::TimeSeries::Sample:0x00007fa25dc01f00 @time=2020-06-25 23:50:51 -0700, @value=0.57e2>,
129
- #<Redis::TimeSeries::Sample:0x00007fa25dc01d20 @time=2020-06-25 23:50:55 -0700, @value=0.58e2>,
130
- #<Redis::TimeSeries::Sample:0x00007fa25dc01b68 @time=2020-06-25 23:50:57 -0700, @value=0.57e2>,
131
- #<Redis::TimeSeries::Sample:0x00007fa25dc019b0 @time=2020-06-25 23:51:30 -0700, @value=0.58e2>]
144
+ #<Redis::TimeSeries::Sample:0x00007fa25dc01d20 @time=2020-06-25 23:50:55 -0700, @value=0.58e2>,
145
+ #<Redis::TimeSeries::Sample:0x00007fa25dc01b68 @time=2020-06-25 23:50:57 -0700, @value=0.57e2>,
146
+ #<Redis::TimeSeries::Sample:0x00007fa25dc019b0 @time=2020-06-25 23:51:30 -0700, @value=0.58e2>]
147
+
148
+ # Limit number of results with count argument
149
+ ts.range(10.minutes.ago..Time.current, count: 2)
150
+ => [#<Redis::TimeSeries::Sample:0x00007fa25dc01f00 @time=2020-06-25 23:50:51 -0700, @value=0.57e2>,
151
+ #<Redis::TimeSeries::Sample:0x00007fa25dc01d20 @time=2020-06-25 23:50:55 -0700, @value=0.58e2>]
152
+
153
+ # Apply aggregations to the range
154
+ ts.range(from: 10.minutes.ago, to: Time.current, aggregation: [:avg, 10.minutes])
155
+ => [#<Redis::TimeSeries::Sample:0x00007fa25dc01f00 @time=2020-06-25 23:50:00 -0700, @value=0.575e2>]
132
156
  ```
133
157
  Get info about the series
134
158
  ```ruby
135
159
  ts.info
136
- => {"total_samples"=>3,
137
- "memory_usage"=>4184,
138
- "first_timestamp"=>1593155422582,
139
- "last_timestamp"=>1593155709333,
140
- "retention_time"=>0,
141
- "chunk_count"=>1,
142
- "max_samples_per_chunk"=>256,
143
- "labels"=>[],
144
- "source_key"=>nil,
145
- "rules"=>[]}
160
+ => #<struct Redis::TimeSeries::Info
161
+ series=
162
+ #<Redis::TimeSeries:0x00007ff46da9b578 @key="ts3", @redis=#<Redis client v4.2.1 for redis://127.0.0.1:6379/0>>,
163
+ total_samples=3,
164
+ memory_usage=4264,
165
+ first_timestamp=1595187993605,
166
+ last_timestamp=1595187993629,
167
+ retention_time=0,
168
+ chunk_count=1,
169
+ max_samples_per_chunk=256,
170
+ labels={"foo"=>"bar"},
171
+ source_key=nil,
172
+ rules=
173
+ [#<Redis::TimeSeries::Rule:0x00007ff46db30c68
174
+ @aggregation=#<Redis::TimeSeries::Aggregation:0x00007ff46db30c18 @duration=3600000, @type="avg">,
175
+ @destination_key="ts1",
176
+ @source=
177
+ #<Redis::TimeSeries:0x00007ff46da9b578
178
+ @key="ts3",
179
+ @redis=#<Redis client v4.2.1 for redis://127.0.0.1:6379/0>>>]>
180
+
181
+ # Each info property is also a method on the time series object
182
+ ts.memory_usage
183
+ => 4208
184
+ ts.labels
185
+ => {"foo"=>"bar"}
186
+ ts.total_samples
187
+ => 3
188
+
189
+ # Total samples also available as #count, #length, and #size
190
+ ts.count
191
+ => 3
192
+ ts.length
193
+ => 3
194
+ ts.size
195
+ => 3
196
+ ```
197
+ Find series matching specific label(s)
198
+ ```ruby
199
+ Redis::TimeSeries.query_index('foo=bar')
200
+ => [#<Redis::TimeSeries:0x00007fc115ba1610
201
+ @key="ts3",
202
+ @redis=#<Redis client v4.2.1 for redis://127.0.0.1:6379/0>,
203
+ @retention=nil,
204
+ @uncompressed=false>]
205
+ # Note that you need at least one "label equals value" filter
206
+ Redis::TimeSeries.query_index('foo!=bar')
207
+ => RuntimeError: Filtering requires at least one equality comparison
208
+ # query_index is also aliased as .where for fluency
209
+ Redis::TimeSeries.where('foo=bar')
210
+ => [#<Redis::TimeSeries:0x00007fb8981010c8
211
+ @key="ts3",
212
+ @redis=#<Redis client v4.2.1 for redis://127.0.0.1:6379/0>,
213
+ @retention=nil,
214
+ @uncompressed=false>]
215
+ ```
216
+ ### Filter DSL
217
+ You can provide filter strings directly, per the time series documentation.
218
+ ```ruby
219
+ Redis::TimeSeries.where('foo=bar')
220
+ => [#<Redis::TimeSeries:0x00007fb8981010c8...>]
221
+ ```
222
+ There is also a hash-based syntax available, which may be more pleasant to work with.
223
+ ```ruby
224
+ Redis::TimeSeries.where(foo: 'bar')
225
+ => [#<Redis::TimeSeries:0x00007fb89811dca0...>]
226
+ ```
227
+ All six filter types are represented in hash format below.
228
+ ```ruby
229
+ {
230
+ foo: 'bar', # label=value (equality)
231
+ foo: { not: 'bar' }, # label!=value (inequality)
232
+ foo: true, # label= (presence)
233
+ foo: false, # label!= (absence)
234
+ foo: [1, 2], # label=(1,2) (any value)
235
+ foo: { not: [1, 2] } # label!=(1,2) (no values)
236
+ }
237
+ ```
238
+ Note the special use of `true` and `false`. If you're representing a boolean value with a label, rather than setting its value to "true" or "false" (which would be treated as strings in Redis anyway), you should add or remove the label from the series.
239
+
240
+ Values can be any object that responds to `.to_s`:
241
+ ```ruby
242
+ class Person
243
+ def initialize(name)
244
+ @name = name
245
+ end
246
+
247
+ def to_s
248
+ @name
249
+ end
250
+ end
251
+
252
+ Redis::TimeSeries.where(person: Person.new('John'))
253
+ #=> TS.QUERYINDEX person=John
146
254
  ```
147
255
 
256
+ ### Compaction Rules
257
+ Add a compaction rule to a series.
258
+ ```ruby
259
+ # Destintation time series needs to be created before the rule is added.
260
+ other_ts = Redis::TimeSeries.create('other_ts')
261
+
262
+ # Aggregation buckets are measured in milliseconds
263
+ ts.create_rule(dest: other_ts, aggregation: [:count, 60000]) # 1 minute
264
+
265
+ # Can provide a string key instead of a time series object
266
+ ts.create_rule(dest: 'other_ts', aggregation: [:avg, 120000])
267
+
268
+ # If you're using Rails or ActiveSupport, you can provide an
269
+ # ActiveSupport::Duration instead of an integer
270
+ ts.create_rule(dest: other_ts, aggregation: [:avg, 2.minutes])
271
+
272
+ # Can also provide an Aggregation object instead of an array
273
+ agg = Redis::TimeSeries::Aggregation.new(:avg, 120000)
274
+ ts.create_rule(dest: other_ts, aggregation: agg)
275
+
276
+ # Class-level method also available
277
+ Redis::TimeSeries.create_rule(source: ts, dest: other_ts, aggregation: ['std.p', 150000])
278
+ ```
279
+ Get existing compaction rules
280
+ ```ruby
281
+ ts.rules
282
+ => [#<Redis::TimeSeries::Rule:0x00007ff46e91c728
283
+ @aggregation=#<Redis::TimeSeries::Aggregation:0x00007ff46e91c6d8 @duration=3600000, @type="avg">,
284
+ @destination_key="ts1",
285
+ @source=
286
+ #<Redis::TimeSeries:0x00007ff46da9b578 @key="ts3", @redis=#<Redis client v4.2.1 for redis://127.0.0.1:6379/0>>>]
287
+
288
+ # Get properties of a rule too
289
+ ts.rules.first.aggregation
290
+ => #<Redis::TimeSeries::Aggregation:0x00007ff46d146d38 @duration=3600000, @type="avg">
291
+ ts.rules.first.destination
292
+ => #<Redis::TimeSeries:0x00007ff46d8a3d60 @key="ts1", @redis=#<Redis client v4.2.1 for redis://127.0.0.1:6379/0>>
293
+ ```
294
+
295
+ Remove an existing compaction rule
296
+ ```ruby
297
+ ts.delete_rule(dest: 'other_ts')
298
+ ts.rules.first.delete
299
+ Redis::TimeSeries.delete_rule(source: ts, dest: 'other_ts')
300
+ ```
301
+
302
+
148
303
  ### TODO
149
304
  * `TS.REVRANGE`
150
305
  * `TS.MRANGE`/`TS.MREVRANGE`
151
- * `TS.QUERYINDEX`
152
- * Compaction rules
153
- * Filters
154
306
  * Probably a bunch more stuff
155
307
 
156
308
  ## Development
157
309
 
158
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
310
+ After checking out the repo, run `bin/setup`. You need the `docker` daemon installed and running. This script will:
311
+ * Install gem dependencies
312
+ * Pull the latest `redislabs/redistimeseries` image
313
+ * Start a Redis server on port 6379
314
+ * Seed three time series with some sample data
315
+ * Attach to the running server and print logs to `STDOUT`
159
316
 
160
- In order to run the specs or use the console, you'll need a Redis server running on the default port 6379 with the RedisTimeSeries module enabled. The easiest way to do so is by running the Docker image:
161
- ```
162
- docker run -p 6379:6379 -it --rm redislabs/redistimeseries
163
- ```
164
-
165
- The `bin/console` script will set up three time series, `@ts1`, `@ts2`, and `@ts3`, with three values in each. **It will also flush the local Redis server each time you run it**, so don't try it if you have data you don't want to lose!
317
+ With the above script running, or after starting a server manually, you can run `bin/console` to interact with it. The three series are named `ts1`, `ts2`, and `ts3`, and are available as instance variables in the console.
166
318
 
167
319
  If you want to see the commands being executed, run the console with `DEBUG=true bin/console` and it will output the raw command strings as they're executed.
168
320
  ```ruby
169
321
  [1] pry(main)> @ts1.increment
170
- DEBUG: TS.INCRBY foo 1
322
+ DEBUG: TS.INCRBY ts1 1
171
323
  => 1593159795467
172
324
  [2] pry(main)> @ts1.get
173
- DEBUG: TS.GET foo
325
+ DEBUG: TS.GET ts1
174
326
  => #<Redis::TimeSeries::Sample:0x00007f8e1a190cf8 @time=2020-06-26 01:23:15 -0700, @value=0.4e1>
175
327
  ```
176
328
 
329
+ Use `rake spec` to run the test suite.
330
+
177
331
  ## Contributing
178
332
 
179
333
  Bug reports and pull requests are welcome on GitHub at https://github.com/dzunk/redis-time-series.
@@ -6,15 +6,9 @@ require 'pry'
6
6
  require 'redis'
7
7
  require 'redis-time-series'
8
8
 
9
- Redis.current.flushall
10
-
11
- @ts1 = Redis::TimeSeries.create('foo')
12
- @ts2 = Redis::TimeSeries.create('bar')
13
- @ts3 = Redis::TimeSeries.create('baz')
14
-
9
+ @ts1 = Redis::TimeSeries.new('ts1')
10
+ @ts2 = Redis::TimeSeries.new('ts2')
11
+ @ts3 = Redis::TimeSeries.new('ts3')
15
12
  @series = [@ts1, @ts2, @ts3]
16
- @series.each do |ts|
17
- 3.times { ts.increment }
18
- end
19
13
 
20
14
  Pry.start
data/bin/setup CHANGED
@@ -1,8 +1,31 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
1
+ #!/usr/bin/env ruby
5
2
 
6
- bundle install
3
+ system 'bundle install'
4
+ system 'docker pull redislabs/redistimeseries:latest'
5
+ container_id = `docker run -p 6379:6379 -dit --rm redislabs/redistimeseries`
6
+ at_exit { system "docker stop #{container_id}" }
7
7
 
8
- # Do any other automated setup that you need to do here
8
+ require 'bundler/setup'
9
+ require 'active_support/core_ext/numeric/time'
10
+ require 'redis'
11
+ require 'redis-time-series'
12
+
13
+ Redis.current.flushall
14
+ ts1 = Redis::TimeSeries.create('ts1')
15
+ ts2 = Redis::TimeSeries.create('ts2')
16
+ ts3 = Redis::TimeSeries.create('ts3')
17
+
18
+ ts1.add 12, 6.minutes.ago
19
+ ts1.add 34, 4.minutes.ago
20
+ ts1.add 56, 2.minutes.ago
21
+
22
+ 10.times { ts2.increment; sleep 0.01 }
23
+
24
+ ts3.labels = { foo: 'bar' }
25
+ ts3.add 1
26
+ sleep 0.01
27
+ ts3.incrby 2
28
+ sleep 0.01
29
+ ts3.decrement
30
+
31
+ system "docker logs -f #{container_id}"