修改死锁

This commit is contained in:
wuweihua
2026-10-21 10:34:45 +08:00
parent d7f7ebd981
commit dd2efef456
3 changed files with 109 additions and 19 deletions
@@ -22,6 +22,8 @@ public class LocalRebillRequest implements Serializable
private List<String> orderIdentifiers;
private List<String> customerCodes;
private List<String> productCodes;
private List<Integer> orderStatuses;
private List<Integer> billingFailStatuses;
private String countryCode;
private String creditArrearsStatus;
private String beginBizDate;
@@ -12,6 +12,8 @@ import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@@ -89,6 +91,8 @@ import com.bms.price.task.PrcCreditArrearsClearAsyncRunner;
@Service
public class PrcOrderBillServiceImpl implements IPrcOrderBillService
{
private static final Logger log = LoggerFactory.getLogger(PrcOrderBillServiceImpl.class);
private static final String DICT_FEE_TYPE = "bms_fee_type";
private static final String FEE_TYPE_REFUND = "REFUND";
@@ -701,8 +705,14 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
persistProductOrRedlineOnly(bill, result, productMode);
}
private static final int LOCAL_REBILL_DB_RETRY_MAX = 3;
private static final long LOCAL_REBILL_DB_RETRY_BASE_MS = 80L;
/**
* Local rebill without outer txn: each {@link #bill} starts its own transaction so
* MySQL deadlock can be retried cleanly (same idea as billing-task billWithDbRetry).
*/
@Override
@Transactional(rollbackFor = Exception.class)
public TmsPriceQuoteResult rebillLocalByBillId(String billId)
{
if (StringUtils.isEmpty(billId))
@@ -723,8 +733,72 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
throw new ServiceException(MessageUtils.message("price.bill.bizDateRequired"));
}
TmsPriceBillRequest req = buildBillRequestFromBill(bill);
// Full bill() path: AR guard, weight rules, quote, persist (local snapshot only).
return self.bill(req);
return billLocalWithDbRetry(billId.trim(), req);
}
private TmsPriceQuoteResult billLocalWithDbRetry(String billId, TmsPriceBillRequest billReq)
{
RuntimeException last = null;
for (int attempt = 1; attempt <= LOCAL_REBILL_DB_RETRY_MAX; attempt++)
{
try
{
return self.bill(billReq);
}
catch (RuntimeException e)
{
last = e;
if (attempt < LOCAL_REBILL_DB_RETRY_MAX && isRetryableDbConflict(e))
{
long sleepMs = LOCAL_REBILL_DB_RETRY_BASE_MS * attempt
+ (long) (Math.random() * LOCAL_REBILL_DB_RETRY_BASE_MS);
log.warn("local rebill db conflict, retry billId={} attempt={}/{} sleepMs={} msg={}",
billId, Integer.valueOf(attempt), Integer.valueOf(LOCAL_REBILL_DB_RETRY_MAX),
Long.valueOf(sleepMs), StringUtils.substring(e.getMessage(), 0, 200));
try
{
Thread.sleep(sleepMs);
}
catch (InterruptedException ie)
{
Thread.currentThread().interrupt();
throw e;
}
continue;
}
throw e;
}
}
throw last != null ? last : new ServiceException("local rebill retry exhausted");
}
/** Deadlock / lock wait — safe to retry whole bill txn (parallel local rebill). */
private static boolean isRetryableDbConflict(Throwable e)
{
Throwable t = e;
while (t != null)
{
String name = t.getClass().getName();
if (name.indexOf("DeadlockLoser") >= 0
|| name.indexOf("CannotAcquireLock") >= 0
|| name.indexOf("MySQLTransactionRollback") >= 0)
{
return true;
}
String msg = t.getMessage();
if (msg != null)
{
String m = msg.toLowerCase();
if (m.indexOf("deadlock") >= 0
|| m.indexOf("try restarting transaction") >= 0
|| m.indexOf("lock wait timeout") >= 0)
{
return true;
}
}
t = t.getCause();
}
return false;
}
@Override
@@ -839,6 +913,8 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
query.setOrderIdentifiers(orderIdentifiers);
query.setCustomerCodes(customerCodes);
query.setProductCodes(productCodes);
query.setOrderStatuses(request.getOrderStatuses());
query.setBillingFailStatuses(request.getBillingFailStatuses());
query.setCreditArrearsStatus(creditArrearsStatus);
query.setCountryCode(countryCode);
if (beginBizDate != null)