Performance Recipes
A few end-to-end recipes. The reference for Glueful's performance surface (query caching, #[CacheResult], db:profile, the query analyzer and optimizer, response caching, configuration tuning) is Advanced → Performance.
Cache a Read, Invalidate on Write
Cache data in a service (not Response objects), and drop the entry in the same service that writes:
use Glueful\Cache\CacheStore;
use Glueful\Database\Connection;
final class CatalogService
{
public function __construct(
private Connection $db,
private CacheStore $cache,
) {}
public function featured(): array
{
return $this->cache->remember('catalog:featured', function () {
return $this->db->table('products')
->where('featured', true)
->orderBy(['popularity' => 'DESC'])
->limit(20)
->get();
}, 600);
}
public function setFeatured(string $uuid, bool $featured): void
{
$this->db->table('products')->where('uuid', $uuid)->update(['featured' => $featured]);
$this->cache->delete('catalog:featured');
}
}
If you use per-query caching (->cache()) instead, invalidate the table's cached queries after the write:
app($context, \Glueful\Database\QueryCacheService::class)->invalidateTable('products');
Table invalidation uses cache tags, which the redis and file stores support (file since 1.91; before, tag invalidation silently did nothing on the default store). On memcached, which has no tags, delete keys you know instead.
HTTP Caching for a Public Endpoint
For data that's the same for every client, let browsers and CDNs cache it, and answer revalidation with 304:
use Glueful\Http\Response;
// routes/api.php
$router->get('/config/public', [PublicConfigController::class, 'show'])
->middleware('conditional_cache:etag');
// Controller
public function show(): Response
{
$config = $this->configService->publicConfig();
return Response::success($config)
->withCacheHeaders(3600, true) // Cache-Control: public, max-age=3600
->withETag(md5(json_encode($config)));
}
For per-user data, use withCacheHeaders(300, false) (private) or no caching headers at all.
Reverse-Proxy Caching
Put nginx (or Varnish, or a CDN) in front of cacheable GET endpoints:
proxy_cache_path /var/cache/nginx/api levels=1:2 keys_zone=api_cache:10m max_size=100m;
location /api/v1/config/public {
proxy_pass http://backend;
proxy_cache api_cache;
proxy_cache_valid 200 1h;
proxy_cache_key "$request_uri";
add_header X-Cache-Status $upstream_cache_status;
}
Only cache responses that don't depend on Authorization or cookies. Actively purging a CDN needs the glueful/cdn extension; without it, edge purges are no-ops.
Find the Slow Query
# What does the database do with it?
php glueful db:profile "SELECT * FROM orders WHERE customer_uuid = ?" --params='["c-123"]' --explain --patterns
# Does the rewrite help?
php glueful db:profile --file=current.sql --compare=candidate.sql
Then index what the plan scans, and re-run. See Query Analysis and Profiling.
API Metrics
Async API performance and usage analytics with batching, daily aggregates, rate limit visibility, and system health endpoints.
Memory Management
Glueful's memory-management tooling — the memory manager, alerting, efficient iterators, object pool, chunked DB processing, lazy container — plus a note on tracing and metrics.