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

20 thoughts on “Problems with cart sessions and WooCommerce

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

    Like

  2. 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?

    Like

    1. 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.

      Like

  3. 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.

    Like

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

        Like

  4. 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

    Like

  5. 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?

    Like

      1. 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.

        Like

    1. 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.

      Like

  6. 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!

    Like

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

    Like

  8. 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!

    Like

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

    Like

Comments are closed.