我正在使用bouncer来满足我的ACL需求,自从我的项目从laravel 5.7升级到5.8后,我注意到我的请求处理所需的时间显着增加。
我正在处理两个模型(让我们称之为Parent
和Child
),以及经过身份验证的用户对它们的权限。
// Takes about 110ms. Eager loads various nested relationships and counters with specific constraints
$parents = Parent::myScope(...)->get();
// Bottleneck. Takes 5 minutes (!). Used to take about 40 seconds on laravel 5.7
$parents->each(function ($parent) {
$parent->permissions = [
'edit' => auth()->user()->can('edit', $parent),
'delete' => auth()->user()->can('delete', $parent),
'restore' => auth()->user()->can('restore', $parent)
];
$parent->children()->each(function ($child) {
$child->permissions = [
'edit' => auth()->user()->can('edit', $child),
'delete' => auth()->user()->can('delete', $child),
'restore' => auth()->user()->can('restore', $child)
];
}
}
我正在附加这样的权限,因为$parents
变量将作为json发送到前端。我很确定这个实现是错误的,并且必须有一个更好的选择,但真正的问题是加载时间的这个莫名的五倍增加。
时间是使用Debugbar
措施获得的。
在monitor
中使用redis-cli
命令(我使用Redis
来缓存权限),我注意到GET请求比以前更慢。事实上,即使我停止加载页面(ESC),对Redis的GET请求也不会立即停止。我不确定这是否是正常行为。
我试图检查保镖回购中的问题,但我没有找到任何东西。
你正在呼叫auth()->user()
数百次。你可以尝试只调用一次吗?
$user = auth()->user();
$parents->each(function ($parent) use ($user) {
$parent->permissions = [
'edit' => $user->can('edit', $parent),
'delete' => $user->can('delete', $parent),
'restore' => $user->can('restore', $parent)
];
$parent->children()->each(function ($child) {
$child->permissions = [
'edit' => $user->can('edit', $child),
'delete' => $user->can('delete', $child),
'restore' => $user->can('restore', $child)
];
}
}
此外,由于你急于加载children
,你不应该在每次循环迭代中再次获取它们:
$parent->children()->each(function ($child) {
// ^^ remove these parentheses
$child->permissions = [
'edit' => $user->can('edit', $child),
'delete' => $user->can('delete', $child),
'restore' => $user->can('restore', $child)
];
}
经过一些测试后,找到了解决方案。事实证明,代码完全没有问题。
服务器出了点问题。我们不知道究竟是什么,但尝试在新安装的机器上运行项目摆脱了那些可怕的处理时间。 (现在第一次请求时间是15秒)
从laravel 5.7迁移到5.8之后,服务器的问题变得更加巧妙,这让我陷入了疯狂的追逐。
附录
罪魁祸首是Xdebug。我们使用它来获得代码覆盖率分析,但性能非常差,我们最终切换到phpdbg。