Better cache

This commit is contained in:
James Cole 2024-07-06 15:42:50 +02:00
parent 7219c90957
commit cba1213dd1
No known key found for this signature in database
GPG Key ID: B49A324B7EAD6D80

View File

@ -29,6 +29,7 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\CurrencyExchangeRate; use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
/** /**
@ -49,9 +50,9 @@ class ExchangeRateConverter
public function convert(TransactionCurrency $from, TransactionCurrency $to, Carbon $date, string $amount): string public function convert(TransactionCurrency $from, TransactionCurrency $to, Carbon $date, string $amount): string
{ {
if (false === config('cer.enabled')) { if (false === config('cer.enabled')) {
Log::debug('ExchangeRateConverter: disabled, return amount as is.');
return $amount; return $amount;
} }
Log::debug('convert()');
$rate = $this->getCurrencyRate($from, $to, $date); $rate = $this->getCurrencyRate($from, $to, $date);
return bcmul($amount, $rate); return bcmul($amount, $rate);
@ -63,9 +64,9 @@ class ExchangeRateConverter
public function getCurrencyRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string public function getCurrencyRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string
{ {
if (false === config('cer.enabled')) { if (false === config('cer.enabled')) {
Log::debug('ExchangeRateConverter: disabled, return "1".');
return '1'; return '1';
} }
Log::debug('getCurrencyRate()');
$rate = $this->getRate($from, $to, $date); $rate = $this->getRate($from, $to, $date);
return '0' === $rate ? '1' : $rate; return '0' === $rate ? '1' : $rate;
@ -76,25 +77,33 @@ class ExchangeRateConverter
*/ */
private function getRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string private function getRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string
{ {
Log::debug('getRate()'); $key = $this->getCacheKey($from, $to, $date);
if ($this->isPrepared && $this->noPreparedRates) { $res = Cache::get($key, null);
$fallback = $this->fallback[$from->id][$to->id] ?? '0';
Log::debug(sprintf('Return fallback rate from #%d to #%d on %s: %s', $from->id, $to->id, $date->format('Y-m-d'), $fallback));
return $fallback; // find in cache
if (null !== $res) {
Log::debug(sprintf('ExchangeRateConverter: Return cached rate from #%d to #%d on %s.', $from->id, $to->id, $date->format('Y-m-d')));
return $res;
} }
// first attempt:
// find in database
$rate = $this->getFromDB($from->id, $to->id, $date->format('Y-m-d')); $rate = $this->getFromDB($from->id, $to->id, $date->format('Y-m-d'));
if (null !== $rate) { if (null !== $rate) {
Cache::forever($key, $rate);
Log::debug(sprintf('ExchangeRateConverter: Return DB rate from #%d to #%d on %s.', $from->id, $to->id, $date->format('Y-m-d')));
return $rate; return $rate;
} }
// no result. perhaps the other way around?
// find reverse in database
$rate = $this->getFromDB($to->id, $from->id, $date->format('Y-m-d')); $rate = $this->getFromDB($to->id, $from->id, $date->format('Y-m-d'));
if (null !== $rate) { if (null !== $rate) {
return bcdiv('1', $rate); $rate = bcdiv('1', $rate);
Cache::forever($key, $rate);
Log::debug(sprintf('ExchangeRateConverter: Return DB rate from #%d to #%d on %s.', $from->id, $to->id, $date->format('Y-m-d')));
return $rate;
} }
// if nothing in place, fall back on the rate for $from to EUR // fallback scenario.
$first = $this->getEuroRate($from, $date); $first = $this->getEuroRate($from, $date);
$second = $this->getEuroRate($to, $date); $second = $this->getEuroRate($to, $date);
@ -102,17 +111,18 @@ class ExchangeRateConverter
if (0 === bccomp('0', $first) || 0 === bccomp('0', $second)) { if (0 === bccomp('0', $first) || 0 === bccomp('0', $second)) {
Log::warning(sprintf('$first is "%s" and $second is "%s"', $first, $second)); Log::warning(sprintf('$first is "%s" and $second is "%s"', $first, $second));
return '0'; return '1';
} }
$second = bcdiv('1', $second); $second = bcdiv('1', $second);
$rate = bcmul($first, $second);
return bcmul($first, $second); Log::debug(sprintf('ExchangeRateConverter: Return DB rate from #%d to #%d on %s.', $from->id, $to->id, $date->format('Y-m-d')));
Cache::forever($key, $rate);
return $rate;
} }
private function getFromDB(int $from, int $to, string $date): ?string private function getFromDB(int $from, int $to, string $date): ?string
{ {
Log::debug('getFromDB()');
if ($from === $to) { if ($from === $to) {
return '1'; return '1';
} }
@ -121,7 +131,7 @@ class ExchangeRateConverter
// perhaps the rate has been cached during this particular run // perhaps the rate has been cached during this particular run
$preparedRate = $this->prepared[$date][$from][$to] ?? null; $preparedRate = $this->prepared[$date][$from][$to] ?? null;
if (null !== $preparedRate && 0 !== bccomp('0', $preparedRate)) { if (null !== $preparedRate && 0 !== bccomp('0', $preparedRate)) {
Log::debug(sprintf('Found prepared rate from #%d to #%d on %s.', $from, $to, $date)); Log::debug(sprintf('ExchangeRateConverter: Found prepared rate from #%d to #%d on %s.', $from, $to, $date));
return $preparedRate; return $preparedRate;
} }
@ -133,7 +143,7 @@ class ExchangeRateConverter
if ('' === $rate) { if ('' === $rate) {
return null; return null;
} }
Log::debug(sprintf('Found cached rate from #%d to #%d on %s.', $from, $to, $date)); Log::debug(sprintf('ExchangeRateConverter: Found !cached! rate from #%d to #%d on %s.', $from, $to, $date));
return $rate; return $rate;
} }
@ -145,22 +155,21 @@ class ExchangeRateConverter
->where('to_currency_id', $to) ->where('to_currency_id', $to)
->where('date', '<=', $date) ->where('date', '<=', $date)
->orderBy('date', 'DESC') ->orderBy('date', 'DESC')
->first() ->first();
;
++$this->queryCount; ++$this->queryCount;
$rate = (string) $result?->rate; $rate = (string) $result?->rate;
if ('' === $rate) { if ('' === $rate) {
app('log')->debug(sprintf('Found no rate for #%d->#%d (%s) in the DB.', $from, $to, $date)); app('log')->debug(sprintf('ExchangeRateConverter: Found no rate for #%d->#%d (%s) in the DB.', $from, $to, $date));
return null; return null;
} }
if (0 === bccomp('0', $rate)) { if (0 === bccomp('0', $rate)) {
app('log')->debug(sprintf('Found rate for #%d->#%d (%s) in the DB, but it\'s zero.', $from, $to, $date)); app('log')->debug(sprintf('ExchangeRateConverter: Found rate for #%d->#%d (%s) in the DB, but it\'s zero.', $from, $to, $date));
return null; return null;
} }
app('log')->debug(sprintf('Found rate for #%d->#%d (%s) in the DB: %s.', $from, $to, $date, $rate)); app('log')->debug(sprintf('ExchangeRateConverter: Found rate for #%d->#%d (%s) in the DB: %s.', $from, $to, $date, $rate));
$cache->store($rate); $cache->store($rate);
// if the rate has not been cached during this particular run, save it // if the rate has not been cached during this particular run, save it
@ -184,7 +193,6 @@ class ExchangeRateConverter
*/ */
private function getEuroRate(TransactionCurrency $currency, Carbon $date): string private function getEuroRate(TransactionCurrency $currency, Carbon $date): string
{ {
Log::debug('getEuroRate()');
$euroId = $this->getEuroId(); $euroId = $this->getEuroId();
if ($euroId === $currency->id) { if ($euroId === $currency->id) {
return '1'; return '1';
@ -252,8 +260,7 @@ class ExchangeRateConverter
->where('to_currency_id', $to->id) ->where('to_currency_id', $to->id)
->where('date', '<=', $end->format('Y-m-d')) ->where('date', '<=', $end->format('Y-m-d'))
->where('date', '>=', $start->format('Y-m-d')) ->where('date', '>=', $start->format('Y-m-d'))
->orderBy('date', 'DESC')->get() ->orderBy('date', 'DESC')->get();
;
++$this->queryCount; ++$this->queryCount;
if (0 === $set->count()) { if (0 === $set->count()) {
Log::debug('No prepared rates found in this period, use the fallback'); Log::debug('No prepared rates found in this period, use the fallback');
@ -319,4 +326,9 @@ class ExchangeRateConverter
} }
Log::debug(sprintf('ExchangeRateConverter ran %d queries.', $this->queryCount)); Log::debug(sprintf('ExchangeRateConverter ran %d queries.', $this->queryCount));
} }
private function getCacheKey(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string
{
return sprintf('cer-%d-%d-%s', $from->id, $to->id, $date->format('Y-m-d'));
}
} }