Session: Option to save session before sending response

Created on 17 Aug 2014  路  30Comments  路  Source: expressjs/session

Looks like fix for https://github.com/expressjs/session/issues/61 (https://github.com/expressjs/session/commit/901c8e062c8789d3963ebe175a75d25d3c229245) is causing some wired race condition.
In my usecase req.session.save is calling also encrypting the data before storing. And usually it takes more little longer than non-encrypted version. Because of the async nature of session save the next request is starting most of the time before the save is complete. And this next request is fetching the old version of session.
Is there any possibility to add some flag to control the async/sync session save or any other solution?

bug enhancement

Most helpful comment

just in case you're not aware and feel like you _have_ to wait on me, there is a work-around, which is to call req.session.save yourself and respond within that callback. for example

app.post('/', function (req, res, next) {
  // do stuff
  req.session.email = email
  req.session.save(function (err) {
    if (err) return next(err)
    res.render('page')
  })
})

All 30 comments

Can you verify if you still have the problem with version 1.7.5? A change was made to mostly address that referenced commit for the case you describe.

i fear i still can see the issue, but commenting out this code https://github.com/expressjs/session/blob/5c503e7552ea48342f4ce4ce1322606eb8ee217c/index.js#L216-L229 it works fine.

Because of the async nature of session save the next request is starting most of the time before the save is complete. And this next request is fetching the old version of session.

can you explain what the "next request" is, exactly?

first page is one email input and next button. The next page needs carry over of this email and more input fields. So next request is part of multipage form submission.

ok. so by "next request is starting most of the time before the save is complete" you mean that you're able to click on the next button on that page before the session has been saved to the store, is that correct? if that is the case, the solution is a feature i have been meaning to add: an option to not send any response at all until the session has been saved to the store. this will make sure that you cannot click the button until the session is in the store.

yeah exactly the issue.
Thanks looking forward to this feature.

just in case you're not aware and feel like you _have_ to wait on me, there is a work-around, which is to call req.session.save yourself and respond within that callback. for example

app.post('/', function (req, res, next) {
  // do stuff
  req.session.email = email
  req.session.save(function (err) {
    if (err) return next(err)
    res.render('page')
  })
})

Excuse for late reply, but this is not really solving the issue. However I see session save happening twice.

However I see session save happening twice.

Oh, you have to set resave: false in your options if you want to use req.session.save, forgot about that, sorry!

As per https://github.com/expressjs/session/blob/master/index.js#L297, if resave: false, if will check isModified, and it will still try to save.

right, but req.session.save will turn isModified to false, thus the second save won't happen if you set resave: false.

I am afraid it's not working, what I am doing is wrapping res.send in express app in one middleware for all the pages and calling req.session.save inside it with resave: false

After you asked about it, I tried it and it worked just fine. I can't really help you with the work-around if I have no idea what your code looks like :) Also, it may be useful to run under DEBUG=express-session to see what is going on.

I tried DEBUG=express-session as well, i am seeing next fetch log before saved log. Let me work on creating a sample app to reproduce this issue.

@dougwilson Sorry for late reply again. here is the sample express app which highlights the issue.

If you run this app and access http://localhost:3000/ and click on post & redirect button, it's not saving the session data properly.

To highlight the issue, I have memory-store with get taking 50ms and set taking 80ms. This delay can be because of network latency or some other heavy async operation in store.

@skoranga the example does not look like it is implementing the work-around I gave you at https://github.com/expressjs/session/issues/74#issuecomment-52414094 , so of course it's not going to work. I already know it does not work :) But the issue is not easy to fix. You have to use that work-around I gave you above.

sorry forgot to add the override. updated the sample-app.

Now it's saving, but res.session.save is getting called twice as i mentioned in one of the above comment.

if you insert this console.log('--saving in express-session--'); in node_modules at https://github.com/expressjs/session/blob/master/index.js#L267, you will see:

--saving in app--
--saving in express-session--

yes. you need to set resave: false in your sample app like i said above in https://github.com/expressjs/session/issues/74#issuecomment-52718760 :)

yes. you need to set resave: false in your sample app like i said above in #74 (comment) :)

nevermind, i see you added resave: false with the update.

OK, I see what the issue is. It looks like it has to do with the fact that the session is a _new_ session when the page is loaded and it thinks the session hasn't been saved even after it is manually saved.

Actually, the double-save would happen in all situations. The double-saving is definitely a bug, which I'll get fixed, now that I see it, thank you :)

great. :)

Hi @skoranga sorry for the delay. Can you npm install express/session to get the current master version and verify that your code is no longer double-saving for me?

thanks @dougwilson. it's working as expected with the latest code. So I will still be setting resave:false and overriding req.session.save, right?

So I will still be setting resave:false and overriding req.session.save, right?

For now, yes. The resave: false thing won't change until 2.0 (which it is basically slated to be flipped from true to fase). The manually calling of req.session.save may change, yes. I wanted to reference this issue in the commit, but I need to re-open it to track the "save session before sending any of the response" request.

The resave: false thing won't change until 2.0 (which it is basically slated to be flipped from true to fase).

I take that back: if you call req.session.save it should not save again, no matter the value of resave.

Great. thanks for clarification.

thanks for the fix. :+1:

@skoranga sorry for the re-close :) I was just fixing the new code to not resave after you manually saved regardless of the resave option :) A npm install express/session now should get you the code you really want.

Yes it works fine. :)

I still have the same issue as @skoranga was having it. The res.session.save() is saving twice. I'm using "express": "4.16.2", "express-session": "1.15.6". I really need help!

I'm saving my sessions on files, I'm using the session-file-store. The problem I'm trying to solve is that when I call "res.redirect('/')", I noticed in the "./express-session/index.js", the res.end() gets ran and inside of that, the following gets ran

...
if (shouldSave(req)) {
        req.session.save(function onsave(err) {
          if (err) {
            defer(next, err);
          }

          writeend();
        });
...

And then the req.session.save runs asynchronously which then the redirection to \ starts happening and the session gets loaded BUT the req.session.save hasn't finished saving the session in the file which then causes to load the old or previous state of the session. I used the suggested workaround (see below) of calling req.session.save before the redirecting

app.post('/', function (req, res, next) {
  // do stuff
  req.session.email = email
  req.session.save(function (err) {
    if (err) return next(err)
    res.render('page')
  })
})

And it works, but, it is calling twice to the req.session.save which is a no no. I really need help.

Was this page helpful?
0 / 5 - 0 ratings