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

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