influxdb-client 1.16.0.pre.2686 → 1.16.0.pre.2765

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: 1980343bc130f3760b59ec721021be779263747c0cd441e30212ceacc821d65b
4
- data.tar.gz: f8c77313016a4b78eeebb82ad30c4b5a3a1275ce36d7e0b381f442f2c7bc7567
3
+ metadata.gz: 877a4c787f2606b1500f37957bdab29e4c58a9a8c810ac1d324aefb207dd476b
4
+ data.tar.gz: 61920b4d1ecfd0b52b74177f723fc85aac6d06185d2ccd529179b7b9531fac47
5
5
  SHA512:
6
- metadata.gz: 48b584e2e946aa3cc5a5db8135a38c155d88e75403bb771a156f4b3537f0c3d4266aa744f2d9d697a07ee9f075485af06efb738ae2190e7d57566282cd00f072
7
- data.tar.gz: dc19ef50dbeb2dbb5e0bb1822ab83ae359418409dd008c41d97be0c0059ebbf58981efeb134485bc0fdfb7d03778443c6f158254915c50e81b6ad285a3d8dd1f
6
+ metadata.gz: 4e19378ef8f37121af2e834142cc5877f045a09ce1ecd903dc39cd7078bc61fbc2fd3dbb9f6b59ed2fe1217eb551b2ccc1eac81c5224a9ea1678f470ec209fc9
7
+ data.tar.gz: c6f38e24dae4bd27523b3727484f71973526ee7624c6b0678ccb88eb3d9a8b2a71eb34b698002359f99fe09841af883bace23c6b619cf5d88c6663e458288cf7
data/README.md CHANGED
@@ -159,13 +159,14 @@ The writes are processed in batches which are configurable by `WriteOptions`:
159
159
 
160
160
  | Property | Description | Default Value |
161
161
  | --- | --- | --- |
162
- | batchSize | the number of data point to collect in batch | 1000 |
163
- | flush_interval | the number of milliseconds before the batch is written | 1000 |
164
- | 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. | 5000 |
162
+ | batchSize | the number of data point to collect in batch | 1_000 |
163
+ | flush_interval | the number of milliseconds before the batch is written | 1_000 |
164
+ | 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. | 5_000 |
165
165
  | jitter_interval | the number of milliseconds to increase the batch flush interval by a random amount | 0 |
166
166
  | max_retries | the number of max retries when write fails | 5 |
167
- | max_retry_delay | maximum delay when retrying write in milliseconds | 180000 |
168
- | exponential_base | the base for the exponential retry delay, the next delay is computed as `retry_interval * exponential_base^(attempts - 1) + random(jitter_interval)` | 5 |
167
+ | max_retry_delay | maximum delay when retrying write in milliseconds | 125_000 |
168
+ | max_retry_time | maximum total retry timeout in milliseconds | 180_000 |
169
+ | exponential_base | the base for the exponential retry delay, the next delay is computed using random exponential backoff as a random value within the interval ``retry_interval * exponential_base^(attempts-1)`` and ``retry_interval * exponential_base^(attempts)``. Example for ``retry_interval=5000, exponential_base=2, max_retry_delay=125000, total=5`` Retry delays are random distributed values within the ranges of ``[5000-10000, 10000-20000, 20000-40000, 40000-80000, 80000-125000]`` | 2 |
169
170
  | batch_abort_on_exception | the batching worker will be aborted after failed retry strategy | false |
170
171
  ```ruby
171
172
  write_options = InfluxDB2::WriteOptions.new(write_type: InfluxDB2::WriteType::BATCHING,
@@ -28,3 +28,4 @@ require 'influxdb2/client/delete_api'
28
28
  require 'influxdb2/client/health_api'
29
29
  require 'influxdb2/client/point'
30
30
  require 'influxdb2/client/flux_table'
31
+ require 'influxdb2/client/write_retry'
@@ -96,36 +96,28 @@ module InfluxDB2
96
96
 
97
97
  def _write(data)
98
98
  data.each do |key, points|
99
- _write_raw(key, points, 1, @write_options.retry_interval)
99
+ _write_raw(key, points)
100
100
  end
101
101
  end
102
102
 
103
- def _write_raw(key, points, attempts, retry_interval)
103
+ def _write_raw(key, points)
104
+ write_retry = InfluxDB2::WriteRetry.new(
105
+ api_client: @api_client,
106
+ max_retries: @write_options.max_retries,
107
+ exponential_base: @write_options.exponential_base,
108
+ retry_interval: @write_options.retry_interval,
109
+ max_retry_delay: @write_options.max_retry_delay,
110
+ max_retry_time: @write_options.max_retry_time
111
+ )
112
+
104
113
  if @write_options.jitter_interval > 0
105
114
  jitter_delay = (@write_options.jitter_interval.to_f / 1_000) * rand
106
115
  sleep jitter_delay
107
116
  end
108
- @api_client.write_raw(points.join("\n"), precision: key.precision, bucket: key.bucket, org: key.org)
109
- rescue InfluxError => e
110
- raise e if attempts > @write_options.max_retries
111
- raise e if (e.code.nil? || e.code.to_i < 429) && !_connection_error(e.original)
112
-
113
- timeout = if e.retry_after.empty?
114
- [retry_interval.to_f, @write_options.max_retry_delay.to_f].min / 1_000
115
- else
116
- e.retry_after.to_f
117
- end
118
-
119
- message = 'The retriable error occurred during writing of data. '\
120
- "Reason: '#{e.message}'. Retry in: #{timeout}s."
121
-
122
- @api_client.log(:warn, message)
123
- sleep timeout
124
- _write_raw(key, points, attempts + 1, retry_interval * @write_options.exponential_base)
125
- end
126
117
 
127
- def _connection_error(error)
128
- InfluxError::HTTP_ERRORS.any? { |c| error.instance_of? c }
118
+ write_retry.retry do
119
+ @api_client.write_raw(points.join("\n"), precision: key.precision, bucket: key.bucket, org: key.org)
120
+ end
129
121
  end
130
122
  end
131
123
  end
@@ -36,19 +36,23 @@ module InfluxDB2
36
36
  # @param [Integer] jitter_interval: the number of milliseconds to increase the batch flush interval
37
37
  # @param [Integer] max_retries: max number of retries when write fails
38
38
  # @param [Integer] max_retry_delay: maximum delay when retrying write in milliseconds
39
- # by a random amount
40
- # @param [Integer] exponential_base: base for the exponential retry delay, the next delay is computed as
41
- # "exponential_base^(attempts-1) + random(jitter_interval)"
39
+ # @param [Integer] max_retry_time: maximum total retry timeout in milliseconds
40
+ # @param [Integer] exponential_base: the next delay is computed using random exponential backoff as a random value
41
+ # within the interval retry_interval*exponential_base^(attempts-1) and retry_interval*exponential_base^(attempts).
42
+ # Example for retry_interval=5000, exponential_base=2, max_retry_delay=125000, total=5
43
+ # Retry delays are random distributed values within the ranges of
44
+ # [5000-10000, 10000-20000, 20000-40000, 40000-80000, 80000-125000]
42
45
  # @param [Boolean] batch_abort_on_exception: batching worker will be aborted after failed retry strategy
43
46
  def initialize(write_type: WriteType::SYNCHRONOUS, batch_size: 1_000, flush_interval: 1_000, retry_interval: 5_000,
44
- jitter_interval: 0, max_retries: 5, max_retry_delay: 180_000, exponential_base: 5,
45
- batch_abort_on_exception: false)
47
+ jitter_interval: 0, max_retries: 5, max_retry_delay: 125_000, max_retry_time: 180_000,
48
+ exponential_base: 2, batch_abort_on_exception: false)
46
49
  _check_not_negative('batch_size', batch_size)
47
50
  _check_not_negative('flush_interval', flush_interval)
48
51
  _check_not_negative('retry_interval', retry_interval)
49
52
  _check_positive('jitter_interval', jitter_interval)
50
- _check_positive('max_retries', jitter_interval)
51
- _check_positive('max_retry_delay', jitter_interval)
53
+ _check_positive('max_retries', max_retries)
54
+ _check_positive('max_retry_delay', max_retry_delay)
55
+ _check_positive('max_retry_time', max_retry_time)
52
56
  _check_positive('exponential_base', exponential_base)
53
57
  @write_type = write_type
54
58
  @batch_size = batch_size
@@ -57,12 +61,13 @@ module InfluxDB2
57
61
  @jitter_interval = jitter_interval
58
62
  @max_retries = max_retries
59
63
  @max_retry_delay = max_retry_delay
64
+ @max_retry_time = max_retry_time
60
65
  @exponential_base = exponential_base
61
66
  @batch_abort_on_exception = batch_abort_on_exception
62
67
  end
63
68
 
64
69
  attr_reader :write_type, :batch_size, :flush_interval, :retry_interval, :jitter_interval,
65
- :max_retries, :max_retry_delay, :exponential_base, :batch_abort_on_exception
70
+ :max_retries, :max_retry_delay, :max_retry_time, :exponential_base, :batch_abort_on_exception
66
71
 
67
72
  def _check_not_negative(key, value)
68
73
  raise ArgumentError, "The '#{key}' should be positive or zero, but is: #{value}" if value <= 0
@@ -0,0 +1,104 @@
1
+ # The MIT License
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module InfluxDB2
22
+ # Exponential random write retry.
23
+ class WriteRetry
24
+ @error_msg_prefix = 'Error with options to with_retries:'
25
+
26
+ # @param [Hash] options the retry options.
27
+ # @option options [Integer] :max_retries (5) The maximum number of times to run the block.
28
+ # @option options [Integer] :retry_interval (5000) number of milliseconds to retry unsuccessful write.
29
+ # @option options [Integer] :max_retry_delay (125_000) maximum delay when retrying write in milliseconds.
30
+ # @option options [Integer] :max_retry_time (180_000) maximum total retry timeout in milliseconds.
31
+ # @option options [Integer] :exponential_base base for the exponential retry delay
32
+ # @option options [Integer] :jitter_delay random milliseconds added to write interval
33
+ def initialize(options = {})
34
+ @api_client = options[:api_client]
35
+ @max_retries = options[:max_retries] || 5
36
+ raise "#{@error_msg_prefix} :max_retries must be greater than 0." unless @max_retries > 0
37
+
38
+ @retry_interval = options[:retry_interval] || 5_000
39
+ @max_retry_delay = options[:max_retry_delay] || 125_000
40
+ @max_retry_time = options[:max_retry_time] || 180_000
41
+ @exponential_base = options[:exponential_base] || 2
42
+ @jitter_interval = options[:jitter_interval] || 0
43
+ raise "#{@error_msg_prefix} :retry_interval cannot be greater than :max_retry_delay." if
44
+ @retry_interval > @max_retry_delay
45
+ end
46
+
47
+ def get_backoff_time(attempts)
48
+ range_start = @retry_interval
49
+ range_stop = @retry_interval * @exponential_base
50
+
51
+ i = 1
52
+ while i < attempts
53
+ i += 1
54
+ range_start = range_stop
55
+ range_stop *= @exponential_base
56
+ break if range_stop > @max_retry_delay
57
+ end
58
+
59
+ range_stop = @max_retry_delay if range_stop > @max_retry_delay
60
+ range_start + (range_stop - range_start) * rand
61
+ end
62
+
63
+ # Runs the supplied code block with a exponential backoff retry strategy.
64
+ def retry
65
+ raise "#{@error_msg_prefix} must be passed a block" unless block_given?
66
+
67
+ attempts = 0
68
+ start_time = Time.now
69
+ begin
70
+ attempts += 1
71
+ yield attempts
72
+ rescue InfluxError => e
73
+ if attempts > @max_retries
74
+ @api_client.log(:error, 'Maximum retry attempts reached.')
75
+ raise e
76
+ end
77
+
78
+ if (Time.now - start_time) * 1000 > @max_retry_time
79
+ @api_client.log(:error, "Maximum retry time #{@max_retry_time} ms exceeded")
80
+ raise e
81
+ end
82
+
83
+ raise e if (e.code.nil? || e.code.to_i < 429) && !_connection_error(e.original)
84
+
85
+ timeout = if e.retry_after.nil? || e.retry_after.empty?
86
+ get_backoff_time(attempts)
87
+ else
88
+ (e.retry_after.to_f * 1000) + @jitter_interval * rand
89
+ end
90
+
91
+ message = 'The retriable error occurred during writing of data. '\
92
+ "Reason: '#{e.message}'. Retry in: #{timeout.to_f / 1000}s."
93
+
94
+ @api_client.log(:warn, message)
95
+ sleep timeout / 1000
96
+ retry
97
+ end
98
+ end
99
+
100
+ def _connection_error(error)
101
+ InfluxError::HTTP_ERRORS.any? { |c| error.instance_of? c }
102
+ end
103
+ end
104
+ end
@@ -292,9 +292,9 @@ class WriteApiRetryStrategyTest < MiniTest::Test
292
292
 
293
293
  @write_client.write(data: ['h2o_feet,location=coyote_creek water_level=1.0 1',
294
294
  InfluxDB2::Point.new(name: 'h2o_feet')
295
- .add_tag('location', 'coyote_creek')
296
- .add_field('water_level', 2.0)
297
- .time(2, InfluxDB2::WritePrecision::NANOSECOND)])
295
+ .add_tag('location', 'coyote_creek')
296
+ .add_field('water_level', 2.0)
297
+ .time(2, InfluxDB2::WritePrecision::NANOSECOND)])
298
298
 
299
299
  sleep(0.5)
300
300
 
@@ -342,32 +342,7 @@ class WriteApiRetryStrategyTest < MiniTest::Test
342
342
 
343
343
  @client.create_write_api(write_options: write_options).write(data: point)
344
344
 
345
- sleep(0.5)
346
-
347
- assert_requested(:post, 'http://localhost:8086/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
348
- times: 1, body: request)
349
-
350
- sleep(2)
351
-
352
- assert_requested(:post, 'http://localhost:8086/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
353
- times: 2, body: request)
354
-
355
- sleep(4)
356
-
357
- assert_requested(:post, 'http://localhost:8086/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
358
- times: 3, body: request)
359
-
360
- sleep(5)
361
-
362
- assert_requested(:post, 'http://localhost:8086/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
363
- times: 4, body: request)
364
-
365
- sleep(5)
366
-
367
- assert_requested(:post, 'http://localhost:8086/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
368
- times: 4, body: request)
369
-
370
- sleep(5)
345
+ sleep(15)
371
346
 
372
347
  assert_requested(:post, 'http://localhost:8086/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
373
348
  times: 4, body: request)
@@ -465,38 +440,14 @@ class WriteApiRetryStrategyTest < MiniTest::Test
465
440
  request = 'h2o,location=europe level=2.0'
466
441
 
467
442
  write_options = InfluxDB2::WriteOptions.new(write_type: InfluxDB2::WriteType::BATCHING,
468
- batch_size: 1, retry_interval: 2_000, max_retries: 3,
443
+ batch_size: 1, retry_interval: 1000, max_retries: 3,
469
444
  max_retry_delay: 5_000, exponential_base: 2)
470
445
 
471
446
  @client.create_write_api(write_options: write_options).write(data: point)
472
447
 
473
- sleep(0.5)
474
-
475
- assert_requested(:post, 'http://localhost:8086/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
476
- times: 1, body: request)
477
-
478
- sleep(2)
479
-
480
- assert_requested(:post, 'http://localhost:8086/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
481
- times: 2, body: request)
482
-
483
- sleep(4)
484
-
485
- assert_requested(:post, 'http://localhost:8086/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
486
- times: 3, body: request)
487
-
488
- sleep(5)
489
-
490
- assert_requested(:post, 'http://localhost:8086/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
491
- times: 4, body: request)
492
-
493
- sleep(5)
494
-
495
- assert_requested(:post, 'http://localhost:8086/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
496
- times: 4, body: request)
497
-
498
- sleep(5)
499
-
448
+ # random sleep intervals
449
+ # [1000, 2000], [2000, 4000], [4000, 5000]
450
+ sleep(11)
500
451
  assert_requested(:post, 'http://localhost:8086/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
501
452
  times: 4, body: request)
502
453
  end
@@ -519,22 +470,7 @@ class WriteApiRetryStrategyTest < MiniTest::Test
519
470
 
520
471
  @client.create_write_api(write_options: write_options).write(data: point)
521
472
 
522
- sleep(0.5)
523
-
524
- assert_requested(:post, 'http://localhost:8086/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
525
- times: 1, body: request)
526
-
527
- sleep(2)
528
-
529
- assert_requested(:post, 'http://localhost:8086/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
530
- times: 2, body: request)
531
-
532
- sleep(4)
533
-
534
- assert_requested(:post, 'http://localhost:8086/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
535
- times: 3, body: request)
536
-
537
- sleep(5)
473
+ sleep(10)
538
474
 
539
475
  assert_requested(:post, 'http://localhost:8086/api/v2/write?bucket=my-bucket&org=my-org&precision=ns',
540
476
  times: 3, body: request)
@@ -624,4 +560,61 @@ class WriteApiRetryStrategyTest < MiniTest::Test
624
560
 
625
561
  assert_equal(message, @logger.messages[0][1])
626
562
  end
563
+
564
+ def test_backoff_time_default
565
+ retries = InfluxDB2::WriteRetry.new
566
+
567
+ backoff = retries.get_backoff_time(1)
568
+ assert_gte backoff, 5_000
569
+ assert_lte backoff, 10_000
570
+
571
+ backoff = retries.get_backoff_time(2)
572
+ assert_gte backoff, 10_000
573
+ assert_lte backoff, 20_000
574
+
575
+ backoff = retries.get_backoff_time(3)
576
+ assert_gte backoff, 20_000
577
+ assert_lte backoff, 40_000
578
+
579
+ backoff = retries.get_backoff_time(4)
580
+ assert_gte backoff, 40_000
581
+ assert_lte backoff, 80_000
582
+
583
+ backoff = retries.get_backoff_time(5)
584
+ assert_gte backoff, 80_000
585
+ assert_lte backoff, 125_000
586
+
587
+ backoff = retries.get_backoff_time(6)
588
+ assert_gte backoff, 80_000
589
+ assert_lte backoff, 125_000
590
+ end
591
+
592
+ def test_backoff_time_custom
593
+ retries = InfluxDB2::WriteRetry.new(
594
+ max_retry_delay: 2_000,
595
+ retry_interval: 100,
596
+ exponential_base: 2,
597
+ max_retries: 5
598
+ )
599
+
600
+ backoff = retries.get_backoff_time(1)
601
+ assert_gte backoff, 100
602
+ assert_lte backoff, 200
603
+
604
+ backoff = retries.get_backoff_time(2)
605
+ assert_gte backoff, 200
606
+ assert_lte backoff, 400
607
+
608
+ backoff = retries.get_backoff_time(3)
609
+ assert_gte backoff, 400
610
+ assert_lte backoff, 800
611
+
612
+ backoff = retries.get_backoff_time(4)
613
+ assert_gte backoff, 800
614
+ assert_lte backoff, 1_600
615
+
616
+ backoff = retries.get_backoff_time(5)
617
+ assert_gte backoff, 1_600
618
+ assert_lte backoff, 2_000
619
+ end
627
620
  end
data/test/test_helper.rb CHANGED
@@ -34,7 +34,7 @@ require 'influxdb-client'
34
34
 
35
35
  require 'minitest/autorun'
36
36
  require 'minitest/reporters'
37
- Minitest::Reporters.use!
37
+ Minitest::Reporters.use! unless ENV['RM_INFO']
38
38
 
39
39
  require 'webmock/minitest'
40
40
 
@@ -46,6 +46,24 @@ class MockLogger
46
46
  end
47
47
 
48
48
  def add(level, &block)
49
- @messages << [level, yield(block)]
49
+ line = yield(block)
50
+ print("#{line}\n")
51
+ @messages << [level, line]
50
52
  end
51
53
  end
54
+
55
+ def assert_gt(val1, val2)
56
+ assert_operator val1, :>, val2
57
+ end
58
+
59
+ def assert_gte(val1, val2)
60
+ assert_operator val1, :>=, val2
61
+ end
62
+
63
+ def assert_lt(val1, val2)
64
+ assert_operator val1, :<, val2
65
+ end
66
+
67
+ def assert_lte(val1, val2)
68
+ assert_operator val1, :<=, val2
69
+ end
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.16.0.pre.2686
4
+ version: 1.16.0.pre.2765
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakub Bednar
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-04 00:00:00.000000000 Z
11
+ date: 2021-06-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -151,6 +151,7 @@ files:
151
151
  - lib/influxdb2/client/version.rb
152
152
  - lib/influxdb2/client/worker.rb
153
153
  - lib/influxdb2/client/write_api.rb
154
+ - lib/influxdb2/client/write_retry.rb
154
155
  - test/influxdb/client_test.rb
155
156
  - test/influxdb/default_api_test.rb
156
157
  - test/influxdb/delete_api_integration_test.rb