diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/currency/CurrencyConversionManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/currency/CurrencyConversionManager.java index cf2770305..cc9665b29 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/currency/CurrencyConversionManager.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/currency/CurrencyConversionManager.java @@ -37,10 +37,10 @@ public class CurrencyConversionManager implements Managed { @VisibleForTesting static final Duration FIXER_REFRESH_INTERVAL = Duration.ofHours(2); - private static final Duration COIN_GECKO_CAP_REFRESH_INTERVAL = Duration.ofMinutes(5); + private static final Duration COIN_GECKO_REFRESH_INTERVAL = Duration.ofMinutes(5); @VisibleForTesting - static final String COIN_GECKO_CAP_SHARED_CACHE_CURRENT_KEY = "CurrencyConversionManager::CoinGeckoCacheCurrent"; + static final String COIN_GECKO_SHARED_CACHE_CURRENT_KEY = "CurrencyConversionManager::CoinGeckoCacheCurrent"; private static final String COIN_GECKO_SHARED_CACHE_DATA_KEY = "CurrencyConversionManager::CoinGeckoCacheData"; @@ -89,7 +89,7 @@ public class CurrencyConversionManager implements Managed { public void start() throws Exception { cacheUpdateFuture = executor.scheduleWithFixedDelay(() -> { try { - updateCacheIfNecessary(); + update(); } catch (Throwable t) { logger.warn("Error updating currency conversions", t); } @@ -104,12 +104,39 @@ public class CurrencyConversionManager implements Managed { } @VisibleForTesting - void updateCacheIfNecessary() throws IOException { + void update() throws IOException { + updateFixerCacheIfNecessary(); + updateCoinGeckoCacheIfNecessary(); + updateEntity(); + } + + private void updateEntity() { + List entities = new LinkedList<>(); + + for (Map.Entry currency : cachedCoinGeckoValues.entrySet()) { + BigDecimal usdValue = stripTrailingZerosAfterDecimal(currency.getValue()); + + Map values = new HashMap<>(); + values.put("USD", usdValue); + + for (Map.Entry conversion : cachedFixerValues.entrySet()) { + values.put(conversion.getKey(), stripTrailingZerosAfterDecimal(conversion.getValue().multiply(usdValue))); + } + + entities.add(new CurrencyConversionEntity(currency.getKey(), values)); + } + + this.cached.set(new CurrencyConversionEntityList(entities, clock.millis())); + } + + private void updateFixerCacheIfNecessary() throws IOException { if (Duration.between(fixerUpdatedTimestamp, clock.instant()).abs().compareTo(FIXER_REFRESH_INTERVAL) >= 0 || cachedFixerValues == null) { this.cachedFixerValues = new HashMap<>(fixerClient.getConversionsForBase("USD")); this.fixerUpdatedTimestamp = clock.instant(); } + } + private void updateCoinGeckoCacheIfNecessary() throws IOException { { final Map coinGeckoValuesFromSharedCache = cacheCluster.withCluster(connection -> { final Map parsedSharedCacheData = new HashMap<>(); @@ -126,9 +153,9 @@ public class CurrencyConversionManager implements Managed { } final boolean shouldUpdateSharedCache = cacheCluster.withCluster(connection -> - "OK".equals(connection.sync().set(COIN_GECKO_CAP_SHARED_CACHE_CURRENT_KEY, + "OK".equals(connection.sync().set(COIN_GECKO_SHARED_CACHE_CURRENT_KEY, "true", - SetArgs.Builder.nx().ex(COIN_GECKO_CAP_REFRESH_INTERVAL)))); + SetArgs.Builder.nx().ex(COIN_GECKO_REFRESH_INTERVAL)))); if (shouldUpdateSharedCache || cachedCoinGeckoValues == null) { final Map conversionRatesFromCoinGecko = new HashMap<>(currencies.size()); @@ -150,23 +177,6 @@ public class CurrencyConversionManager implements Managed { }); } } - - List entities = new LinkedList<>(); - - for (Map.Entry currency : cachedCoinGeckoValues.entrySet()) { - BigDecimal usdValue = stripTrailingZerosAfterDecimal(currency.getValue()); - - Map values = new HashMap<>(); - values.put("USD", usdValue); - - for (Map.Entry conversion : cachedFixerValues.entrySet()) { - values.put(conversion.getKey(), stripTrailingZerosAfterDecimal(conversion.getValue().multiply(usdValue))); - } - - entities.add(new CurrencyConversionEntity(currency.getKey(), values)); - } - - this.cached.set(new CurrencyConversionEntityList(entities, clock.millis())); } private BigDecimal stripTrailingZerosAfterDecimal(BigDecimal bigDecimal) { diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/currency/CurrencyConversionManagerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/currency/CurrencyConversionManagerTest.java index 126591491..2434245c0 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/currency/CurrencyConversionManagerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/currency/CurrencyConversionManagerTest.java @@ -50,7 +50,7 @@ class CurrencyConversionManagerTest { CurrencyConversionManager manager = new CurrencyConversionManager(fixerClient, coinGeckoClient, REDIS_CLUSTER_EXTENSION.getRedisCluster(), List.of("FOO"), EXECUTOR, Clock.systemUTC()); - manager.updateCacheIfNecessary(); + manager.update(); CurrencyConversionEntityList conversions = manager.getCurrencyConversions().orElseThrow(); @@ -79,7 +79,7 @@ class CurrencyConversionManagerTest { CurrencyConversionManager manager = new CurrencyConversionManager(fixerClient, CoinGeckoClient, REDIS_CLUSTER_EXTENSION.getRedisCluster(), List.of("FOO"), EXECUTOR, Clock.systemUTC()); - manager.updateCacheIfNecessary(); + manager.update(); CurrencyConversionEntityList conversions = manager.getCurrencyConversions().orElseThrow(); @@ -108,7 +108,7 @@ class CurrencyConversionManagerTest { CurrencyConversionManager manager = new CurrencyConversionManager(fixerClient, CoinGeckoClient, REDIS_CLUSTER_EXTENSION.getRedisCluster(), List.of("FOO"), EXECUTOR, Clock.systemUTC()); - manager.updateCacheIfNecessary(); + manager.update(); CurrencyConversionEntityList conversions = manager.getCurrencyConversions().orElseThrow(); @@ -137,11 +137,11 @@ class CurrencyConversionManagerTest { CurrencyConversionManager manager = new CurrencyConversionManager(fixerClient, CoinGeckoClient, REDIS_CLUSTER_EXTENSION.getRedisCluster(), List.of("FOO"), EXECUTOR, Clock.systemUTC()); - manager.updateCacheIfNecessary(); + manager.update(); when(CoinGeckoClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("3.50")); - manager.updateCacheIfNecessary(); + manager.update(); CurrencyConversionEntityList conversions = manager.getCurrencyConversions().orElseThrow(); @@ -169,13 +169,13 @@ class CurrencyConversionManagerTest { CurrencyConversionManager manager = new CurrencyConversionManager(fixerClient, CoinGeckoClient, REDIS_CLUSTER_EXTENSION.getRedisCluster(), List.of("FOO"), EXECUTOR, Clock.systemUTC()); - manager.updateCacheIfNecessary(); + manager.update(); REDIS_CLUSTER_EXTENSION.getRedisCluster().useCluster(connection -> - connection.sync().del(CurrencyConversionManager.COIN_GECKO_CAP_SHARED_CACHE_CURRENT_KEY)); + connection.sync().del(CurrencyConversionManager.COIN_GECKO_SHARED_CACHE_CURRENT_KEY)); when(CoinGeckoClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("3.50")); - manager.updateCacheIfNecessary(); + manager.update(); CurrencyConversionEntityList conversions = manager.getCurrencyConversions().orElseThrow(); @@ -210,7 +210,7 @@ class CurrencyConversionManagerTest { CurrencyConversionManager manager = new CurrencyConversionManager(fixerClient, CoinGeckoClient, REDIS_CLUSTER_EXTENSION.getRedisCluster(), List.of("FOO"), EXECUTOR, clock); - manager.updateCacheIfNecessary(); + manager.update(); when(CoinGeckoClient.getSpotPrice(eq("FOO"), eq("USD"))).thenReturn(new BigDecimal("3.50")); when(fixerClient.getConversionsForBase(eq("USD"))).thenReturn(Map.of( @@ -223,7 +223,7 @@ class CurrencyConversionManagerTest { when(clock.instant()).thenReturn(afterFixerExpiration); when(clock.millis()).thenReturn(afterFixerExpiration.toEpochMilli()); - manager.updateCacheIfNecessary(); + manager.update(); CurrencyConversionEntityList conversions = manager.getCurrencyConversions().orElseThrow();