Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/MiguelNavas19/miapibcv/llms.txt

Use this file to discover all available pages before exploring further.

Why Caching?

Mi API BCV implements an intelligent caching strategy to:
  1. Reduce database queries: Avoid hitting the database on every API request
  2. Improve response times: Serve cached data in milliseconds instead of querying
  3. Minimize resource usage: Lower CPU and database load on high-traffic scenarios
  4. Provide consistency: Same data for all requests within the cache window

Cache Implementation

Cache Configuration

By default, Mi API BCV uses database caching as configured in .env:
.env
CACHE_STORE=database
Other supported cache drivers:
  • file - File-based caching
  • redis - Redis (requires Redis server)
  • memcached - Memcached (requires Memcached server)
  • database - Database table (default, no additional setup needed)

Cache Duration

All cached data has a 1-hour TTL (Time To Live):
app/Http/Controllers/Api/ScraperController.php
$cacheKey = 'tasas_bancos_' . now()->toDateString();

$records = Cache::remember($cacheKey, 3600, function () {
    return ReferenceRecord::where('date', now()->toDateString())
        ->get()
        ->keyBy('source');
});
Why 1 hour?
  • Exchange rates don’t change frequently during the day
  • Balances freshness with performance
  • Scheduled updates run multiple times, ensuring fresh data

Cache Keys

Cache keys are date-based to ensure proper isolation:
'tasas_bancos_' . now()->toDateString()
// Example: 'tasas_bancos_2026-03-04'
This means:
  • Each date has its own cache entry
  • Historical queries get their own cache
  • Today’s data is cached separately from yesterday’s

Example Cache Keys

  • Today’s rates: tasas_bancos_2026-03-04
  • Yesterday’s rates: tasas_bancos_2026-03-03
  • Specific date: tasas_bancos_2026-03-01

Cache Flow

1

API Request Received

Client makes a request to / or /info/{date}/{source?}
2

Generate Cache Key

Controller generates a cache key based on the requested date:
$cacheKey = 'tasas_bancos_' . $queryDate;
3

Check Cache

Laravel checks if the key exists in cache:
Cache::remember($cacheKey, 3600, function () {
    // This closure only runs if cache misses
    return ReferenceRecord::where('date', $queryDate)->get();
});
4

Cache Hit: Return Immediately

If cached data exists and hasn’t expired, return it instantly without touching the database.
5

Cache Miss: Query Database

If no cached data or it expired:
  1. Query the database
  2. Store the result in cache for 3600 seconds
  3. Return the data

Automatic Cache Invalidation

When new exchange rates are scraped and saved, the cache is automatically invalidated using a model observer:
app/Observers/ReferenceObserver.php
class ReferenceObserver
{
    public function saved(ReferenceRecord $referenceRecord): void
    {
        Cache::forget('tasas_bancos_' . $referenceRecord->date);
    }
    
    public function created(ReferenceRecord $referenceRecord): void
    {
        Cache::forget('tasas_bancos_' . $referenceRecord->date);
    }
    
    public function updated(ReferenceRecord $referenceRecord): void
    {
        Cache::forget('tasas_bancos_' . $referenceRecord->date);
    }
    
    public function deleted(ReferenceRecord $referenceRecord): void
    {
        Cache::forget('tasas_bancos_' . $referenceRecord->date);
    }
}

Observer Registration

The observer is registered using PHP attributes:
app/Models/ReferenceRecord.php
use Illuminate\Database\Eloquent\Attributes\ObservedBy;

#[ObservedBy([ReferenceObserver::class])]
class ReferenceRecord extends Model
{
    //
}
This approach ensures cache consistency without manual cache management in controllers or commands.

Cache Warming

The system implements cache warming through the scheduled rates:update command:
routes/console.php
$schedules = [
    '00:00', '00:30', '02:00', '03:00', '03:30', '04:00',
    '05:00', '05:30', '06:30', '07:00', '07:30'
];

foreach ($schedules as $time) {
    Schedule::command('rates:update')->dailyAt($time);
}
How it works:
  1. Command fetches fresh rates from banks
  2. New records are saved to database
  3. Observer automatically invalidates old cache
  4. Next API request rebuilds cache with fresh data
This “warm cache” strategy ensures the first request after an update gets fresh data and caches it for subsequent requests.

Cache Performance

Without Cache

Request → Controller → Database Query → Process Results → Response
Average: ~50-100ms

With Cache (Hit)

Request → Controller → Cache Retrieve → Response
Average: ~5-10ms
Performance improvement: ~10x faster response times

Cache Operations

Viewing Cache Contents

For debugging, you can inspect cache contents using tinker:
php artisan tinker
Cache::get('tasas_bancos_2026-03-04');

Manually Clearing Cache

# Clear all cache
php artisan cache:clear

# Clear specific key in tinker
php artisan tinker
Cache::forget('tasas_bancos_2026-03-04');

Checking Cache Driver

php artisan tinker
config('cache.default');

Advanced Caching Strategies

Using Redis for Better Performance

For high-traffic applications, switch to Redis:
1

Install Redis

sudo apt-get install redis-server  # Ubuntu/Debian
brew install redis                 # macOS
2

Install PHP Redis Extension

pecl install redis
3

Update .env

CACHE_STORE=redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
4

Clear Config Cache

php artisan config:clear

Cache Tags (Redis/Memcached only)

If using Redis, you can implement cache tags for better organization:
// Tag-based caching
Cache::tags(['rates', 'bcv'])->remember($cacheKey, 3600, function () {
    return ReferenceRecord::where('source', 'bcv')->get();
});

// Clear all rate caches
Cache::tags(['rates'])->flush();
Cache tags are NOT supported with file or database drivers. Only use with Redis or Memcached.

Cache Best Practices

1. Use Date-Based Keys

Always include the date in cache keys to prevent data from different days mixing:
// Good
$cacheKey = 'tasas_bancos_' . $date;

// Bad
$cacheKey = 'tasas_bancos';

2. Set Appropriate TTL

1 hour is ideal for exchange rates, but adjust based on your needs:
// 1 hour (current)
Cache::remember($key, 3600, $callback);

// 30 minutes
Cache::remember($key, 1800, $callback);

// 1 day
Cache::remember($key, 86400, $callback);

3. Handle Cache Failures Gracefully

try {
    $data = Cache::remember($key, 3600, function () {
        return ReferenceRecord::where('date', $date)->get();
    });
} catch (\Exception $e) {
    // Fallback to direct database query
    $data = ReferenceRecord::where('date', $date)->get();
    Log::warning('Cache failure', ['error' => $e->getMessage()]);
}

4. Monitor Cache Hit Ratio

Track cache effectiveness:
// Log cache hits and misses
Log::info('Cache check', [
    'key' => $cacheKey,
    'hit' => Cache::has($cacheKey)
]);

Troubleshooting

Check your cache configuration:
php artisan config:cache
php artisan cache:clear
Verify CACHE_STORE in .env matches your setup.
Cache may not be invalidating properly. Check:
  1. Is the observer registered?
  2. Are database writes actually saving?
  3. Try manual cache clear:
php artisan cache:clear
Run migrations to create the cache table:
php artisan migrate
The 0001_01_01_000001_create_cache_table migration creates it.
Ensure Redis is running:
redis-cli ping
# Should return: PONG
Check connection settings in .env.

Cache Monitoring

For production environments, monitor cache performance:
// In a custom middleware or service
use Illuminate\Support\Facades\Cache;

class CacheMonitoring
{
    public function logCacheStats()
    {
        $stats = [
            'driver' => config('cache.default'),
            'keys' => Cache::getStore()->getKeys(), // Driver-specific
        ];
        
        Log::info('Cache stats', $stats);
    }
}

Summary

Mi API BCV’s caching strategy:
  • 1-hour TTL balances freshness and performance
  • Date-based keys ensure data isolation
  • Automatic invalidation via model observers
  • Database driver for simple setup (switchable to Redis)
  • Cache warming through scheduled commands
  • ~10x performance improvement on cache hits