From 39c94cc6725ebb2e0ea31a097cf7d67db8e1f652 Mon Sep 17 00:00:00 2001 From: Thomas Raffray Date: Wed, 31 Jan 2024 12:00:51 +0100 Subject: [PATCH] fix(swift): retry strategy (#2642) --- .../RetryStrategy/AlgoliaRetryStrategy.swift | 4 +- templates/swift/client_configuration.mustache | 124 +++++++++--------- 2 files changed, 68 insertions(+), 60 deletions(-) diff --git a/clients/algoliasearch-client-swift/Sources/Core/Networking/RetryStrategy/AlgoliaRetryStrategy.swift b/clients/algoliasearch-client-swift/Sources/Core/Networking/RetryStrategy/AlgoliaRetryStrategy.swift index 9e0d3f9ec0..19ea67f8cc 100644 --- a/clients/algoliasearch-client-swift/Sources/Core/Networking/RetryStrategy/AlgoliaRetryStrategy.swift +++ b/clients/algoliasearch-client-swift/Sources/Core/Networking/RetryStrategy/AlgoliaRetryStrategy.swift @@ -42,7 +42,9 @@ class AlgoliaRetryStrategy: RetryStrategy { return HostIterator { [weak self] in guard let retryStrategy = self else { return nil } return retryStrategy.queue.sync { - retryStrategy.hosts.first { $0.supports(callType) && $0.isUp } + retryStrategy.hosts + .sorted { $0.lastUpdated.compare($1.lastUpdated) == .orderedAscending } + .first { $0.supports(callType) && $0.isUp } } } } diff --git a/templates/swift/client_configuration.mustache b/templates/swift/client_configuration.mustache index c726e514af..a20cb18705 100644 --- a/templates/swift/client_configuration.mustache +++ b/templates/swift/client_configuration.mustache @@ -32,8 +32,10 @@ public struct Configuration: Core.Configuration, Credentials { writeTimeout: TimeInterval = DefaultConfiguration.default.writeTimeout, readTimeout: TimeInterval = DefaultConfiguration.default.readTimeout, logLevel: LogLevel = DefaultConfiguration.default.logLevel, - defaultHeaders: [String: String]? = DefaultConfiguration.default.defaultHeaders{{#isSearchClient}}, - batchSize: Int = 1000{{/isSearchClient}}) throws { + defaultHeaders: [String: String]? = DefaultConfiguration.default.defaultHeaders, + hosts: [RetryableHost]? = nil{{#isSearchClient}}, + batchSize: Int = 1000{{/isSearchClient}} + ) throws { guard !appId.isEmpty else { throw AlgoliaError.invalidCredentials("appId") } @@ -52,72 +54,76 @@ public struct Configuration: Core.Configuration, Credentials { "X-Algolia-API-Key": apiKey, "Content-Type": "application/json" ].merging(defaultHeaders ?? [:]) { (_, new) in new }{{#isSearchClient}} - self.batchSize = batchSize{{/isSearchClient}} - {{^uniqueHost}}{{^hasRegionalHost}} - func buildHost(_ components: (suffix: String, callType: RetryableHost.CallTypeSupport)) throws - -> RetryableHost - { - guard let url = URL(string: "https://\(appId)\(components.suffix)") else { - throw AlgoliaError.runtimeError("Malformed URL") - } - - return RetryableHost(url: url, callType: components.callType) - } + UserAgentController.append(UserAgent(title: "{{#lambda.client-to-name}}{{client}}{{/lambda.client-to-name}}", version: Version.current.description)) - let hosts = try [ - ("-dsn.algolia.net", .read), - (".algolia.net", .write), - ].map(buildHost) - - let commonHosts = try [ - ("-1.algolianet.com", .universal), - ("-2.algolianet.com", .universal), - ("-3.algolianet.com", .universal), - ].map(buildHost).shuffled() - - self.hosts = hosts + commonHosts -{{/hasRegionalHost}}{{/uniqueHost}} -{{#uniqueHost}} - guard let url = URL(string: "https://{{{.}}}") else { - throw AlgoliaError.runtimeError("Malformed URL") - } + guard let hosts = hosts else { + {{^uniqueHost}}{{^hasRegionalHost}} + func buildHost(_ components: (suffix: String, callType: RetryableHost.CallTypeSupport)) throws + -> RetryableHost + { + guard let url = URL(string: "https://\(appId)\(components.suffix)") else { + throw AlgoliaError.runtimeError("Malformed URL") + } - self.hosts = [ - .init(url: url) - ] -{{/uniqueHost}} -{{#hasRegionalHost}} - guard {{#fallbackToAliasHost}}region == nil || {{/fallbackToAliasHost}}authorizedRegions.contains(region{{#fallbackToAliasHost}}!{{/fallbackToAliasHost}}) else { - throw AlgoliaError.runtimeError( - "`region` {{^fallbackToAliasHost}}is required and {{/fallbackToAliasHost}}must be one of the following: \(authorizedRegions.map { $0.rawValue }.joined(separator: ", "))" - ) + return RetryableHost(url: url, callType: components.callType) } - {{#fallbackToAliasHost}} - if let region = region { - {{/fallbackToAliasHost}} - guard let url = URL(string: "https://{{{regionalHost}}}".replacingOccurrences(of: "{region}", with: region.rawValue)) else { - throw AlgoliaError.runtimeError("Malformed URL") - } + let hosts = try [ + ("-dsn.algolia.net", .read), + (".algolia.net", .write), + ].map(buildHost) + + let commonHosts = try [ + ("-1.algolianet.com", .universal), + ("-2.algolianet.com", .universal), + ("-3.algolianet.com", .universal), + ].map(buildHost).shuffled() + + self.hosts = hosts + commonHosts + {{/hasRegionalHost}}{{/uniqueHost}} + {{#uniqueHost}} + guard let url = URL(string: "https://{{{.}}}") else { + throw AlgoliaError.runtimeError("Malformed URL") + } - self.hosts = [ - .init(url: url) - ] - {{#fallbackToAliasHost}} - } else { - guard let url = URL(string: "https://{{{hostWithFallback}}}") else { - throw AlgoliaError.runtimeError("Malformed URL") + self.hosts = [ + .init(url: url) + ] + {{/uniqueHost}} + {{#hasRegionalHost}} + guard {{#fallbackToAliasHost}}region == nil || {{/fallbackToAliasHost}}authorizedRegions.contains(region{{#fallbackToAliasHost}}!{{/fallbackToAliasHost}}) else { + throw AlgoliaError.runtimeError( + "`region` {{^fallbackToAliasHost}}is required and {{/fallbackToAliasHost}}must be one of the following: \(authorizedRegions.map { $0.rawValue }.joined(separator: ", "))" + ) } - self.hosts = [ - .init(url: url) - ] - } - {{/fallbackToAliasHost}} - {{/hasRegionalHost}} + {{#fallbackToAliasHost}} + if let region = region { + {{/fallbackToAliasHost}} + guard let url = URL(string: "https://{{{regionalHost}}}".replacingOccurrences(of: "{region}", with: region.rawValue)) else { + throw AlgoliaError.runtimeError("Malformed URL") + } + + self.hosts = [ + .init(url: url) + ] + {{#fallbackToAliasHost}} + } else { + guard let url = URL(string: "https://{{{hostWithFallback}}}") else { + throw AlgoliaError.runtimeError("Malformed URL") + } + + self.hosts = [ + .init(url: url) + ] + } + {{/fallbackToAliasHost}} + {{/hasRegionalHost}} + return + } - UserAgentController.append(UserAgent(title: "{{#lambda.client-to-name}}{{client}}{{/lambda.client-to-name}}", version: Version.current.description)) + self.hosts = hosts } } \ No newline at end of file