1

I store my session in database, because there are 5 different servers that need to share it.

My experience is, that session_start reads the session from the database, and it's written back to the database, when the execution of the php file is over.

Let's say initially the session content is ['data1' => 0, 'data2' => 0] and I have two ajax files: ajax1.php, that sets data1 to 1, and ajax2.php that sets data2 to 2.

They run simultaneously like this:
ajax1.php ----------------------
ajax2.php      -------------
(So ajax2 starts running later, but finishes earlier)

In the situation I presented above, the following happens respectively:

  1. ajax1 reads session from db: data1 == 0 and data2 == 0
  2. ajax2 reads session from db: data1 == 0 and data2 == 0
  3. ajax1 sets its "local" superglobal $_SESSION: data1 = 1
  4. ajax2 sets its "local" superglobal $_SESSION: data2 = 2
  5. ajax2 writes the session to the database ['data1' => 0, 'data2' => 2]
  6. ajax1 writes the session to the database ['data1' => 1, 'data2' => 0]

This results that after they run the session contains ['data1' => 1, 'data2' => 0] when ['data1' => 1, 'data2' => 2] would be expected.

This is my problem, I need help:

  • Has someone encountered the same situation?
  • Is it "normal" that this happens, or it's my implementation which must have a problem?
  • And of course, how should I deal with this situation?
  • All other experience, thoughts, materials are welcome.

Thank you.

  • I think that you can use NFS to store your session and read it across different servers (from the same NFS). – thexpand Aug 11 '17 at 07:26
  • @CvetomirLazarov Not sure if NFS is working with file locks / session locks properly. – Daniel W. Aug 11 '17 at 07:34
  • @DanFromGermany I guess that you are right. My bad :( Here is a good read: https://stackoverflow.com/questions/12552955/why-not-storage-php-session-on-a-nfs-volume – thexpand Aug 11 '17 at 07:42

2 Answers2

0

I would suggest that you dont write the entire session with each ajax, just the data you have changed. If ajax1 is dedicated to data1, then it should never touch data2 at all, and the same for ajax2 in regards to data1.

EDIT

Refactor the logic a bit. Store in $_SESSION only the info needed to find the info in a database, in Memcached or in a Redis layer. Use the session key, or some other unique identifier you put in $_SESSION to access data you manually write in another table or storage technology. Since its session data, its not meant to survive reboots and does not need to be written to disk on each server. I would recommend memcached begause its easy to use, and really really fast.

Example: See example architecture here

Community
  • 1
  • 1
Canis
  • 4,130
  • 1
  • 23
  • 27
  • How would you implement this? PHP sessions simply `serialize()` `$_SESSION` and flush it to the storage. It does not distinct between single key value pairs in the session global. – Daniel W. Aug 11 '17 at 07:33
  • @DanFromGermany Refactor the logic a bit. Store in session only the info needed to find the info in a database, in memcached or in a Redis layer. Use the session key, or some other unique identifier that gets stored in the session DB by PHP to access data you manually write in another table or storage technology. Since its session, I would recommend memcached. – Canis Aug 11 '17 at 07:38
  • Because of 5 different servers, the stored data in memcached should be shared, no? – Steve George Aug 11 '17 at 07:48
  • @SteveGeorge Yes, the servers would all be talking to the memcached server. – Canis Aug 11 '17 at 07:50
  • @SteveGeorge See my added image – Canis Aug 11 '17 at 07:53
  • Creating a server for that purpose (or making one capable of doing that) is not that easy here.. – Steve George Aug 11 '17 at 07:54
  • @SteveGeorge if you can run memcached on the same servers you already have, you can look into syncing them or clustering them. A little phph memcached tutorial here with multiple memcached servers. https://www.digitalocean.com/community/tutorials/how-to-share-php-sessions-on-multiple-memcached-servers-on-ubuntu-14-04 – Canis Aug 11 '17 at 07:57
  • When you only save the session ID in memcache and do a second request do retreive data from a database, you don't need memcache. Just use the ID from the cookie. This only works when writing your own session handler as I suggested. – Daniel W. Aug 11 '17 at 12:44
  • @DanFromGermany The sessionid is the key to get your session data from memcached, not save the sessionid itself. Memcached is a key-valuestore. Using the id as key is trivial: `$key = session_id() . "data1"; $data1 = Memcache::get($key);` – Canis Aug 11 '17 at 12:47
  • You don't understand the problem :-D The session data gets `serialize()`d and the whole data goes into a **single** object in memcached. It does not store different keys per session. Example: `$_SESSION['a']` and `$_SESSION['b']` are stored in memcached like `phpsessions-=data:2:{a;data1;b;data2}`. – Daniel W. Aug 11 '17 at 13:18
  • @DanFromGermany I'm trying to tell you NOT to use $_SESSION to store data that needs to be synchronized across servers BECAUSE it gets serialized in full... Use the PHPSESSION_ID `session_id()` that the system generates OR another unique value like a UUID that you give the client in a cookie, as the key AND ONLY THE KEY. Then you can read and write INDIVIDUAL DATA FIELDS. – Canis Aug 11 '17 at 17:09
0

This is one of the reasons why HHVM for example is faster than PHP7 in some situations, because it does not lock session files.

Read this helpful Q/A:

PHP & Sessions: Is there any way to disable PHP session locking?

There always seems to be some kind of race condition.

It might be helpful to use session_write_close() as soon as you don't write anymore data.

The ultimate solution would be to write your own session handler and not use PHP internal session functions. Then you have the most control over sessions and locking.

Have a look at this comment on PHP.net which describes exactly your problem:

http://php.net/manual/en/function.session-set-save-handler.php#49630

You could also try to implement your MySQL session handler differently and add lock support.

Daniel W.
  • 31,164
  • 13
  • 93
  • 151
  • I'm not sure this is right, but I experience that there is no lock after I moved storing the session from file to DB. If there was, ajax2 would wait till ajax1 finishes with writing the session, wouldn't it? – Steve George Aug 11 '17 at 07:51
  • Someone experienced the very same http://php.net/manual/en/function.session-set-save-handler.php#49630. – Steve George Aug 11 '17 at 08:26
  • @SteveGeorge The link you provided is the same as I posted. Default file based session handling WILL lock the files and therefore ajax2 would wait until ajax1 finishes. This does not apply when memcached or HHVM is used and I think it doesn't apply when oracle is used. – Daniel W. Aug 11 '17 at 12:38