Skip to content

Commit d2d187e

Browse files
glbrnttgjcairo
andauthored
Use ServiceConfiguration more widely (#1794)
Motivation: ServiceConfiguration was recently added which has a lot of overlap with MethodConfigurations. These should be de-duped where possible. Modifications: - Make `MethodConfigurations` underscored-public and refactor to use `MethodConfiguration.Name` - Pass `ServiceConfiguration` instead of `MethodConfigurations` where it makes sense - Update docs and tests * [ProtobufCodeGen] Use the generated input and output type names (#1792) Motivation: protoc-gen-swift generates message type names that are different from the ones in the file descriptors. The CodeGenerationRequest object should contain the generated names for the ProtobufCodeGen. Modifications: - the parser uses a SwiftProtobufNamer to get the generated type names and adds them in the CodeGenerationRequest objects - propagated the change and modified tests accordingly Result: The input and output type names will be the generated ones and the generated code will compile. Co-authored-by: Gustavo Cairo <[email protected]>
1 parent 8bfddf3 commit d2d187e

File tree

11 files changed

+158
-85
lines changed

11 files changed

+158
-85
lines changed

Sources/GRPCCore/GRPCClient.swift

Lines changed: 54 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import Atomics
2828
///
2929
/// However, in most cases you should prefer wrapping the ``GRPCClient`` with a generated stub.
3030
///
31-
/// You can set ``MethodConfiguration``s on this client to override whatever configurations have been
31+
/// You can set ``ServiceConfiguration``s on this client to override whatever configurations have been
3232
/// set on the given transport. You can also use ``ClientInterceptor``s to implement cross-cutting
3333
/// logic which apply to all RPCs. Example uses of interceptors include authentication and logging.
3434
///
@@ -37,21 +37,32 @@ import Atomics
3737
/// The following example demonstrates how to create and configure a client.
3838
///
3939
/// ```swift
40-
/// // Create a configuration object for the client.
40+
/// // Create a configuration object for the client and override the timeout for the 'Get' method on
41+
/// // the 'echo.Echo' service. This configuration takes precedence over any set by the transport.
4142
/// var configuration = GRPCClient.Configuration()
42-
///
43-
/// // Override the timeout for the 'Get' method on the 'echo.Echo' service. This configuration
44-
/// // takes precedence over any set by the transport.
45-
/// let echoGet = MethodDescriptor(service: "echo.Echo", method: "Get")
46-
/// configuration.method.overrides[echoGet] = MethodConfiguration(
47-
/// executionPolicy: nil,
48-
/// timeout: .seconds(5)
43+
/// configuration.service.override = ServiceConfiguration(
44+
/// methodConfiguration: [
45+
/// MethodConfiguration(
46+
/// names: [
47+
/// MethodConfiguration.Name(service: "echo.Echo", method: "Get")
48+
/// ],
49+
/// timeout: .seconds(5)
50+
/// )
51+
/// ]
4952
/// )
5053
///
51-
/// // Configure a fallback timeout for all RPCs if no configuration is provided in the overrides
52-
/// // or by the transport.
53-
/// let defaultMethodConfiguration = MethodConfiguration(executionPolicy: nil, timeout: seconds(10))
54-
/// configuration.method.defaults.setDefaultConfiguration(defaultMethodConfiguration)
54+
/// // Configure a fallback timeout for all RPCs (indicated by an empty service and method name) if
55+
/// // no configuration is provided in the overrides or by the transport.
56+
/// configuration.service.defaults = ServiceConfiguration(
57+
/// methodConfiguration: [
58+
/// MethodConfiguration(
59+
/// names: [
60+
/// MethodConfiguration.Name(service: "", method: "")
61+
/// ],
62+
/// timeout: .seconds(10)
63+
/// )
64+
/// ]
65+
/// )
5566
///
5667
/// // Finally create a transport and instantiate the client, adding an interceptor.
5768
/// let inProcessServerTransport = InProcessServerTransport()
@@ -114,6 +125,11 @@ public struct GRPCClient: Sendable {
114125
/// The configuration used by the client.
115126
public let configuration: Configuration
116127

128+
/// A cache of method config overrides.
129+
private let methodConfigurationOverrides: _MethodConfigurations
130+
/// A cache of method config defaults.
131+
private let methodConfigurationDefaults: _MethodConfigurations
132+
117133
/// The current state of the client.
118134
private let state: ManagedAtomic<State>
119135

@@ -149,6 +165,14 @@ public struct GRPCClient: Sendable {
149165
self.transport = transport
150166
self.interceptors = interceptors
151167
self.configuration = configuration
168+
169+
self.methodConfigurationOverrides = _MethodConfigurations(
170+
serviceConfiguration: self.configuration.service.overrides
171+
)
172+
self.methodConfigurationDefaults = _MethodConfigurations(
173+
serviceConfiguration: self.configuration.service.defaults
174+
)
175+
152176
self.state = ManagedAtomic(.notStarted)
153177
}
154178

@@ -380,15 +404,15 @@ public struct GRPCClient: Sendable {
380404
}
381405

382406
private func resolveMethodConfiguration(for descriptor: MethodDescriptor) -> MethodConfiguration {
383-
if let configuration = self.configuration.method.overrides[descriptor] {
407+
if let configuration = self.methodConfigurationOverrides[descriptor] {
384408
return configuration
385409
}
386410

387-
if let configuration = self.transport.executionConfiguration(forMethod: descriptor) {
411+
if let configuration = self.transport.configuration(forMethod: descriptor) {
388412
return configuration
389413
}
390414

391-
if let configuration = self.configuration.method.defaults[descriptor] {
415+
if let configuration = self.methodConfigurationDefaults[descriptor] {
392416
return configuration
393417
}
394418

@@ -400,41 +424,44 @@ public struct GRPCClient: Sendable {
400424
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
401425
extension GRPCClient {
402426
public struct Configuration: Sendable {
403-
/// Configuration for how methods are executed.
427+
/// Configuration for how the client interacts with services.
404428
///
405-
/// Method configuration determines how each RPC is executed by the client. Some services and
429+
/// The configuration includes information about how the client should load balance requests,
430+
/// how retries should be throttled and how methods should be executed. Some services and
406431
/// transports provide this information to the client when the server name is resolved. However,
407-
/// you override this configuration and set default values should no override be set and the
432+
/// you can override this configuration and set default values should no override be set and the
408433
/// transport doesn't provide a value.
409-
public var method: Method
434+
///
435+
/// See also ``ServiceConfiguration``.
436+
public var service: Service
410437

411438
/// Creates a new default configuration.
412439
public init() {
413-
self.method = Method()
440+
self.service = Service()
414441
}
415442
}
416443
}
417444

418445
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
419446
extension GRPCClient.Configuration {
420-
/// Configuration for how methods should be executed.
447+
/// Configuration for how the client interacts with services.
421448
///
422449
/// In most cases the client should defer to the configuration provided by the transport as this
423450
/// can be provided to the transport as part of name resolution when establishing a connection.
424451
///
425452
/// The client first checks ``overrides`` for a configuration, followed by the transport, followed
426453
/// by ``defaults``.
427-
public struct Method: Sendable, Hashable {
454+
public struct Service: Sendable, Hashable {
428455
/// Configuration to use in precedence to that provided by the transport.
429-
public var overrides: MethodConfigurations
456+
public var overrides: ServiceConfiguration
430457

431458
/// Configuration to use only if there are no overrides and the transport doesn't specify
432459
/// any configuration.
433-
public var defaults: MethodConfigurations
460+
public var defaults: ServiceConfiguration
434461

435462
public init() {
436-
self.overrides = MethodConfigurations()
437-
self.defaults = MethodConfigurations()
463+
self.overrides = ServiceConfiguration()
464+
self.defaults = ServiceConfiguration()
438465
}
439466
}
440467
}

Sources/GRPCCore/MethodConfigurations.swift renamed to Sources/GRPCCore/Internal/_MethodConfigurations.swift

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
* limitations under the License.
1515
*/
1616

17+
// TODO: when swift(>=5.9), use 'package' access level
18+
1719
/// A collection of ``MethodConfiguration``s, mapped to specific methods or services.
1820
///
1921
/// When creating a new instance, no overrides and no default will be set for using when getting
@@ -23,12 +25,20 @@
2325
///
2426
/// Use the subscript to get and set configurations for specific methods.
2527
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
26-
public struct MethodConfigurations: Sendable, Hashable {
27-
private var elements: [MethodDescriptor: MethodConfiguration]
28+
public struct _MethodConfigurations: Sendable, Hashable {
29+
private var elements: [MethodConfiguration.Name: MethodConfiguration]
2830

29-
/// Create a new ``MethodConfigurations`` with no overrides and no default configuration.
30-
public init() {
31+
/// Create a new ``_MethodConfigurations``.
32+
///
33+
/// - Parameter serviceConfiguration: The configuration to read ``MethodConfiguration`` from.
34+
public init(serviceConfiguration: ServiceConfiguration = ServiceConfiguration()) {
3135
self.elements = [:]
36+
37+
for configuration in serviceConfiguration.methodConfiguration {
38+
for name in configuration.names {
39+
self.elements[name] = configuration
40+
}
41+
}
3242
}
3343

3444
/// Get or set the corresponding ``MethodConfiguration`` for the given ``MethodDescriptor``.
@@ -44,34 +54,36 @@ public struct MethodConfigurations: Sendable, Hashable {
4454
/// - descriptor: The ``MethodDescriptor`` for which to get or set a ``MethodConfiguration``.
4555
public subscript(_ descriptor: MethodDescriptor) -> MethodConfiguration? {
4656
get {
47-
if let configuration = self.elements[descriptor] {
57+
var name = MethodConfiguration.Name(service: descriptor.service, method: descriptor.method)
58+
59+
if let configuration = self.elements[name] {
4860
return configuration
4961
}
5062

5163
// Check if the config is set at the service level by clearing the method.
52-
var descriptor = descriptor
53-
descriptor.method = ""
64+
name.method = ""
5465

55-
if let configuration = self.elements[descriptor] {
66+
if let configuration = self.elements[name] {
5667
return configuration
5768
}
5869

5970
// Check if the config is set at the global level by clearing the service and method.
60-
descriptor.service = ""
61-
return self.elements[descriptor]
71+
name.service = ""
72+
return self.elements[name]
6273
}
6374

6475
set {
65-
self.elements[descriptor] = newValue
76+
let name = MethodConfiguration.Name(service: descriptor.service, method: descriptor.method)
77+
self.elements[name] = newValue
6678
}
6779
}
6880

6981
/// Set a default configuration for all methods that have no overrides.
7082
///
7183
/// - Parameter configuration: The default configuration.
7284
public mutating func setDefaultConfiguration(_ configuration: MethodConfiguration?) {
73-
let descriptor = MethodDescriptor(service: "", method: "")
74-
self.elements[descriptor] = configuration
85+
let name = MethodConfiguration.Name(service: "", method: "")
86+
self.elements[name] = configuration
7587
}
7688

7789
/// Set a default configuration for a service.
@@ -87,6 +99,7 @@ public struct MethodConfigurations: Sendable, Hashable {
8799
_ configuration: MethodConfiguration?,
88100
forService service: String
89101
) {
90-
self.elements[MethodDescriptor(service: service, method: "")] = configuration
102+
let name = MethodConfiguration.Name(service: "", method: "")
103+
self.elements[name] = configuration
91104
}
92105
}

Sources/GRPCCore/Transport/ClientTransport.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,9 @@ public protocol ClientTransport: Sendable {
7171
_ closure: (_ stream: RPCStream<Inbound, Outbound>) async throws -> T
7272
) async throws -> T
7373

74-
/// Returns the execution configuration for a given method.
74+
/// Returns the configuration for a given method.
7575
///
7676
/// - Parameter descriptor: The method to lookup configuration for.
77-
/// - Returns: Execution configuration for the method, if it exists.
78-
func executionConfiguration(
79-
forMethod descriptor: MethodDescriptor
80-
) -> MethodConfiguration?
77+
/// - Returns: Configuration for the method, if it exists.
78+
func configuration(forMethod descriptor: MethodDescriptor) -> MethodConfiguration?
8179
}

Sources/GRPCCore/Transport/RetryThrottle.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,14 @@ public struct RetryThrottle: Sendable {
103103
self.scaledTokensAvailable = LockedValueBox(scaledTokens)
104104
}
105105

106+
/// Create a new throttle.
107+
///
108+
/// - Parameter policy: The policy to use to configure the throttle.
109+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
110+
public init(policy: ServiceConfiguration.RetryThrottlingPolicy) {
111+
self.init(maximumTokens: policy.maxTokens, tokenRatio: policy.tokenRatio)
112+
}
113+
106114
/// Records a success, adding a token to the throttle.
107115
@usableFromInline
108116
func recordSuccess() {

Sources/GRPCInProcessTransport/InProcessClientTransport.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -98,22 +98,22 @@ public struct InProcessClientTransport: ClientTransport {
9898

9999
public let retryThrottle: RetryThrottle?
100100

101-
private let methodConfiguration: MethodConfigurations
101+
private let methodConfiguration: _MethodConfigurations
102102
private let state: _LockedValueBox<State>
103103

104104
/// Creates a new in-process client transport.
105105
///
106106
/// - Parameters:
107107
/// - server: The in-process server transport to connect to.
108-
/// - methodConfiguration: Method specific configuration.
109-
/// - retryThrottle: A throttle to apply to RPCs which are hedged or retried.
108+
/// - serviceConfiguration: Service configuration.
110109
public init(
111110
server: InProcessServerTransport,
112-
methodConfiguration: MethodConfigurations = MethodConfigurations(),
113-
retryThrottle: RetryThrottle? = nil
111+
serviceConfiguration: ServiceConfiguration = ServiceConfiguration()
114112
) {
115-
self.retryThrottle = retryThrottle
116-
self.methodConfiguration = methodConfiguration
113+
self.retryThrottle = serviceConfiguration.retryThrottlingPolicy.map {
114+
RetryThrottle(policy: $0)
115+
}
116+
self.methodConfiguration = _MethodConfigurations(serviceConfiguration: serviceConfiguration)
117117
self.state = _LockedValueBox(.unconnected(.init(serverTransport: server)))
118118
}
119119

@@ -336,7 +336,7 @@ public struct InProcessClientTransport: ClientTransport {
336336
///
337337
/// - Parameter descriptor: The method to lookup configuration for.
338338
/// - Returns: Execution configuration for the method, if it exists.
339-
public func executionConfiguration(
339+
public func configuration(
340340
forMethod descriptor: MethodDescriptor
341341
) -> MethodConfiguration? {
342342
self.methodConfiguration[descriptor]

Sources/GRPCInProcessTransport/InProcessTransport.swift

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,16 @@ public enum InProcessTransport {
2222
/// and a client using that server transport.
2323
///
2424
/// - Parameters:
25-
/// - methodConfiguration: Method specific configuration used by the client transport to
26-
/// determine how RPCs should be executed.
27-
/// - retryThrottle: The retry throttle the client transport uses to determine whether a call
28-
/// should be retried.
25+
/// - serviceConfiguration: Configuration describing how methods should be executed.
2926
/// - Returns: A tuple containing the connected server and client in-process transports.
3027
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
3128
public static func makePair(
32-
methodConfiguration: MethodConfigurations = MethodConfigurations(),
33-
retryThrottle: RetryThrottle? = nil
29+
serviceConfiguration: ServiceConfiguration = ServiceConfiguration()
3430
) -> (server: InProcessServerTransport, client: InProcessClientTransport) {
3531
let server = InProcessServerTransport()
3632
let client = InProcessClientTransport(
3733
server: server,
38-
methodConfiguration: methodConfiguration,
39-
retryThrottle: retryThrottle
34+
serviceConfiguration: serviceConfiguration
4035
)
4136
return (server, client)
4237
}

Tests/GRPCCoreTests/MethodConfigurationsTests.swift renamed to Tests/GRPCCoreTests/Internal/MethodConfigurationsTests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ final class MethodConfigurationsTests: XCTestCase {
2525
nonFatalStatusCodes: []
2626
)
2727
let defaultConfiguration = MethodConfiguration(names: [], executionPolicy: .hedge(policy))
28-
var configurations = MethodConfigurations()
28+
var configurations = _MethodConfigurations()
2929
configurations.setDefaultConfiguration(defaultConfiguration)
3030
let descriptor = MethodDescriptor(service: "test", method: "first")
3131
let retryPolicy = RetryPolicy(
@@ -48,7 +48,7 @@ final class MethodConfigurationsTests: XCTestCase {
4848
nonFatalStatusCodes: []
4949
)
5050
let defaultConfiguration = MethodConfiguration(names: [], executionPolicy: .hedge(policy))
51-
var configurations = MethodConfigurations()
51+
var configurations = _MethodConfigurations()
5252
configurations.setDefaultConfiguration(defaultConfiguration)
5353
let firstDescriptor = MethodDescriptor(service: "test", method: "")
5454
let retryPolicy = RetryPolicy(
@@ -72,7 +72,7 @@ final class MethodConfigurationsTests: XCTestCase {
7272
nonFatalStatusCodes: []
7373
)
7474
let defaultConfiguration = MethodConfiguration(names: [], executionPolicy: .hedge(policy))
75-
var configurations = MethodConfigurations()
75+
var configurations = _MethodConfigurations()
7676
configurations.setDefaultConfiguration(defaultConfiguration)
7777
let firstDescriptor = MethodDescriptor(service: "test1", method: "first")
7878
let retryPolicy = RetryPolicy(

Tests/GRPCCoreTests/Test Utilities/Transport/AnyTransport.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ struct AnyClientTransport: ClientTransport, Sendable {
4848
}
4949

5050
self._configuration = { descriptor in
51-
transport.executionConfiguration(forMethod: descriptor)
51+
transport.configuration(forMethod: descriptor)
5252
}
5353
}
5454

@@ -72,7 +72,7 @@ struct AnyClientTransport: ClientTransport, Sendable {
7272
return result as! T
7373
}
7474

75-
func executionConfiguration(
75+
func configuration(
7676
forMethod descriptor: MethodDescriptor
7777
) -> MethodConfiguration? {
7878
self._configuration(descriptor)

Tests/GRPCCoreTests/Test Utilities/Transport/StreamCountingTransport.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,10 @@ struct StreamCountingClientTransport: ClientTransport, Sendable {
6666
}
6767
}
6868

69-
func executionConfiguration(
69+
func configuration(
7070
forMethod descriptor: MethodDescriptor
7171
) -> MethodConfiguration? {
72-
self.transport.executionConfiguration(forMethod: descriptor)
72+
self.transport.configuration(forMethod: descriptor)
7373
}
7474
}
7575

0 commit comments

Comments
 (0)