Problems with cart sessions and WooCommerce

I was asked about WooCommerce’s session handling at WCEU (where I seized up; darn social phobia) so I thought it would be good to give a brief history of our handling of sessions, and how things are changing in 2.1.

2811214043_12666b9efd_z

Cart sessions have been a long standing source of frustration in WooCommerce. To clarify, the session is the part which tracks a particular user’s cart object – without this the user wouldn’t be able to use the cart nor checkout.

The most obvious solution would be to use PHP’s built in sessions and session_start(). This was the first thing we used (obviously) and you’d expect this to work perfectly fine…but it doesn’t for the following reasons.

Support and host issues

SESSION related support tickets were too frequent e.g. errors like “No such file or directory”, mainly caused by (bad?) hosting providers not having them configured correctly, or not supporting them at all. Albeit easy to diagnose, most users were reluctant to ask there hosts, and most surprisingly some hosts even tried to blame our platform. Frustrating for all those involved.

WordPress itself is stateless

WordPress core is stateless – it acts the same regardless of which user is using it. It can tell if you are logged in and show you correct pages and screens but thats about it. There is no built in handling for sessions.

Problems with caching

Page cache added by plugins, and by hosts such as WPEngine, can break sessions. Well, not break, just show a page which is cached and as a result doesn’t reflect the current users cart.

When a cart session is present some data needs to be un-cached, as do some pages such as cart, checkout and account.

Problems with load balancers

WPEngine sum this issue up nicely:

For our customers who are set up on clusters, we would have to completely change how our load balancers work, just to make sure that $_SESSION variables were available between different servers.

Load balancers need to be configured to share session data across their servers if used.

WP_SESSION to the rescue?

Eric Mann proposed a system in which a cookie tracks your session, and the session data is stored in transients; http://eamann.com/tech/introducing-wp_session/.

On the back end, the object stores its data in WordPress transients – one transient per user – each with a unique ID provided by WordPress’ PasswordHash object to ensure uniqueness. If you’ve got a caching plugin installed that uses memcached, then transients (like options) can be cached in memory, making the system very performant.

To summarise how it works:

  1. On page load, a session ID is created for a user (if it doesn’t exist) and is stored in a cookie.
  2. Functions use WP_SESSION to store data, instead of $_SESSION.
  3. On shutdown (a WordPress hook, called last) data is written to a transient.

This works well, and we used something similar/adapted in WooCommerce 2.0. Pippin Williamson also used it in Easy Digital Downloads. Our solution was tweaked to use the option table instead of transients, because transients are not persistent – we don’t want cart data disappearing!

Varnish caching issues

WordPress targeted hosting providers such as WPEngine tend to use Varnish caching to improve site loading times. These need to be configured to “ignore” WooCommerce’s cookies to prevent caching when users have carts, otherwise users can potentially see each others carts and the system goes to pot.

Although some of this can be handled by the host themselves, there are still issues. You can read about some of the issues faced and suggestions in this Github Issue, the key ones being the names of our cookies needing special handling, and the existence of the ‘session’ for all visitors preventing caching completely.

We’ve worked on this in WooCommerce 2.1 though!

Testing 2.1

In 2.1 we’ve made two key changes to our session handling:

  1. We’ve renamed the cookie used to track customer ID so that it includes _wp_ in the name. This should let plugins like batcache exclude this cookie automatically (WordPress core cookies have this naming structure).
  2. We’ve tweaked the session and cart handlers to only create a session if the user has a cart. If the user doesn’t have a cart, the site remains stateless. Caching is allowed.

So now is the time to test this out 🙂 If you use a host with caching please give us your thoughts, and I hope this post gives you all an understanding to why we’ve gone in this direction.

Photo by Arthur Pewty, CC-BY-2.0


Posted

in

by

Comments

20 responses to “Problems with cart sessions and WooCommerce”

  1. Mike Van Winkle avatar
    Mike Van Winkle

    Nice explanation, Mike. And thanks for being so willing to adapt the plugin for the needs of high-performance hosting.

  2. Josh Eaton avatar
    Josh Eaton

    Mike,

    Thanks for the informative post. Does this affect how a WooCommerce extension should handle sessions unrelated to carts?

    As in, if I was writing an extension that needed to store some session data but needed to do it prior to a user adding something to their cart (on the product page), how should I handle that going forward with 2.1?

    1. mikejolley avatar
      mikejolley

      If you need to store data about a user before they have a cart (baring in mind WC doesn’t) you would need to ensure the customer session was started yourself, via your own code.

      Thats necessary unfortunately – and by doing so you will probably stop caching on pages.

      1. mrdonthave avatar
        mrdonthave

        could you please explain in short how i can start the WC session on a article page / before the user has a cart?

  3. Ryan Doherty avatar

    So when will the next version of WooCommerce be released? I just spent hours debugging why my WooCommerce site was so slow only to find out it had filled up the options table with > 5k sessions. Deleting them sped up my site a lot.

    1. Whereskarlo avatar

      How did you delete them? Was it easy? It would be nice to try that too.

      1. Ryan avatar
        Ryan

        Under WooCommerce => System Settings => Tools there is a button labeled ‘Clear all sessions’. There’s also ‘Clear transients’

  4. Naomi Niles avatar

    Hi Mike,

    Thanks for this information! We recently upgraded to 2.1, but now sometimes things are adding to cart and other times not. We can’t figure out why. Is this likely a sessions issue still?

    Thank you! 🙂
    Naomi

    1. mikejolley avatar
      mikejolley

      Could be anything – most likely outdated template files and unrelated to sessions.

  5. Jeremy Pry avatar
    Jeremy Pry

    Mike,

    “Our solution was tweaked to use the option table instead of transients, because transients are not persistent – we don’t want cart data disappearing!”

    The “transients are not persistent” portion above actually isn’t true. The WordPress Transients API will utilize the wp_options table to store transients as long as there is no persistent object cache. However, if there IS a persistent object cache, then that will be used instead.

    Wouldn’t it be better to use the native Transients API instead of automatically writing to the wp_options table, regardless of whether there is persistent object caching or not?

    1. mikejolley avatar
      mikejolley

      Have a read here http://journal.ryanmccue.info/296/youre-using-transients-wrong/ They aren’t suitable for persistent data and cannot be relied upon.

      1. Jeremy Pry avatar
        Jeremy Pry

        I can understand the billing lock issue, and I agree that you shouldn’t use transients just to make sure that no one is billed twice. However, for cart data, transients should work perfectly. If someone is actively browsing around the site there’s a low likelihood that their cached cart will be seen as stale and deleted.

      2. Tree avatar
        Tree

        While I’m not sure that transients is the solution, I have to question the use of the options table too. When you get large numbers of sessions the options table chokes with the other WC transient CRUD operations. I just put a question up on Stack Overflow about this giving me problems after just three days of being live: https://stackoverflow.com/questions/23353347/woocommerce-generating-more-sessions-than-users.

    2. Donovan Hernandez avatar
      Donovan Hernandez

      I’d prefer WooCommerce use the Transients API as well. As it stands, all the “_wc_session_” objects fill the options table, and other plugins that scan the table for autoloads, have to look through 50k+ rows. Having these objects sit in memcached should be more efficient.

    3. Ross McKay avatar

      (late reply, but…) you cannot rely on transients to stick around for long enough, especially with an object cache. If there’s something else pushing things into the object cache and it needs to find some space, those transients (cart contents!) are lost. WP Engine tells the story nicely: http://wpengine.com/2013/02/21/wordpress-transient-api/

  6. Matt Mizwicki avatar
    Matt Mizwicki

    Hi Mike,
    I’ve looked high and low for this information. Thank you so much.

    One thing I’d like to clarify. Will items stay in a users cart indefinitely until either a) the user clears cookies, or b) a WordPress admin clears all sessions?

    So could a user with no account and not logged in, technically come back to a cart six months later and still see the items they had added? (Assuming they didn’t clear cookies and all sessions weren’t cleared.)

    Thanks again!

  7. murdrae avatar
    murdrae

    Anyone else still running into issues w/ cart sessions and varnish? I cannot add item to cart while logged in as admin or user.

  8. Umesh avatar
    Umesh

    Hi, Is that caching problem on wp-engine fixed in woocommerce 2.1?

  9. alexstanhope avatar
    alexstanhope

    Hi Mike,

    Thanks for an interesting post!

    On the subject of WooCommerce carts, you seem like a chap who might know a thing or two about these 😉

    I did have a question for you on updating stock levels for products when they are added to the cart (and preferably “releasing” them back to stock if they are removed from the cart prior to checkout) – how easy is it to achieve something like this, and what would the best steps be?

    I can see there are a few functions/filters that could be called in to play like woocommerce_stock_amount, wc_update_product_stock and add_to_cart_class and so forth, but I’d love any guidance you could spare on how to bring it all together 🙂

    Thanks in advance for any help with this!

  10. shady avatar
    shady

    I am having the same problem. It always shows that your cart is currently emply. Please help regarding this

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.