Stand With Ukraine. Stop Putin. Stop War.

When did you last check the size of your modx_session database table? Was it huge, like gigabytes huge? If so, you're not alone. 

To understand the problem, you need a little bit of background.

How do sessions work in PHP?

Typically, the standard PHP session handler will create a session when requested, and store it as a simple file somewhere on the system. The path it writes session to is configured with the session.save_path configuration in the php.ini, and can point anywhere. When that option is empty, it writes to the temp directory on the server. 

Creating and loading sessions is simple enough, but the next thing the session handler does is clean up sessions. This is called garbage control, or gc. This removes sessions beyond their expiration time, to make sure it doesn't keep growing indefinitely and takes up your vital disk space. 

Garbage control doesn't have to run on every request. If your session/cookie life time is configured to a week and you're not too picky about the exact timing they are removed, then sessions only really need to be checked once a day. Cleaning up sessions can take a little time and resources, so PHP is by default configured to only do that once every 100 requests.  

How do sessions work in MODX?

MODX registers a custom session handler that takes care of storing, retrieving, and cleaning up sessions. It writes this to one of its own database tables (modx_session), rather than files. This allows MODX a little more control over the flow of sessions. 

It is also possible to instruct MODX to use standard PHP sessions, and there's also an extra available to write sessions to Redis. But the vast majority of sites will simply be using the default, writing to the database.

So, why does MODX not clean up its session table?

MODX awaits the signal from PHP that it's time to clean up sessions. This relies on 2 configuration options in PHP:

  • session.gc_probability
  • session.gc_divisor

You can find the official documentation for those options here.

Usually the probability is set to 1, and divisor to a value like 100 or 1000. That means that in approximately 1/100 requests, the garbage control will run. 

When MODX does not seem to be cleaning up its table, it's usually because of an attempt to improve the performance of the garbage collection, by bypassing PHP and off-loading it to a cron job that runs outside of the request/response cycle. 

Those environments assume PHP writes its sessions to a standard location in the filesystem, and clean up that directory based on the timestamp on the file. The session.gc_probability option is then set to 0, to tell PHP to never run its own session garbage collection.

That works great - if your sessions are written to the standard location. Which MODX doesn't. 

How common is this?

Based on data from SiteDash, which checks the size and status of your session table automatically, it's pretty common indeed. Out of a sample of 1727 sites, 27% seem to be affected by this.

How can I fix this?

Re-enable session.gc_probability. Set it to 1, and make sure session.gc_divisor is also set properly for your traffic.

Depending on your host and if you have access to a server control panel, you may be able of changing it yourself. In other cases, contact your host and ask them how it should be changed. 

Anton Tarasov

Hi Mark,

Great article, this is what I've encountered the other day and your post is very timely.I hope you don't mind, I translated your article and posted on Russian MODX community website - here: https://modx.pro/development/19863

Thanks!

Michael Snow

OK, so what about when session.gc_probability is on, session.gc_divisor is set properly (1000 in my case), and the sessions table STILL isn't getting cleaned up? What could be happening then?

Mark Hamstra

One other person reached out to me saying they were still seeing a large table despite the session gc settings being (apparently) properly configured. They were going to check in with the host but never reported back any findings.

What's important to reiterate is that it will run the garbage collection rarely if a site doesn't get much traffic. Some of my MODX Cloud sites are flagging this in SiteDash, even though they are properly configured (1 in 1000), simply because they don't get 1000 hits often enough.

If anonymous sessions are disabled, it may also be possible the gc only runs once every 1000 requests when a non-anonymous user hits up the site as no session is initialised. Or you may have similar issues if there's a caching layer in front of MODX (e.g. varnish, cloudflare, etc); only requests that end up hitting MODX/PHP have a chance of running gc.

Michael Snow

OK, that makes sense. One question, though. The MODX settings have "session_cookie_lifetime" and "session_gc_maxlifetime". One of my sites had 27000 rows in the session table, but I noticed the oldest one was only 7 days. So, I changed the "session_gc_maxlifetime" to 1 day instead. Now there are only 2,000 rows in the table. That's good, but if session_gc_maxlifetime is less than session_cookie_lifetime, are session cookies still going to last the longer value? "session_cookie_lifetime" affects the amount of time a user's "remember be" login lasts, and I want that to be a much longer time than the garbage collection time. When someone logs in, does the cookie set in the user's browser retain all the settings, or is it just a reference to the session entry in the session table, which will no longer exist after 1 day, the way I have that set now?

Comments are closed :(

While I would prefer to keep comments open indefinitely, the amount of spam that old articles attract is becoming a strain to keep up with and I can't always answer questions about ancient blog postings. If you have valuable feedback or important questions, please feel free to get in touch.