redis-time-series 0.5.2 → 0.6.0

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: 461a6f3842867d1ba51440e7576aad3e9b92ecd02700cd59e5508c0e8bbfcaa7
4
- data.tar.gz: 0c03af91af0eaaa37917db2cad21417c38709a53b223ed2864bad0f9857fa1f7
3
+ metadata.gz: 4648678522434687a6e278490c7a308c64f6460394b8ebde16b2ce2daa9e92ad
4
+ data.tar.gz: e2f48fb1c8efebb3b4d6e4c47d1d86137a10ac8a54735c94b3bdb35ef6396651
5
5
  SHA512:
6
- metadata.gz: 56dcd4439b2f4f06469237d2925d84b2ffabc40e2dccb608d5489b78f0e2a5b299881487b55e67fb48f1c9d0f7f56951ee98254ae84229b1827fb4cf3683ca44
7
- data.tar.gz: 860e638f906b9dc19358e6156ad6a0488452be0be499853926742073cf6e85d00f4e95755f1659feae9763334eebbab044147fa3d94512dbb2a91360ec08728b
6
+ metadata.gz: 269a787d8d2a2a467a52e915ab717bc5b20eb28cacc55eb5dfc9ce3909a7d83cdd4e15b19afc92612d19fa551c6ba793df76ceef03d8bb8bdb16b6dc0253d2cc
7
+ data.tar.gz: c5d845052ca8f2a518fb3a20a82a8d8aa35128cb824f54d2531a949767e90f8c7979ede07ba1bd56ae30a825edb9f6be90e52ca404a14199bfd13484026d8d40
@@ -11,9 +11,13 @@ on:
11
11
  jobs:
12
12
  spec:
13
13
  runs-on: ubuntu-latest
14
+ strategy:
15
+ matrix:
16
+ image_version: ['latest', 'edge']
17
+ ruby_version: ['2.6', '2.7']
14
18
  services:
15
19
  redis:
16
- image: redislabs/redistimeseries:latest
20
+ image: redislabs/redistimeseries:${{ matrix.image_version }}
17
21
  ports:
18
22
  - 6379:6379/tcp
19
23
  env:
@@ -25,16 +29,18 @@ jobs:
25
29
  - name: Set up Ruby
26
30
  uses: ruby/setup-ruby@v1
27
31
  with:
28
- ruby-version: 2.6
32
+ ruby-version: ${{ matrix.ruby_version }}
29
33
  - name: Set up CodeClimate
30
34
  run: |
31
35
  curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
32
36
  chmod +x ./cc-test-reporter
33
37
  ./cc-test-reporter before-build
34
38
  - name: Install dependencies
35
- run: bundle install
39
+ run: |
40
+ bundle install
41
+ bundle exec appraisal install
36
42
  - name: Run specs
37
- run: bundle exec rake spec
43
+ run: bundle exec appraisal rake spec
38
44
  - name: Upload coverage report
39
45
  run: ./cc-test-reporter after-build -t simplecov coverage/.resultset.json
40
46
  - uses: actions/upload-artifact@v2
data/.gitignore CHANGED
@@ -6,6 +6,7 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ Gemfile.lock
9
10
 
10
11
  # rspec failure tracking
11
12
  .rspec_status
@@ -0,0 +1,7 @@
1
+ appraise 'redis 3' do
2
+ gem 'redis', '~> 3.3'
3
+ end
4
+
5
+ appraise 'redis 4' do
6
+ gem 'redis', '~> 4.0'
7
+ end
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.6.0
6
+ * Add CHUNK_SIZE param to CREATE, ADD, INCRBY, DECRBY commands (#53)
7
+ * Add duplication policy to TS.CREATE and TS.ADD commands (#51)
8
+ * Add support for endless ranges to TS.RANGE (#50)
9
+ * Cast label values to integers in Info struct (#49)
10
+ * Build against edge upstream in addition to latest stable (#48)
11
+
5
12
  ## 0.5.2
6
13
  * Add chunk_type to info struct (#47)
7
14
 
@@ -1,9 +1,11 @@
1
1
  require 'bigdecimal'
2
2
  require 'forwardable'
3
3
  require 'ext/time_msec'
4
+
4
5
  require 'redis/time_series/client'
5
6
  require 'redis/time_series/errors'
6
7
  require 'redis/time_series/aggregation'
8
+ require 'redis/time_series/duplicate_policy'
7
9
  require 'redis/time_series/filters'
8
10
  require 'redis/time_series/rule'
9
11
  require 'redis/time_series/info'
@@ -37,6 +37,11 @@ class Redis
37
37
  # With no value, the series will not be trimmed.
38
38
  # @option options [Boolean] :uncompressed
39
39
  # When true, series data will be stored in an uncompressed format.
40
+ # @option options [String, Symbol] :duplicate_policy
41
+ # A duplication policy to resolve conflicts when adding values to the series.
42
+ # Valid values are in Redis::TimeSeries::DuplicatePolicy::VALID_POLICIES
43
+ # @option options [Integer] :chunk_size
44
+ # Amount of memory, in bytes, to allocate for each chunk of data
40
45
  #
41
46
  # @return [Redis::TimeSeries] the created time series
42
47
  # @see https://oss.redislabs.com/redistimeseries/commands/#tscreate
@@ -154,21 +159,33 @@ class Redis
154
159
  # @param value [Numeric] the value to add
155
160
  # @param timestamp [Time, Numeric] the +Time+, or integer timestamp in milliseconds, to add the value
156
161
  # @param uncompressed [Boolean] if true, stores data in an uncompressed format
162
+ # @param on_duplicate [String, Symbol] a duplication policy for conflict resolution
163
+ # @param chunk_size [Integer] set default chunk size, in bytes, for the time series
157
164
  #
158
165
  # @return [Sample] the value that was added
159
166
  # @raise [Redis::CommandError] if the value being added is older than the latest timestamp in the series
160
- def add(value, timestamp = '*', uncompressed: nil)
161
- ts = cmd 'TS.ADD', key, timestamp, value, ('UNCOMPRESSED' if uncompressed)
167
+ #
168
+ # @see TimeSeries::DuplicatePolicy
169
+ def add(value, timestamp = '*', uncompressed: nil, on_duplicate: nil, chunk_size: nil)
170
+ ts = cmd 'TS.ADD',
171
+ key,
172
+ timestamp,
173
+ value,
174
+ ('UNCOMPRESSED' if uncompressed),
175
+ (['CHUNK_SIZE', chunk_size] if chunk_size),
176
+ (DuplicatePolicy.new(on_duplicate).to_a('ON_DUPLICATE') if on_duplicate)
162
177
  Sample.new(ts, value)
163
178
  end
164
179
 
165
180
  # Issues a TS.CREATE command for the current series.
166
181
  # You should use class method {Redis::TimeSeries.create} instead.
167
182
  # @api private
168
- def create(retention: nil, uncompressed: nil, labels: nil)
183
+ def create(retention: nil, uncompressed: nil, labels: nil, duplicate_policy: nil, chunk_size: nil)
169
184
  cmd 'TS.CREATE', key,
170
185
  (['RETENTION', retention] if retention),
171
186
  ('UNCOMPRESSED' if uncompressed),
187
+ (['CHUNK_SIZE', chunk_size] if chunk_size),
188
+ (DuplicatePolicy.new(duplicate_policy).to_a if duplicate_policy),
172
189
  (['LABELS', labels.to_a] if labels&.any?)
173
190
  self
174
191
  end
@@ -206,11 +223,17 @@ class Redis
206
223
  # @param value [Integer] the amount to decrement by
207
224
  # @param timestamp [Time, Integer] the Time or integer millisecond timestamp to save the new value at
208
225
  # @param uncompressed [Boolean] if true, stores data in an uncompressed format
226
+ # @param chunk_size [Integer] set default chunk size, in bytes, for the time series
209
227
  #
210
228
  # @return [Integer] the timestamp the value was stored at
211
229
  # @see https://oss.redislabs.com/redistimeseries/commands/#tsincrbytsdecrby
212
- def decrby(value = 1, timestamp = nil, uncompressed: nil)
213
- cmd 'TS.DECRBY', key, value, (timestamp if timestamp), ('UNCOMPRESSED' if uncompressed)
230
+ def decrby(value = 1, timestamp = nil, uncompressed: nil, chunk_size: nil)
231
+ cmd 'TS.DECRBY',
232
+ key,
233
+ value,
234
+ (timestamp if timestamp),
235
+ ('UNCOMPRESSED' if uncompressed),
236
+ (['CHUNK_SIZE', chunk_size] if chunk_size)
214
237
  end
215
238
  alias decrement decrby
216
239
 
@@ -241,11 +264,17 @@ class Redis
241
264
  # @param value [Integer] the amount to increment by
242
265
  # @param timestamp [Time, Integer] the Time or integer millisecond timestamp to save the new value at
243
266
  # @param uncompressed [Boolean] if true, stores data in an uncompressed format
267
+ # @param chunk_size [Integer] set default chunk size, in bytes, for the time series
244
268
  #
245
269
  # @return [Integer] the timestamp the value was stored at
246
270
  # @see https://oss.redislabs.com/redistimeseries/commands/#tsincrbytsdecrby
247
- def incrby(value = 1, timestamp = nil, uncompressed: nil)
248
- cmd 'TS.INCRBY', key, value, (timestamp if timestamp), ('UNCOMPRESSED' if uncompressed)
271
+ def incrby(value = 1, timestamp = nil, uncompressed: nil, chunk_size: nil)
272
+ cmd 'TS.INCRBY',
273
+ key,
274
+ value,
275
+ (timestamp if timestamp),
276
+ ('UNCOMPRESSED' if uncompressed),
277
+ (['CHUNK_SIZE', chunk_size] if chunk_size)
249
278
  end
250
279
  alias increment incrby
251
280
 
@@ -312,12 +341,12 @@ class Redis
312
341
  # `range` will swallow all parameters if they're all hash syntax
313
342
  count = range.delete(:count)
314
343
  aggregation = range.delete(:aggregation)
315
- range = range.fetch(:from)..range.fetch(:to)
344
+ range = range.fetch(:from)..range[:to]
316
345
  end
317
346
  cmd('TS.RANGE',
318
347
  key,
319
- range.min,
320
- range.max,
348
+ (range.begin || '-'),
349
+ (range.end || '+'),
321
350
  (['COUNT', count] if count),
322
351
  Aggregation.parse(aggregation)&.to_a
323
352
  ).map { |ts, val| Sample.new(ts, val) }
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+ class Redis
3
+ class TimeSeries
4
+ # Duplication policies can be applied to a time series in order to resolve conflicts
5
+ # when adding data that already exists in the series.
6
+ #
7
+ # @see https://oss.redislabs.com/redistimeseries/master/configuration/#duplicate_policy
8
+ class DuplicatePolicy
9
+ VALID_POLICIES = %i[
10
+ block
11
+ first
12
+ last
13
+ min
14
+ max
15
+ sum
16
+ ].freeze
17
+
18
+ attr_reader :policy
19
+
20
+ def initialize(policy)
21
+ policy = policy.to_s.downcase.to_sym
22
+ if VALID_POLICIES.include?(policy)
23
+ @policy = policy
24
+ else
25
+ raise UnknownPolicyError, "#{policy} is not a valid duplicate policy"
26
+ end
27
+ end
28
+
29
+ def to_a(cmd = 'DUPLICATE_POLICY')
30
+ [cmd, policy]
31
+ end
32
+
33
+ def to_s(cmd = 'DUPLICATE_POLICY')
34
+ to_a(cmd).join(' ')
35
+ end
36
+
37
+ def ==(other)
38
+ return policy == other.policy if other.is_a?(self.class)
39
+ policy == self.class.new(other).policy
40
+ end
41
+
42
+ VALID_POLICIES.each do |policy|
43
+ define_method("#{policy}?") do
44
+ @policy == policy
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -15,5 +15,10 @@ class Redis
15
15
  # an unknown type, or when calling a command with an invalid aggregation value.
16
16
  # @see Redis::TimeSeries::Aggregation
17
17
  class AggregationError < Error; end
18
+
19
+ # +UnknownPolicyError+ is raised when attempting to apply an unkown type of
20
+ # duplicate policy when creating or adding to a series.
21
+ # @see Redis::TimeSeries::DuplicatePolicy
22
+ class UnknownPolicyError < Error; end
18
23
  end
19
24
  end
@@ -10,6 +10,10 @@ class Redis
10
10
  #
11
11
  # @!attribute [r] chunk_count
12
12
  # @return [Integer] number of memory chunks used for the time-series
13
+ # @!attribute [r] chunk_size
14
+ # @return [Integer] amount of allocated memory in bytes
15
+ # @!attribute [r] chunk_type
16
+ # @return [String] whether the chunk is "compressed" or "uncompressed"
13
17
  # @!attribute [r] first_timestamp
14
18
  # @return [Integer] first timestamp present in the time-series (milliseconds since epoch)
15
19
  # @!attribute [r] labels
@@ -52,20 +56,43 @@ class Redis
52
56
  :total_samples,
53
57
  keyword_init: true
54
58
  ) do
55
- # @api private
56
- # @return [Info]
57
- def self.parse(series:, data:)
58
- data.each_slice(2).reduce({}) do |h, (key, value)|
59
- # Convert camelCase info keys to snake_case
60
- key = key.gsub(/(.)([A-Z])/,'\1_\2').downcase.to_sym
61
- next h unless members.include?(key)
62
- h[key] = value
63
- h
64
- end.then do |parsed_hash|
65
- parsed_hash[:series] = series
66
- parsed_hash[:labels] = parsed_hash[:labels].to_h
67
- parsed_hash[:rules] = parsed_hash[:rules].map { |d| Rule.new(source: series, data: d) }
68
- new(parsed_hash)
59
+ class << self
60
+ # @api private
61
+ # @return [Info]
62
+ def parse(series:, data:)
63
+ build_hash(data)
64
+ .merge(series: series)
65
+ .then(&method(:parse_labels))
66
+ .then(&method(:parse_policies))
67
+ .then(&method(:parse_rules))
68
+ .then(&method(:new))
69
+ end
70
+
71
+ private
72
+
73
+ def build_hash(data)
74
+ data.each_slice(2).reduce({}) do |h, (key, value)|
75
+ # Convert camelCase info keys to snake_case
76
+ key = key.gsub(/(.)([A-Z])/,'\1_\2').downcase.to_sym
77
+ # Skip unknown properties
78
+ next h unless members.include?(key)
79
+ h.merge(key => value)
80
+ end
81
+ end
82
+
83
+ def parse_labels(hash)
84
+ hash[:labels] = hash[:labels].to_h.transform_values { |v| v.to_i.to_s == v ? v.to_i : v }
85
+ hash
86
+ end
87
+
88
+ def parse_policies(hash)
89
+ hash[:duplicate_policy] = DuplicatePolicy.new(hash[:duplicate_policy]) if hash[:duplicate_policy]
90
+ hash
91
+ end
92
+
93
+ def parse_rules(hash)
94
+ hash[:rules] = hash[:rules].map { |d| Rule.new(source: hash[:series], data: d) }
95
+ hash
69
96
  end
70
97
  end
71
98
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  class Redis
3
3
  class TimeSeries
4
- VERSION = '0.5.2'
4
+ VERSION = '0.6.0'
5
5
  end
6
6
  end
@@ -31,10 +31,11 @@ Gem::Specification.new do |spec|
31
31
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
32
32
  spec.require_paths = ['lib']
33
33
 
34
- spec.add_dependency 'redis', '~> 4.0'
34
+ spec.add_dependency 'redis', '>= 3.3', '< 5'
35
35
 
36
36
  spec.add_development_dependency 'activesupport', '~> 6.0'
37
- spec.add_development_dependency 'bundler', '~> 1.17'
37
+ spec.add_development_dependency 'appraisal'
38
+ spec.add_development_dependency 'bundler', '~> 2.0'
38
39
  spec.add_development_dependency 'pry', '~> 0.13'
39
40
  spec.add_development_dependency 'rake', '~> 13.0'
40
41
  spec.add_development_dependency 'rspec', '~> 3.0'
metadata CHANGED
@@ -1,29 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-time-series
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Duszynski
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-10 00:00:00.000000000 Z
11
+ date: 2020-12-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '3.3'
20
+ - - "<"
18
21
  - !ruby/object:Gem::Version
19
- version: '4.0'
22
+ version: '5'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - "~>"
27
+ - - ">="
25
28
  - !ruby/object:Gem::Version
26
- version: '4.0'
29
+ version: '3.3'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '5'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: activesupport
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -38,20 +44,34 @@ dependencies:
38
44
  - - "~>"
39
45
  - !ruby/object:Gem::Version
40
46
  version: '6.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: appraisal
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
41
61
  - !ruby/object:Gem::Dependency
42
62
  name: bundler
43
63
  requirement: !ruby/object:Gem::Requirement
44
64
  requirements:
45
65
  - - "~>"
46
66
  - !ruby/object:Gem::Version
47
- version: '1.17'
67
+ version: '2.0'
48
68
  type: :development
49
69
  prerelease: false
50
70
  version_requirements: !ruby/object:Gem::Requirement
51
71
  requirements:
52
72
  - - "~>"
53
73
  - !ruby/object:Gem::Version
54
- version: '1.17'
74
+ version: '2.0'
55
75
  - !ruby/object:Gem::Dependency
56
76
  name: pry
57
77
  requirement: !ruby/object:Gem::Requirement
@@ -108,7 +128,7 @@ dependencies:
108
128
  - - "<"
109
129
  - !ruby/object:Gem::Version
110
130
  version: '0.18'
111
- description:
131
+ description:
112
132
  email:
113
133
  - dzunk@hey.com
114
134
  executables: []
@@ -118,9 +138,9 @@ files:
118
138
  - ".github/workflows/rspec.yml"
119
139
  - ".gitignore"
120
140
  - ".rspec"
141
+ - Appraisals
121
142
  - CHANGELOG.md
122
143
  - Gemfile
123
- - Gemfile.lock
124
144
  - LICENSE.txt
125
145
  - README.md
126
146
  - Rakefile
@@ -131,6 +151,7 @@ files:
131
151
  - lib/redis/time_series.rb
132
152
  - lib/redis/time_series/aggregation.rb
133
153
  - lib/redis/time_series/client.rb
154
+ - lib/redis/time_series/duplicate_policy.rb
134
155
  - lib/redis/time_series/errors.rb
135
156
  - lib/redis/time_series/filters.rb
136
157
  - lib/redis/time_series/info.rb
@@ -145,7 +166,7 @@ metadata:
145
166
  homepage_uri: https://github.com/dzunk/redis-time-series
146
167
  source_code_uri: https://github.com/dzunk/redis-time-series
147
168
  changelog_uri: https://github.com/dzunk/redis-time-series/blob/master/CHANGELOG.md
148
- post_install_message:
169
+ post_install_message:
149
170
  rdoc_options: []
150
171
  require_paths:
151
172
  - lib
@@ -160,8 +181,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
160
181
  - !ruby/object:Gem::Version
161
182
  version: '0'
162
183
  requirements: []
163
- rubygems_version: 3.0.3
164
- signing_key:
184
+ rubygems_version: 3.2.2
185
+ signing_key:
165
186
  specification_version: 4
166
187
  summary: A Ruby adapter for the RedisTimeSeries module.
167
188
  test_files: []
@@ -1,66 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- redis-time-series (0.5.2)
5
- redis (~> 4.0)
6
-
7
- GEM
8
- remote: https://rubygems.org/
9
- specs:
10
- activesupport (6.0.3.1)
11
- concurrent-ruby (~> 1.0, >= 1.0.2)
12
- i18n (>= 0.7, < 2)
13
- minitest (~> 5.1)
14
- tzinfo (~> 1.1)
15
- zeitwerk (~> 2.2, >= 2.2.2)
16
- coderay (1.1.3)
17
- concurrent-ruby (1.1.6)
18
- diff-lcs (1.3)
19
- docile (1.3.2)
20
- i18n (1.8.3)
21
- concurrent-ruby (~> 1.0)
22
- json (2.3.1)
23
- method_source (1.0.0)
24
- minitest (5.14.1)
25
- pry (0.13.1)
26
- coderay (~> 1.1)
27
- method_source (~> 1.0)
28
- rake (13.0.1)
29
- redis (4.2.2)
30
- rspec (3.9.0)
31
- rspec-core (~> 3.9.0)
32
- rspec-expectations (~> 3.9.0)
33
- rspec-mocks (~> 3.9.0)
34
- rspec-core (3.9.2)
35
- rspec-support (~> 3.9.3)
36
- rspec-expectations (3.9.2)
37
- diff-lcs (>= 1.2.0, < 2.0)
38
- rspec-support (~> 3.9.0)
39
- rspec-mocks (3.9.1)
40
- diff-lcs (>= 1.2.0, < 2.0)
41
- rspec-support (~> 3.9.0)
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)
48
- thread_safe (0.3.6)
49
- tzinfo (1.2.7)
50
- thread_safe (~> 0.1)
51
- zeitwerk (2.3.0)
52
-
53
- PLATFORMS
54
- ruby
55
-
56
- DEPENDENCIES
57
- activesupport (~> 6.0)
58
- bundler (~> 1.17)
59
- pry (~> 0.13)
60
- rake (~> 13.0)
61
- redis-time-series!
62
- rspec (~> 3.0)
63
- simplecov (< 0.18)
64
-
65
- BUNDLED WITH
66
- 1.17.2