Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nocache: Memory usage gets bigger and bigger #7039

Closed
j3ll3yfi5h opened this issue Nov 11, 2022 · 37 comments · Fixed by #8956
Closed

nocache: Memory usage gets bigger and bigger #7039

j3ll3yfi5h opened this issue Nov 11, 2022 · 37 comments · Fixed by #8956

Comments

@j3ll3yfi5h
Copy link
Contributor

j3ll3yfi5h commented Nov 11, 2022

Bug description

Using logged_in condition inside nocache tags causes memory usage to get bigger and bigger

nocache tags files (storage/framework/views/nocache) are created on every page request and get never cleaned.

How to reproduce

Add a collection with entries.

{{ collection from="your_collection" }}
	{{ title|wrap:h2 }}
	{{ nocache }}
		{{ if logged_in }}Logged in{{ else }}Logged out{{ /if }}
	{{ /nocache }}
{{ /collection }}

Bildschirm­foto 2022-11-11 um 16 54 10
Bildschirm­foto 2022-11-11 um 16 54 20
Bildschirm­foto 2022-11-11 um 16 54 30
Bildschirm­foto 2022-11-11 um 16 54 46
Bildschirm­foto 2022-11-11 um 16 54 57

Logs

No response

Environment

Environment
Application Name: Statamic
Laravel Version: 9.39.0
PHP Version: 8.0.16

Installation

Fresh statamic/statamic site via CLI

Antlers Parser

runtime (new)

Additional details

No response

@j3ll3yfi5h j3ll3yfi5h changed the title Using if logged_in wrapped with nocache condition: Memory usage gets bigger and bigger Using if logged_in wrapped with nocache: Memory usage gets bigger and bigger Nov 11, 2022
@j3ll3yfi5h
Copy link
Contributor Author

Hm. This seems to be a general issue with all nocache tags, the storage/framework/views/nocache gets bigger and bigger.

@j3ll3yfi5h j3ll3yfi5h changed the title Using if logged_in wrapped with nocache: Memory usage gets bigger and bigger nocache: Memory usage gets bigger and bigger Nov 11, 2022
@mikemartin
Copy link

mikemartin commented Nov 13, 2022

We had 2 site outages over the weekend after introducing the {{nocache}} tag on a half static cached site.

Environment
Application Name: ShiftCare
Laravel Version: 9.39.0
PHP Version: 8.0.5
Composer Version: 2.4.4
Environment: production
Debug Mode: OFF
URL: shiftcare.com
Maintenance Mode: OFF

Cache
Config: CACHED
Events: NOT CACHED
Routes: CACHED
Views: CACHED

Drivers
Broadcasting: pusher
Cache: statamic
Database: mysql
Logs: stack / single
Mail: smtp
Queue: sync
Session: file

Statamic
Addons: 9
Antlers: runtime
Version: 3.3.54 PRO

Statamic Addons
aerni/advanced-seo: 1.3.3
doublethreedigital/duplicator: 2.3.3
mattrothenberg/statamic-mapbox-address: 0.7.2
mikemartin/helpscout-beacon: 1.0.2
reachweb/multisite-propagator: 1.0.0
rias/statamic-color-swatches: 2.1.7
rias/statamic-redirect: 2.5.0
spatie/statamic-responsive-images: 2.14.4
webographen/statamic-widget-continue-editing: 1.0.1

@arthurperton
Copy link
Contributor

Do you see any errors in the log? Like Serialization of 'Closure' is not allowed or anything else?

@j3ll3yfi5h
Copy link
Contributor Author

@arthurperton after about 10 refreshes it says:

[2022-11-16 08:01:52] local.ERROR: Allowed memory size of 536870912 bytes exhausted (tried to allocate 79695872 bytes) {"exception":"[object] (Symfony\\Component\\ErrorHandler\\Error\\FatalError(code: 0): Allowed memory size of 536870912 bytes exhausted (tried to allocate 79695872 bytes) at /.../.../.../.../vendor/laravel/framework/src/Illuminate/Cache/FileStore.php:77)
[stacktrace]
#0 {main}
"} 

@jasonvarga
Copy link
Member

Can you provide the actual template where you're using the nocache tag?

@j3ll3yfi5h
Copy link
Contributor Author

j3ll3yfi5h commented Nov 16, 2022

layout.antlers.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>{{ title }}</title>
</head>
<body>
	{{ template_content }}
</body>
</html>

default.antlers.html

{{ title|wrap:h1 }}

{{ collection from="jobs" }}
	{{ title|wrap:h2 }}
	{{ nocache }}
		{{ if logged_in }}Logged in{{ else }}Logged out{{ /if }}
	{{ /nocache }}
{{ /collection }}

Btw: my jobs collection for testing has 292 entries.

@jasonvarga
Copy link
Member

Thanks!

@marijoo
Copy link
Contributor

marijoo commented Mar 10, 2023

Anything new on this one? This needs to be addressed as it makes Static Caching unusable more or less.

@marijoo
Copy link
Contributor

marijoo commented Apr 21, 2023

Nothing? We are running a relatively small site (about) 100-200 pages in total and the load is insane. The site is unusable without static caching since load times explode to 10 seconds or so.

With full measure caching everything is fine – besides the fact that warming the static cache with static:warm takes a huuuuge amount of memory and CPU and a lot of time – until everything breaks after a few days of usage and nocache tags aren’t replaced anymore.

@jasonvarga
Copy link
Member

nocache tags files (storage/framework/views/nocache) are created on every page request and get never cleaned.

Do more get created on every request or only the first request for a page?

@j3ll3yfi5h
Copy link
Contributor Author

nocache tags files (storage/framework/views/nocache) are created on every page request and get never cleaned.

Do more get created on every request or only the first request for a page?

@jasonvarga
on every request

@jasonvarga
Copy link
Member

Do you even have static caching enabled?

@marijoo
Copy link
Contributor

marijoo commented Apr 26, 2023

We do have static caching enabled and after a few days of usage the nocache directory has an insane amount of files:

ls -l storage/framework/views/nocache/ | wc -l
521017

Again the nocache tags were not replaced anymore. I don’t know if this is a coincidence. After manually deleting everything from storage/framework/views/nocache/ the nocache tags stayed empty. After running php please static:clear && php please static:warm everything is back to normal again.

Here is the info from artisan about:

  Environment ............................................................  
  Laravel Version ................................................. 9.52.6  
  PHP Version ...................................................... 8.2.3  
  Composer Version ................................................. 2.5.4  
  Environment ................................................. production  
  Debug Mode ......................................................... OFF  
  Maintenance Mode ................................................... OFF  

  Cache ..................................................................  
  Config .......................................................... CACHED  
  Events .......................................................... CACHED  
  Routes .......................................................... CACHED  
  Views ........................................................... CACHED  

  Drivers ................................................................  
  Broadcasting ....................................................... log  
  Cache ......................................................... statamic  
  Database ......................................................... mysql  
  Logs ............................................................. daily  
  Mail .............................................................. smtp  
  Queue ............................................................. sync  
  Session ........................................................... file  

  Localization ...........................................................  
  Installed ....................................................... de, en  
  LaravelLang\Attributes\Plugin ................................... v2.3.3  
  LaravelLang\Lang\Plugin ........................................ 12.21.1  
  Protected Locales ............................................... de, en  
  Publisher Version .............................................. v14.6.4  

  Statamic ...............................................................  
  Addons ............................................................... 1  
  Antlers ........................................................ runtime  
  Stache Watcher ................................................ Disabled  
  Static Caching .................................................... full  
  Version ...................................................... 3.4.7 PRO  

  Statamic Addons ........................................................  
  thoughtco/statamic-blurhash ...................................... 1.0.4

After warming the cache there are only 303 files in the directory:

ls -l storage/framework/views/nocache/ | wc -l
303

We have 128 URLs right now and use the {{ nocache }} tag in 5 locations, 3 of them are located on every page since these are placed in the layout.

@marijoo
Copy link
Contributor

marijoo commented Apr 26, 2023

Maybe this is another topic, but wouldn’t it be nice to have the possibility to specify a key that would be used instead of the auto-generated one if passed? In our case there is no need to even have 300 different versions of the nocache content. It is the same across all pages, so we could just pass the same key in all files:

{{ nocache key="announcements" }}
    // output dynamic announcements here
{{ /nocache }}

@jasonvarga
Copy link
Member

We do have static caching enabled

I was directing the question to @j3ll3yfi5h. Since more stuff is generated on every request, it seems like maybe Static Caching wasn't enabled at all. Even since you say you can sit there and hit refresh and watch the files grow. If the page was cached, there shouldn't have been another nocache tag for Statamic to parse and create a new file.

Maybe this is another topic

This is sorta the exact topic. 😄 The reason so many files are being generated is because keys are unique for each instance of the nocache tag. We give each region a unique ID so that if you're in a loop, we can be sure that instance of the loop gets its proper data. (See commit) A separate file is generated even if it's identical to another.

It should be possible to re-use the files, but store the data uniquely.

But that would only solve the issue of there being lots of temporary view files. I'm not 100% sure the number of files is the actual issue.

I wonder if the array of cached URLs ends up being the problem. Maybe if the URLs are being hit with different variations of query parameters.

Would you be able to run php artisan tinker and then \Illuminate\Support\Facades\Cache::get('nocache::urls');?
How many items are in the returned array?

@marijoo
Copy link
Contributor

marijoo commented Apr 26, 2023

I ran count(\Illuminate\Support\Facades\Cache::get('nocache::urls')) which gives me a count of 141. Since writing the above post ~4 hours ago the files inside storage/framework/views/nocache/ have reached 2061.

@jasonvarga
Copy link
Member

That's probably it, then. 😄

Can you look at the values in there? Are there are duplicates?

@j3ll3yfi5h
Copy link
Contributor Author

Do you even have static caching enabled?

@jasonvarga Hm, I hope I had. But without enabling it, it definitely happens...😅

I'm not sure anymore, if I missed this with testing, sorry!

@j3ll3yfi5h
Copy link
Contributor Author

@jasonvarga Am I right, that the nocache folder will never be empty after any artisan command? It gets bigger again, because after clearing the cache, new files are generated?

@jasonvarga
Copy link
Member

There's currently nothing that would delete files from the nocache directory, so that'll just keep growing.

We should add some garbage collection to that, but I'm not convinced the number of files in that directory is the issue.

@marijoo
Copy link
Contributor

marijoo commented Apr 27, 2023

Can you look at the values in there? Are there are duplicates?

Absolutely, I checked some of the files and they contain recurring contents. There are 6879 files now and by the filesize I can determine that there are only 5 different contents repeating over and over again. I attached a screenshot.

We should add some garbage collection to that, but I'm not convinced the number of files in that directory is the issue.

A million redundant files is a problem for sure. As described above deleting these files and rebuilding the static cache fixes the problems for a few days.

@jasonvarga
Copy link
Member

I attached a screenshot.

Nothing attached.

A million redundant files is a problem for sure.

Ok yes it's definitely a problem too, but it sounds to me the size of that cache file becomes a problem before the files do. It's chewing up memory when it needs to read and write that growing array over and over.

@marijoo
Copy link
Contributor

marijoo commented Apr 27, 2023

Sorry, here is the screenshot.

Just in case: There are 9672 files now and count(\Illuminate\Support\Facades\Cache::get('nocache::urls')) gives a count of 197. Let me know if I can help with additional information.

@jasonvarga
Copy link
Member

Oh I thought it was going to be a screenshot of the cached array. I believe you that there are lots of files.

@marijoo
Copy link
Contributor

marijoo commented Apr 27, 2023

Here is a screenshot of a part of the array. The array seems to be blown up by non-existing URLs, which is expected behavior afaik – even though this seems to be some kind of brute force vector if this is what messes up the site.

The pages like /, /events and /parkplan all exist, but the query string versions don’t and I wouldn’t want them cached if this was configurable.

I cannot tell if the array had an enormous size when the site broke down the last time, but I will check next time.

nocache_array

@marijoo
Copy link
Contributor

marijoo commented May 2, 2023

Little update — 5 more days down the road: 82700 files, 155 array records. We now delete the folder contents on deploy and on a daily basis.

@johncarter-
Copy link
Contributor

public_html/storage/framework/views/nocache: 3656487 🤯

@seamofreality
Copy link

seamofreality commented Oct 1, 2023

I'd be very interested to learn about the underlying issue here since we've run into the same problem.

From what I've read here I'd also argue, that the amount of files doesn't seem to be a limiting factor.
Our nocache directory is currently 491M with 123335 files within.
Most files are the same size and there's no surprisingly large files there.

Our memory exhaustion error occurs just like described above, in the file FileStore.php, Line 77 - see file on GitHub.
That'd make me think whatever the $value is has become a problem for serialization?

The issue occured after the website has been live for a couple of weeks. We had to clear the cache via php artisan cache:clear which got rid of the Allowed memory size exhausted error. Then it was another couple of weeks before it reoccured.

We're using a nocache partial on some sub pages and in the navigation part of our website, which is included on every page of the website. The website does not have very many pages (< 50), not much going on there. The problem occured with caching completely disabled at first. Now we've enabled 'half' caching and are waiting to see what happens. Haven't been able to trigger the error. Not very much fun to anticipate the customer's website going down, tho. 🫣

@simonerd
Copy link
Contributor

simonerd commented Oct 12, 2023

We ran into this issue big time in a recent project (edit: using half static caching). There is a good amount of traffic on that page and the server response time got worse and worse within minutes.

As observed by others, the cause of the problem doesn't look to be the amount of files in the storage/framework/views/nocache, but rather the content of the nocache::session.{md5_page_url} cache object. As described by Jason above, every nocache region gets it's unique id. For every region id there is a corresponding file in the views folder, but also an array value in the cache, that contains rendered data (?):

Screenshot_2023-10-12_13-29-17

Same as with the files, this array never gets cleaned and with every request to a page, data for every nocache region on the page is added to the array. The bigger the array, the longer it takes the application to load a key. Within a day of the application running, we ran into a "memory limit exhausted" error.

What currently makes the application work fine is running this command every five minutes:

collect(Cache::get('nocache::urls', []))->each(function ($url) {
    Cache::forget('nocache::session.' . md5($url));
});

This effectively clears the whole nocache session cache.

Obviously we would prefer to have a more appropriate solution than this.

@marijoo
Copy link
Contributor

marijoo commented Oct 12, 2023

Maybe this is another topic, but wouldn’t it be nice to have the possibility to specify a key that would be used instead of the auto-generated one if passed? In our case there is no need to even have 300 different versions of the nocache content. It is the same across all pages, so we could just pass the same key in all files:

{{ nocache key="announcements" }}

    // output dynamic announcements here

{{ /nocache }}

The proposal I made a while back would not help with the problem that the cache never gets cleared but it could prevent hundreds or thousands of redundant copies in a lot of cases and give control to developers to summarize regions.

@stanbridge-wcorrea
Copy link

stanbridge-wcorrea commented Oct 17, 2023

I'm running out of memory and I think it's related to this issue. I'm using the full measure caching and I have just one URL that is excluded from the Static cache, when I hit that URL I run into that issue. The error seems to be in FileStore.php, Line 77 - see file on GitHub.

The funny thing is ... I'm getting this error after more or less 13 hours after I clear the cache.

The solution for me was to set up a cron to clear the cache again every 12 hours.

If I include all URLS to static cache, it doesn't run into issues.

@stanbridge-wcorrea
Copy link

We ran into this issue big time in a recent project. There is a good amount of traffic on that page and the server response time got worse and worse within minutes.

As observed by others, the cause of the problem doesn't look to be the amount of files in the storage/framework/views/nocache, but rather the content of the nocache::session.{md5_page_url} cache object. As described by Jason above, every nocache region gets it's unique id. For every region id there is a corresponding file in the views folder, but also an array value in the cache, that contains rendered data (?):

Screenshot_2023-10-12_13-29-17

Same as with the files, this array never gets cleaned and with every request to a page, data for every nocache region on the page is added to the array. The bigger the array, the longer it takes the application to load a key. Within a day of the application running, we ran into a "memory limit exhausted" error.

What currently makes the application work fine is running this command every five minutes:

collect(Cache::get('nocache::urls', []))->each(function ($url) {
    Cache::forget('nocache::session.' . md5($url));
});

This effectively clears the whole nocache session cache.

Obviously we would prefer to have a more appropriate solution than this.

Are you using full measure static cache? I tried to run your code for clearing the nocache cache, but that gives me errors when loading those tags.

@simonerd
Copy link
Contributor

Are you using full measure static cache? I tried to run your code for clearing the nocache cache, but that gives me errors when loading those tags.

Sorry, I didn't mention that. We're using half measure caching.

For full measure caching it seems fine to create new files and add data to the nocache::session array on every request, as this should only happen on a "first" request, as the corresponding html file is also generated on that request and the next request wouldn't go through the application. I assume that if you kill the array (and therefore all stored region ids), the generated html doesn't have a matching region id to render. However, if you have pages excluded from static caching, the problem still exists but you can't resolve it via the described way.

Makes the problem even more urgent, it seems.

@stanbridge-wcorrea
Copy link

Are you using full measure static cache? I tried to run your code for clearing the nocache cache, but that gives me errors when loading those tags.

Sorry, I didn't mention that. We're using half measure caching.

For full measure caching it seems fine to create new files and add data to the nocache::session array on every request, as this should only happen on a "first" request, as the corresponding html file is also generated on that request and the next request wouldn't go through the application. I assume that if you kill the array (and therefore all stored region ids), the generated html doesn't have a matching region id to render. However, if you have pages excluded from static caching, the problem still exists but you can't resolve it via the described way.

Makes the problem even more urgent, it seems.

Yes, as you mentioned when you have pages excluded from static cache and those pages use the {{ nocache }} tag, those pages will run out of memory at some point, however you can use the code that you mentioned cleaning the nocache::session cache when you visit those pages.

I have fixed (workaround) creating a custom tag that clears the nocache::session only for excluded pages, but that fixes the issue for me that is using the static cache full measure, not sure for those using half measure, I think it's worse.

I'm not sure if I should open a new issue about this, but I have detailed steps on how to reproduce it.

@jasonvarga
Copy link
Member

To anyone that was following along with this, we fixed it in #8956 but it has shown to be problematic.

The automatic performance improvements will be reverted in the next release. You can still use them but you need to opt into them. See details in #9124.

@jeroenimpres
Copy link
Contributor

Thanks for commenting it here @jasonvarga I like the solution to have it be opt in

@ajcsilva
Copy link

ajcsilva commented Oct 3, 2024

Hi, I was wondering the the support for keys on the nocache tag were going to be implemented? I'm still dealing with a memory/views build up issue. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.