Skip to content

Commit b8dade9

Browse files
authored
Merge pull request #8914 from ProcessMaker/feature/FOUR-32256
FOUR-32256: PERF Reduce duplicate permission queries on menu and auth checks
2 parents 5f89240 + 5ac4c17 commit b8dade9

7 files changed

Lines changed: 63 additions & 9 deletions

File tree

ProcessMaker/Http/Middleware/GenerateMenus.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ public static function userHasPermission($permission)
353353
return $user && $user->can($permission) && $user->hasPermission($permission);
354354
}
355355

356-
$userPermissions = $user->permissions()->pluck('group')->unique()->toArray();
356+
$userPermissions = $user->cachedPermissionGroups();
357357
$defaultPermissions = Permission::DEFAULT_PERMISSIONS;
358358
$userWithDefaultPermissions = empty(array_diff($userPermissions, $defaultPermissions));
359359

ProcessMaker/Models/Permission.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@ public static function byResource($resource)
9898
return $filtered;
9999
}
100100

101+
public static function cachedNames(): array
102+
{
103+
return Cache::remember('permissions', 86400, function () {
104+
return self::pluck('name')->toArray();
105+
});
106+
}
107+
101108
public static function byName($name)
102109
{
103110
try {
@@ -139,7 +146,7 @@ public static function getUsersByGroup(array $groups)
139146
->on('assignables.assignable_id', '=', 'users.id');
140147
})
141148
->select('users.*')
142-
->union(\ProcessMaker\Models\User::where('is_administrator', '=', true))
149+
->union(User::where('is_administrator', '=', true))
143150
->groupBy('users.id')
144151
->get();
145152

@@ -148,8 +155,7 @@ public static function getUsersByGroup(array $groups)
148155

149156
private static function clearAndRebuildCache()
150157
{
151-
// Rebuild and update the permissions cache
152-
$permissions = self::pluck('name')->toArray();
153-
Cache::put('permissions', $permissions, 86400);
158+
Cache::forget('permissions');
159+
self::cachedNames();
154160
}
155161
}

ProcessMaker/Models/User.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ public function getFullName()
269269
public function hasPermissionsFor(...$resources)
270270
{
271271
if ($this->is_administrator) {
272-
$perms = Permission::all(['name'])->pluck('name');
272+
$perms = collect(Permission::cachedNames());
273273
} else {
274274
$perms = collect(session('permissions'));
275275
}

ProcessMaker/Providers/AuthServiceProvider.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,7 @@ public function defineGates()
9292

9393
try {
9494
// Cache the permissions for a day to improve performance
95-
$permissions = Cache::remember('permissions', 86400, function () {
96-
return Permission::pluck('name')->toArray();
97-
});
95+
$permissions = Permission::cachedNames();
9896
foreach ($permissions as $permission) {
9997
Gate::define($permission, function ($user) use ($permission) {
10098
return $user->hasPermission($permission);

ProcessMaker/Services/PermissionCacheService.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ class PermissionCacheService implements PermissionCacheInterface
1818

1919
private const LEGACY_USER_PERMISSIONS_KEY = 'user';
2020

21+
private const USER_PERMISSION_GROUPS_KEY = 'user_permission_groups';
22+
2123
private const TRACKED_PERMISSION_KEYS = 'permission_cache_keys';
2224

2325
private const TRACKED_PERMISSION_KEYS_LOCK = 'permission_cache_keys_lock';
@@ -91,11 +93,42 @@ public function cacheGroupPermissions(int $groupId, array $permissions): void
9193
/**
9294
* Invalidate user permissions cache
9395
*/
96+
public function rememberUserPermissionGroups(int $userId, int $ttl, callable $callback): array
97+
{
98+
$key = $this->getUserPermissionGroupsKey($userId);
99+
100+
try {
101+
$groups = Cache::remember($key, $ttl, $callback);
102+
$this->trackPermissionKey($key);
103+
104+
return is_array($groups) ? $groups : [];
105+
} catch (\Exception $e) {
106+
Log::warning("Failed to remember user permission groups for user {$userId}: " . $e->getMessage());
107+
108+
$groups = $callback();
109+
110+
return is_array($groups) ? $groups : [];
111+
}
112+
}
113+
114+
public function forgetUserPermissionGroups(int $userId): void
115+
{
116+
$key = $this->getUserPermissionGroupsKey($userId);
117+
118+
try {
119+
Cache::forget($key);
120+
$this->untrackPermissionKey($key);
121+
} catch (\Exception $e) {
122+
Log::warning("Failed to forget user permission groups for user {$userId}: " . $e->getMessage());
123+
}
124+
}
125+
94126
public function invalidateUserPermissions(int $userId): void
95127
{
96128
$keys = [
97129
$this->getUserPermissionsKey($userId),
98130
$this->getLegacyUserPermissionsKey($userId),
131+
$this->getUserPermissionGroupsKey($userId),
99132
];
100133

101134
try {
@@ -216,6 +249,11 @@ private function getLegacyUserPermissionsKey(int $userId): string
216249
return self::LEGACY_USER_PERMISSIONS_KEY . "_{$userId}_permissions";
217250
}
218251

252+
private function getUserPermissionGroupsKey(int $userId): string
253+
{
254+
return self::USER_PERMISSION_GROUPS_KEY . ":{$userId}";
255+
}
256+
219257
/**
220258
* Warm up cache for a user
221259
*/

ProcessMaker/Traits/HasAuthorization.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ function () use ($user) {
4444
return $this->addCategoryViewPermissions($permissions);
4545
}
4646

47+
public function cachedPermissionGroups(): array
48+
{
49+
return app(PermissionCacheService::class)->rememberUserPermissionGroups(
50+
$this->id,
51+
86400,
52+
fn () => $this->permissions()->pluck('group')->unique()->values()->toArray()
53+
);
54+
}
55+
4756
public function loadGroupPermissions()
4857
{
4958
$processedGroups = [];

tests/unit/ProcessMaker/Services/PermissionCacheServiceTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,17 +127,20 @@ public function test_invalidate_user_permissions_clears_cache_correctly()
127127
// Cache user permissions
128128
$this->cacheService->cacheUserPermissions($this->userId, $this->userPermissions);
129129
$this->cacheService->putLegacyUserPermissions($this->userId, $this->userPermissions, 3600);
130+
$this->cacheService->rememberUserPermissionGroups($this->userId, 3600, fn () => ['Projects', 'Process Catalog']);
130131

131132
// Verify cache exists
132133
$this->assertNotNull(Cache::get("user_permissions:{$this->userId}"));
133134
$this->assertNotNull(Cache::get("user_{$this->userId}_permissions"));
135+
$this->assertNotNull(Cache::get("user_permission_groups:{$this->userId}"));
134136

135137
// Invalidate cache
136138
$this->cacheService->invalidateUserPermissions($this->userId);
137139

138140
// Verify cache was cleared
139141
$this->assertNull(Cache::get("user_permissions:{$this->userId}"));
140142
$this->assertNull(Cache::get("user_{$this->userId}_permissions"));
143+
$this->assertNull(Cache::get("user_permission_groups:{$this->userId}"));
141144
}
142145

143146
/**

0 commit comments

Comments
 (0)