diff --git a/http-service/src/main/java/net/runelite/http/service/SpringBootWebApplication.java b/http-service/src/main/java/net/runelite/http/service/SpringBootWebApplication.java index 4ff7fd3b1d..1ec97c0e0c 100644 --- a/http-service/src/main/java/net/runelite/http/service/SpringBootWebApplication.java +++ b/http-service/src/main/java/net/runelite/http/service/SpringBootWebApplication.java @@ -44,11 +44,13 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.support.SpringBootServletInitializer; import org.springframework.context.annotation.Bean; +import org.springframework.scheduling.annotation.EnableScheduling; import org.sql2o.Sql2o; import org.sql2o.converters.Converter; import org.sql2o.quirks.NoQuirks; @SpringBootApplication +@EnableScheduling @Slf4j public class SpringBootWebApplication extends SpringBootServletInitializer { diff --git a/http-service/src/main/java/net/runelite/http/service/item/ItemController.java b/http-service/src/main/java/net/runelite/http/service/item/ItemController.java index d943f0747e..859540b4df 100644 --- a/http-service/src/main/java/net/runelite/http/service/item/ItemController.java +++ b/http-service/src/main/java/net/runelite/http/service/item/ItemController.java @@ -176,25 +176,30 @@ public class ItemController .build(); } } + else if (priceEntry == null) + { + // Price is unknown + List prices = itemService.fetchPrice(itemId); + + if (prices == null || prices.isEmpty()) + { + cachedEmpty.put(itemId, itemId); + return ResponseEntity.notFound() + .header(RUNELITE_CACHE, "MISS") + .build(); + } + + // Get the most recent price + priceEntry = prices.get(prices.size() - 1); + hit = false; + } else { Instant cacheTime = now.minus(CACHE_DUATION); - if (priceEntry == null || priceEntry.getFetched_time().isBefore(cacheTime)) + if (priceEntry.getFetched_time().isBefore(cacheTime)) { - // Price is unknown, or was fetched too long ago - List prices = itemService.fetchPrice(itemId); - - if (prices == null || prices.isEmpty()) - { - cachedEmpty.put(itemId, itemId); - return ResponseEntity.notFound() - .header(RUNELITE_CACHE, "MISS") - .build(); - } - - // Get the most recent price - priceEntry = prices.get(prices.size() - 1); - hit = false; + // Queue a check for the price + itemService.queueLookup(itemId); } } diff --git a/http-service/src/main/java/net/runelite/http/service/item/ItemService.java b/http-service/src/main/java/net/runelite/http/service/item/ItemService.java index 4c34562447..9138df358c 100644 --- a/http-service/src/main/java/net/runelite/http/service/item/ItemService.java +++ b/http-service/src/main/java/net/runelite/http/service/item/ItemService.java @@ -32,6 +32,7 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentLinkedQueue; import lombok.extern.slf4j.Slf4j; import net.runelite.http.api.RuneLiteAPI; import net.runelite.http.api.item.ItemType; @@ -40,6 +41,7 @@ import okhttp3.Request; import okhttp3.Response; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.sql2o.Connection; import org.sql2o.Query; @@ -78,7 +80,10 @@ public class ItemService private static final String CREATE_PRICES_FK = "ALTER TABLE `prices`\n" + " ADD CONSTRAINT `item` FOREIGN KEY (`item`) REFERENCES `items` (`id`);"; + private static final int MAX_PENDING_LOOKUPS = 512; + private final Sql2o sql2o; + private final ConcurrentLinkedQueue pendingPriceLookups = new ConcurrentLinkedQueue<>(); @Autowired public ItemService(@Qualifier("Runelite SQL2O") Sql2o sql2o) @@ -339,4 +344,26 @@ public class ItemService return response.body().bytes(); } } + + public void queueLookup(int itemId) + { + if (pendingPriceLookups.size() >= MAX_PENDING_LOOKUPS) + { + return; + } + + pendingPriceLookups.add(itemId); + } + + @Scheduled(fixedDelay = 5000) + public void checkPrices() + { + Integer itemId = pendingPriceLookups.poll(); + if (itemId == null) + { + return; + } + + fetchPrice(itemId); + } } diff --git a/http-service/src/test/java/net/runelite/http/service/SpringBootWebApplicationTest.java b/http-service/src/test/java/net/runelite/http/service/SpringBootWebApplicationTest.java index 0edc296898..0503c075d8 100644 --- a/http-service/src/test/java/net/runelite/http/service/SpringBootWebApplicationTest.java +++ b/http-service/src/test/java/net/runelite/http/service/SpringBootWebApplicationTest.java @@ -34,11 +34,13 @@ import org.junit.Test; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; +import org.springframework.scheduling.annotation.EnableScheduling; import org.sql2o.Sql2o; import org.sql2o.converters.Converter; import org.sql2o.quirks.NoQuirks; @SpringBootApplication +@EnableScheduling public class SpringBootWebApplicationTest { @Bean("Runelite SQL2O")