retryTransientSessionFileIo<T> function

Future<Result<T, FileError>> retryTransientSessionFileIo<T>(
  1. Future<Result<T, FileError>> operation(), {
  2. required String op,
  3. required String path,
  4. SessionIoRetryConfig config = const SessionIoRetryConfig(),
})

Runs operation — one FileSystem-shaped call returning a Result — retrying while it fails with a not-found-shaped FileError (the transient-ENOENT shape; every other error code is returned immediately). The last Err is returned as-is on exhaustion, so the caller's _fsOrThrow names it (SessionErrorCode.notFound via the existing mapping). Success and non-retryable failures are indistinguishable from an un-wrapped call.

op names the operation in the log line (open / create / append); path is the addressed session file.

Implementation

Future<Result<T, FileError>> retryTransientSessionFileIo<T>(
  Future<Result<T, FileError>> Function() operation, {
  required String op,
  required String path,
  SessionIoRetryConfig config = const SessionIoRetryConfig(),
}) async {
  final policy = config.policy;
  var attempt = 0;
  var waited = Duration.zero;
  var backoff = policy.initialBackoff;
  while (true) {
    final result = await operation();
    attempt++;
    if (!result.isErr) return result;
    final error = result.errorOrNull!;
    if (error.code != FileErrorCode.notFound) return result;
    if (attempt >= policy.maxAttempts) return result;
    final remaining = policy.maxTotalBackoff - waited;
    if (remaining <= Duration.zero) return result;
    if (backoff > remaining) backoff = remaining;
    waited += backoff;
    final retryIn = backoff;
    backoff *= policy.multiplier;
    // One line per retry: the next attempt's number, the wait, the file,
    // and the underlying error — post-mortem greps start at
    // `session_io_retry`.
    config.logger?.call(
      'session_io_retry op=$op attempt=${attempt + 1}/${policy.maxAttempts} '
      'path=$path waitMs=${retryIn.inMilliseconds} error=${error.message}',
    );
    await config.delay(retryIn);
  }
}