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 +4 -4
- data/CHANGELOG.md +1 -0
- data/README.md +2 -0
- data/lib/influxdb2/client/influx_error.rb +7 -3
- data/lib/influxdb2/client/worker.rb +33 -11
- data/lib/influxdb2/client/write_api.rb +31 -7
- data/test/influxdb/write_api_batching_test.rb +127 -1
- data/test/influxdb/write_api_test.rb +19 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5a1a523648c0252791755455bff70a6d2fddb7e6de8de793aa6e92871e9a76d
|
4
|
+
data.tar.gz: 804b8543ec36402c6fb62d361b83eec7a03c5b4d46e23d225171ba702aad07e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 060ec40dd8735238025978a3c404ab38f0ecaa90e894a06f22390cc83ff976820c42963e3a155fff9fc14b5b24d9c942ec2b2647d47e1a6aa15daf16ab4898c1
|
7
|
+
data.tar.gz: e6336c095161b4750603d2714dd67383d7c3340442be492d758f32a9ddb073e098f83879943e09fb164c82ac3734bde641750a1fa3281a62987179c5e9626a6d
|
data/CHANGELOG.md
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
84
|
-
|
95
|
+
def _write(data)
|
96
|
+
data.each do |key, points|
|
97
|
+
_write_raw(key, points)
|
98
|
+
end
|
85
99
|
end
|
86
100
|
|
87
|
-
def
|
88
|
-
|
89
|
-
@
|
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
|
-
|
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?)
|
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.
|
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-
|
11
|
+
date: 2020-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|