Browse Source

Set tolerance to zero when using `Task.sleep` (#94)

`Task.sleep` will by default try and coalesce multiple timers into one,
mostly for client-specific reasons such as performance, power
consumption, etc.

However, this is undesirable on servers, as it can increase latency,
memory usage, and (in the case of gRPC) may result in timeouts not
firing when they should.

We can avoid this by setting the sleep `tolerance` to zero.
Gus Cairo 9 months ago
parent
commit
14ebd0b429

+ 1 - 1
Sources/GRPCNIOTransportCore/Client/Connection/LoadBalancers/Subchannel.swift

@@ -308,7 +308,7 @@ extension Subchannel {
       )
       group.addTask {
         do {
-          try await Task.sleep(for: duration)
+          try await Task.sleep(for: duration, tolerance: .zero)
           self.input.continuation.yield(.backedOff)
         } catch {
           // Can only be a cancellation error, swallow it. No further connection attempts will be

+ 2 - 2
Tests/GRPCNIOTransportCoreTests/Client/Connection/GRPCChannelTests.swift

@@ -775,7 +775,7 @@ final class GRPCChannelTests: XCTestCase {
           group.addTask {
             // Sleep a little to increase the chances of the stream being queued before the channel
             // reacts to the close.
-            try await Task.sleep(for: .milliseconds(10))
+            try await Task.sleep(for: .milliseconds(10), tolerance: .zero)
             channel.beginGracefulShutdown()
           }
 
@@ -842,7 +842,7 @@ final class GRPCChannelTests: XCTestCase {
       try await doAnRPC()
 
       // Wait for the idle time to pass.
-      try await Task.sleep(for: .milliseconds(100))
+      try await Task.sleep(for: .milliseconds(100), tolerance: .zero)
 
       // Do another RPC.
       try await doAnRPC()

+ 1 - 1
Tests/GRPCNIOTransportCoreTests/Client/Connection/LoadBalancers/LoadBalancerTest.swift

@@ -94,7 +94,7 @@ enum LoadBalancerTest {
 
     try await withThrowingTaskGroup(of: TestEvent.self) { group in
       group.addTask {
-        try? await Task.sleep(for: timeout)
+        try? await Task.sleep(for: timeout, tolerance: .zero)
         return .timedOut
       }
 

+ 1 - 1
Tests/GRPCNIOTransportCoreTests/Test Utilities/Task+Poll.swift

@@ -28,7 +28,7 @@ extension Task where Success == Never, Failure == Never {
       if canReturn { return true }
 
       start = start.advanced(by: interval)
-      try await Task.sleep(until: start)
+      try await Task.sleep(until: start, tolerance: .zero)
     }
 
     return false