Ever debug a bug where your financial reports showed negative margins, only to discover you were mixing cached and fresh data in the same transaction?
I hit this in an API endpoint that calculated order profitability. The method fetched product costs fresh from the database (good!), but the companion method that grabbed retail prices was still using Redis cache (bad!). When prices changed, we’d calculate margins using old cached prices and new costs. Hello, mystery losses.
The fix wasn’t just “disable cache everywhere” — Redis cache is there for a reason. The real issue was inconsistent cache behavior within the same transaction.
Here’s the pattern that saved us:
class OrderCalculator
{
public function calculateMargin(Order $order, bool $useCache = true): float
{
$cost = $this->getCost($order->product_id, useCache: $useCache);
$price = $this->getPrice($order->product_id, useCache: $useCache);
return $price - $cost;
}
private function getCost(int $productId, bool $useCache = true): float
{
if (!$useCache) {
return Product::find($productId)->cost;
}
return Cache::remember("product.{$productId}.cost", 3600, function() use ($productId) {
return Product::find($productId)->cost;
});
}
private function getPrice(int $productId, bool $useCache = true): float
{
if (!$useCache) {
return Product::find($productId)->price;
}
return Cache::remember("product.{$productId}.price", 3600, function() use ($productId) {
return Product::find($productId)->price;
});
}
}
// In write operations (order creation, updates):
$margin = $calculator->calculateMargin($order, useCache: false);
// In read operations (reports, dashboards):
$margin = $calculator->calculateMargin($order, useCache: true);
The key insight: make cache behavior explicit and consistent. When you’re writing data or making financial calculations, all related lookups should use the same freshness guarantee. Add a useCache parameter (default true for reads) and disable it for writes.
Your future self debugging production at 2am will thank you.

Leave a Reply