influxdb-client 1.2.0.pre.535 → 1.2.0.pre.562

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 76180dd26673089eef78a0e5a0ee439de3fc78ff7287e9fda338ea537f867be4
4
- data.tar.gz: 9fc1d0ccac566b186ed8fd4b6909325b399d6596aea2ce3d31902ac48d3a7cb4
3
+ metadata.gz: a5a1a523648c0252791755455bff70a6d2fddb7e6de8de793aa6e92871e9a76d
4
+ data.tar.gz: 804b8543ec36402c6fb62d361b83eec7a03c5b4d46e23d225171ba702aad07e2
5
5
  SHA512:
6
- metadata.gz: 1caa69a1606fd833f732bac74d40362f97c420f3c529b6eda7a0ec9a865fd7de6aee4ab50a26a07d4379754070e25a71e69789580cc2f3cdd7431518c3468823
7
- data.tar.gz: 98ade13f63f89d09aaab9d323d5677116374618ed817d955b46fbc77a314e3993f5f82d48b0d1efd2367b22592cd1c95ecc094274cd18837b005ab85131b965c
6
+ metadata.gz: 060ec40dd8735238025978a3c404ab38f0ecaa90e894a06f22390cc83ff976820c42963e3a155fff9fc14b5b24d9c942ec2b2647d47e1a6aa15daf16ab4898c1
7
+ data.tar.gz: e6336c095161b4750603d2714dd67383d7c3340442be492d758f32a9ddb073e098f83879943e09fb164c82ac3734bde641750a1fa3281a62987179c5e9626a6d
@@ -2,6 +2,7 @@
2
2
 
3
3
  ### Features
4
4
  1. [#23](https://github.com/influxdata/influxdb-client-ruby/issues/23): Added DeleteApi to delete time series data from InfluxDB.
5
+ 1. [#24](https://github.com/influxdata/influxdb-client-ruby/issues/24): Added jitter_interval and retry_interval to WriteApi
5
6
 
6
7
  ### Bugs
7
8
  1. [#22](https://github.com/influxdata/influxdb-client-ruby/pull/22): Fixed batch write
data/README.md CHANGED
@@ -123,6 +123,8 @@ The writes are processed in batches which are configurable by `WriteOptions`:
123
123
  | --- | --- | --- |
124
124
  | **batchSize** | the number of data point to collect in batch | 1000 |
125
125
  | **flushInterval** | the number of milliseconds before the batch is written | 1000 |
126
+ | **retry_interval** | the number of milliseconds to retry unsuccessful write. The retry interval is used when the InfluxDB server does not specify "Retry-After" header. | 1000 |
127
+ | **jitter_interval** | the number of milliseconds to increase the batch flush interval by a random amount | 0 |
126
128
 
127
129
  ```ruby
128
130
  write_options = InfluxDB2::WriteOptions.new(write_type: InfluxDB2::WriteType::BATCHING,
@@ -5,22 +5,26 @@ module InfluxDB2
5
5
  attr_reader :code
6
6
  # Reference code unique to the error type
7
7
  attr_reader :reference
8
+ # The Retry-After header describes when to try the request again.
9
+ attr_reader :retry_after
8
10
 
9
- def initialize(message:, code:, reference:)
11
+ def initialize(message:, code:, reference:, retry_after:)
10
12
  super(message)
11
13
 
12
14
  @code = code
13
15
  @reference = reference
16
+ @retry_after = retry_after
14
17
  end
15
18
 
16
19
  def self.from_response(response)
17
20
  json = JSON.parse(response.body)
18
- obj = new(message: json['message'] || '', code: response.code, reference: json['code'] || '')
21
+ obj = new(message: json['message'] || '', code: response.code, reference: json['code'] || '',
22
+ retry_after: response['Retry-After'] || '')
19
23
  obj
20
24
  end
21
25
 
22
26
  def self.from_message(message)
23
- obj = new(message: message, code: '', reference: '')
27
+ obj = new(message: message, code: '', reference: '', retry_after: '')
24
28
  obj
25
29
  end
26
30
  end
@@ -33,24 +33,36 @@ module InfluxDB2
33
33
 
34
34
  @thread_flush = Thread.new do
35
35
  until api_client.closed
36
- sleep @write_options.flush_interval / 1_000
37
- check_background_queue
36
+ sleep @write_options.flush_interval.to_f / 1_000
37
+ _check_background_queue
38
38
  end
39
39
  end
40
40
 
41
41
  @thread_size = Thread.new do
42
42
  until api_client.closed
43
- check_background_queue(size: true) if @queue.length >= @write_options.batch_size
43
+ _check_background_queue(size: true) if @queue.length >= @write_options.batch_size
44
44
  sleep 0.01
45
45
  end
46
46
  end
47
47
  end
48
48
 
49
49
  def push(payload)
50
- @queue.push(payload)
50
+ if payload.respond_to? :each
51
+ payload.each do |item|
52
+ push(item)
53
+ end
54
+ else
55
+ @queue.push(payload)
56
+ end
57
+ end
58
+
59
+ def flush_all
60
+ _check_background_queue until @queue.empty?
51
61
  end
52
62
 
53
- def check_background_queue(size: false)
63
+ private
64
+
65
+ def _check_background_queue(size: false)
54
66
  @queue_event.pop
55
67
  data = {}
56
68
  points = 0
@@ -74,20 +86,30 @@ module InfluxDB2
74
86
  end
75
87
 
76
88
  begin
77
- write(data) unless data.values.flatten.empty?
89
+ _write(data) unless data.values.flatten.empty?
78
90
  ensure
79
91
  @queue_event.push(true)
80
92
  end
81
93
  end
82
94
 
83
- def flush_all
84
- check_background_queue until @queue.empty?
95
+ def _write(data)
96
+ data.each do |key, points|
97
+ _write_raw(key, points)
98
+ end
85
99
  end
86
100
 
87
- def write(data)
88
- data.each do |key, points|
89
- @api_client.write_raw(points.join("\n"), precision: key.precision, bucket: key.bucket, org: key.org)
101
+ def _write_raw(key, points)
102
+ if @write_options.jitter_interval.positive?
103
+ jitter_delay = (@write_options.jitter_interval.to_f / 1_000) * rand
104
+ sleep jitter_delay
90
105
  end
106
+ @api_client.write_raw(points.join("\n"), precision: key.precision, bucket: key.bucket, org: key.org)
107
+ rescue InfluxError => e
108
+ raise e if e.code.nil? || !(%w[429 503].include? e.code)
109
+
110
+ timeout = e.retry_after.empty? ? @write_options.retry_interval.to_f / 1_000 : e.retry_after.to_f
111
+ sleep timeout
112
+ _write_raw(key, points)
91
113
  end
92
114
  end
93
115
  end
@@ -27,17 +27,36 @@ module InfluxDB2
27
27
 
28
28
  # Creates write api configuration.
29
29
  #
30
- # @param write_type: methods of write (batching, asynchronous, synchronous)
31
- # @param batch_size: the number of data point to collect in batch
32
- # @param flush_interval: flush data at least in this interval
33
30
  class WriteOptions
34
- def initialize(write_type: WriteType::SYNCHRONOUS, batch_size: 1_000, flush_interval: 1_000)
31
+ # @param [WriteType] write_type: methods of write (batching, synchronous)
32
+ # @param [Integer] batch_size: the number of data point to collect in batch
33
+ # @param [Integer] flush_interval: flush data at least in this interval
34
+ # @param [Integer] retry_interval: number of milliseconds to retry unsuccessful write.
35
+ # The retry interval is used when the InfluxDB server does not specify "Retry-After" header.
36
+ # @param [Integer] jitter_interval: the number of milliseconds to increase the batch flush interval
37
+ # by a random amount
38
+ def initialize(write_type: WriteType::SYNCHRONOUS, batch_size: 1_000, flush_interval: 1_000, retry_interval: 1_000,
39
+ jitter_interval: 0)
40
+ _check_not_negative('batch_size', batch_size)
41
+ _check_not_negative('flush_interval', flush_interval)
42
+ _check_not_negative('retry_interval', retry_interval)
43
+ _check_positive('jitter_interval', jitter_interval)
35
44
  @write_type = write_type
36
45
  @batch_size = batch_size
37
46
  @flush_interval = flush_interval
47
+ @retry_interval = retry_interval
48
+ @jitter_interval = jitter_interval
38
49
  end
39
50
 
40
- attr_reader :write_type, :batch_size, :flush_interval
51
+ attr_reader :write_type, :batch_size, :flush_interval, :retry_interval, :jitter_interval
52
+
53
+ def _check_not_negative(key, value)
54
+ raise ArgumentError, "The '#{key}' should be positive or zero, but is: #{value}" if value <= 0
55
+ end
56
+
57
+ def _check_positive(key, value)
58
+ raise ArgumentError, "The '#{key}' should be positive number, but is: #{value}" if value < 0
59
+ end
41
60
  end
42
61
 
43
62
  SYNCHRONOUS = InfluxDB2::WriteOptions.new(write_type: WriteType::SYNCHRONOUS)
@@ -210,9 +229,14 @@ module InfluxDB2
210
229
  elsif data.is_a?(Hash)
211
230
  _generate_payload(Point.from_hash(data), bucket: bucket, org: org, precision: precision)
212
231
  elsif data.respond_to? :map
213
- data.map do |item|
232
+ payloads = data.map do |item|
214
233
  _generate_payload(item, bucket: bucket, org: org, precision: precision)
215
- end.reject(&:nil?).join("\n".freeze)
234
+ end.reject(&:nil?)
235
+ if @write_options.write_type == WriteType::BATCHING
236
+ payloads
237
+ else
238
+ payloads.join("\n".freeze)
239
+ end
216
240
  end
217
241
  end
218
242
  end
@@ -25,7 +25,7 @@ class WriteApiBatchingTest < MiniTest::Test
25
25
  WebMock.disable_net_connect!
26
26
 
27
27
  @write_options = InfluxDB2::WriteOptions.new(write_type: InfluxDB2::WriteType::BATCHING,
28
- batch_size: 2, flush_interval: 5_000)
28
+ batch_size: 2, flush_interval: 5_000, retry_interval: 2_000)
29
29
  @client = InfluxDB2::Client.new('http://localhost:9999',
30
30
  'my-token',
31
31
  bucket: 'my-bucket',
@@ -44,6 +44,29 @@ class WriteApiBatchingTest < MiniTest::Test
44
44
  WebMock.reset!
45
45
  end
46
46
 
47
+ def test_batch_configuration
48
+ error = assert_raises ArgumentError do
49
+ InfluxDB2::WriteOptions.new(write_type: InfluxDB2::WriteType::BATCHING, batch_size: 0)
50
+ end
51
+ assert_equal "The 'batch_size' should be positive or zero, but is: 0", error.message
52
+
53
+ error = assert_raises ArgumentError do
54
+ InfluxDB2::WriteOptions.new(write_type: InfluxDB2::WriteType::BATCHING, flush_interval: -10)
55
+ end
56
+ assert_equal "The 'flush_interval' should be positive or zero, but is: -10", error.message
57
+
58
+ error = assert_raises ArgumentError do
59
+ InfluxDB2::WriteOptions.new(write_type: InfluxDB2::WriteType::BATCHING, retry_interval: 0)
60
+ end
61
+ assert_equal "The 'retry_interval' should be positive or zero, but is: 0", error.message
62
+
63
+ InfluxDB2::WriteOptions.new(write_type: InfluxDB2::WriteType::BATCHING, jitter_interval: 0)
64
+ error = assert_raises ArgumentError do
65
+ InfluxDB2::WriteOptions.new(write_type: InfluxDB2::WriteType::BATCHING, jitter_interval: -10)
66
+ end
67
+ assert_equal "The 'jitter_interval' should be positive number, but is: -10", error.message
68
+ end
69
+
47
70
  def test_batch_size
48
71
  stub_request(:post, 'http://localhost:9999/api/v2/write?bucket=my-bucket&org=my-org&precision=ns')
49
72
  .to_return(status: 204)
@@ -163,4 +186,107 @@ class WriteApiBatchingTest < MiniTest::Test
163
186
  "h2o_feet,location=coyote_creek level\\ water_level=2.0 2\n" \
164
187
  'h2o_feet,location=coyote_creek level\\ water_level=3.0 3')
165
188
  end
189
+
190
+ def test_retry_interval_by_config
191
+ error_body = '{"code":"temporarily unavailable","message":"Token is temporarily over quota. '\
192
+ 'The Retry-After header describes when to try the write again."}'
193
+
194
+ stub_request(:post, 'http://localhost:9999/api/v2/write?bucket=my-bucket&org=my-org&precision=ns')
195
+ .to_return(status: 429, headers: { 'X-Platform-Error-Code' => 'temporarily unavailable' }, body: error_body).then
196
+ .to_return(status: 204)
197
+
198
+ request = "h2o_feet,location=coyote_creek water_level=1.0 1\n" \
199
+ 'h2o_feet,location=coyote_creek water_level=2.0 2'
200
+
201
+ @write_client.write(data: ['h2o_feet,location=coyote_creek water_level=1.0 1',
202
+ 'h2o_feet,location=coyote_creek water_level=2.0 2'])
203
+
204
+ sleep(0.5)
205
+
206
+ assert_requested(:post, 'http://localhost:9999/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
207
+ times: 1, body: request)
208
+
209
+ sleep(1)
210
+
211
+ assert_requested(:post, 'http://localhost:9999/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
212
+ times: 1, body: request)
213
+
214
+ sleep(1)
215
+
216
+ assert_requested(:post, 'http://localhost:9999/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
217
+ times: 2, body: request)
218
+ end
219
+
220
+ def test_retry_interval_by_header
221
+ error_body = '{"code":"temporarily unavailable","message":"Server is temporarily unavailable to accept writes. '\
222
+ 'The Retry-After header describes when to try the write again."}'
223
+
224
+ stub_request(:post, 'http://localhost:9999/api/v2/write?bucket=my-bucket&org=my-org&precision=ns')
225
+ .to_return(status: 503, headers: { 'X-Platform-Error-Code' => 'temporarily unavailable', 'Retry-After' => '3' },
226
+ body: error_body).then
227
+ .to_return(status: 204)
228
+
229
+ request = "h2o_feet,location=coyote_creek water_level=1.0 1\n" \
230
+ 'h2o_feet,location=coyote_creek water_level=2.0 2'
231
+
232
+ @write_client.write(data: ['h2o_feet,location=coyote_creek water_level=1.0 1',
233
+ InfluxDB2::Point.new(name: 'h2o_feet')
234
+ .add_tag('location', 'coyote_creek')
235
+ .add_field('water_level', 2.0)
236
+ .time(2, InfluxDB2::WritePrecision::NANOSECOND)])
237
+
238
+ sleep(0.5)
239
+
240
+ assert_requested(:post, 'http://localhost:9999/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
241
+ times: 1, body: request)
242
+
243
+ sleep(1)
244
+
245
+ assert_requested(:post, 'http://localhost:9999/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
246
+ times: 1, body: request)
247
+
248
+ sleep(1)
249
+
250
+ assert_requested(:post, 'http://localhost:9999/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
251
+ times: 1, body: request)
252
+
253
+ sleep(1)
254
+
255
+ assert_requested(:post, 'http://localhost:9999/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
256
+ times: 2, body: request)
257
+ end
258
+
259
+ def test_jitter_interval
260
+ @client.close!
261
+
262
+ @client = InfluxDB2::Client.new('http://localhost:9999',
263
+ 'my-token',
264
+ bucket: 'my-bucket',
265
+ org: 'my-org',
266
+ precision: InfluxDB2::WritePrecision::NANOSECOND,
267
+ use_ssl: false)
268
+
269
+ @write_options = InfluxDB2::WriteOptions.new(write_type: InfluxDB2::WriteType::BATCHING,
270
+ batch_size: 2, flush_interval: 5_000, jitter_interval: 2_000)
271
+ @write_client = @client.create_write_api(write_options: @write_options)
272
+
273
+ stub_request(:post, 'http://localhost:9999/api/v2/write?bucket=my-bucket&org=my-org&precision=ns')
274
+ .to_return(status: 204)
275
+
276
+ request = "h2o_feet,location=coyote_creek water_level=1.0 1\n" \
277
+ 'h2o_feet,location=coyote_creek water_level=2.0 2'
278
+
279
+ @write_client.write(data: ['h2o_feet,location=coyote_creek water_level=1.0 1',
280
+ 'h2o_feet,location=coyote_creek water_level=2.0 2'])
281
+
282
+ sleep(0.1)
283
+
284
+ assert_requested(:post, 'http://localhost:9999/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
285
+ times: 0, body: request)
286
+
287
+ sleep(2)
288
+
289
+ assert_requested(:post, 'http://localhost:9999/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
290
+ times: 1, body: request)
291
+ end
166
292
  end
@@ -129,6 +129,25 @@ class WriteApiTest < MiniTest::Test
129
129
  times: 1, body: expected)
130
130
  end
131
131
 
132
+ def test_array_of_array
133
+ stub_request(:any, 'http://localhost:9999/api/v2/write?bucket=my-bucket&org=my-org&precision=ns')
134
+ .to_return(status: 204)
135
+ client = InfluxDB2::Client.new('http://localhost:9999', 'my-token',
136
+ bucket: 'my-bucket',
137
+ org: 'my-org',
138
+ precision: InfluxDB2::WritePrecision::NANOSECOND,
139
+ use_ssl: false)
140
+
141
+ client.create_write_api.write(data: ['h2o,location=west value=33i 15', ['h2o,location=west value=34i 16',
142
+ 'h2o,location=west value=35i 17']])
143
+
144
+ expected = "h2o,location=west value=33i 15\nh2o,location=west value=34i 16"\
145
+ "\nh2o,location=west value=35i 17"
146
+
147
+ assert_requested(:post, 'http://localhost:9999/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
148
+ times: 1, body: expected)
149
+ end
150
+
132
151
  def test_authorization_header
133
152
  stub_request(:any, 'http://localhost:9999/api/v2/write?bucket=my-bucket&org=my-org&precision=ns')
134
153
  .to_return(status: 204)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: influxdb-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0.pre.535
4
+ version: 1.2.0.pre.562
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakub Bednar
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-25 00:00:00.000000000 Z
11
+ date: 2020-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler