From c5cf12b130f56b1b235f01c75a187a12c9666ec3 Mon Sep 17 00:00:00 2001 From: George Barnett Date: Tue, 14 Jan 2025 15:04:35 +0000 Subject: [PATCH 1/3] Rename 'GRPCClient.run()' Motivation: To support swift-service-lifecycle the grpc client and server types will conform to its 'Service' protocol. This has a requirement for a method called 'run()'. Ideally this would respect graceful shutdown. We can't do this with the current client as we already have a run method. To do this we'd need to wrap the type and redeclare all of it's methods. Using a wrapper isn't a good user experience. Modifications: - Rename run to 'maintainConnections()' - Deprecate 'run()', we'll remove this later Result: - run is deprecated --- Sources/GRPCCore/GRPCClient.swift | 17 +++++++++++------ Tests/GRPCCoreTests/GRPCClientTests.swift | 12 ++++++------ .../InProcessTransportTests.swift | 4 ++-- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/Sources/GRPCCore/GRPCClient.swift b/Sources/GRPCCore/GRPCClient.swift index b9d3234b1..4db64449b 100644 --- a/Sources/GRPCCore/GRPCClient.swift +++ b/Sources/GRPCCore/GRPCClient.swift @@ -45,10 +45,10 @@ private import Synchronization /// ## Creating a client manually /// /// If the `with`-style methods for creating clients isn't suitable for your application then you -/// can create and run a client manually. This requires you to call the ``run()`` method in a task +/// can create and run a client manually. This requires you to call the ``maintainConnections()`` method in a task /// which instructs the client to start connecting to the server. /// -/// The ``run()`` method won't return until the client has finished handling all requests. You can +/// The ``maintainConnections()`` method won't return until the client has finished handling all requests. You can /// signal to the client that it should stop creating new request streams by calling ``beginGracefulShutdown()``. /// This gives the client enough time to drain any requests already in flight. To stop the client /// more abruptly you can cancel the task running your client. If your application requires @@ -114,7 +114,7 @@ public final class GRPCClient: Sendable { func checkExecutable() throws { switch self { case .notStarted, .running: - // Allow .notStarted as making a request can race with 'run()'. Transports should tolerate + // Allow .notStarted as making a request can race with 'maintainConnections()'. Transports should tolerate // queuing the request if not yet started. () case .stopping, .stopped: @@ -208,7 +208,7 @@ public final class GRPCClient: Sendable { /// /// The client, and by extension this function, can only be run once. If the client is already /// running or has already been closed then a ``RuntimeError`` is thrown. - public func run() async throws { + public func maintainConnections() async throws { try self.stateMachine.withLock { try $0.state.run() } // When this function exits the client must have stopped. @@ -227,6 +227,11 @@ public final class GRPCClient: Sendable { } } + @available(*, deprecated, renamed: "maintainConnections", message: "It'll be removed before v2.") + public func run() async throws { + try await self.maintainConnections() + } + /// Close the client. /// /// The transport will be closed: this means that it will be given enough time to wait for @@ -338,7 +343,7 @@ public final class GRPCClient: Sendable { /// Start a bidirectional streaming RPC. /// - /// - Note: ``run()`` must have been called and still executing, and ``beginGracefulShutdown()`` mustn't + /// - Note: ``maintainConnections()`` must have been called and still executing, and ``beginGracefulShutdown()`` mustn't /// have been called. /// /// - Parameters: @@ -430,7 +435,7 @@ public func withGRPCClient( try await withThrowingDiscardingTaskGroup { group in let client = GRPCClient(transport: transport, interceptorPipeline: interceptorPipeline) group.addTask { - try await client.run() + try await client.maintainConnections() } let result = try await handleClient(client) diff --git a/Tests/GRPCCoreTests/GRPCClientTests.swift b/Tests/GRPCCoreTests/GRPCClientTests.swift index 0152a9c81..1ea77cc93 100644 --- a/Tests/GRPCCoreTests/GRPCClientTests.swift +++ b/Tests/GRPCCoreTests/GRPCClientTests.swift @@ -336,7 +336,7 @@ final class GRPCClientTests: XCTestCase { } group.addTask { - try await client.run() + try await client.maintainConnections() } // Wait for client and server to be running. @@ -377,13 +377,13 @@ final class GRPCClientTests: XCTestCase { let inProcess = InProcessTransport() let client = GRPCClient(transport: inProcess.client) // Run the client. - let task = Task { try await client.run() } + let task = Task { try await client.maintainConnections() } task.cancel() try await task.value // Client is stopped, should throw an error. await XCTAssertThrowsErrorAsync(ofType: RuntimeError.self) { - try await client.run() + try await client.maintainConnections() } errorHandler: { error in XCTAssertEqual(error.code, .clientIsStopped) } @@ -393,13 +393,13 @@ final class GRPCClientTests: XCTestCase { let inProcess = InProcessTransport() let client = GRPCClient(transport: inProcess.client) // Run the client. - let task = Task { try await client.run() } + let task = Task { try await client.maintainConnections() } // Make sure the client is run for the first time here. try await Task.sleep(for: .milliseconds(10)) // Client is already running, should throw an error. await XCTAssertThrowsErrorAsync(ofType: RuntimeError.self) { - try await client.run() + try await client.maintainConnections() } errorHandler: { error in XCTAssertEqual(error.code, .clientIsAlreadyRunning) } @@ -551,7 +551,7 @@ struct ClientTests { } group.addTask { - try await client.run() + try await client.maintainConnections() } // Make sure both server and client are running diff --git a/Tests/GRPCInProcessTransportTests/InProcessTransportTests.swift b/Tests/GRPCInProcessTransportTests/InProcessTransportTests.swift index 786f6fe99..5c10ab0c5 100644 --- a/Tests/GRPCInProcessTransportTests/InProcessTransportTests.swift +++ b/Tests/GRPCInProcessTransportTests/InProcessTransportTests.swift @@ -35,7 +35,7 @@ struct InProcessTransportTests { let client = GRPCClient(transport: inProcess.client) group.addTask { - try await client.run() + try await client.maintainConnections() } try await execute(server, client) @@ -60,7 +60,7 @@ struct InProcessTransportTests { #expect(messages == ["isCancelled=true"]) } - // Finally, shutdown the client so its run() method returns. + // Finally, shutdown the client so its maintainConnections() method returns. client.beginGracefulShutdown() } } From 129bd4ffc4c7637ceb00e3491c06797dee781c9f Mon Sep 17 00:00:00 2001 From: George Barnett Date: Fri, 17 Jan 2025 09:33:33 +0000 Subject: [PATCH 2/3] run connections --- Sources/GRPCCore/GRPCClient.swift | 16 ++++++++-------- Tests/GRPCCoreTests/GRPCClientTests.swift | 12 ++++++------ .../InProcessTransportTests.swift | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Sources/GRPCCore/GRPCClient.swift b/Sources/GRPCCore/GRPCClient.swift index 4db64449b..a19a4941b 100644 --- a/Sources/GRPCCore/GRPCClient.swift +++ b/Sources/GRPCCore/GRPCClient.swift @@ -45,10 +45,10 @@ private import Synchronization /// ## Creating a client manually /// /// If the `with`-style methods for creating clients isn't suitable for your application then you -/// can create and run a client manually. This requires you to call the ``maintainConnections()`` method in a task +/// can create and run a client manually. This requires you to call the ``runConnections()`` method in a task /// which instructs the client to start connecting to the server. /// -/// The ``maintainConnections()`` method won't return until the client has finished handling all requests. You can +/// The ``runConnections()`` method won't return until the client has finished handling all requests. You can /// signal to the client that it should stop creating new request streams by calling ``beginGracefulShutdown()``. /// This gives the client enough time to drain any requests already in flight. To stop the client /// more abruptly you can cancel the task running your client. If your application requires @@ -114,7 +114,7 @@ public final class GRPCClient: Sendable { func checkExecutable() throws { switch self { case .notStarted, .running: - // Allow .notStarted as making a request can race with 'maintainConnections()'. Transports should tolerate + // Allow .notStarted as making a request can race with 'runConnections()'. Transports should tolerate // queuing the request if not yet started. () case .stopping, .stopped: @@ -208,7 +208,7 @@ public final class GRPCClient: Sendable { /// /// The client, and by extension this function, can only be run once. If the client is already /// running or has already been closed then a ``RuntimeError`` is thrown. - public func maintainConnections() async throws { + public func runConnections() async throws { try self.stateMachine.withLock { try $0.state.run() } // When this function exits the client must have stopped. @@ -227,9 +227,9 @@ public final class GRPCClient: Sendable { } } - @available(*, deprecated, renamed: "maintainConnections", message: "It'll be removed before v2.") + @available(*, deprecated, renamed: "runConnections", message: "It'll be removed before v2.") public func run() async throws { - try await self.maintainConnections() + try await self.runConnections() } /// Close the client. @@ -343,7 +343,7 @@ public final class GRPCClient: Sendable { /// Start a bidirectional streaming RPC. /// - /// - Note: ``maintainConnections()`` must have been called and still executing, and ``beginGracefulShutdown()`` mustn't + /// - Note: ``runConnections()`` must have been called and still executing, and ``beginGracefulShutdown()`` mustn't /// have been called. /// /// - Parameters: @@ -435,7 +435,7 @@ public func withGRPCClient( try await withThrowingDiscardingTaskGroup { group in let client = GRPCClient(transport: transport, interceptorPipeline: interceptorPipeline) group.addTask { - try await client.maintainConnections() + try await client.runConnections() } let result = try await handleClient(client) diff --git a/Tests/GRPCCoreTests/GRPCClientTests.swift b/Tests/GRPCCoreTests/GRPCClientTests.swift index 1ea77cc93..9300bf44e 100644 --- a/Tests/GRPCCoreTests/GRPCClientTests.swift +++ b/Tests/GRPCCoreTests/GRPCClientTests.swift @@ -336,7 +336,7 @@ final class GRPCClientTests: XCTestCase { } group.addTask { - try await client.maintainConnections() + try await client.runConnections() } // Wait for client and server to be running. @@ -377,13 +377,13 @@ final class GRPCClientTests: XCTestCase { let inProcess = InProcessTransport() let client = GRPCClient(transport: inProcess.client) // Run the client. - let task = Task { try await client.maintainConnections() } + let task = Task { try await client.runConnections() } task.cancel() try await task.value // Client is stopped, should throw an error. await XCTAssertThrowsErrorAsync(ofType: RuntimeError.self) { - try await client.maintainConnections() + try await client.runConnections() } errorHandler: { error in XCTAssertEqual(error.code, .clientIsStopped) } @@ -393,13 +393,13 @@ final class GRPCClientTests: XCTestCase { let inProcess = InProcessTransport() let client = GRPCClient(transport: inProcess.client) // Run the client. - let task = Task { try await client.maintainConnections() } + let task = Task { try await client.runConnections() } // Make sure the client is run for the first time here. try await Task.sleep(for: .milliseconds(10)) // Client is already running, should throw an error. await XCTAssertThrowsErrorAsync(ofType: RuntimeError.self) { - try await client.maintainConnections() + try await client.runConnections() } errorHandler: { error in XCTAssertEqual(error.code, .clientIsAlreadyRunning) } @@ -551,7 +551,7 @@ struct ClientTests { } group.addTask { - try await client.maintainConnections() + try await client.runConnections() } // Make sure both server and client are running diff --git a/Tests/GRPCInProcessTransportTests/InProcessTransportTests.swift b/Tests/GRPCInProcessTransportTests/InProcessTransportTests.swift index 4e5902c64..ea7938b9c 100644 --- a/Tests/GRPCInProcessTransportTests/InProcessTransportTests.swift +++ b/Tests/GRPCInProcessTransportTests/InProcessTransportTests.swift @@ -60,7 +60,7 @@ struct InProcessTransportTests { #expect(messages == ["isCancelled=true"]) } - // Finally, shutdown the client so its maintainConnections() method returns. + // Finally, shutdown the client so its runConnections() method returns. client.beginGracefulShutdown() } } From 9ee19dcd8fbf62c36e64926b1b17a3e81625e9e8 Mon Sep 17 00:00:00 2001 From: George Barnett Date: Fri, 17 Jan 2025 09:42:16 +0000 Subject: [PATCH 3/3] fix test --- Tests/GRPCInProcessTransportTests/InProcessTransportTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/GRPCInProcessTransportTests/InProcessTransportTests.swift b/Tests/GRPCInProcessTransportTests/InProcessTransportTests.swift index ea7938b9c..2a90aee5e 100644 --- a/Tests/GRPCInProcessTransportTests/InProcessTransportTests.swift +++ b/Tests/GRPCInProcessTransportTests/InProcessTransportTests.swift @@ -35,7 +35,7 @@ struct InProcessTransportTests { let client = GRPCClient(transport: inProcess.client) group.addTask { - try await client.maintainConnections() + try await client.runConnections() } try await execute(server, client)