No sequential control was implemented for internal asynchronous service requests.
In the Cancel Order method of the Cancel Service, it calls the Insidepayment service to execute a refund (Request 1) and the OrderOther service to update the order status (Request 2).
- Request 1 sets the order status to Canceling (status = 7).
- Request 2 sets the order status to Canceled (status = 4).
When network latency occurs, Request 2 may complete before Request 1, resulting in an incorrect final order status (Refunding).
We inject a fault in ts-cancel-service to make it send refund and cancel requests asynchronously.
To simulate network latency, we add a sleep in the Controller of the microservice handling the refund request.
No validation was performed on the result of the previous request before processing the next one.
A user sends a series of requests: Query → Book → Pay → Cancel.
- The payment request (Request 1) experiences network latency and has not yet executed the payment logic.
- The cancel request (Request 2) arrives first and is executed, changing the order status to Canceled (status = 4).
- Then, Request 1 arrives and starts executing the payment logic, but finds the order status incorrect (expected status: Not Paid, status = 0).
No fault injection is needed because TrainTicket inherently has this issue.
A concurrency bug caused by master-slave database replication delay in a read-write separation setup.
We added Order-other-service2, which only handles order query requests and retrieves data from the slave database.
When a user books a ticket:
- Order-other-mysql inserts a new order record, which is later synchronized to the slave database Order-other-mysql-slave.
- Database synchronization may experience delays.
If the user queries the successfully booked ticket via Order-other-service2 during this delay, the response will be None.
Reproducing the ticket overselling issue—multiple concurrent booking requests lead to race conditions.
We inject a fault in ts-preserve-other-service. To trigger this fault, submit a request with the SeatClass field set to 9.
The relevant code is as follows:
// ts-common/src/main/java/edu/fudan/common/entity/SeatClass.java
TESTCLASS (9, "TestSeat");// PreserveOtherServiceImpl.java
private int testSeat = 1;
@Override
public Response preserve(OrderTicketsInfo oti, HttpHeaders httpHeaders) {
....
else if (oti.getSeatType() == SeatClass.TESTCLASS.getCode()){
try {
Thread.sleep(100); // simulate the database operation to check the number of seats.
} catch (Exception e){
e.printStackTrace();
}
if (testSeat <= 0){
PreserveOtherServiceImpl.LOGGER.warn("[Fault 4 Injection][preserve][Step 3][Check seat][Check seat is Not enough]");
return new Response<>(0, "Check Seat Not Enough", null);
}
try {
Thread.sleep(1000); // simulate the delay of database operation
} catch (Exception e){
e.printStackTrace();
}
testSeat --;
if (testSeat < 0){
throw new RuntimeException("[Fault 4 Injection][The seat has been oversold!]");
}
}
....
}We inject a fault in ts-consign-service. When a user submits a consign request:
- The service first checks if a consign record exists in the database.
- If not, it proceeds with an insert operation.
However, there may be a delay between the query and the insert. If another consign request arrives during this time, the database may execute duplicate inserts, resulting in a duplicated key error.
Request path: /consigns/fault/{orderId}