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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With