Skip to content

Task 1-2: Concurrent Confirm Lock - Rental Order

Phase: 1 Priority: High Module: application-core Depends on: Không có Reference: docs/BountyHunter-Backend/details/feature-nft-management/SPEC.md

Background

confirmNftRental(renterId, orderId) không có explicit lock. Race condition: 2 renters gọi đồng thời, cả 2 thấy status=NEW → cả 2 được confirm → double charge cho cả 2 renters.

Tasks

DI Note: NftRentalOrderService cần inject LockerService — thêm private final LockerService lockerService; và inject qua constructor (dùng @RequiredArgsConstructor). LockerService là shared service trong application-core hoặc common module — xác nhận package bằng grep: grep -rn "class LockerService". Lock timeout nên ngắn (5-10s) vì operation không lâu — xác nhận signature của lockerService.getInLock() (timeout param có thể optional).

File: application-core/service/nft_rental/nft_rental_order/NftRentalOrderService.java

  • [ ] Inject LockerService qua constructor nếu chưa có:

    @Service
    @RequiredArgsConstructor
    public class NftRentalOrderService {
        private final LockerService lockerService;  // thêm vào danh sách inject
        // ... các dependencies khác
    }
    

  • [ ] Wrap confirm logic với distributed lock per orderId:

    public void confirmNftRental(String renterId, String orderId) {
        // Lock key pattern: "NFT_RENTAL_CONFIRM_LOCK:{orderId}"
        lockerService.getInLock(
            "NFT_RENTAL_CONFIRM_LOCK",
            orderId,
            () -> {
                NftRentalOrderModel order = findById(orderId)
                    .orElseThrow(() -> new BadRequestException(RENTAL_ORDER_NOT_FOUND));
    
                // Guard inside lock: re-check status để tránh TOCTOU race
                if (order.getRentalStatus() != NftEnum.RentalOrderStatus.NEW) {
                    throw new BadRequestException("RENTAL_ORDER_NOT_AVAILABLE");
                }
    
                // ... proceed with coin deduction, status update, notification ...
            }
        );
    }
    

  • [ ] Verify lock timeout: nếu getInLock() hỗ trợ timeout param → set 5-10s

  • [ ] Đảm bảo re-check status bên trong lock (double-check locking pattern)

Verification / Acceptance Criteria

  • [ ] 2 concurrent confirm-rent/{orderId} requests → chỉ 1 thành công, 1 nhận RENTAL_ORDER_NOT_AVAILABLE
  • [ ] Không có double charge: B-Coin chỉ bị deduct 1 lần
  • [ ] Không có double deposit: owner chỉ nhận 1 lần
  • [ ] Lock được release sau khi confirm hoàn tất (không deadlock)
  • [ ] RENTAL_ORDER_NOT_FOUND vẫn trả về đúng khi orderId không tồn tại

Notes

  • Lock key pattern: "NFT_RENTAL_CONFIRM_LOCK" + ":" + orderId
  • Lock timeout nên ngắn (5-10s) vì operation không lâu

Files to Modify

  • application-core/service/nft_rental/nft_rental_order/NftRentalOrderService.java