esse 0.5.0 → 0.5.2

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: 15b531b7c6876d665c12fd773d25fa68acda30c4c92cf7999790315f4fe35b2d
4
- data.tar.gz: 9520b76a128e752bed3db6d2a1a96e38e2107ca790c6d0c0327c2690d91b21d7
3
+ metadata.gz: fd1807aa9b2b2c92564d5e35d5a62282c67b9d25883fdc6166ca8981ee9008ac
4
+ data.tar.gz: 1cf17acb25d5b6bcf88e0999fc29c90a564b55d31ac51dce148f397c17ced4c4
5
5
  SHA512:
6
- metadata.gz: 865aa67f404a82e573009e73186dd60ee9c3f978ba3c93b39337184c2b8c9c9415bc116573c5eb4f21cb7e4ffc7e19aae4957e54fe7b8aa99a8804e86469b13f
7
- data.tar.gz: bcacb23ae7b1b1a073298a59090455e30d6aa9531b93a815871762ed9e3364de873795c47ca0b2d495d8d9394110fd623011d3e4e3054a34ddf89b5e35947ded
6
+ metadata.gz: bd72ab5edf509428c62ea598305a8351105c0073fa63a2246eb9eecb84b99fa2115355b1c7f881afdd500196a0d31482a7be7ebb8f4b92743eca3a8bd775840c
7
+ data.tar.gz: 94f0e1fed7ccaac67b8795a349ef51ab0cbfa9251fc219ffa49510a25a7e3ea52e44ae3ec386a7437014a5314ae65a0a20bfca542babddbb124a3381e49fd1c0
data/lib/esse/config.rb CHANGED
@@ -43,7 +43,7 @@ module Esse
43
43
  # end
44
44
  class Config
45
45
  DEFAULT_CLUSTER_ID = :default
46
- ATTRIBUTES = %i[indices_directory bulk_wait_interval].freeze
46
+ ATTRIBUTES = %i[indices_directory bulk_wait_interval bulk_retry_on_failure_max_retries bulk_retry_on_failure_wait].freeze
47
47
 
48
48
  # The location of the indices. Defaults to the `app/indices`
49
49
  attr_reader :indices_directory
@@ -51,9 +51,17 @@ module Esse
51
51
  # wait a given period between posting pages to give Elasticsearch time to catch up.
52
52
  attr_reader :bulk_wait_interval
53
53
 
54
+ # number of retries on transient server errors (502, 503, 504, 429) before raising
55
+ attr_reader :bulk_retry_on_failure_max_retries
56
+
57
+ # base wait in seconds between transient-error retries (doubles each attempt)
58
+ attr_reader :bulk_retry_on_failure_wait
59
+
54
60
  def initialize
55
61
  self.indices_directory = 'app/indices'
56
62
  self.bulk_wait_interval = 0.1
63
+ self.bulk_retry_on_failure_max_retries = 3
64
+ self.bulk_retry_on_failure_wait = 2.0
57
65
  @clusters = {}
58
66
  cluster(DEFAULT_CLUSTER_ID) # initialize the :default client
59
67
  end
@@ -81,6 +89,14 @@ module Esse
81
89
  @bulk_wait_interval = value.to_f
82
90
  end
83
91
 
92
+ def bulk_retry_on_failure_max_retries=(value)
93
+ @bulk_retry_on_failure_max_retries = value.to_i
94
+ end
95
+
96
+ def bulk_retry_on_failure_wait=(value)
97
+ @bulk_retry_on_failure_wait = value.to_f
98
+ end
99
+
84
100
  def load(arg)
85
101
  case arg
86
102
  when Hash
data/lib/esse/errors.rb CHANGED
@@ -57,6 +57,7 @@ module Esse
57
57
  'ExpectationFailed' => 'ExpectationFailedError', # 417
58
58
  'ImATeapot' => 'ImATeapotError', # 418
59
59
  'TooManyConnectionsFromThisIP' => 'TooManyConnectionsFromThisIPError', # 421
60
+ 'TooManyRequests' => 'TooManyRequestsError', # 429
60
61
  'UpgradeRequired' => 'UpgradeRequiredError', # 426
61
62
  'BlockedByWindowsParentalControls' => 'BlockedByWindowsParentalControlsError', # 450
62
63
  'RequestHeaderTooLarge' => 'RequestHeaderTooLargeError', # 494
@@ -18,11 +18,13 @@ module Esse
18
18
  # document still returns 413 does the error bubble up.
19
19
  #
20
20
  # @yield [RequestBody] A request body instance
21
- def each_request(max_retries: 4, last_retry_in_small_chunks: true, last_retry_per_document: true)
21
+ def each_request(max_retries: 4, last_retry_in_small_chunks: true, last_retry_per_document: true,
22
+ retry_on_failure_max_retries: 3, retry_on_failure_wait: 2.0)
22
23
  # @TODO create indexes when by checking all the index suffixes (if mapping is not empty)
23
24
  requests = [optimistic_request]
24
25
  retry_count = 0
25
26
  too_large_retry_count = 0
27
+ transient_failure_count = 0
26
28
 
27
29
  begin
28
30
  requests.each do |request|
@@ -59,11 +61,23 @@ module Esse
59
61
 
60
62
  raise e unless last_retry_per_document
61
63
  requests = requests_per_document
64
+ ids = document_ids
65
+ id_info = ids.any? ? " (document IDs: #{ids.join(', ')})" : ''
62
66
  Esse.logger.warn <<~MSG
63
- Request entity too large after balancing, retrying one document per request as a last resort.
67
+ Request entity too large after balancing, retrying one document per request as a last resort#{id_info}.
64
68
  If a single document still exceeds the bulk size, the error will be raised.
65
69
  MSG
66
70
  retry
71
+ rescue Esse::Transport::BadGatewayError,
72
+ Esse::Transport::ServiceUnavailableError,
73
+ Esse::Transport::GatewayTimeoutError,
74
+ Esse::Transport::TooManyRequestsError => e
75
+ transient_failure_count += 1
76
+ raise e if transient_failure_count >= retry_on_failure_max_retries
77
+ wait = retry_on_failure_wait * (2**(transient_failure_count - 1))
78
+ Esse.logger.warn "#{e.class} error, retrying in #{wait}s (attempt #{transient_failure_count}/#{retry_on_failure_max_retries})"
79
+ sleep(wait)
80
+ retry
67
81
  end
68
82
  end
69
83
 
@@ -80,9 +94,11 @@ module Esse
80
94
 
81
95
  def requests_in_small_chunks(chunk_size: 1)
82
96
  arr = build_per_document_requests(chunk_size: chunk_size)
97
+ ids = document_ids
98
+ id_info = ids.any? ? " (document IDs: #{ids.join(', ')})" : ''
83
99
  Esse.logger.warn <<~MSG
84
- Retrying the last request in small chunks of #{chunk_size} documents.
85
- This is a last resort to avoid timeout errors, consider increasing the bulk size or reducing the batch size.
100
+ Timeout error, retrying one document per request as a last resort#{id_info}.
101
+ Consider increasing the bulk size or reducing the batch size.
86
102
  MSG
87
103
  arr
88
104
  end
@@ -91,6 +107,10 @@ module Esse
91
107
  build_per_document_requests(chunk_size: 1)
92
108
  end
93
109
 
110
+ def document_ids
111
+ (@create + @index + @update + @delete).filter_map { |op| op.values.first[:_id] }
112
+ end
113
+
94
114
  def build_per_document_requests(chunk_size: 1)
95
115
  arr = []
96
116
  @create.each_slice(chunk_size) { |slice| arr << Import::RequestBodyAsJson.new.tap { |r| r.create = slice } }
@@ -72,6 +72,22 @@ module Esse
72
72
  @bulk_wait_interval = value.to_f
73
73
  end
74
74
 
75
+ def bulk_retry_on_failure_max_retries
76
+ @bulk_retry_on_failure_max_retries || Esse.config.bulk_retry_on_failure_max_retries
77
+ end
78
+
79
+ def bulk_retry_on_failure_max_retries=(value)
80
+ @bulk_retry_on_failure_max_retries = value.to_i
81
+ end
82
+
83
+ def bulk_retry_on_failure_wait
84
+ @bulk_retry_on_failure_wait || Esse.config.bulk_retry_on_failure_wait
85
+ end
86
+
87
+ def bulk_retry_on_failure_wait=(value)
88
+ @bulk_retry_on_failure_wait = value.to_f
89
+ end
90
+
75
91
  def mapping_single_type=(value)
76
92
  @mapping_single_type = !!value
77
93
  end
@@ -256,7 +256,10 @@ module Esse
256
256
  delete: to_delete,
257
257
  index: to_index,
258
258
  update: to_update,
259
- ).each_request do |request_body|
259
+ ).each_request(
260
+ retry_on_failure_max_retries: bulk_retry_on_failure_max_retries,
261
+ retry_on_failure_wait: bulk_retry_on_failure_wait,
262
+ ) do |request_body|
260
263
  cluster.api.bulk(**definition, body: request_body.body) do |event_payload|
261
264
  event_payload[:body_stats] = request_body.stats
262
265
  if bulk_wait_interval > 0
data/lib/esse/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Esse
4
- VERSION = '0.5.0'
4
+ VERSION = '0.5.2'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: esse
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcos G. Zimmermann
8
8
  autorequire:
9
9
  bindir: exec
10
10
  cert_chain: []
11
- date: 2026-05-15 00:00:00.000000000 Z
11
+ date: 2026-05-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: multi_json