Mojolicious 模板缓存已过时

Mojolicious template cache is stale

我目前正在使用 Mojolicious 开发一个小型单页 Web 应用程序。该应用程序有一个 Javascript 前端(使用 Backbone),它与 REST-ish API 对话;源码布局大致为:

use Mojolicious::Lite;

# ... setup code ...

get '/' => sub {
    my $c = shift;
    # fetch+stash data for bootstrapped collections...
    $c->render('app_template');
};

get '/api_endpoint' => sub {
    my $c = shift;
    # fetch appropriate API data...
    $c->render(json => $response);
};

# ... more API endpoints ...

app->start;

应用程序模板使用了 EP,但极少;唯一的服务器端模板指令只是为引导集合插入 JSON。它作为普通的 CGI 脚本通过 Apache 部署。 (这不是最优的,但它适用于低流量的内部使用,更复杂的服务器配置在上下文中是有问题的。)Perl CGI 通过 mod_perl.

配置

这在大多数情况下都有效,但有时渲染器会以某种方式想到它应该缓存模板并忽略对其的更改。 error_log 中的调试记录显示 "Rendering cached template" 而不是正常的 "Rendering template",并且我对模板的新更改不再出现在浏览器中。我找不到可靠的方法来阻止它,尽管它最终会根据我无法辨别的条件自行停止。

如何可靠地更改应用通知模板?或者,如何完全禁用模板缓存?

旧答案见下文。

我将这个答案的发现变成了一个插件,并在 CPAN 上以 Mojolicious::Plugin::Renderer::WithoutCache after discussing wit Grinnz 的形式在 IRC 上发布,他们鼓励发布。

你可以这样使用它:

use Mojolicious::Lite;
plugin 'Renderer::WithoutCache';

它将创建一个什么都不做的新缓存 object,并将其全局安装到渲染器中。这样,就不需要像我下​​面最初的回答那样每次都创建它。

理论上,这应该比 Grinnz' approach 快(更明智),并且由于您明确不想缓存,您显然希望事情越快越好,对吧?它应该更快,因为真正的 Mojo::Cache 仍然需要去尝试设置缓存,但随后因为没有更多的空闲键而中止,并且它每次都会尝试从缓存中查找值。

我用这两个 Dumbbench and Benchmark 进行了基准测试。两者都显示出微不足道的结果。我 运行 他们每个人都有几次,但他们波动很大,不清楚哪个更快。我包含了 运行 的输出,我的实现速度更快,但它仍然显示出差异是多么微小。

Dumbbench 基准测试:

use Dumbbench;
use Mojolicious::Renderer;
use Mojolicious::Controller;
use Mojolicious::Plugin::Renderer::WithoutCache::Cache;

my $controller         = Mojolicious::Controller->new;
my $renderer_zero_keys = Mojolicious::Renderer->new;
$renderer_zero_keys->cache->max_keys(0);

my $renderer_nocache = Mojolicious::Renderer->new;
$renderer_nocache->cache( Mojolicious::Plugin::Renderer::WithoutCache::Cache->new );

my $bench = Dumbbench->new(
    target_rel_precision => 0.005,
    initial_runs         => 5000,
);

$bench->add_instances(
    Dumbbench::Instance::PerlSub->new(
        name => 'max_keys',
        code => sub {
            $renderer_zero_keys->render( $controller, { text => 'foobar' } );
        }
    ),
    Dumbbench::Instance::PerlSub->new(
        name => 'WithoutCache',
        code => sub {
            $renderer_nocache->render( $controller, { text => 'foobar' } );
        }
    ),
);

$bench->run;
$bench->report;

__END__
max_keys: Ran 8544 iterations (3335 outliers).
max_keys: Rounded run time per iteration: 5.19018e-06 +/- 4.1e-10 (0.0%)
WithoutCache: Ran 5512 iterations (341 outliers).
WithoutCache: Rounded run time per iteration: 5.0802e-06 +/- 5.6e-09 (0.1%)

基准与基准:

use Benchmark 'cmpthese';
use Mojolicious::Renderer;
use Mojolicious::Controller;
use Mojolicious::Plugin::Renderer::WithoutCache::Cache;

my $controller         = Mojolicious::Controller->new;
my $renderer_zero_keys = Mojolicious::Renderer->new;
$renderer_zero_keys->cache->max_keys(0);

my $renderer_nocache = Mojolicious::Renderer->new;
$renderer_nocache->cache( Mojolicious::Plugin::Renderer::WithoutCache::Cache->new );

cmpthese(
    -5,
    {
        'max_keys' => sub {
            $renderer_zero_keys->render( $controller, { text => 'foobar' } );
        },
        'WithoutCache' => sub {
            $renderer_nocache->render( $controller, { text => 'foobar' } );
        },
    }
);

__END__
                 Rate     max_keys WithoutCache
max_keys     190934/s           --          -2%
WithoutCache 193846/s           2%           --

我在一个有很多调用的重负载环境中进行侦察,它最终会有所作为,但这很难证明。所以如果你不想考虑缓存的内部结构,这个插件可能会有用。


旧答案:

正在查看 Mojolicious::Plugin::EPRenderer I found out that there is a cache. It's a Mojo::Cache instance, which has the methods get, set and max_keys, and inherits from Mojo::Base(可能就像 Mojolicious 中的所有内容)。

::EPRenderer gets a $renderer, which is a Mojolicious::Renderer. It holds the Mojo::Cache instance. I looked at $c with Data::Printer,发现有一个 $c->app 包含所有这些。

知道了这一点,您就可以轻松地制作自己的缓存 class 什么都不做。

package Renderer::NoCache;
use Mojo::Base -base;

sub get {}
sub set {}
sub max_keys {}

现在您将其粘贴到 $c

package Foo;
use Mojolicious::Lite;

get '/' => sub {
    my $c = shift;

    $c->app->renderer->cache( Renderer::NoCache->new );

    $c->render(template => 'foo', name => 'World');
};

app->start;

__DATA__

@@ foo.html.ep
Hello <%= $name =%>.

现在每次尝试 getset 缓存都什么都不做。它会尝试缓存,但它永远找不到任何东西。

当然每次都搞一个新的object当然不好了。最好在启动时制作一次 object 并将其放入 app 的内部永久版本。你有 CGI,所以它可能没有什么不同。


您也可以只 monkey-patch Mojo::Cache 中的 get。这种更 hacky 的方法会做同样的事情:

package Foo;
use Mojolicious::Lite;

*Mojo::Cache::get = sub { };

get '/' => sub {
    my $c = shift;

    $c->render(template => 'foo', name => 'World');
};

app->start;

但请注意:我们刚刚禁用了从您的应用程序中使用 Mojo::Cache 的 每个缓存 中获取数据。这可能不是你想要的。

How can I make the app notice template changes reliably?

这就是 morbo development server is for. Morbo wouldn't be used for your live code deployment, but for a development environment where you are continually changing your code and templates. Generally changes to live code and templates are meant to be handled by restarting the application server, or Apache in your case. (Hypnotoad 的 hot-restart 功能)

Alternatively, how can I disable template caching completely?

为此,添加以下设置代码(在路由之外,use Mojolicious::Lite 之后):

app->renderer->cache->max_keys(0);