Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase cloud function returns 304 error and then restarts

I've been stuck on this for months. I have removed some minor details from the function but nothing major. I have this https cloud function that ends a session and then uses endTime and startTime to calculate bill which is then returned to the client.

startTime is retrived from the realtime firebase database (which the session starter function put there).

My code snippet:

exports.endSession = functions.https.onRequest(async (req, res) => {
    console.log("endSession() called.")
    if(req.method == 'GET'){
        bid = req.query.bid
        session_cost = req.query.sessioncost
    }else{
        bid = req.body.bid
        session_cost = req.body.sessioncost
    }

start_time_ref = admin.database().ref("/online_sessions/").child(bid).child("start_time")
start_time_snapshot = await start_time_ref.once('value')

console.log("start_time_snapshot: "+start_time_snapshot.val())

start_time_snapshot = moment(start_time_snapshot.val(), 'dddd MMMM Do YYYY HH:mm:ss Z');
endDateTime = getDateTime()

console.log("startTime: " + start_time_snapshot.toString())
console.log("endTime: " + endDateTime.toString())
hour_difference = getHourDifference(start_time_snapshot, endDateTime)

bill = ride_cost * Math.ceil(hour_difference)
console.log("bill: "+bill)

var s_phone
sSessionlinks_ref = admin.database().ref('/sSessionlinks/')
sSessionlinks_snapshot = await sSessionlinks_ref.once('value')

sSessionlinks_snapshot.forEach((sid)=>{
    if(sid.val() == bid){
        s_phone = sid.key
    }
})

s_fcm_token_ref = admin.database().ref("/s/").child(s_phone).child("FCM")
s_fcm_token_snapshot = await s_fcm_token_ref.once('value')

try{ // telling another client that session has ended.
    await admin.messaging().send({
        data: {
            type: "sessionCompleted",
            bill: bill.toString()
        },
        token: s_fcm_token_snapshot.val() 
    })
}catch(error){

}

//deleting this session from online sessions
online_session_ref = admin.database().ref('/online_sessions/').child(bid)
await online_session_ref.remove()

//puting this session as available
available_session_ref = admin.database().ref('/available_sessions/')
json = {}
json[bid] = s_phone
await available_session_ref.update(json) // session made available


res.status(200).send(bill.toString())
// here it *sometimes* returns 304 and then restarts but since i've already removed online_session_ref I cannot get startTime again because its removed with online_sessions so it fails.
    // return
})

When its first called. It does all the calculations correctly but responds with a 304. So (I think the client) resends the request and the function is called again but since session is destroyed so it cannot calculate startTime.

Why is it that when its first called, even though all the calculations happen correctly it returns a 304 and not a 200? This problem doesn't happen all the time. It usually happens when this function is called after a long time but I'm not certain with that. I don't know what causes this.

Helper functions I've used:

function getHourDifference(s, e){
    return moment.duration(e.diff(s)).asHours()
}


function getDateTime(){
    d = moment.utc().utcOffset('+0530')
    return d
}

When function end first time the text payload is Function execution took 794 ms, finished with status code 304

When it runs the second time (where it cannot get startTime cause its been removed in first run. There shouldn't be a second run in the first place.), the payload text is Function execution took 234 ms, finished with status code 200 (its 200 but return NaN becuase it cannot do calculation without startTime.

EDIT: As some of you asked me to tell how the the function is being called:

Its being called from and android app using Volley. The parameters are assured to not be null. The code segment to call that function is:

    // Setting the button's listeners.
    endSessionButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

                progressDialog = new SweetAlertDialog(getContext(), SweetAlertDialog.PROGRESS_TYPE);
                progressDialog.getProgressHelper().setBarColor(Color.parseColor("#A5DC86"));
                progressDialog.setTitleText("Ending session...");

                AlertDialog endDialog = new AlertDialog.Builder(getContext()).create();
                endDialog.setTitle("End Session?");

                Log.e("sessioncost", String.valueOf(session_cost));

                endDialog.setButton(Dialog.BUTTON_POSITIVE, "Yes", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        progressDialog.show();
                        // Instantiate the RequestQueue.
                        RequestQueue queue = Volley.newRequestQueue(getContext());

                        String url = "https://us-central1-something-something.cloudfunctions.net/endSession?bid=" + bid + "&sessioncost=" + session_cost;
                        Log.e("end sesion button", url);
                        // Request a string response from the provided URL.
                        StringRequest endSessionRequest = new StringRequest(Request.Method.GET, url,
                                new Response.Listener<String>() {
                                    @Override
                                    public void onResponse(final String response) {
                                        progressDialog.dismiss();
                                        Toast.makeText(getContext(), "Session Completed", Toast.LENGTH_LONG).show();

                                        progressDialog = new SweetAlertDialog(getContext(), SweetAlertDialog.SUCCESS_TYPE);
                                        progressDialog.getProgressHelper().setBarColor(Color.parseColor("#A5DC86"));
                                        progressDialog.setTitleText("Session Completed: Bill");
                                        progressDialog.setContentText("Please pay ?" + response + " to s.");
                                        progressDialog.setCancelable(false);
                                        progressDialog.show();

                                        changeState('1');

                                        bill_in_paise = Float.parseFloat(response) * 100;
                                        Log.e("bill", bill_in_paise.toString());
                                        progressDialog.setConfirmClickListener(new SweetAlertDialog.OnSweetClickListener() {
                                            @Override
                                            public void onClick(SweetAlertDialog sweetAlertDialog) {

                                                sweetAlertDialog.dismiss();
                                                Intent intent = new Intent(getContext(), PaymentActivity.class);
                                                intent.putExtra("amt", bill_in_paise.toString());
                                                startActivityForResult(intent, REQUEST_CODE);
                                            }
                                        });


                                    }
                                }, new Response.ErrorListener() {
                            @Override
                            public void onErrorResponse(VolleyError error) {
                                Toast.makeText(getContext(), error.toString(), Toast.LENGTH_LONG).show();
                            }// onErrorResnponse - END
                        });

                        // Add the request to the RequestQueue. Cuz volley is asyc af.
                        queue.add(endSessionRequest);
                        // VOLLEY REQUEST - END
                    }
                });
                endDialog.setButton(Dialog.BUTTON_NEGATIVE, "No", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Toast.makeText(getContext(), "Session not cancelled.  " + which, Toast.LENGTH_LONG).show();
                    }
                });

                endDialog.show();


            }

        }
    }); // endSessionButton onclick - end

UPDATE: @tuledev helped fix the 304 with a work around but the problem is still here. Even when the status code is 200 the cloud function is somehow called again and I get a NaN bill. At this point I don't know what's causing this.

like image 796
ParmuTownley Avatar asked Mar 08 '19 18:03

ParmuTownley


1 Answers

The 304 status comes because the response is the same as the previous. Firebase cloud responses 304 and the client will get the cached data.

For preventing 304 status, we can return the value + uid of the bill, or something make the response diff from the previous.

As OP and I discussed, the 304 is solved but the problem's still here. So I think the problem comes from the client side.

Hope somebody can HELP him.

Edit: OP here, I changed the client code to using okhttp instead of volley and after testing for 2 days everything seems fine. Tuledev's answer did fix 304 but even after 200 the problem persisted. Just use okhttp instead of volley.

like image 75
tuledev Avatar answered Oct 22 '22 13:10

tuledev