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

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: 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