Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

[Question] Dio interceptor with retry not triggered when timeout() is invoked #442

Open
adummy832 opened this issue Jan 25, 2024 · 5 comments

Comments

@adummy832
Copy link

adummy832 commented Jan 25, 2024

Hello, @knaeckeKami, i have this very basic app that uses dio and gql.
My problem is that the interceptor for retry is not being triggered.

I just wanted to ask if you have any idea of what am i doing wrong?
Also, would greatly appreciate if you could point me into some examples
on how to properly implement this Retry? Thanks!

Please refer to the code below.

import 'dart:async';

import 'package:dio/dio.dart';
import 'package:dio_smart_retry/dio_smart_retry.dart';
import 'package:ferry/ferry.dart';
import 'package:gql_dio_link/gql_dio_link.dart';

class DioClientManager {
  late final Dio _dio;
  late final Link _dioLink;

  DioClientManager() {
    const baseUrl = 'https://randomuser.me/api/?results=20&gender=female,male';

    final options = BaseOptions(
      baseUrl: baseUrl,
      connectTimeout: const Duration(seconds: 10),
      receiveTimeout: const Duration(seconds: 10),
    );

    _dio = Dio(options);

    _dio.interceptors.add(
      RetryInterceptor(
        dio: _dio,
        logPrint: print,
        retryEvaluator: (error, attempt) {
          /// Error/exception not being triggered ???
          /// Need help !!!

          print('$error');
          print('${error.message}');

          return true;
        },
      ),
    );

    _dioLink = Link.from([
      DioLink(baseUrl, client: _dio),
    ]);
  }

  Dio get client => _dio;

  Link get link => _dioLink;
}

class FerryClient extends Client {
  FerryClient({
    required Link link,
    required Cache cache,
  }) : super(link: link, cache: cache);

  @override
  Stream<OperationResponse<TData, TVars>> request<TData, TVars>(
    OperationRequest<TData, TVars> request, [
    NextTypedLink<TData, TVars>? forward,
  ]) async* {
    /// Set a faster duration, must handled on Retry Interceptor ???
    /// Need help !!!
    
    const duration = Duration(milliseconds: 100);

    final stream = super.request(request, forward).timeout(
      duration,
      onTimeout: (EventSink<OperationResponse<TData, TVars>> sink) {
        // Return error on timeout
        final error = DioException(
          requestOptions: RequestOptions(),
          message: 'My beautiful error message',
        );

        sink.addError(error);
        sink.close();
      },
    );

    yield* stream;
  }
}

void main() {
  final dioClientMgr = DioClientManager();
  final ferryClient = FerryClient(
    link: dioClientMgr.link,
    cache: Cache(),
  );
  
  /// Set some random request object
  final req = OperationRequest(...);
  ferryClient.request(req);
}
@adummy832 adummy832 changed the title Dio interceptor with retry not triggered when timeout() is invoked [Question] Dio interceptor with retry not triggered when timeout() is invoked Jan 25, 2024
@knaeckeKami
Copy link
Collaborator

knaeckeKami commented Jan 25, 2024

you need to listen to the request to execute it

and you can use the error link for single retries: https://pub.dev/packages/gql_error_link

@adummy832
Copy link
Author

adummy832 commented Jan 25, 2024

@knaeckeKami, I updated my code by modifying the dioLink and removing the interceptor but sadly it still does not work. Am i missing something here? Really need your guidance. Thanks man.

/// dioLink
_dioLink = Link.from([
  ErrorLink(
    onException: (request, forward, exception) async* {
      /// Still does not print ???
      /// Need help !!!
      print('VERIFY_PRINT: 1');

      if (exception is DioLinkTimeoutException) {
        print('VERIFY_PRINT: 2');
        yield* forward(request);
        return;
      }

      throw exception;
    },
  ),
  DioLink(
    baseUrl,
    client: _dio,
  ),
]);

/// Request onTimeout function body
final requestOptions = RequestOptions();
final error = DioLinkTimeoutException(
  type: DioExceptionType.connectionTimeout,
  originalException: DioException.connectionTimeout(
    timeout: duration,
    requestOptions: requestOptions,
  ),
  originalStackTrace: null,
);

sink.addError(error);
sink.close();

/// Client
final req = OperationRequest(...);
final response = ferryClient.request(req).first;
/// TODO: Check response

@knaeckeKami
Copy link
Collaborator

please share a minimal reproducible example of what you're trying to do

@adummy832
Copy link
Author

Hello, @knaeckeKami I created a repository for this alongside with the configs that I have.
Hoping you could check this one out. Thanks man.

Repo: https://github.com/adummy832/sample_v2

@knaeckeKami
Copy link
Collaborator

knaeckeKami commented Jan 26, 2024

Yeah, that's not going to work. Generally, don't extend the client class.

If you add the error in a subclass of the client after the request() method, the Dio links have no chance of handling it, they already ran at this point.

If you want to test timeout, you can replace the diolink with a custom link that just throws a timeout error for every request.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants