Jump to content

Weekly update – 19 May 2023


ryan
 Share

Recommended Posts

This week I've done some work on the core $cache API (WireCache) to make it able to support other cache storage options. To do that, I had to move all the storage code out of the WireCache class and into a separate class that implements the new WireCacheInterface. So it was kind of a major refactor. Now the WireCache class is independent of storage method, which will make it more flexible in the long term. The first class that implements the new interface is the new WireCacheDatabase. This contains the cache database-storage code that was previously in the WireCache class. But the plan is to also add a WireCacheFilesystem (in progress), and make it possible for others to develop WireCache modules, perhaps for Redis, Memcache, etc. I've been wanting to do this because in some cases I've noticed significantly better read performance from file-based caches. (Though admittedly, at the expense of write performance.) But it made me think it would just be better to have more cache storage options, and also be nice to take advantage of even better cache options available in different environments, like in the AWS environment we run this site on. 

One of the issues with changing the cache used by the system is that the $modules API (Modules class) depends on WireCache for quite a few things. And the modules basically can't load without the relevant caches being available. At present, $cache has to load before $modules, which makes the whole idea of WireCache-modules a bit of a chicken-or-egg first situation. So I've been working to decouple $modules from WireCache, or at least make it able to function if its cache isn't available on occasion. I made some good progress there, but found that there was a little bit of a performance hit in doing so, so I reverted those changes and put them behind a toggle in the Modules class to experiment with further. But while doing that, I found some other ways to improve the performance of the modules loader. So you'll find the dev branch boots a little faster this week. Maybe not noticeably so (since PW already boots fast), but measurably so. I'm always looking for opportunities to improve performance — even small performance improvements amount to large savings over time.

While on the topic of caches, I've also added an experimental $pages->loader()->findCache($selector) method which works exactly like $pages->find($selector) method, except that it caches the page IDs that were found for a period of time that you specify in the 2nd argument (default is 60 seconds). I imagine this method would be useful for complicated or slow page finding operations that don't need to restart from scratch on every request. This is an alternative to markup caching for greater control. But since it caches the result of the find operation (page IDs), and not the actual pages, it has a different set of benefits (and drawbacks) relative to markup caches. I'm still experimenting with this method to get more feedback and make sure it's worthwhile, so far it appears to be. This will likely become accessible at $pages->findCache() once out of the experimental stage. That's all for this week. Thanks for reading and have a great weekend! 

  • Like 24
Link to comment
Share on other sites

Great stuff, @ryan!

10 hours ago, ryan said:

I made some good progress there, but found that there was a little bit of a performance hit in doing so, so I reverted those changes and put them behind a toggle in the Modules class to experiment with further. But while doing that, I found some other ways to improve the performance of the modules loader. So you'll find the dev branch boots a little faster this week. Maybe not noticeably so (since PW already boots fast), but measurably so. I'm always looking for opportunities to improve performance — even small performance improvements amount to large savings over time.

Would you mind sharing how you measure those performance related things and maybe more importantly how you classify these results. I mean ... I guess you use ProfilerPro for the measuring, but what if, let's say a module that I'm developing adds 3ms penalty due to whatever. Is that what you'd call "a little bit of a performance hit" or would that mean 10ms or maybe 100?

  • Like 1
Link to comment
Share on other sites

Those are awesome news for us! We've had a long standing problem with moving ProcessWire installations from one server to another concerning the caches table. It would be great to completely decouple the modules cache from the user generated cache. Maybe move the modules cache to a separate db table. It seems intuitive (and most administrators seem to take this as granted) that you can simply purge the caches table to reduce the database dump size. But it is not like that ATM.

Just had this problem yesterday with an experienced ops engineer, who doesn't have any PW experience... so your are magically right on time @ryan, as multiple times before!

  • Like 7
Link to comment
Share on other sites

Just wanted to say that this is indeed a very nice update!

For an ongoing project I'm relying heavily on WireCache for caching due to the nature of the site: most users are logged in and there is a ton of traffic at very specific times. Keen to implement any performance enhancements, and Redis has been on the top of my list. Definitely interested in using (or developing, if need be) a Redis WireCache module.

Sure, we have CacheRedis already, but a drop-in solution would be even better.

(... not to mention that core support for drop-in caching modules is one instance where WP has been ahead of PW. Not that it matters all that much, but still happy to be able to tick that box ?)

  • Like 7
Link to comment
Share on other sites

@bernhard

Quote

Would you mind sharing how you measure those performance related things and maybe more importantly how you classify these results. I mean ... I guess you use ProfilerPro for the measuring, but what if, let's say a module that I'm developing adds 3ms penalty due to whatever. Is that what you'd call "a little bit of a performance hit" or would that mean 10ms or maybe 100?

Usually ProfilerPro would be great for this, but in some cases I can't use it because ProfilerPro is a module and I'm timing something that happens prior to modules loading. When PW is in debug mode, it times all of the boot processes, so I can just look at those. I also add other Debug::timer() calls as needed to time specific things. When testing in the admin, you can finish a timer with Debug::saveTimer($timer); and it'll show the result in the Debug > Timers section of the admin. But you can't look at any one instance of a timer. You've got to take several samples before you really know if there's a trend in the numbers. I'm usually looking for consistently 10 ms or more when it comes to improvements.

@Ivan Gretsky

Quote

Those are awesome news for us! We've had a long standing problem with moving ProcessWire installations from one server to another concerning the caches table. It would be great to completely decouple the modules cache from the user generated cache. Maybe move the modules cache to a separate db table. It seems intuitive (and most administrators seem to take this as granted) that you can simply purge the caches table to reduce the database dump size. But it is not like that ATM.

The cache supports an expiration flag represented by the WireCache::expireReserved constant. It means that rows with that flag are reserved and should never be deleted. If you use the $cache API to clear the cache, it'll do it in a safe way. But if you just omit the caches table from a DB dump or manually delete all the rows in the caches table, then it would be problematic. I agree it would be better not to have to consider this. I'm not sure we need to keep that flag other than for modules, so this is one reason why I'm looking to have the modules use some other storage method for its cache. Though if new caching options came along (Redis, etc.) it would sure be nice for modules to be able to utilize that caching method too, so there are tradeoffs. Ideally, the modules would still use WireCache, but be able to recover easily if its caches get deleted, so that's probably what I'm going to work towards, but it's not as easy as it sounds.

@teppo

Quote

Keen to implement any performance enhancements, and Redis has been on the top of my list. Definitely interested in using (or developing, if need be) a Redis WireCache module.

Sounds great! I have no experience with Redis but this server seems to have it and I've also been curious about it. I really like the idea of dedicated cache systems independent of the DB or file system. I'd definitely like for PW to be able to support them, and am glad to hear you are interested in it too. 

  • Like 6
Link to comment
Share on other sites

Sounds good. Ive found myself implementing a cache in front of $cache that stores stuff in ram (with db persistence too) for data you may need a few times in one request.

I realized there was no way to replace $cache with my own extended class, so I just do this in another module. I could probably use a hook as alternative.

  • Like 1
Link to comment
Share on other sites

On 5/22/2023 at 10:02 AM, ryan said:

When testing in the admin, you can finish a timer with Debug::saveTimer($timer); and it'll show the result in the Debug > Timers section of the admin.

I'm both embarrassed to say that I didn't even realize this was here (despite it being front-and-center in the UI) and elated to discover it today thanks to this sentence. ♥ PW has so many (amazing) things included, it's hard to keep track. Even though you've built it all, I'm surprised you're able to keep track of them all too!!

  • Like 4
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...