Skip to content

Commit

Permalink
Merge branch 'simulate-compute-intensive-task'
Browse files Browse the repository at this point in the history
  • Loading branch information
davidkopp committed Dec 16, 2023
2 parents ce71623 + 7a65b40 commit 9b4b114
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 46 deletions.
22 changes: 12 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,18 @@ Depending on your active Spring profiles, different property files are used. See

**T2 configuration:**

| property | read from env var | description |
|-------------------------------|-------------------------------|-------------------------------------------------------------------------------------------------|
| t2.cart.TTL | T2_CART_TTL | time to live of items in cart (in seconds) |
| t2.cart.taskRate | T2_CART_TASKRATE | rate at which the cart checks for items that exceeded their TTL (in milliseconds) |
| t2.inventory.size | T2_INVENTORY_SIZE | number of items to be generated into the inventory repository on start up |
| t2.inventory.TTL | T2_INVENTORY_TTL | time to live of reservations (in seconds) |
| t2.inventory.taskRate | T2_INVENTORY_TASKRATE | rate at which the inventory checks for reservations that exceeded their TTL (in milliseconds). |
| t2.payment.provider.enabled | T2_PAYMENT_PROVIDER_ENABLED | boolean value, defaults to true. if false, no connection to payment provider is made. |
| t2.payment.provider.timeout | T2_PAYMENT_PROVIDER_TIMEOUT | timeout in seconds. the payment service waits this long for an reply from the payment provider. |
| t2.payment.provider.dummy.url | T2_PAYMENT_PROVIDER_DUMMY_URL | url of the payment provider. |
| property | read from env var | description |
|--------------------------------------------------|-----------------------------------------------|------------------------------------------------------------------------------------------------------------------------------|
| t2.cart.TTL | T2_CART_TTL | time to live of items in cart (in seconds) |
| t2.cart.taskRate | T2_CART_TASKRATE | rate at which the cart checks for items that exceeded their TTL (in milliseconds) |
| t2.inventory.size | T2_INVENTORY_SIZE | number of items to be generated into the inventory repository on start up |
| t2.inventory.TTL | T2_INVENTORY_TTL | time to live of reservations (in seconds) |
| t2.inventory.taskRate | T2_INVENTORY_TASKRATE | rate at which the inventory checks for reservations that exceeded their TTL (in milliseconds). |
| t2.payment.provider.enabled | T2_PAYMENT_PROVIDER_ENABLED | boolean value, defaults to true. if false, no connection to payment provider is made. |
| t2.payment.provider.timeout | T2_PAYMENT_PROVIDER_TIMEOUT | timeout in seconds. the payment service waits this long for an reply from the payment provider. |
| t2.payment.provider.dummy.url | T2_PAYMENT_PROVIDER_DUMMY_URL | url of the payment provider. |
| t2.order.simulateComputeIntensiveTask.enabled | T2_SIMULATE_COMPUTE_INTENSIVE_TASK_ENABLED | boolean value, defaults to false. if true, a compute intensive calculation method gets used to calculate the order total |
| t2.order.simulateComputeIntensiveTask.iterations | T2_SIMULATE_COMPUTE_INTENSIVE_TASK_ITERATIONS | number of iterations the compute intensive calculation method uses. 1000000000 needs around 10 sec (depends on your machine) |

Setting either `TTL` or `taskrate` to a value less or equal to zero disables the collection of expired entries (cart module and inventory module).

Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ services:
T2_PAYMENT_PROVIDER_DUMMY_URL: http://creditinstitute:8080/pay
T2_PAYMENT_PROVIDER_TIMEOUT: 5
T2_PAYMENT_PROVIDER_ENABLED: true
T2_SIMULATE_COMPUTE_INTENSIVE_TASK_ENABLED: false
T2_SIMULATE_COMPUTE_INTENSIVE_TASK_ITERATIONS: 1000000000
SPRING_DATASOURCE_DRIVER_CLASS_NAME: org.postgresql.Driver
SPRING_DATASOURCE_USERNAME: postgres
SPRING_DATASOURCE_PASSWORD: postgres
Expand Down
60 changes: 31 additions & 29 deletions src/main/java/de/unistuttgart/t2/modulith/order/OrderService.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package de.unistuttgart.t2.modulith.order;

import de.unistuttgart.t2.modulith.cart.CartContent;
import de.unistuttgart.t2.modulith.cart.CartService;
import de.unistuttgart.t2.modulith.inventory.InventoryService;
import de.unistuttgart.t2.modulith.inventory.Product;
import de.unistuttgart.t2.modulith.order.calculation.ComputeIntensiveTotalCalculator;
import de.unistuttgart.t2.modulith.order.calculation.SimpleTotalCalculator;
import de.unistuttgart.t2.modulith.order.calculation.ITotalCalculator;
import de.unistuttgart.t2.modulith.order.repository.OrderItem;
import de.unistuttgart.t2.modulith.order.repository.OrderRepository;
import de.unistuttgart.t2.modulith.order.repository.OrderStatus;
Expand All @@ -12,11 +13,11 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.stereotype.Service;

import java.util.NoSuchElementException;
import java.util.Optional;

/**
* Creates and updates orders.
Expand All @@ -36,16 +37,41 @@ public class OrderService {

private final OrderRepository orderRepository;

private final ITotalCalculator totalCalculator;

private final Logger LOG = LoggerFactory.getLogger(getClass());

public OrderService(CartService cartService,
InventoryService inventoryService,
PaymentService paymentService,
OrderRepository orderRepository) {
this.cartService = cartService;
this.inventoryService = inventoryService;
this.paymentService = paymentService;
this.orderRepository = orderRepository;

this.totalCalculator = new SimpleTotalCalculator(cartService, inventoryService);
}

@Autowired
public OrderService(@Autowired CartService cartService,
@Autowired InventoryService inventoryService,
@Autowired PaymentService paymentService,
@Autowired OrderRepository orderRepository) {
@Autowired OrderRepository orderRepository,
@Value("${t2.order.simulateComputeIntensiveTask.enabled}") boolean simulateComputeIntensiveTask,
@Value("${t2.order.simulateComputeIntensiveTask.iterations}") int simulateComputeIntensiveTaskIterations) {
this.cartService = cartService;
this.inventoryService = inventoryService;
this.paymentService = paymentService;
this.orderRepository = orderRepository;

if (!simulateComputeIntensiveTask) {
this.totalCalculator = new SimpleTotalCalculator(cartService, inventoryService);
} else {
this.totalCalculator = new ComputeIntensiveTotalCalculator(cartService, inventoryService, simulateComputeIntensiveTaskIterations);
LOG.warn("Simulate compute intensive task enabled! Order total will be calculated {} times.",
simulateComputeIntensiveTaskIterations);
}
}

/**
Expand Down Expand Up @@ -96,7 +122,7 @@ public String confirmOrder(String sessionId, String cardNumber, String cardOwner
// Calculating total
double total;
try {
total = getTotal(sessionId);
total = totalCalculator.calculate(sessionId);
} catch (Exception e) {
throw new Exception(String.format("No order placed for session '%s'. Calculating total failed.", sessionId), e);
}
Expand Down Expand Up @@ -128,28 +154,4 @@ public String confirmOrder(String sessionId, String cardNumber, String cardOwner

return orderId;
}

/**
* Calculates the total of a users cart.
* <p>
* Depends on the cart module to get the cart content and depends on the inventory module to get the price per
* unit.
*
* @param sessionId identifies the session to get total for
* @return the total money to pay for products in the cart
*/
private double getTotal(String sessionId) {
CartContent cart = cartService.getCart(sessionId).orElse(new CartContent());

double total = 0;

for (String productId : cart.getProductIds()) {
Optional<Product> product = inventoryService.getSingleProduct(productId);
if (product.isEmpty()) {
return 0;
}
total += product.get().getPrice() * cart.getUnits(productId);
}
return total;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package de.unistuttgart.t2.modulith.order.calculation;

import de.unistuttgart.t2.modulith.cart.CartContent;
import de.unistuttgart.t2.modulith.cart.CartService;
import de.unistuttgart.t2.modulith.inventory.InventoryService;
import de.unistuttgart.t2.modulith.inventory.Product;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Optional;

/**
* Calculator to simulate a compute intensive task.
* Calculates the total sum of order.
*
* @author davidkopp
*/
public class ComputeIntensiveTotalCalculator implements ITotalCalculator {

private final CartService cartService;
private final InventoryService inventoryService;
private final int iterations;

private final Logger LOG = LoggerFactory.getLogger(getClass());

public ComputeIntensiveTotalCalculator(CartService cartService, InventoryService inventoryService, int iterations) {
this.cartService = cartService;
this.inventoryService = inventoryService;
this.iterations = iterations;
}

/**
* Calculates the total of a users cart many times to simulate a compute intensive task.
* <p>
* Depends on the cart module to get the cart content and depends on the inventory module to get the price per
* unit.
*
* @param sessionId identifies the session to get total for
* @return the total money to pay for products in the cart
*/
public double calculate(String sessionId) {
LOG.debug("Compute intensive order calculation started with {} iterations", iterations);
CartContent cart = cartService.getCart(sessionId).orElse(new CartContent());

double total = 0;
for (String productId : cart.getProductIds()) {
Optional<Product> product = inventoryService.getSingleProduct(productId);
if (product.isEmpty()) {
return 0;
}

// simulate compute intensive task
double temp = 0;
for (int i = 0; i < iterations; i++) {
temp += product.get().getPrice() * cart.getUnits(productId);
}
total += temp / iterations;
}
LOG.debug("Compute intensive order calculation finished");
return total;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package de.unistuttgart.t2.modulith.order.calculation;

public interface ITotalCalculator {

double calculate(String sessionId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package de.unistuttgart.t2.modulith.order.calculation;

import de.unistuttgart.t2.modulith.cart.CartContent;
import de.unistuttgart.t2.modulith.cart.CartService;
import de.unistuttgart.t2.modulith.inventory.InventoryService;
import de.unistuttgart.t2.modulith.inventory.Product;

import java.util.Optional;

/**
* Simple calculator to get total sum of order (default).
*
* @author davidkopp
*/
public class SimpleTotalCalculator implements ITotalCalculator {

private final CartService cartService;
private final InventoryService inventoryService;

public SimpleTotalCalculator(CartService cartService, InventoryService inventoryService) {
this.cartService = cartService;
this.inventoryService = inventoryService;
}

/**
* Calculates the total of a users cart.
* <p>
* Depends on the cart module to get the cart content and depends on the inventory module to get the price per
* unit.
*
* @param sessionId identifies the session to get total for
* @return the total money to pay for products in the cart
*/
public double calculate(String sessionId) {
CartContent cart = cartService.getCart(sessionId).orElse(new CartContent());

double total = 0;

for (String productId : cart.getProductIds()) {
Optional<Product> product = inventoryService.getSingleProduct(productId);
if (product.isEmpty()) {
return 0;
}
total += product.get().getPrice() * cart.getUnits(productId);
}
return total;
}
}
4 changes: 4 additions & 0 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ t2:
timeout: ${T2_PAYMENT_PROVIDER_TIMEOUT:5}
dummy:
url: ${T2_PAYMENT_PROVIDER_DUMMY_URL}
order:
simulateComputeIntensiveTask:
enabled: ${T2_SIMULATE_COMPUTE_INTENSIVE_TASK_ENABLED:false}
iterations: ${T2_SIMULATE_COMPUTE_INTENSIVE_TASK_ITERATIONS:1000000000}
10 changes: 7 additions & 3 deletions src/test/java/de/unistuttgart/t2/modulith/TestData.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ public final class TestData {
public static String anotherProductId = "foo2";
public static int anotherUnits = 42;
public static String sessionId = "bar";
private static final double price = 1.0;
private static final double anotherPrice = 2.0;
public static double totalOfCart = units * price;
public static double totalOfCartMulti = (units * price) + (anotherUnits * anotherPrice);

public static Optional<CartContent> cartResponse() {
return Optional.of(new CartContent(new HashMap<>(Map.of(productId, units))));
Expand Down Expand Up @@ -45,15 +49,15 @@ public static List<Product> productsBasedOnCartContent(CartContent cartContent)
}

public static Product productBase(String productId, int units) {
return new Product(productId, "name", "description", units, 1.0);
return new Product(productId, "name", "description", units, price);
}

public static Optional<Product> inventoryResponse() {
return Optional.of(new Product(productId, "name", "description", 5, 1.0));
return Optional.of(new Product(productId, "name", "description", 5, price));
}

public static Optional<Product> anotherInventoryResponse() {
return Optional.of(new Product(anotherProductId, "name2", "description2", 5, 1.0));
return Optional.of(new Product(anotherProductId, "name2", "description2", 5, anotherPrice));
}

public static List<Product> inventoryResponseAllProducts() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package de.unistuttgart.t2.modulith.order;

import de.unistuttgart.t2.modulith.cart.CartService;
import de.unistuttgart.t2.modulith.inventory.InventoryService;
import de.unistuttgart.t2.modulith.order.calculation.ComputeIntensiveTotalCalculator;
import de.unistuttgart.t2.modulith.order.calculation.SimpleTotalCalculator;
import de.unistuttgart.t2.modulith.order.calculation.ITotalCalculator;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.context.ActiveProfiles;

import static de.unistuttgart.t2.modulith.TestData.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
@ActiveProfiles("test")
public class CalculatorTests {

@Mock
CartService cartService;

@Mock
InventoryService inventoryService;

@BeforeEach
public void beforeEach() {
when(cartService.getCart(sessionId)).thenReturn(cartResponseMulti());
when(inventoryService.getSingleProduct(productId)).thenReturn(inventoryResponse());
when(inventoryService.getSingleProduct(anotherProductId)).thenReturn(anotherInventoryResponse());
}

@Test
public void defaultCalculator() {
ITotalCalculator calculator = new SimpleTotalCalculator(cartService, inventoryService);
double result = calculator.calculate(sessionId);

assertEquals(totalOfCartMulti, result);
}

@Test
public void computeIntensiveCalculator() {
int iterations = 1_000_0000; // use a higher number like e.g. 1_000_000_000 to see that it is actually compute intensive
ITotalCalculator calculator = new ComputeIntensiveTotalCalculator(cartService, inventoryService, iterations);
double result = calculator.calculate(sessionId);

assertEquals(totalOfCartMulti, result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import de.unistuttgart.t2.modulith.order.repository.OrderItem;
import de.unistuttgart.t2.modulith.order.repository.OrderRepository;
import de.unistuttgart.t2.modulith.payment.PaymentService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.context.ActiveProfiles;
Expand All @@ -19,9 +19,6 @@
@ActiveProfiles("test")
public class OrderServiceTests {

@InjectMocks
OrderService orderService;

@Mock
CartService cartService;

Expand All @@ -34,6 +31,13 @@ public class OrderServiceTests {
@Mock
OrderRepository orderRepository;

OrderService orderService;

@BeforeEach
public void beforeEach() {
this.orderService = new OrderService(cartService, inventoryService, paymentService, orderRepository);
}

@Test
public void confirmOrderSucceeds() throws Exception {

Expand Down
4 changes: 4 additions & 0 deletions src/test/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,7 @@ t2:
timeout: 5
dummy:
url: http://foo.bar/pay
order:
simulateComputeIntensiveTask:
enabled: false
iterations: 1

0 comments on commit 9b4b114

Please sign in to comment.