11

This is my network request sample

networkAPI.postData(myData).enqueue(new Callback<MyResponse>() {
    @Override
    public void onResponse(Call<MyResponse> call, Response<MyResponse> response) {
         if (response.code() == 201) {
             // success
         }
    }
    @Override
    public void onFailure(Call<MyResponse> call, Throwable t) {

    }
});

It is the request I should call only once as I've one time usable voucher code. If I turn off my network connection immediately or if it disconnects instantly after calling this method, then it moves to onFailure method with IOException in throwable which generally refers connection error, But the point is data posted to server and voucher code has been used and the problem is onFailure method was called here.

I am sure that it is not a parsing failure hence I tried using Void too. Why onResponse is not called even after posting the data. How to overcome such situation?

Reaz Murshed
  • 23,691
  • 13
  • 78
  • 98
Shree Krishna
  • 8,474
  • 6
  • 40
  • 68
  • I understand you have a one time voucher code, but surely there should be a way to get the why the voucher if the network fails. Is there not? – Dracarys Jul 15 '18 at 05:51
  • @I_of_T Actually I didn't get you properly but the problem is voucher code posted and onFailure called here as I mentioned above. I could not be sure if data posted or not in this kind of situation. The API calls are already finalized and I can't request anything to be handled from server side. – Shree Krishna Jul 15 '18 at 06:00
  • you are obviously having some sort of connection error, I can't help you without seeing your actually network call. Does the call work on postman? – Dracarys Jul 15 '18 at 06:32
  • @I_of_T In fact everything works fine either in postman or in my request call. But the QA reported that if he turns off wifi or connection immediately after the network request starts and before it's completion. Then we run into onfailure but found that the voucher code is posted. I am currently not into the problem, I am in the situation where such problem might be generated in client's devices and I need to fix it. Retrofit is used in the entire project so I can't replace it though with some kind of native call. I hope you're getting me. – Shree Krishna Jul 15 '18 at 06:43
  • 1
    This should be managed on the server side. – Dracarys Jul 15 '18 at 08:13
  • 1
    Sounds like the writing to the DB is not atomic. It is not your problem. – loshkin Jul 15 '18 at 10:55
  • Also, that is some intense QA. I'm impressed someone actually tested that. – Sal Jul 15 '18 at 10:59
  • i'd like to make a suggestion, send your data but also add a listener. it should be listening to the network, if the network is disconnected follow up a call to the db that sends an onNetworkIssue packet. on the server side if the onNetworkIssue packet is detected then send a message to client to not post data.packet racing is interesting. – EvOlaNdLuPiZ Jul 17 '18 at 19:21
  • I completely agree with Dracarys and toshkinl. There is not much we can do on this from client side. Check [this](https://stackoverflow.com/questions/51317786/onfailure-called-and-ioexception-thrown-but-data-posted/51462173#51462173) answer – Bertram Gilfoyle Jul 22 '18 at 11:00

5 Answers5

5

I don't know what your backend is but in C# WebApi, on the server side, you can check if the client is still connected via Response.IsClientConnected but this flag is only for graceful tcp disconnects i believe. It won't work if the client shuts off his internet suddenly. This is simply due to the nature of http (and underlying tcp)

Also, I think it's worth mentioning that there's always the possibility of the request reaching the server but the response not reaching the client. There's simply no way to guarantee that the server wont process the voucher without the successful response to the client. The client could send the request and as soon as you're ready to return status code 200, the client may disconnect. This should always be considered in web service design.

Best thing to do is to give the client some way of viewing past redeemed vouchers. Or on a page fresh or something you can automatically check if there was a past voucher redemption attempt (via another webservice method) and display a response appropriately.

Sal
  • 5,129
  • 5
  • 27
  • 53
2

I think the retrofit methods are working as expected. This might happen that you have posted some data to your server and even if the internet is connected, your server application is taking too much time to process the request and once the server responded back with a success status, the application side request is timed out. The point is there are several such cases, in which you might face the same problem.

I can think of a graceful solution to this specific problem by building a server-client handshaking about using a voucher. You might consider having a local database setup (it can be any storing mechanism which can be handled locally) tracking the voucher codes that you have tried to use so far. In that case, when a request is initiated, a flag will be generated locally and on getting a successful response from server side the flag is cleared.

Now, what are the advantages of using this special flag indicating if the voucher is used or not? The main advantage is that you can sync the status of availing the voucher later on receiving a broadcast when your internet is connected again. You might consider checking the flag also in application start-up to sync between server and client in case of any prior flag is opened.

The key idea of my answer is to build a server-client handshaking mechanism in which you can confirm if the voucher is availed or not.

Reaz Murshed
  • 23,691
  • 13
  • 78
  • 98
1

My random guess is that your response has changed dynamically(may be even null) in your specific error scenario. Since you are expecting an object of type MyResponse, when a dynamically changed response is returned, your retrofit call may not be able to handle the response and hence jumps to the onFailure method. In this case your data gets posted to your backend whereas retrofit gets into onFailure

Possible Solution

First try to find out the response in case where the particular error occurs and try to make a model for it(use void for null)

Try dynamically parsing json by making use of deserialiser to handle the dynamic error response and proper response at run time. Hence by using a deserialiser, you can make your error response(case where data gets posted) also appear in onResponse

Navneet Krishna
  • 5,009
  • 5
  • 25
  • 44
1

According to the question i believe you are posting the data correctly with network and when the server tries to give you the response, your system loses connection due to network failure and retrofit gives a failure callback.

Solution

You can call a retry api dialog when the execution reaches onfailure due to network failure and ask the user to send the api again and get the response correctly. So by until and unless the user is given the appropriate response, he will be blocked to move further.

fightingCoder
  • 594
  • 3
  • 7
0

It is rather a back-end problem IMO. Such tasks should be done as a transaction and should be committed only at the very end.

If you need exact result, you have shutdown functions and ignore user abort (in PHP. I believe there will be similar things in other platforms) to do commit action from. I prefer the former.


Here is an example pseudo code for PHP.

/**
Define a global variable to identify whether the execution completed 
successfully or an error (such as client disconnection) occurred 
*/
$isProgramExecutionSuccessful = FALSE;

/**
shutdown function to execute finally
*/
function onShutdown() {
    $error = error_get_last();
    if ( $isProgramExecutionSuccessful == TRUE ) {
        // commit transactions here
    }
}

/**
Register shutdown function
*/
register_shutdown_function('onShutdown');

//Do database operations as transactions here

/**
At the end flag execution as successful
*/
$isProgramExecutionSuccessful = TRUE;
Bertram Gilfoyle
  • 9,899
  • 6
  • 42
  • 67