소스 검색

A few helpers to pave the way for subchannel (#1867)

Motivation:

To keep the subchannel PR smaller, a few related but standalone bits can
be added separately.

Modifications:

- Bump the NIO version to pickup a NIOAsyncChannel fix and a new cascade
  function
- Add the `ConnectivityState` enum
- Add `ProcessUniqueID` and `SubchannelID`

Result:

Subchannel PR will be smaller.
George Barnett 1 년 전
부모
커밋
44da29c40d

+ 0 - 16
Sources/GRPCHTTP2Core/Client/Connection/ClientConnectionHandler.swift

@@ -513,19 +513,3 @@ extension ClientConnectionHandler {
     }
   }
 }
-
-extension Optional {
-  // TODO: replace with https://github.com/apple/swift-nio/pull/2697
-  mutating func setOrCascade<Value>(
-    to promise: EventLoopPromise<Value>?
-  ) where Wrapped == EventLoopPromise<Value> {
-    guard let promise = promise else { return }
-
-    switch self {
-    case .none:
-      self = .some(promise)
-    case .some(let existing):
-      existing.futureResult.cascade(to: promise)
-    }
-  }
-}

+ 45 - 0
Sources/GRPCHTTP2Core/Client/Connection/ConnectivityState.swift

@@ -0,0 +1,45 @@
+/*
+ * Copyright 2024, gRPC Authors All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+enum ConnectivityState: Sendable, Hashable {
+  /// This channel isn't trying to create a connection because of a lack of new or pending RPCs.
+  ///
+  /// New streams may be created in this state. Doing so will cause the channel to enter the
+  /// connecting state.
+  case idle
+
+  /// The channel is trying to establish a connection and is waiting to make progress on one of the
+  /// steps involved in name resolution, TCP connection establishment or TLS handshake.
+  case connecting
+
+  /// The channel has successfully established a connection all the way through TLS handshake (or
+  /// equivalent) and protocol-level (HTTP/2, etc) handshaking.
+  case ready
+
+  /// There has been some transient failure (such as a TCP 3-way handshake timing out or a socket
+  /// error). Channels in this state will eventually switch to the ``connecting`` state and try to
+  /// establish a connection again. Since retries are done with exponential backoff, channels that
+  /// fail to connect will start out spending very little time in this state but as the attempts
+  /// fail repeatedly, the channel will spend increasingly large amounts of time in this state.
+  case transientFailure
+
+  /// This channel has started shutting down. Any new RPCs should fail immediately. Pending RPCs
+  /// may continue running until the application cancels them. Channels may enter this state either
+  /// because the application explicitly requested a shutdown or if a non-recoverable error has
+  /// happened during attempts to connect. Channels that have entered this state will never leave
+  /// this state.
+  case shutdown
+}

+ 39 - 0
Sources/GRPCHTTP2Core/Internal/ProcessUniqueID.swift

@@ -0,0 +1,39 @@
+/*
+ * Copyright 2024, gRPC Authors All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Atomics
+
+/// An ID which is unique within this process.
+struct ProcessUniqueID: Hashable, Sendable, CustomStringConvertible {
+  private static let source = ManagedAtomic(UInt64(0))
+  private let rawValue: UInt64
+
+  init() {
+    self.rawValue = Self.source.loadThenWrappingIncrement(ordering: .relaxed)
+  }
+
+  var description: String {
+    String(describing: self.rawValue)
+  }
+}
+
+/// A process-unique ID for a subchannel.
+struct SubchannelID: Hashable, Sendable, CustomStringConvertible {
+  private let id = ProcessUniqueID()
+  var description: String {
+    "subchan_\(self.id)"
+  }
+}

+ 45 - 0
Tests/GRPCHTTP2CoreTests/Internal/ProcessUniqueIDTests.swift

@@ -0,0 +1,45 @@
+/*
+ * Copyright 2024, gRPC Authors All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import XCTest
+
+@testable import GRPCHTTP2Core
+
+final class ProcessUniqueIDTests: XCTestCase {
+  func testProcessUniqueIDIsUnique() {
+    var ids: Set<ProcessUniqueID> = []
+    for _ in 1 ... 100 {
+      let (inserted, _) = ids.insert(ProcessUniqueID())
+      XCTAssertTrue(inserted)
+    }
+
+    XCTAssertEqual(ids.count, 100)
+  }
+
+  func testProcessUniqueIDDescription() {
+    let id = ProcessUniqueID()
+    let description = String(describing: id)
+    // We can't verify the exact description as we don't know what value to expect, we only
+    // know that it'll be an integer.
+    XCTAssertNotNil(UInt64(description))
+  }
+
+  func testSubchannelIDDescription() {
+    let id = SubchannelID()
+    let description = String(describing: id)
+    XCTAssert(description.hasPrefix("subchan_"))
+  }
+}