Skip to content

Commit 52deda5

Browse files
feat: Add Redis caching layer for policies
This commit introduces a Redis caching layer to the DBAL adapter to improve performance and reduce database load. Features: - Policies loaded via `loadPolicy` and `loadFilteredPolicy` are now cached in Redis. - Configurable Redis connection parameters (host, port, password, database, TTL, prefix). - Automatic cache invalidation for the main policy cache (`all_policies`) when policies are modified. - A `preheatCache()` method to proactively load all policies into Redis. - The adapter remains fully functional if Redis is not configured. Includes: - Updates to `Adapter.php` to integrate caching logic. - Addition of `predis/predis` as a dependency. - Unit tests for the caching functionality in `AdapterWithRedisTest.php`. - Documentation in `README.md` for the new feature. Known limitations: - Cache invalidation for `loadFilteredPolicy` currently only clears the global `all_policies` key, not specific filtered policy keys, to avoid using `KEYS` in production with Predis.
1 parent 1a1e817 commit 52deda5

File tree

4 files changed

+1002
-478
lines changed

4 files changed

+1002
-478
lines changed

README.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,81 @@ if ($e->enforce($sub, $obj, $act) === true) {
6262
}
6363
```
6464

65+
### Redis Caching
66+
67+
To improve performance and reduce database load, the adapter supports caching policy data using [Redis](https://redis.io/). When enabled, Casbin policies will be fetched from Redis if available, falling back to the database if the cache is empty.
68+
69+
#### Configuration
70+
71+
To enable Redis caching, pass a Redis configuration array as the second argument to the `Adapter::newAdapter()` method or the `Adapter` constructor.
72+
73+
Available Redis configuration options:
74+
75+
* `host` (string): Hostname or IP address of the Redis server. Default: `'127.0.0.1'`.
76+
* `port` (int): Port number of the Redis server. Default: `6379`.
77+
* `password` (string, nullable): Password for Redis authentication. Default: `null`.
78+
* `database` (int): Redis database index. Default: `0`.
79+
* `ttl` (int): Cache Time-To-Live in seconds. Policies stored in Redis will expire after this duration. Default: `3600` (1 hour).
80+
* `prefix` (string): Prefix for all Redis keys created by this adapter. Default: `'casbin_policies:'`.
81+
82+
**Example:**
83+
84+
```php
85+
require_once './vendor/autoload.php';
86+
87+
use Casbin\Enforcer;
88+
use CasbinAdapter\DBAL\Adapter as DatabaseAdapter;
89+
90+
// Database configuration (as before)
91+
$dbConfig = [
92+
'driver' => 'pdo_mysql',
93+
'host' => '127.0.0.1',
94+
'dbname' => 'test',
95+
'user' => 'root',
96+
'password' => '',
97+
'port' => '3306',
98+
];
99+
100+
// Redis configuration
101+
$redisConfig = [
102+
'host' => '127.0.0.1', // Optional, defaults to '127.0.0.1'
103+
'port' => 6379, // Optional, defaults to 6379
104+
'password' => null, // Optional, defaults to null
105+
'database' => 0, // Optional, defaults to 0
106+
'ttl' => 7200, // Optional, Cache policies for 2 hours
107+
'prefix' => 'myapp_casbin:' // Optional, Custom prefix
108+
];
109+
110+
// Initialize adapter with both DB and Redis configurations
111+
$adapter = DatabaseAdapter::newAdapter($dbConfig, $redisConfig);
112+
113+
$e = new Enforcer('path/to/model.conf', $adapter);
114+
115+
// ... rest of your Casbin usage
116+
```
117+
118+
#### Cache Preheating
119+
120+
The adapter provides a `preheatCache()` method to proactively load all policies from the database and store them in the Redis cache. This can be useful during application startup or as part of a scheduled task to ensure the cache is warm, reducing latency on initial policy checks.
121+
122+
**Example:**
123+
124+
```php
125+
if ($adapter->preheatCache()) {
126+
// Cache preheating was successful
127+
echo "Casbin policy cache preheated successfully.\n";
128+
} else {
129+
// Cache preheating failed (e.g., Redis not available or DB error)
130+
echo "Casbin policy cache preheating failed.\n";
131+
}
132+
```
133+
134+
#### Cache Invalidation
135+
136+
The cache is designed to be automatically invalidated when policy-modifying methods are called on the adapter (e.g., `addPolicy()`, `removePolicy()`, `savePolicy()`, etc.). Currently, this primarily clears the cache key for all policies (`{$prefix}all_policies`).
137+
138+
**Important Note:** The automatic invalidation for *filtered policies* (policies loaded via `loadFilteredPolicy()`) is limited. Due to the way `predis/predis` client works and to avoid using performance-detrimental commands like `KEYS *` in production environments, the adapter does not automatically delete cache entries for specific filters by pattern. If you rely heavily on `loadFilteredPolicy` and make frequent policy changes, consider a lower TTL for your Redis cache or implement a more sophisticated cache invalidation strategy for filtered results outside of this adapter if needed. The main `{$prefix}all_policies` cache is cleared on any policy change, which means subsequent calls to `loadPolicy()` will refresh from the database and update this general cache.
139+
65140
### Getting Help
66141

67142
- [php-casbin](https://github.com/php-casbin/php-casbin)

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
"require": {
2121
"php": ">=8.0",
2222
"casbin/casbin": "^4.0",
23-
"doctrine/dbal": "^3.9|^4.0"
23+
"doctrine/dbal": "^3.9|^4.0",
24+
"predis/predis": "^2.0"
2425
},
2526
"require-dev": {
2627
"phpunit/phpunit": "~9.0",

0 commit comments

Comments
 (0)