From 89f933a55dd1b6ee1b7eba3fbfec904b3f919672 Mon Sep 17 00:00:00 2001 From: Thomas Dalous Date: Tue, 16 Jul 2024 14:24:38 +0200 Subject: [PATCH 1/9] fix(debug): debug.log file is now created if does not exist already --- lib/algolia/logger_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/algolia/logger_helper.rb b/lib/algolia/logger_helper.rb index 0500a378..60ade20b 100644 --- a/lib/algolia/logger_helper.rb +++ b/lib/algolia/logger_helper.rb @@ -5,7 +5,7 @@ class LoggerHelper # @param debug_file [nil|String] file used to output the logs # def self.create(debug_file = nil) - file = debug_file || (ENV['ALGOLIA_DEBUG'] ? File.new('debug.log') : $stderr) + file = debug_file || (ENV['ALGOLIA_DEBUG'] ? File.open('debug.log', 'a') : $stderr) instance = ::Logger.new file instance.progname = 'algolia' instance From ba53da68a4a9264c22e6258e72041dd459b3f835 Mon Sep 17 00:00:00 2001 From: Thomas Dalous Date: Tue, 16 Jul 2024 14:26:12 +0200 Subject: [PATCH 2/9] fix(typo): s/stategy/strategy --- lib/algolia/transport/retry_strategy.rb | 2 +- test/algolia/unit/retry_strategy_test.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/algolia/transport/retry_strategy.rb b/lib/algolia/transport/retry_strategy.rb index 48055995..57576c2a 100644 --- a/lib/algolia/transport/retry_strategy.rb +++ b/lib/algolia/transport/retry_strategy.rb @@ -1,6 +1,6 @@ module Algolia module Transport - # Class RetryStatregy + # Class RetryStrategy class RetryStrategy include RetryOutcomeType diff --git a/test/algolia/unit/retry_strategy_test.rb b/test/algolia/unit/retry_strategy_test.rb index d9128cea..c9eebbb2 100644 --- a/test/algolia/unit/retry_strategy_test.rb +++ b/test/algolia/unit/retry_strategy_test.rb @@ -86,7 +86,7 @@ def test_failure_when_all_hosts_are_down end end - describe 'retry stategy decisions' do + describe 'retry strategy decisions' do def before_all super @app_id = 'app_id' From a3caec293039f6a3af6a3edbc7c838d22b4b7066 Mon Sep 17 00:00:00 2001 From: Thomas Dalous Date: Tue, 16 Jul 2024 14:27:08 +0200 Subject: [PATCH 3/9] fix(requests): ensure last retry error message is forwarded when raised --- lib/algolia/transport/transport.rb | 10 ++++++++-- test/algolia/unit/retry_strategy_test.rb | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/algolia/transport/transport.rb b/lib/algolia/transport/transport.rb index 80719bce..46827b47 100644 --- a/lib/algolia/transport/transport.rb +++ b/lib/algolia/transport/transport.rb @@ -49,6 +49,8 @@ def write(method, path, body = {}, opts = {}) # @return [Response] response of the request # def request(call_type, method, path, body = {}, opts = {}) + last_retry_error = nil + @retry_strategy.get_tryable_hosts(call_type).each do |host| opts[:timeout] ||= get_timeout(call_type) * (host.retry_count + 1) opts[:connect_timeout] ||= @config.connect_timeout * (host.retry_count + 1) @@ -73,10 +75,14 @@ def request(call_type, method, path, body = {}, opts = {}) decoded_error = json_to_hash(response.error, @config.symbolize_keys) raise AlgoliaHttpError.new(get_option(decoded_error, 'status'), get_option(decoded_error, 'message')) end - return json_to_hash(response.body, @config.symbolize_keys) unless outcome == RETRY + if outcome == RETRY + last_retry_error = response.error + else + return json_to_hash(response.body, @config.symbolize_keys) + end end - raise AlgoliaUnreachableHostError, 'Unreachable hosts' + raise AlgoliaUnreachableHostError, "Unreachable hosts. Last error: #{last_retry_error}" end private diff --git a/test/algolia/unit/retry_strategy_test.rb b/test/algolia/unit/retry_strategy_test.rb index c9eebbb2..12242ba5 100644 --- a/test/algolia/unit/retry_strategy_test.rb +++ b/test/algolia/unit/retry_strategy_test.rb @@ -82,7 +82,7 @@ def test_failure_when_all_hosts_are_down index.save_object({ objectID: 'one' }) end - assert_equal 'Unreachable hosts', exception.message + assert_includes exception.message, 'Unreachable hosts. Last error: SSL_connect' end end From d0d48b9adab34cbf888a057ee45ec725e1b65732 Mon Sep 17 00:00:00 2001 From: Thomas Dalous Date: Tue, 16 Jul 2024 17:24:28 +0200 Subject: [PATCH 4/9] fix(requests): add host to last error --- lib/algolia/transport/transport.rb | 6 +++--- test/algolia/unit/retry_strategy_test.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/algolia/transport/transport.rb b/lib/algolia/transport/transport.rb index 46827b47..f22bbc1f 100644 --- a/lib/algolia/transport/transport.rb +++ b/lib/algolia/transport/transport.rb @@ -49,7 +49,7 @@ def write(method, path, body = {}, opts = {}) # @return [Response] response of the request # def request(call_type, method, path, body = {}, opts = {}) - last_retry_error = nil + last_retry = {host: nil, error: nil} @retry_strategy.get_tryable_hosts(call_type).each do |host| opts[:timeout] ||= get_timeout(call_type) * (host.retry_count + 1) @@ -76,13 +76,13 @@ def request(call_type, method, path, body = {}, opts = {}) raise AlgoliaHttpError.new(get_option(decoded_error, 'status'), get_option(decoded_error, 'message')) end if outcome == RETRY - last_retry_error = response.error + last_retry = {host: host.url, error: response.error} else return json_to_hash(response.body, @config.symbolize_keys) end end - raise AlgoliaUnreachableHostError, "Unreachable hosts. Last error: #{last_retry_error}" + raise AlgoliaUnreachableHostError, "Unreachable hosts. Last error for #{last_retry[:host]}: #{last_retry[:error]}" end private diff --git a/test/algolia/unit/retry_strategy_test.rb b/test/algolia/unit/retry_strategy_test.rb index 12242ba5..6f702ecb 100644 --- a/test/algolia/unit/retry_strategy_test.rb +++ b/test/algolia/unit/retry_strategy_test.rb @@ -82,7 +82,7 @@ def test_failure_when_all_hosts_are_down index.save_object({ objectID: 'one' }) end - assert_includes exception.message, 'Unreachable hosts. Last error: SSL_connect' + assert_includes exception.message, 'Unreachable hosts. Last error for 0.0.0.0: SSL_connect' end end From 813f6325868b8c8fdec33fa09045bc9ac5a99760 Mon Sep 17 00:00:00 2001 From: Thomas Dalous Date: Wed, 17 Jul 2024 14:24:54 +0200 Subject: [PATCH 5/9] fix: make debug file readable not just write-only --- lib/algolia/logger_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/algolia/logger_helper.rb b/lib/algolia/logger_helper.rb index 60ade20b..1fb841ab 100644 --- a/lib/algolia/logger_helper.rb +++ b/lib/algolia/logger_helper.rb @@ -5,7 +5,7 @@ class LoggerHelper # @param debug_file [nil|String] file used to output the logs # def self.create(debug_file = nil) - file = debug_file || (ENV['ALGOLIA_DEBUG'] ? File.open('debug.log', 'a') : $stderr) + file = debug_file || (ENV['ALGOLIA_DEBUG'] ? File.open('debug.log', 'a+') : $stderr) instance = ::Logger.new file instance.progname = 'algolia' instance From 337d63958fef13090a6e59b4d26d27287df71e1e Mon Sep 17 00:00:00 2001 From: Thomas Dalous Date: Fri, 19 Jul 2024 11:17:16 +0200 Subject: [PATCH 6/9] fix(requests): keep track of all retry errors --- lib/algolia/error.rb | 6 ++++++ lib/algolia/transport/transport.rb | 6 +++--- test/algolia/unit/retry_strategy_test.rb | 8 ++++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/algolia/error.rb b/lib/algolia/error.rb index b7484f14..0695ee61 100644 --- a/lib/algolia/error.rb +++ b/lib/algolia/error.rb @@ -10,6 +10,12 @@ class AlgoliaError < StandardError # Used when hosts are unreachable # class AlgoliaUnreachableHostError < AlgoliaError + attr_reader :errors + + def initialize(message, errors = []) + super(message) + @errors = errors + end end # An exception class raised when the REST API returns an error. diff --git a/lib/algolia/transport/transport.rb b/lib/algolia/transport/transport.rb index f22bbc1f..7308c47d 100644 --- a/lib/algolia/transport/transport.rb +++ b/lib/algolia/transport/transport.rb @@ -49,7 +49,7 @@ def write(method, path, body = {}, opts = {}) # @return [Response] response of the request # def request(call_type, method, path, body = {}, opts = {}) - last_retry = {host: nil, error: nil} + retry_errors = [] @retry_strategy.get_tryable_hosts(call_type).each do |host| opts[:timeout] ||= get_timeout(call_type) * (host.retry_count + 1) @@ -76,13 +76,13 @@ def request(call_type, method, path, body = {}, opts = {}) raise AlgoliaHttpError.new(get_option(decoded_error, 'status'), get_option(decoded_error, 'message')) end if outcome == RETRY - last_retry = {host: host.url, error: response.error} + retry_errors << {host: host.url, error: response.error} else return json_to_hash(response.body, @config.symbolize_keys) end end - raise AlgoliaUnreachableHostError, "Unreachable hosts. Last error for #{last_retry[:host]}: #{last_retry[:error]}" + raise AlgoliaUnreachableHostError.new("Unreachable hosts. Last error for #{retry_errors.last[:host]}: #{retry_errors.last[:error]}", retry_errors) end private diff --git a/test/algolia/unit/retry_strategy_test.rb b/test/algolia/unit/retry_strategy_test.rb index 6f702ecb..83c17d54 100644 --- a/test/algolia/unit/retry_strategy_test.rb +++ b/test/algolia/unit/retry_strategy_test.rb @@ -73,7 +73,7 @@ def test_resets_all_hosts_when_expired_according_to_write_type describe 'All hosts are unreachable' do def test_failure_when_all_hosts_are_down - stateful_hosts = ['0.0.0.0'] + stateful_hosts = ['0.0.0.0', '1.0.0.0'] @config = Algolia::Search::Config.new(application_id: 'foo', api_key: 'bar', custom_hosts: stateful_hosts) client = Algolia::Search::Client.create_with_config(@config) index = client.init_index(get_test_index_name('failure')) @@ -82,7 +82,11 @@ def test_failure_when_all_hosts_are_down index.save_object({ objectID: 'one' }) end - assert_includes exception.message, 'Unreachable hosts. Last error for 0.0.0.0: SSL_connect' + assert_includes exception.message, 'Unreachable hosts. Last error for 1.0.0.0: SSL_connect' + assert_equal exception.errors, [ + {:host=>"0.0.0.0", :error=>"SSL_connect SYSCALL returned=5 errno=0 peeraddr=127.0.0.1:443 state=error: certificate verify failed"}, + {:host=>"1.0.0.0", :error=>"SSL_connect returned=1 errno=0 peeraddr=1.0.0.0:443 state=error: ssl/tls alert handshake failure"} + ] end end From fbebb48d73c0c58ffb06be162bee973ac0cdc5a3 Mon Sep 17 00:00:00 2001 From: Thomas Dalous Date: Fri, 19 Jul 2024 11:31:08 +0200 Subject: [PATCH 7/9] fix(debug): make sure it always fallback to stderr --- lib/algolia/logger_helper.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/algolia/logger_helper.rb b/lib/algolia/logger_helper.rb index 1fb841ab..7f490055 100644 --- a/lib/algolia/logger_helper.rb +++ b/lib/algolia/logger_helper.rb @@ -5,8 +5,17 @@ class LoggerHelper # @param debug_file [nil|String] file used to output the logs # def self.create(debug_file = nil) - file = debug_file || (ENV['ALGOLIA_DEBUG'] ? File.open('debug.log', 'a+') : $stderr) - instance = ::Logger.new file + file = debug_file + + if file.nil? && ENV['ALGOLIA_DEBUG'] + begin + file = File.new('debug.log', 'a+') + rescue Errno::EACCES, Errno::ENOENT => e + puts "Failed to open debug.log: #{e.message}. Falling back to $stderr." + end + end + + instance = ::Logger.new(file || $stderr) instance.progname = 'algolia' instance end From 53de2fa8e6f01c9c4c036d13ccaa045344f617db Mon Sep 17 00:00:00 2001 From: Thomas Dalous Date: Tue, 23 Jul 2024 10:59:12 +0200 Subject: [PATCH 8/9] refacto --- lib/algolia/error.rb | 3 +++ lib/algolia/transport/transport.rb | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/algolia/error.rb b/lib/algolia/error.rb index 0695ee61..49bdad61 100644 --- a/lib/algolia/error.rb +++ b/lib/algolia/error.rb @@ -13,6 +13,9 @@ class AlgoliaUnreachableHostError < AlgoliaError attr_reader :errors def initialize(message, errors = []) + errors.last&.tap do |last_error| + message += " Last error for #{last_error[:host]}: #{last_error[:error]}" + end super(message) @errors = errors end diff --git a/lib/algolia/transport/transport.rb b/lib/algolia/transport/transport.rb index 7308c47d..6a0a1693 100644 --- a/lib/algolia/transport/transport.rb +++ b/lib/algolia/transport/transport.rb @@ -82,7 +82,7 @@ def request(call_type, method, path, body = {}, opts = {}) end end - raise AlgoliaUnreachableHostError.new("Unreachable hosts. Last error for #{retry_errors.last[:host]}: #{retry_errors.last[:error]}", retry_errors) + raise AlgoliaUnreachableHostError.new("Unreachable hosts.", retry_errors) end private From 41f34e2a033b13357b06026c28e38fd057340054 Mon Sep 17 00:00:00 2001 From: Thomas Dalous Date: Wed, 24 Jul 2024 10:22:44 +0200 Subject: [PATCH 9/9] fix: tests --- test/algolia/unit/retry_strategy_test.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/algolia/unit/retry_strategy_test.rb b/test/algolia/unit/retry_strategy_test.rb index 83c17d54..7da1360c 100644 --- a/test/algolia/unit/retry_strategy_test.rb +++ b/test/algolia/unit/retry_strategy_test.rb @@ -83,10 +83,10 @@ def test_failure_when_all_hosts_are_down end assert_includes exception.message, 'Unreachable hosts. Last error for 1.0.0.0: SSL_connect' - assert_equal exception.errors, [ - {:host=>"0.0.0.0", :error=>"SSL_connect SYSCALL returned=5 errno=0 peeraddr=127.0.0.1:443 state=error: certificate verify failed"}, - {:host=>"1.0.0.0", :error=>"SSL_connect returned=1 errno=0 peeraddr=1.0.0.0:443 state=error: ssl/tls alert handshake failure"} - ] + assert_equal exception.errors.size, 2 + assert_equal exception.errors.last.keys, [:host, :error] + assert_equal exception.errors.first[:host], '0.0.0.0' + assert_equal exception.errors.last[:host], '1.0.0.0' end end