redis-time-series 0.2.0 → 0.3.0
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 +4 -4
- data/CHANGELOG.md +3 -0
- data/Gemfile.lock +1 -1
- data/README.md +26 -11
- data/bin/console +3 -9
- data/bin/setup +28 -6
- data/lib/redis-time-series.rb +3 -2
- data/lib/redis/time_series.rb +6 -0
- data/lib/redis/time_series/filter.rb +118 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0d699daec01bb891952501de6a51cc4b966be74a0f8585030bafb4f6a908b36e
|
4
|
+
data.tar.gz: dfa027d344c4d5485b0a22a33edd67973c2a60cb60dbbaa1a57101ccce8833fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: acadcad9c6919e810f0e100bfe20b60055507e2efa98d0853fca6c15a252f99b65c722c71acc99d37d8d9f72964d91f862c9604fb96458ca3faaddce9f841752
|
7
|
+
data.tar.gz: 489415e8247d8cffefcf13e4e70c82cde752170c4416bc0973495c5d38b1d27d2b554cdd62db8729c9c71b125cb4848aa5a1efd81b998aa5f6e5683c13be8ccc
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-

|
1
|
+
[](https://github.com/dzunk/redis-time-series/actions?query=workflow%3ARSpec+branch%3Amaster)
|
2
|
+
[](https://badge.fury.io/rb/redis-time-series)
|
2
3
|
|
3
4
|
# RedisTimeSeries
|
4
5
|
|
@@ -159,36 +160,50 @@ ts.length
|
|
159
160
|
ts.size
|
160
161
|
=> 3
|
161
162
|
```
|
163
|
+
Find series matching specific label(s)
|
164
|
+
```ruby
|
165
|
+
Redis::TimeSeries.queryindex('foo=bar')
|
166
|
+
=> [#<Redis::TimeSeries:0x00007fc115ba1610
|
167
|
+
@key="ts3",
|
168
|
+
@redis=#<Redis client v4.2.1 for redis://127.0.0.1:6379/0>,
|
169
|
+
@retention=nil,
|
170
|
+
@uncompressed=false>]
|
171
|
+
# Note that you need at least one "label equals value" filter
|
172
|
+
Redis::TimeSeries.queryindex('foo!=bar')
|
173
|
+
=> RuntimeError: Filtering requires at least one equality comparison
|
174
|
+
```
|
175
|
+
|
162
176
|
|
163
177
|
### TODO
|
164
178
|
* `TS.REVRANGE`
|
165
179
|
* `TS.MRANGE`/`TS.MREVRANGE`
|
166
|
-
* `TS.QUERYINDEX`
|
167
180
|
* Compaction rules
|
168
181
|
* Filters
|
169
182
|
* Probably a bunch more stuff
|
170
183
|
|
171
184
|
## Development
|
172
185
|
|
173
|
-
After checking out the repo, run `bin/setup
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
186
|
+
After checking out the repo, run `bin/setup`. You need the `docker` daemon installed and running. This script will:
|
187
|
+
* Install gem dependencies
|
188
|
+
* Pull the latest `redislabs/redistimeseries` image
|
189
|
+
* Start a Redis server on port 6379
|
190
|
+
* Seed three time series with some sample data
|
191
|
+
* Attach to the running server and print logs to `STDOUT`
|
179
192
|
|
180
|
-
|
193
|
+
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.
|
181
194
|
|
182
195
|
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.
|
183
196
|
```ruby
|
184
197
|
[1] pry(main)> @ts1.increment
|
185
|
-
DEBUG: TS.INCRBY
|
198
|
+
DEBUG: TS.INCRBY ts1 1
|
186
199
|
=> 1593159795467
|
187
200
|
[2] pry(main)> @ts1.get
|
188
|
-
DEBUG: TS.GET
|
201
|
+
DEBUG: TS.GET ts1
|
189
202
|
=> #<Redis::TimeSeries::Sample:0x00007f8e1a190cf8 @time=2020-06-26 01:23:15 -0700, @value=0.4e1>
|
190
203
|
```
|
191
204
|
|
205
|
+
Use `rake spec` to run the test suite.
|
206
|
+
|
192
207
|
## Contributing
|
193
208
|
|
194
209
|
Bug reports and pull requests are welcome on GitHub at https://github.com/dzunk/redis-time-series.
|
data/bin/console
CHANGED
@@ -6,15 +6,9 @@ require 'pry'
|
|
6
6
|
require 'redis'
|
7
7
|
require 'redis-time-series'
|
8
8
|
|
9
|
-
Redis.
|
10
|
-
|
11
|
-
@
|
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; sleep 0.01 }
|
18
|
-
end
|
19
13
|
|
20
14
|
Pry.start
|
data/bin/setup
CHANGED
@@ -1,8 +1,30 @@
|
|
1
|
-
#!/usr/bin/env
|
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`
|
7
6
|
|
8
|
-
|
7
|
+
require 'bundler/setup'
|
8
|
+
require 'active_support/core_ext/numeric/time'
|
9
|
+
require 'redis'
|
10
|
+
|
11
|
+
Redis.current.flushall
|
12
|
+
ts1 = Redis::TimeSeries.create('ts1')
|
13
|
+
ts2 = Redis::TimeSeries.create('ts2')
|
14
|
+
ts3 = Redis::TimeSeries.create('ts3')
|
15
|
+
|
16
|
+
ts1.add 12, 6.minutes.ago
|
17
|
+
ts1.add 34, 4.minutes.ago
|
18
|
+
ts1.add 56, 2.minutes.ago
|
19
|
+
|
20
|
+
10.times { ts2.increment; sleep 0.01 }
|
21
|
+
|
22
|
+
ts3.labels = { foo: 'bar' }
|
23
|
+
ts3.add 1
|
24
|
+
sleep 0.01
|
25
|
+
ts3.incrby 2
|
26
|
+
sleep 0.01
|
27
|
+
ts3.decrement
|
28
|
+
|
29
|
+
at_exit { system "docker stop #{container_id}" }
|
30
|
+
system "docker logs -f #{container_id}"
|
data/lib/redis-time-series.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
require 'bigdecimal'
|
2
2
|
require 'forwardable'
|
3
3
|
require 'time/msec'
|
4
|
+
require 'redis/time_series/filter'
|
4
5
|
require 'redis/time_series/info'
|
5
|
-
require 'redis/time_series'
|
6
6
|
require 'redis/time_series/sample'
|
7
|
+
require 'redis/time_series'
|
7
8
|
|
8
9
|
class RedisTimeSeries
|
9
|
-
VERSION = '0.
|
10
|
+
VERSION = '0.3.0'
|
10
11
|
end
|
data/lib/redis/time_series.rb
CHANGED
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
class Redis
|
3
|
+
class TimeSeries
|
4
|
+
class Filter
|
5
|
+
Equal = Struct.new(:label, :value) do
|
6
|
+
self::REGEX = /^[^!]+=[^(]+/
|
7
|
+
|
8
|
+
def self.parse(str)
|
9
|
+
new(*str.split('='))
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"#{label}=#{value}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
NotEqual = Struct.new(:label, :value) do
|
18
|
+
self::REGEX = /^.+!=[^(]+/
|
19
|
+
|
20
|
+
def self.parse(str)
|
21
|
+
new(*str.split('!='))
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
"#{label}!=#{value}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Absent = Struct.new(:label) do
|
30
|
+
self::REGEX = /^[^!]+=$/
|
31
|
+
|
32
|
+
def self.parse(str)
|
33
|
+
new(str.delete('='))
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
"#{label}="
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
Present = Struct.new(:label) do
|
42
|
+
self::REGEX = /^.+!=$/
|
43
|
+
|
44
|
+
def self.parse(str)
|
45
|
+
new(str.delete('!='))
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
"#{label}!="
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
AnyValue = Struct.new(:label, :values) do
|
54
|
+
self::REGEX = /^[^!]+=\(.+\)/
|
55
|
+
|
56
|
+
def self.parse(str)
|
57
|
+
label, values = str.split('=')
|
58
|
+
values = values.tr('()', '').split(',')
|
59
|
+
new(label, values)
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_s
|
63
|
+
"#{label}=(#{values.join(',')})"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
NoValues = Struct.new(:label, :values) do
|
68
|
+
self::REGEX = /^.+!=\(.+\)/
|
69
|
+
|
70
|
+
def self.parse(str)
|
71
|
+
label, values = str.split('!=')
|
72
|
+
values = values.tr('()', '').split(',')
|
73
|
+
new(label, values)
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_s
|
77
|
+
"#{label}!=(#{values.join(',')})"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
TYPES = [Equal, NotEqual, Absent, Present, AnyValue, NoValues]
|
82
|
+
TYPES.each do |type|
|
83
|
+
define_method "#{type.to_s.split('::').last.gsub(/(.)([A-Z])/,'\1_\2').downcase}_filters" do
|
84
|
+
filters.select { |f| f.is_a? type }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
attr_reader :filters
|
89
|
+
|
90
|
+
def initialize(filters = nil)
|
91
|
+
filters = parse_string(filters) if filters.is_a?(String)
|
92
|
+
@filters = filters.presence || {}
|
93
|
+
end
|
94
|
+
|
95
|
+
def validate!
|
96
|
+
valid? || raise('Filtering requires at least one equality comparison')
|
97
|
+
end
|
98
|
+
|
99
|
+
def valid?
|
100
|
+
!!filters.find { |f| f.is_a? Equal }
|
101
|
+
end
|
102
|
+
|
103
|
+
def to_a
|
104
|
+
filters.map(&:to_s)
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def parse_string(filter_string)
|
110
|
+
filter_string.split(' ').map do |str|
|
111
|
+
match = TYPES.find { |f| f::REGEX.match? str }
|
112
|
+
raise "Unable to parse '#{str}'" unless match
|
113
|
+
match.parse(str)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis-time-series
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Duszynski
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-07-
|
11
|
+
date: 2020-07-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -114,6 +114,7 @@ files:
|
|
114
114
|
- bin/setup
|
115
115
|
- lib/redis-time-series.rb
|
116
116
|
- lib/redis/time_series.rb
|
117
|
+
- lib/redis/time_series/filter.rb
|
117
118
|
- lib/redis/time_series/info.rb
|
118
119
|
- lib/redis/time_series/sample.rb
|
119
120
|
- lib/time/msec.rb
|