Optimize Token Refresh with Conditional Queuing and Typed Error Mapping
Introduction:
This PR enhances AtomNetworking by optimizing bearer token refresh handling. It introduces conditional serialization of network calls - bypassing the queue when no refresh is needed for better performance - while maintaining strict FIFO ordering and deduplication during refresh scenarios.
Motivation:
Previously, all network operations were serialized through RequestableQueueManager, even when the access token was valid. This caused unnecessary delays in high-throughput scenarios. Additionally, error propagation from generic Error to typed AtomError was verbose and repetitive.
The goal is to:
- Only serialize calls when token refresh is needed or in progress.
- Enqueue calls during refresh and execute them immediately after completion.
- Preserve FIFO order and race safety during refresh.
Proposed Solution:
Conditional Serialization in Service**:
- Added
needsSerialization()toSessionActorto check both.requiresRefreshandisRefreshing. - Introduced
awaitOrEnqueue(_:)inServiceto:- Bypass the queue and execute directly if no refresh needed.
- Enqueue if refresh is required or ongoing.
Code Changes
SessionActor+Authorization.swift
/// Determines whether the access token should be refreshed based on the service configuration.
///
/// This asynchronous function checks the authentication method in the service configuration. It only proceeds if the method is a
/// bearer token with a writable component.
///
/// - Returns: A `Bool` value indicating if the access token needs refreshing. Returns `true` if refresh is required; otherwise, `false`.
func needsSerialization() async -> Bool {
guard case let .bearer(_, _, writable) = serviceConfiguration.authenticationMethod else {
return false
}
return writable.tokenCredential.requiresRefresh || isRefreshing
}Service+ConditionalQueuing.swift
/// Executes or enqueues an asynchronous task based on whether the access token requires refreshing.
///
/// This function creates a task to check if the session's access token needs to be refreshed using `sessionActor.shouldRefreshAccessToken()`. If a
/// refresh is required, the provided task is enqueued in the `requestableQueueManager` for later execution. If no refresh is needed, the task is
/// executed immediately within the detached task context.
///
/// This mechanism ensures that tasks dependent on a valid access token are properly sequenced without blocking the caller.
///
/// - Parameter task: An escaping, sendable asynchronous closure that performs the desired operation. The closure takes no parameters and returns `Void`.
func awaitOrEnqueue(_ task: @escaping @Sendable () async -> Void) {
Task {
if await sessionActor.shouldRefreshAccessToken() {
requestableQueueManager.enqueue(task)
} else {
await task()
}
}
}Additional Info:
- Optimize access token refresh with conditional queuing.
- Updated documentation.
Source Compatibility:
Please check the box to indicate the impact of this proposal on source compatibility.
- This change is additive and does not impact existing source code.
- This change breaks existing source code.