diff --git a/Sources/GRPCHealthService/Generated/health.grpc.swift b/Sources/GRPCHealthService/Generated/health.grpc.swift index 9eb2972..25d80bd 100644 --- a/Sources/GRPCHealthService/Generated/health.grpc.swift +++ b/Sources/GRPCHealthService/Generated/health.grpc.swift @@ -49,6 +49,18 @@ package enum Grpc_Health_V1_Health { method: "Check" ) } + /// Namespace for "List" metadata. + package enum List { + /// Request type for "List". + package typealias Input = Grpc_Health_V1_HealthListRequest + /// Response type for "List". + package typealias Output = Grpc_Health_V1_HealthListResponse + /// Descriptor for "List". + package static let descriptor = GRPCCore.MethodDescriptor( + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "grpc.health.v1.Health"), + method: "List" + ) + } /// Namespace for "Watch" metadata. package enum Watch { /// Request type for "Watch". @@ -64,6 +76,7 @@ package enum Grpc_Health_V1_Health { /// Descriptors for all methods in the "grpc.health.v1.Health" service. package static let descriptors: [GRPCCore.MethodDescriptor] = [ Check.descriptor, + List.descriptor, Watch.descriptor ] } @@ -107,8 +120,6 @@ extension Grpc_Health_V1_Health { /// > /// > Clients should set a deadline when calling Check, and can declare the /// > server unhealthy if they do not receive a timely response. - /// > - /// > Check implementations should be idempotent and side effect free. /// /// - Parameters: /// - request: A streaming request of `Grpc_Health_V1_HealthCheckRequest` messages. @@ -122,6 +133,34 @@ extension Grpc_Health_V1_Health { context: GRPCCore.ServerContext ) async throws -> GRPCCore.StreamingServerResponse + /// Handle the "List" method. + /// + /// > Source IDL Documentation: + /// > + /// > List provides a non-atomic snapshot of the health of all the available + /// > services. + /// > + /// > The server may respond with a RESOURCE_EXHAUSTED error if too many services + /// > exist. + /// > + /// > Clients should set a deadline when calling List, and can declare the server + /// > unhealthy if they do not receive a timely response. + /// > + /// > Clients should keep in mind that the list of health services exposed by an + /// > application can change over the lifetime of the process. + /// + /// - Parameters: + /// - request: A streaming request of `Grpc_Health_V1_HealthListRequest` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A streaming response of `Grpc_Health_V1_HealthListResponse` messages. + func list( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + /// Handle the "Watch" method. /// /// > Source IDL Documentation: @@ -180,8 +219,6 @@ extension Grpc_Health_V1_Health { /// > /// > Clients should set a deadline when calling Check, and can declare the /// > server unhealthy if they do not receive a timely response. - /// > - /// > Check implementations should be idempotent and side effect free. /// /// - Parameters: /// - request: A request containing a single `Grpc_Health_V1_HealthCheckRequest` message. @@ -195,6 +232,34 @@ extension Grpc_Health_V1_Health { context: GRPCCore.ServerContext ) async throws -> GRPCCore.ServerResponse + /// Handle the "List" method. + /// + /// > Source IDL Documentation: + /// > + /// > List provides a non-atomic snapshot of the health of all the available + /// > services. + /// > + /// > The server may respond with a RESOURCE_EXHAUSTED error if too many services + /// > exist. + /// > + /// > Clients should set a deadline when calling List, and can declare the server + /// > unhealthy if they do not receive a timely response. + /// > + /// > Clients should keep in mind that the list of health services exposed by an + /// > application can change over the lifetime of the process. + /// + /// - Parameters: + /// - request: A request containing a single `Grpc_Health_V1_HealthListRequest` message. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A response containing a single `Grpc_Health_V1_HealthListResponse` message. + func list( + request: GRPCCore.ServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.ServerResponse + /// Handle the "Watch" method. /// /// > Source IDL Documentation: @@ -251,8 +316,6 @@ extension Grpc_Health_V1_Health { /// > /// > Clients should set a deadline when calling Check, and can declare the /// > server unhealthy if they do not receive a timely response. - /// > - /// > Check implementations should be idempotent and side effect free. /// /// - Parameters: /// - request: A `Grpc_Health_V1_HealthCheckRequest` message. @@ -266,6 +329,34 @@ extension Grpc_Health_V1_Health { context: GRPCCore.ServerContext ) async throws -> Grpc_Health_V1_HealthCheckResponse + /// Handle the "List" method. + /// + /// > Source IDL Documentation: + /// > + /// > List provides a non-atomic snapshot of the health of all the available + /// > services. + /// > + /// > The server may respond with a RESOURCE_EXHAUSTED error if too many services + /// > exist. + /// > + /// > Clients should set a deadline when calling List, and can declare the server + /// > unhealthy if they do not receive a timely response. + /// > + /// > Clients should keep in mind that the list of health services exposed by an + /// > application can change over the lifetime of the process. + /// + /// - Parameters: + /// - request: A `Grpc_Health_V1_HealthListRequest` message. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A `Grpc_Health_V1_HealthListResponse` to respond with. + func list( + request: Grpc_Health_V1_HealthListRequest, + context: GRPCCore.ServerContext + ) async throws -> Grpc_Health_V1_HealthListResponse + /// Handle the "Watch" method. /// /// > Source IDL Documentation: @@ -316,6 +407,17 @@ extension Grpc_Health_V1_Health.StreamingServiceProtocol { ) } ) + router.registerHandler( + forMethod: Grpc_Health_V1_Health.Method.List.descriptor, + deserializer: GRPCProtobuf.ProtobufDeserializer(), + serializer: GRPCProtobuf.ProtobufSerializer(), + handler: { request, context in + try await self.list( + request: request, + context: context + ) + } + ) router.registerHandler( forMethod: Grpc_Health_V1_Health.Method.Watch.descriptor, deserializer: GRPCProtobuf.ProtobufDeserializer(), @@ -344,6 +446,17 @@ extension Grpc_Health_V1_Health.ServiceProtocol { return GRPCCore.StreamingServerResponse(single: response) } + package func list( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse { + let response = try await self.list( + request: GRPCCore.ServerRequest(stream: request), + context: context + ) + return GRPCCore.StreamingServerResponse(single: response) + } + package func watch( request: GRPCCore.StreamingServerRequest, context: GRPCCore.ServerContext @@ -372,6 +485,19 @@ extension Grpc_Health_V1_Health.SimpleServiceProtocol { ) } + package func list( + request: GRPCCore.ServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.ServerResponse { + return GRPCCore.ServerResponse( + message: try await self.list( + request: request.message, + context: context + ), + metadata: [:] + ) + } + package func watch( request: GRPCCore.ServerRequest, context: GRPCCore.ServerContext @@ -416,8 +542,6 @@ extension Grpc_Health_V1_Health { /// > /// > Clients should set a deadline when calling Check, and can declare the /// > server unhealthy if they do not receive a timely response. - /// > - /// > Check implementations should be idempotent and side effect free. /// /// - Parameters: /// - request: A request containing a single `Grpc_Health_V1_HealthCheckRequest` message. @@ -436,6 +560,39 @@ extension Grpc_Health_V1_Health { onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result ) async throws -> Result where Result: Sendable + /// Call the "List" method. + /// + /// > Source IDL Documentation: + /// > + /// > List provides a non-atomic snapshot of the health of all the available + /// > services. + /// > + /// > The server may respond with a RESOURCE_EXHAUSTED error if too many services + /// > exist. + /// > + /// > Clients should set a deadline when calling List, and can declare the server + /// > unhealthy if they do not receive a timely response. + /// > + /// > Clients should keep in mind that the list of health services exposed by an + /// > application can change over the lifetime of the process. + /// + /// - Parameters: + /// - request: A request containing a single `Grpc_Health_V1_HealthListRequest` message. + /// - serializer: A serializer for `Grpc_Health_V1_HealthListRequest` messages. + /// - deserializer: A deserializer for `Grpc_Health_V1_HealthListResponse` messages. + /// - options: Options to apply to this RPC. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. + func list( + request: GRPCCore.ClientRequest, + serializer: some GRPCCore.MessageSerializer, + deserializer: some GRPCCore.MessageDeserializer, + options: GRPCCore.CallOptions, + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable + /// Call the "Watch" method. /// /// > Source IDL Documentation: @@ -507,8 +664,6 @@ extension Grpc_Health_V1_Health { /// > /// > Clients should set a deadline when calling Check, and can declare the /// > server unhealthy if they do not receive a timely response. - /// > - /// > Check implementations should be idempotent and side effect free. /// /// - Parameters: /// - request: A request containing a single `Grpc_Health_V1_HealthCheckRequest` message. @@ -538,6 +693,50 @@ extension Grpc_Health_V1_Health { ) } + /// Call the "List" method. + /// + /// > Source IDL Documentation: + /// > + /// > List provides a non-atomic snapshot of the health of all the available + /// > services. + /// > + /// > The server may respond with a RESOURCE_EXHAUSTED error if too many services + /// > exist. + /// > + /// > Clients should set a deadline when calling List, and can declare the server + /// > unhealthy if they do not receive a timely response. + /// > + /// > Clients should keep in mind that the list of health services exposed by an + /// > application can change over the lifetime of the process. + /// + /// - Parameters: + /// - request: A request containing a single `Grpc_Health_V1_HealthListRequest` message. + /// - serializer: A serializer for `Grpc_Health_V1_HealthListRequest` messages. + /// - deserializer: A deserializer for `Grpc_Health_V1_HealthListResponse` messages. + /// - options: Options to apply to this RPC. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. + package func list( + request: GRPCCore.ClientRequest, + serializer: some GRPCCore.MessageSerializer, + deserializer: some GRPCCore.MessageDeserializer, + options: GRPCCore.CallOptions = .defaults, + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message + } + ) async throws -> Result where Result: Sendable { + try await self.client.unary( + request: request, + descriptor: Grpc_Health_V1_Health.Method.List.descriptor, + serializer: serializer, + deserializer: deserializer, + options: options, + onResponse: handleResponse + ) + } + /// Call the "Watch" method. /// /// > Source IDL Documentation: @@ -600,8 +799,6 @@ extension Grpc_Health_V1_Health.ClientProtocol { /// > /// > Clients should set a deadline when calling Check, and can declare the /// > server unhealthy if they do not receive a timely response. - /// > - /// > Check implementations should be idempotent and side effect free. /// /// - Parameters: /// - request: A request containing a single `Grpc_Health_V1_HealthCheckRequest` message. @@ -626,6 +823,45 @@ extension Grpc_Health_V1_Health.ClientProtocol { ) } + /// Call the "List" method. + /// + /// > Source IDL Documentation: + /// > + /// > List provides a non-atomic snapshot of the health of all the available + /// > services. + /// > + /// > The server may respond with a RESOURCE_EXHAUSTED error if too many services + /// > exist. + /// > + /// > Clients should set a deadline when calling List, and can declare the server + /// > unhealthy if they do not receive a timely response. + /// > + /// > Clients should keep in mind that the list of health services exposed by an + /// > application can change over the lifetime of the process. + /// + /// - Parameters: + /// - request: A request containing a single `Grpc_Health_V1_HealthListRequest` message. + /// - options: Options to apply to this RPC. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. + package func list( + request: GRPCCore.ClientRequest, + options: GRPCCore.CallOptions = .defaults, + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message + } + ) async throws -> Result where Result: Sendable { + try await self.list( + request: request, + serializer: GRPCProtobuf.ProtobufSerializer(), + deserializer: GRPCProtobuf.ProtobufDeserializer(), + options: options, + onResponse: handleResponse + ) + } + /// Call the "Watch" method. /// /// > Source IDL Documentation: @@ -682,8 +918,6 @@ extension Grpc_Health_V1_Health.ClientProtocol { /// > /// > Clients should set a deadline when calling Check, and can declare the /// > server unhealthy if they do not receive a timely response. - /// > - /// > Check implementations should be idempotent and side effect free. /// /// - Parameters: /// - message: request message to send. @@ -712,6 +946,49 @@ extension Grpc_Health_V1_Health.ClientProtocol { ) } + /// Call the "List" method. + /// + /// > Source IDL Documentation: + /// > + /// > List provides a non-atomic snapshot of the health of all the available + /// > services. + /// > + /// > The server may respond with a RESOURCE_EXHAUSTED error if too many services + /// > exist. + /// > + /// > Clients should set a deadline when calling List, and can declare the server + /// > unhealthy if they do not receive a timely response. + /// > + /// > Clients should keep in mind that the list of health services exposed by an + /// > application can change over the lifetime of the process. + /// + /// - Parameters: + /// - message: request message to send. + /// - metadata: Additional metadata to send, defaults to empty. + /// - options: Options to apply to this RPC, defaults to `.defaults`. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. + package func list( + _ message: Grpc_Health_V1_HealthListRequest, + metadata: GRPCCore.Metadata = [:], + options: GRPCCore.CallOptions = .defaults, + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message + } + ) async throws -> Result where Result: Sendable { + let request = GRPCCore.ClientRequest( + message: message, + metadata: metadata + ) + return try await self.list( + request: request, + options: options, + onResponse: handleResponse + ) + } + /// Call the "Watch" method. /// /// > Source IDL Documentation: diff --git a/Sources/GRPCHealthService/Generated/health.pb.swift b/Sources/GRPCHealthService/Generated/health.pb.swift index ea2cde5..6b6028f 100644 --- a/Sources/GRPCHealthService/Generated/health.pb.swift +++ b/Sources/GRPCHealthService/Generated/health.pb.swift @@ -105,6 +105,29 @@ package struct Grpc_Health_V1_HealthCheckResponse: Sendable { package init() {} } +package struct Grpc_Health_V1_HealthListRequest: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + package var unknownFields = SwiftProtobuf.UnknownStorage() + + package init() {} +} + +package struct Grpc_Health_V1_HealthListResponse: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// statuses contains all the services and their respective status. + package var statuses: Dictionary = [:] + + package var unknownFields = SwiftProtobuf.UnknownStorage() + + package init() {} +} + // MARK: - Code below here is support for the SwiftProtobuf runtime. fileprivate let _protobuf_package = "grpc.health.v1" @@ -181,3 +204,54 @@ extension Grpc_Health_V1_HealthCheckResponse.ServingStatus: SwiftProtobuf._Proto 3: .same(proto: "SERVICE_UNKNOWN"), ] } + +extension Grpc_Health_V1_HealthListRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + package static let protoMessageName: String = _protobuf_package + ".HealthListRequest" + package static let _protobuf_nameMap = SwiftProtobuf._NameMap() + + package mutating func decodeMessage(decoder: inout D) throws { + // Load everything into unknown fields + while try decoder.nextFieldNumber() != nil {} + } + + package func traverse(visitor: inout V) throws { + try unknownFields.traverse(visitor: &visitor) + } + + package static func ==(lhs: Grpc_Health_V1_HealthListRequest, rhs: Grpc_Health_V1_HealthListRequest) -> Bool { + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Grpc_Health_V1_HealthListResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + package static let protoMessageName: String = _protobuf_package + ".HealthListResponse" + package static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "statuses"), + ] + + package mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeMapField(fieldType: SwiftProtobuf._ProtobufMessageMap.self, value: &self.statuses) }() + default: break + } + } + } + + package func traverse(visitor: inout V) throws { + if !self.statuses.isEmpty { + try visitor.visitMapField(fieldType: SwiftProtobuf._ProtobufMessageMap.self, value: self.statuses, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + package static func ==(lhs: Grpc_Health_V1_HealthListResponse, rhs: Grpc_Health_V1_HealthListResponse) -> Bool { + if lhs.statuses != rhs.statuses {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/Sources/GRPCHealthService/HealthService+Service.swift b/Sources/GRPCHealthService/HealthService+Service.swift index be158b8..2dd0d60 100644 --- a/Sources/GRPCHealthService/HealthService+Service.swift +++ b/Sources/GRPCHealthService/HealthService+Service.swift @@ -15,12 +15,16 @@ */ internal import GRPCCore +internal import SwiftProtobuf private import Synchronization @available(gRPCSwiftExtras 2.0, *) extension HealthService { internal struct Service: Grpc_Health_V1_Health.ServiceProtocol { private let state = Self.State() + /// Defines the maximum number of resources a `List` request can return. + /// An `RPCError` with the code `ResourceExhaused` is thrown if this limit is exceeded. + private let listMaxAllowedServices = 100 } } @@ -42,6 +46,30 @@ extension HealthService.Service { return ServerResponse(message: response) } + func list( + request: ServerRequest, + context: ServerContext + ) async throws -> ServerResponse { + let serviceStatuses = self.state.listStatuses() + + guard serviceStatuses.count <= listMaxAllowedServices else { + throw RPCError( + code: .resourceExhausted, + message: "Server health list exceeds maximum capacity: \(listMaxAllowedServices)." + ) + } + + var listResponse = Grpc_Health_V1_HealthListResponse() + + for (service, status) in serviceStatuses { + listResponse.statuses[service] = .with { response in + response.status = status + } + } + + return ServerResponse(message: listResponse) + } + func watch( request: ServerRequest, context: ServerContext @@ -92,6 +120,10 @@ extension HealthService.Service { } } + fileprivate func listStatuses() -> [String: Grpc_Health_V1_HealthCheckResponse.ServingStatus] { + self.lockedStorage.withLock { $0.mapValues { $0.currentStatus } } + } + fileprivate func addContinuation( _ continuation: AsyncStream.Continuation, forService service: String diff --git a/Tests/GRPCHealthServiceTests/HealthTests.swift b/Tests/GRPCHealthServiceTests/HealthTests.swift index 640b282..fe2e214 100644 --- a/Tests/GRPCHealthServiceTests/HealthTests.swift +++ b/Tests/GRPCHealthServiceTests/HealthTests.swift @@ -285,6 +285,95 @@ final class HealthTests: XCTestCase { } } } + + func testListServicesEmpty() async throws { + try await withHealthClient { (healthClient, healthProvider) in + let message = Grpc_Health_V1_HealthListRequest() + + let response = try await healthClient.list(message) + XCTAssertEqual(response.statuses, [:]) + } + } + + func testListServices() async throws { + try await withHealthClient { (healthClient, healthProvider) in + let message = Grpc_Health_V1_HealthListRequest() + + // Service descriptors and their randomly generated status. + let testServiceDescriptors: [(ServiceDescriptor, ServingStatus)] = (0 ..< 10).map { i in + ( + ServiceDescriptor(package: "test", service: "Service\(i)"), + Bool.random() ? .notServing : .serving + ) + } + + for (index, (descriptor, status)) in testServiceDescriptors.enumerated() { + healthProvider.updateStatus( + status, + forService: descriptor + ) + + let response = try await healthClient.list(message) + let statuses = response.statuses + XCTAssertTrue(statuses.count == index + 1) + + for (descriptor, status) in testServiceDescriptors.prefix(index + 1) { + let receivedStatus = try XCTUnwrap(statuses[descriptor.fullyQualifiedService]?.status) + let expectedStatus = Grpc_Health_V1_HealthCheckResponse.ServingStatus( + status + ) + + XCTAssertEqual(receivedStatus, expectedStatus) + } + } + } + } + + func testListOnServer() async throws { + try await withHealthClient { (healthClient, healthProvider) in + let message = Grpc_Health_V1_HealthListRequest() + + healthProvider.updateStatus(.notServing, forService: "") + + var response = try await healthClient.list(message) + XCTAssertEqual(try XCTUnwrap(response.statuses[""]?.status), .notServing) + + healthProvider.updateStatus(.serving, forService: "") + + response = try await healthClient.list(message) + XCTAssertEqual(try XCTUnwrap(response.statuses[""]?.status), .serving) + } + } + + func testListExceedingMaxAllowedServices() async throws { + try await withHealthClient { (healthClient, healthProvider) in + let message = Grpc_Health_V1_HealthListRequest() + let listMaxAllowedServices = 100 + + for index in 1 ... listMaxAllowedServices { + healthProvider.updateStatus( + .notServing, + forService: ServiceDescriptor(package: "test", service: "Service\(index)") + ) + + let response = try await healthClient.list(message) + XCTAssertTrue(response.statuses.count == index) + } + + healthProvider.updateStatus(.notServing, forService: ServiceDescriptor.testService) + + do { + _ = try await healthClient.list(message) + XCTFail("should error") + } catch { + let resolvedError = try XCTUnwrap( + error as? RPCError, + "health client list throws unexpected error: \(error)" + ) + XCTAssertEqual(resolvedError.code, .resourceExhausted) + } + } + } } @available(gRPCSwiftExtras 2.0, *) diff --git a/Tests/GRPCReflectionServiceTests/Generated/DescriptorSets/health.pb b/Tests/GRPCReflectionServiceTests/Generated/DescriptorSets/health.pb index ff3b6a7..05219e7 100644 Binary files a/Tests/GRPCReflectionServiceTests/Generated/DescriptorSets/health.pb and b/Tests/GRPCReflectionServiceTests/Generated/DescriptorSets/health.pb differ diff --git a/dev/protos/upstream/grpc/health/v1/health.proto b/dev/protos/upstream/grpc/health/v1/health.proto index 13b03f5..288ce7c 100644 --- a/dev/protos/upstream/grpc/health/v1/health.proto +++ b/dev/protos/upstream/grpc/health/v1/health.proto @@ -24,6 +24,7 @@ option go_package = "google.golang.org/grpc/health/grpc_health_v1"; option java_multiple_files = true; option java_outer_classname = "HealthProto"; option java_package = "io.grpc.health.v1"; +option objc_class_prefix = "GrpcHealthV1"; message HealthCheckRequest { string service = 1; @@ -39,6 +40,13 @@ message HealthCheckResponse { ServingStatus status = 1; } +message HealthListRequest {} + +message HealthListResponse { + // statuses contains all the services and their respective status. + map statuses = 1; +} + // Health is gRPC's mechanism for checking whether a server is able to handle // RPCs. Its semantics are documented in // https://github.com/grpc/grpc/blob/master/doc/health-checking.md. @@ -50,10 +58,21 @@ service Health { // // Clients should set a deadline when calling Check, and can declare the // server unhealthy if they do not receive a timely response. - // - // Check implementations should be idempotent and side effect free. rpc Check(HealthCheckRequest) returns (HealthCheckResponse); + // List provides a non-atomic snapshot of the health of all the available + // services. + // + // The server may respond with a RESOURCE_EXHAUSTED error if too many services + // exist. + // + // Clients should set a deadline when calling List, and can declare the server + // unhealthy if they do not receive a timely response. + // + // Clients should keep in mind that the list of health services exposed by an + // application can change over the lifetime of the process. + rpc List(HealthListRequest) returns (HealthListResponse); + // Performs a watch for the serving status of the requested service. // The server will immediately send back a message indicating the current // serving status. It will then subsequently send a new message whenever