Skip to content

Commit 9314176

Browse files
authored
feat: major improvements and comprehensive test coverage (#12)
* fix: file driver deletion bug and docs update * feat: major improvements and comprehensive test coverage - Refactor to eliminate code duplication - Add missing KeyAwareFileStore methods - Optimize Redis/file/database key retrieval - Add error handling, logging, and validation - Add comprehensive test suite (integration, edge cases, unit tests) - Fix test infrastructure and linting issues - Update documentation and remove Memcached support * add more docs * test: add file driver tests for forgetKey with correct mock expectations
1 parent b4d39f4 commit 9314176

16 files changed

+1953
-416
lines changed

README.md

Lines changed: 271 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ return [
3939

4040
// Number of visible items in scroll
4141
'search_scroll' => env('CACHE_UI_SEARCH_SCROLL', 15),
42+
43+
// Maximum number of keys to retrieve (null = unlimited)
44+
'keys_limit' => env('CACHE_UI_KEYS_LIMIT', null),
45+
46+
// Enable error logging for cache operations
47+
'enable_logging' => env('CACHE_UI_ENABLE_LOGGING', false),
48+
49+
// Timeout in seconds for cache operations (0 = no timeout)
50+
'operation_timeout' => env('CACHE_UI_OPERATION_TIMEOUT', 0),
4251
];
4352
```
4453

@@ -48,6 +57,9 @@ You can also configure these values in your `.env` file:
4857
CACHE_UI_DEFAULT_STORE=redis
4958
CACHE_UI_PREVIEW_LIMIT=150
5059
CACHE_UI_SEARCH_SCROLL=20
60+
CACHE_UI_KEYS_LIMIT=1000
61+
CACHE_UI_ENABLE_LOGGING=true
62+
CACHE_UI_OPERATION_TIMEOUT=30
5163
```
5264

5365
### Custom File Cache Driver (Only for File Store)
@@ -56,7 +68,8 @@ If you are using the `file` cache driver (default in Laravel), you should use ou
5668

5769
**Why?** The standard Laravel `file` driver stores keys as hashes, making them unreadable. This custom driver wraps the value to store the real key, allowing you to see and search for them.
5870

59-
> **Important**: This is **NOT** needed for Redis or Database drivers, as they support listing keys natively.
71+
> [!IMPORTANT]
72+
> This is **NOT** needed for Redis or Database drivers, as they support listing keys natively.
6073
6174
#### Driver Configuration
6275

@@ -89,6 +102,7 @@ use Abr4xas\CacheUiLaravel\KeyAwareFileStore;
89102
use Illuminate\Support\Facades\Cache;
90103
use Illuminate\Support\ServiceProvider;
91104
use Illuminate\Foundation\Application;
105+
use Illuminate\Filesystem\Filesystem;
92106

93107
class AppServiceProvider extends ServiceProvider
94108
{
@@ -107,7 +121,7 @@ class AppServiceProvider extends ServiceProvider
107121
{
108122
// Register the custom file cache driver
109123
Cache::extend('key-aware-file', fn (Application $app, array $config) => Cache::repository(new KeyAwareFileStore(
110-
$app['files'],
124+
$app->make(Filesystem::class),
111125
$config['path'],
112126
$config['file_permission'] ?? null
113127
)));
@@ -121,6 +135,7 @@ class AppServiceProvider extends ServiceProvider
121135
-**Full compatibility**: Works exactly like the standard `file` driver
122136
-**Better experience**: Enables more intuitive cache key search and management
123137
-**Backward compatibility**: Existing cache files continue to work
138+
-**Complete API**: Implements all Laravel cache methods (`put`, `get`, `add`, `forever`, `increment`, `decrement`, `remember`, `rememberForever`, `pull`, `has`, `flush`)
124139

125140
#### Migration from Standard File Driver
126141

@@ -156,6 +171,11 @@ php artisan cache:list --store=redis
156171
- 📋 **List all keys**: View all available keys in your cache
157172
- 🗑️ **Selective deletion**: Delete individual keys without affecting the rest of the cache
158173
- 🔌 **Multiple drivers**: Supports Redis, File and Database
174+
-**Performance optimized**: Uses SCAN for Redis (safe for production) and supports key limits
175+
- 📊 **Additional info**: View cache value, size, expiration, and type
176+
- 📤 **Export functionality**: Export key lists to files
177+
- 🔎 **Pattern filtering**: Filter keys using regex patterns
178+
- 🛡️ **Error handling**: Comprehensive error handling with optional logging
159179

160180
### Supported Drivers
161181

@@ -167,7 +187,8 @@ php artisan cache:list --store=redis
167187
| **Array** | ⚠️ No | Not supported (doesn't persist) |
168188
| **Memcached** | ⚠️ No | Not currently supported |
169189

170-
> **Note**: The `key-aware-file` driver is **only** needed if you use the `file` cache driver. If you use Redis or Database, you don't need to change your driver configuration.
190+
> [!WARNING]
191+
> The `key-aware-file` driver is **only** needed if you use the `file` cache driver. If you use Redis or Database, you don't need to change your driver configuration.
171192
172193
### Usage Example
173194

@@ -187,7 +208,40 @@ Are you sure you want to delete this cache key? › No / Yes
187208
🗑️ The key 'user_1_profile' has been successfully deleted
188209
```
189210
190-
### Programmatic Usage (optional)
211+
### Advanced Command Options
212+
213+
The command supports several useful options:
214+
215+
```bash
216+
# Show cache value before deletion
217+
php artisan cache:list --show-value
218+
219+
# Export keys list to a file
220+
php artisan cache:list --export=keys.txt
221+
222+
# Filter keys by regex pattern
223+
php artisan cache:list --filter="/^user_/"
224+
225+
# Show additional information (size, type, expiration)
226+
php artisan cache:list --info
227+
228+
# Limit number of keys displayed (useful for large caches)
229+
php artisan cache:list --limit=50
230+
231+
# Combine multiple options
232+
php artisan cache:list --store=redis --show-value --info --limit=100
233+
```
234+
235+
#### Option Details
236+
237+
- **`--store=`**: Specify which cache store to use (defaults to Laravel's default cache store)
238+
- **`--show-value`**: Display the cache value before confirming deletion
239+
- **`--export=`**: Export the list of keys to a file (one key per line)
240+
- **`--filter=`**: Filter keys using a regex pattern (e.g., `/^user_/` matches keys starting with "user_")
241+
- **`--info`**: Show additional information about each key (size, expiration time, data type)
242+
- **`--limit=`**: Limit the number of keys to retrieve and display (helps with performance on large caches)
243+
244+
### Programmatic Usage
191245
192246
You can also use the `CacheUiLaravel` class directly in your code:
193247
@@ -200,40 +254,196 @@ $keys = CacheUiLaravel::getAllKeys();
200254
// Get all cache keys from a specific store
201255
$redisKeys = CacheUiLaravel::getAllKeys('redis');
202256
257+
// Get limited number of keys (useful for large caches)
258+
$limitedKeys = CacheUiLaravel::getAllKeys('redis', 100);
259+
203260
// Delete a specific key from default store
204261
$deleted = CacheUiLaravel::forgetKey('user_1_profile');
205262
206263
// Delete a key from a specific store
207264
$deleted = CacheUiLaravel::forgetKey('session_data', 'redis');
265+
266+
// The methods include validation:
267+
// - Empty or invalid store names will use the default store
268+
// - Empty keys will return false
269+
// - Non-existent stores will throw an exception (if logging is enabled, errors are logged)
208270
```
209271
272+
### Advanced Use Cases
273+
274+
#### Batch Operations
275+
276+
```php
277+
use Abr4xas\CacheUiLaravel\Facades\CacheUiLaravel;
278+
279+
// Get all keys matching a pattern
280+
$allKeys = CacheUiLaravel::getAllKeys('redis');
281+
$userKeys = array_filter($allKeys, fn($key) => str_starts_with($key, 'user_'));
282+
283+
// Delete multiple keys
284+
foreach ($userKeys as $key) {
285+
CacheUiLaravel::forgetKey($key, 'redis');
286+
}
287+
```
288+
289+
#### Monitoring Cache Size
290+
291+
```php
292+
use Abr4xas\CacheUiLaravel\Facades\CacheUiLaravel;
293+
use Illuminate\Support\Facades\Cache;
294+
295+
// Use limit parameter for better performance on large caches
296+
$keys = CacheUiLaravel::getAllKeys('redis', 1000);
297+
$totalSize = 0;
298+
299+
foreach ($keys as $key) {
300+
$value = Cache::get($key);
301+
if ($value !== null) {
302+
$totalSize += strlen(serialize($value));
303+
}
304+
}
305+
306+
echo "Total cache size: " . number_format($totalSize / 1024 / 1024, 2) . " MB";
307+
```
308+
309+
#### Error Handling and Logging
310+
311+
The package includes comprehensive error handling. You can enable logging to track cache operation errors:
312+
313+
```php
314+
// In config/cache-ui-laravel.php or .env
315+
'enable_logging' => true,
316+
317+
// Errors will be logged to Laravel's log file
318+
// Example log entry:
319+
// [2024-01-01 12:00:00] local.WARNING: Cache UI: Failed to retrieve keys from store 'redis': Connection timeout
320+
```
321+
322+
When logging is enabled, the following operations will log errors:
323+
- Key retrieval failures
324+
- Key deletion failures
325+
- Driver-specific errors (Redis connection, file system, database)
326+
327+
#### Cache Key Analysis
328+
329+
```php
330+
use Abr4xas\CacheUiLaravel\Facades\CacheUiLaravel;
331+
332+
$keys = CacheUiLaravel::getAllKeys('redis');
333+
$patterns = [];
334+
335+
foreach ($keys as $key) {
336+
$prefix = explode('_', $key)[0] ?? 'unknown';
337+
$patterns[$prefix] = ($patterns[$prefix] ?? 0) + 1;
338+
}
339+
340+
arsort($patterns);
341+
print_r($patterns); // Shows key distribution by prefix
342+
```
343+
344+
#### Performance Optimization
345+
346+
For large caches, always use the `limit` parameter to improve performance:
347+
348+
```php
349+
use Abr4xas\CacheUiLaravel\Facades\CacheUiLaravel;
350+
351+
// Get first 100 keys (useful for pagination)
352+
$firstBatch = CacheUiLaravel::getAllKeys('redis', 100);
353+
354+
// Process in batches
355+
$offset = 0;
356+
$limit = 100;
357+
do {
358+
$keys = CacheUiLaravel::getAllKeys('redis', $limit);
359+
// Process keys...
360+
$offset += $limit;
361+
} while (count($keys) === $limit);
362+
```
363+
364+
**Note**: The package automatically uses `SCAN` for Redis instead of `KEYS *`, which is safer for production environments as it doesn't block the Redis server.
365+
210366
## Testing
211367
368+
Run the test suite:
369+
212370
```bash
371+
# Run all tests
213372
composer test:unit
373+
374+
# Run specific test file
375+
vendor/bin/pest tests/Unit/CacheUiLaravelMethodsTest.php
376+
377+
# Run with coverage
378+
vendor/bin/pest --coverage
214379
```
215380
381+
The package includes comprehensive test coverage:
382+
- **Unit tests**: Individual component testing
383+
- **Integration tests**: End-to-end workflow testing
384+
- **Edge case tests**: Error handling and boundary conditions
385+
386+
## Technical Details
387+
388+
### Performance Optimizations
389+
390+
The package includes several performance optimizations:
391+
392+
1. **Redis SCAN**: Uses `SCAN` instead of `KEYS *` to prevent blocking Redis in production
393+
2. **Key Limits**: Optional limit parameters for all drivers to avoid loading all keys at once
394+
3. **Early Termination**: File driver stops reading files once the limit is reached
395+
4. **Efficient Queries**: Database driver uses optimized queries with limits
396+
397+
### Error Handling
398+
399+
The package includes comprehensive error handling:
400+
401+
- **Validation**: Input validation for store names and cache keys
402+
- **Graceful Degradation**: Falls back to alternative methods when primary methods fail
403+
- **Logging**: Optional error logging via Laravel's Log facade
404+
- **Exception Handling**: Catches and handles driver-specific exceptions
405+
406+
### KeyAwareFileStore Methods
407+
408+
The `key-aware-file` driver implements all standard Laravel cache methods:
409+
410+
- `put($key, $value, $seconds)` - Store an item with expiration
411+
- `get($key, $default = null)` - Retrieve an item
412+
- `add($key, $value, $seconds)` - Store an item only if it doesn't exist
413+
- `forever($key, $value)` - Store an item permanently
414+
- `increment($key, $value = 1)` - Increment a numeric value
415+
- `decrement($key, $value = 1)` - Decrement a numeric value
416+
- `remember($key, $ttl, $callback)` - Get or store a value
417+
- `rememberForever($key, $callback)` - Get or store a value permanently
418+
- `pull($key, $default = null)` - Get and delete an item
419+
- `has($key)` - Check if an item exists
420+
- `forget($key)` - Delete an item
421+
- `flush()` - Clear all items
422+
216423
## TODO
217424
218-
The following tests need to be implemented to fully validate the new `KeyAwareFileStore` functionality:
425+
The following tests and improvements are planned or in progress:
219426
220427
### Unit Tests for KeyAwareFileStore
221-
- [ ] Test `put()` method with various data types (string, integer, array, boolean, null)
222-
- [ ] Test `get()` method with wrapped and unwrapped data formats
223-
- [ ] Test `add()` method behavior and return values
224-
- [ ] Test `forever()` method with zero expiration
225-
- [ ] Test `increment()` method with numeric values
226-
- [ ] Test backward compatibility with legacy cache files
227-
- [ ] Test error handling for corrupted cache files
228-
- [ ] Test file permissions and directory creation
428+
- [x] Test `put()` method with various data types (string, integer, array, boolean, null)
429+
- [x] Test `get()` method with wrapped and unwrapped data formats
430+
- [x] Test `add()` method behavior and return values
431+
- [x] Test `forever()` method with zero expiration
432+
- [x] Test `increment()` method with numeric values
433+
- [x] Test `decrement()` method with numeric values
434+
- [x] Test backward compatibility with legacy cache files
435+
- [x] Test error handling for corrupted cache files
436+
- [x] Test file permissions and directory creation
437+
- [x] Test `remember()` and `rememberForever()` methods
438+
- [x] Test `pull()`, `has()`, and `flush()` methods
229439
230440
### Integration Tests
231-
- [ ] Test complete cache workflow (store → retrieve → delete)
232-
- [ ] Test multiple keys with different expiration times
233-
- [ ] Test cache key listing with `getAllKeys()` method
441+
- [x] Test complete cache workflow (store → retrieve → delete)
442+
- [x] Test multiple keys with different expiration times
443+
- [x] Test cache key listing with `getAllKeys()` method
234444
- [x] Test cache key deletion with `forgetKey()` method
235-
- [ ] Test mixed wrapped and legacy data scenarios
236-
- [ ] Test performance with large numbers of cache keys
445+
- [x] Test mixed wrapped and legacy data scenarios
446+
- [x] Test performance with large numbers of cache keys
237447
238448
### Driver Registration Tests
239449
- [ ] Test custom driver registration in `AppServiceProvider`
@@ -243,17 +453,54 @@ The following tests need to be implemented to fully validate the new `KeyAwareFi
243453
- [ ] Test error handling for invalid paths and permissions
244454
245455
### CacheUiLaravel Integration Tests
246-
- [ ] Test `getAllKeys()` method with `key-aware-file` driver
456+
- [x] Test `getAllKeys()` method with `key-aware-file` driver
247457
- [x] Test `forgetKey()` method with `key-aware-file` driver
248-
- [ ] Test mixed driver scenarios (Redis + File + Database)
249-
- [ ] Test error handling and graceful degradation
458+
- [x] Test mixed driver scenarios (Redis + File + Database)
459+
- [x] Test error handling and graceful degradation
250460
251461
### Edge Cases and Error Handling
462+
- [x] Test with invalid serialized data
463+
- [x] Test with very large cache values
464+
- [x] Test with special characters in cache keys
252465
- [ ] Test with read-only file systems
253466
- [ ] Test with insufficient disk space
254-
- [ ] Test with invalid serialized data
255-
- [ ] Test with very large cache values
256-
- [ ] Test with special characters in cache keys
467+
468+
### Performance Tests
469+
- [ ] Test benchmark for operations with many keys
470+
- [ ] Test load scenarios to validate optimizations
471+
472+
## Configuration Options
473+
474+
The package provides several configuration options in `config/cache-ui-laravel.php`:
475+
476+
| Option | Type | Default | Description |
477+
|--------|------|---------|-------------|
478+
| `default_store` | string\|null | `null` | Default cache store to use when running the command |
479+
| `preview_limit` | int | `100` | Maximum characters to display in value preview |
480+
| `search_scroll` | int | `15` | Number of visible items in search menu |
481+
| `keys_limit` | int\|null | `null` | Maximum number of keys to retrieve (null = unlimited) |
482+
| `enable_logging` | bool | `false` | Enable error logging for cache operations |
483+
| `operation_timeout` | int | `0` | Timeout in seconds for cache operations (0 = no timeout) |
484+
485+
### Performance Considerations
486+
487+
- **Redis**: The package uses `SCAN` instead of `KEYS *` for safer key retrieval in production environments
488+
- **File Driver**: Supports optional `limit` parameter to avoid reading all files in large cache directories
489+
- **Database Driver**: Supports optional `limit` parameter to avoid loading all cache records at once
490+
- **Recommended**: Always use the `--limit` option or `keys_limit` config for large caches to improve performance
491+
492+
### Environment Variables
493+
494+
You can configure these options via environment variables:
495+
496+
```env
497+
CACHE_UI_DEFAULT_STORE=redis
498+
CACHE_UI_PREVIEW_LIMIT=150
499+
CACHE_UI_SEARCH_SCROLL=20
500+
CACHE_UI_KEYS_LIMIT=1000
501+
CACHE_UI_ENABLE_LOGGING=true
502+
CACHE_UI_OPERATION_TIMEOUT=30
503+
```
257504
258505
## Changelog
259506

0 commit comments

Comments
 (0)