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

Fixing forced OpCache invalidation on every template include, which is resulting in fast raising wasted OpCache memory #1007 #1047

Merged
merged 9 commits into from
Aug 14, 2024

Conversation

stephanlueckl
Copy link
Contributor

Fixes: #1007

\Smarty\Template\Compiled::loadCompiledTemplate invalidates the cache entry of a compiled template on every call (or include) as it is forced to do so.
This results in fast growing wasted memory and after reaching the tipping point in a total cleanup and refresh of the opcache.

I do not think, that is necessary to force it, seeing this change as hotfix.

Nice article about wasted memory of the opcache and it's refreshment mechanism:
https://www.codana.be/en/insights/php-opcache-realpath-cache-and-preloading

@wisskid
Copy link
Contributor

wisskid commented Jul 27, 2024

I really wonder why this line was in here in the first place. I guess it was already present in Smarty 4, which makes me wonder if this actually fixes #1007. If so, this must mean there some unintended change in the flow between v4 and v5.

@stephanlueckl
Copy link
Contributor Author

@wisskid
You are right, this fixes only the symptom of raising wasted memory.
The Smarty5 method \Smarty\Template\Compiled::loadCompiledTemplate is the same like in Smarty4

The root cause is in the different call mechanism between Smarty 4 and Smarty 5

Smarty 4 loadCompiledTemplate is only called when it was new compiled

public function process(Smarty_Internal_Template $_smarty_tpl)
    {
        $source = &$_smarty_tpl->source;
        $smarty = &$_smarty_tpl->smarty;
        if ($source->handler->recompiled) {
            $source->handler->process($_smarty_tpl);
        } elseif (!$source->handler->uncompiled) {
            if (!$this->exists || $smarty->force_compile
                || ($_smarty_tpl->compile_check && $source->getTimeStamp() > $this->getTimeStamp())
            ) {
                $this->compileTemplateSource($_smarty_tpl);
                $compileCheck = $_smarty_tpl->compile_check;
                $_smarty_tpl->compile_check = Smarty::COMPILECHECK_OFF;
                $this->loadCompiledTemplate($_smarty_tpl);
                $_smarty_tpl->compile_check = $compileCheck;
            } else {
// Loading an already compiled file (standard case)
                $_smarty_tpl->mustCompile = true;
                @include $this->filepath;
// after the include statement mustcompile is false again 
                if ($_smarty_tpl->mustCompile) {
                    $this->compileTemplateSource($_smarty_tpl);
                    $compileCheck = $_smarty_tpl->compile_check;
                    $_smarty_tpl->compile_check = Smarty::COMPILECHECK_OFF;
                    $this->loadCompiledTemplate($_smarty_tpl);
                    $_smarty_tpl->compile_check = $compileCheck;
                }
            }
            $_smarty_tpl->_subTemplateRegister();
            $this->processed = true;
        }
    }

Smarty 5 loadCompiledTemplate is always used,

private function compileAndLoad(Template $_smarty_tpl) 
	{
		if ($_smarty_tpl->getSource()->handler->recompiled) {
			$this->recompile($_smarty_tpl);
			return;
		}

		if ($this->exists && !$_smarty_tpl->getSmarty()->force_compile
			&& !($_smarty_tpl->compile_check && $_smarty_tpl->getSource()->getTimeStamp() > $this->getTimeStamp())
		) {
// Loading an already compiled file (standard case)		
			$this->loadCompiledTemplate($_smarty_tpl);
		}

		if (!$this->isValid) {
			$this->compileAndWrite($_smarty_tpl);
			$this->loadCompiledTemplate($_smarty_tpl);
		}

		$this->processed = true;
	}

Thinking of invalidation is only necessary, when the file is changed in the process.
I would suggest an optional invalidation param to the loadCompiledTemplate() method.
Change will soon follow in the pull request.

As of the forced invalidation, i still think this is not necessary - if the file hasn't change, why force to reload it?

@stephanlueckl
Copy link
Contributor Author

Fix is working for this issue, Fast growing of the wasted memory of OPCache stopped.

image
Update on this server went online at 16:05.
No more OPCache restarts, CPU spikes stopped.

@wisskid
Copy link
Contributor

wisskid commented Jul 29, 2024

Nice work @stephanlueckl !

@wisskid
Copy link
Contributor

wisskid commented Jul 29, 2024

As of the forced invalidation, i still think this is not necessary - if the file hasn't change, why force to reload it?

I honestly don't have a clue, but there may be some reason both of us don't understand yet, so I'd be more comfortable to leave it in. Your PR looks great, I'm running the tests now.

@stephanlueckl
Copy link
Contributor Author

I honestly don't have a clue, but there may be some reason both of us don't understand yet, so I'd be more comfortable to leave it in. Your PR looks great, I'm r

Okay, i've changed this line back in the last commit

@tungps250991
Copy link

@stephanlueckl
Thank you for the work, it really saves us!

@wisskid
I tried applying this solution & the performance issue here is gone.
Could you please merge this to new v.5 release soon? It would be very helpful since we're going to release the product with Smarty upgrade by the end of this week :)
Thank you!

@wisskid
Copy link
Contributor

wisskid commented Jul 30, 2024

Expect a new release around Aug 15th.

@wisskid wisskid merged commit 1ccfca1 into smarty-php:master Aug 14, 2024
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

CPU spikes in prod every 5 minutes with Smarty v5
4 participants