2014-11-12 6 views
0

Ticket 개체의 큐가 데이터베이스에 보관됩니다. 큐를 포함하는 우리의 실체는 다음과 같습니다@OrderColumn을 사용하여 @ManyToMany가 지원하는 List에 간격이 나타나는 이유

@Entity 
public class StoreQueueCollection { 

    @Id 
    private int storeId; 

    @ManyToMany(fetch=FetchType.LAZY) 
    @OrderColumn 
    private List<Ticket> mainQueue = new ArrayList<Ticket>(); 

    @ManyToMany(fetch=FetchType.LAZY) 
    @OrderColumn 
    private List<Ticket> cancelledQueue = new ArrayList<Ticket>(); 

    .. etc 

우리가 큐의 끝 (호출에 새 티켓을 추가 한 (이 changeStatus 전화) 다른 하나의 큐에서 티켓을 이동 작동하고, 다른 이 새로운 Ticket).

두 작업이 같은 큐에서 인터리브 될 때 작업은 기본적으로 작동하지만 큐에 "갭"이 생깁니다. 데이터베이스에서 이것은 다음과 같이 테이블의 순서 열에 누락 된 색인 (0, 1, 2, 4)처럼 보입니다. 대기열이 Java로 다시로드되면 누락 된 색인은 대기열의 널 (null) 요소가됩니다.

일관성없는 인터리브 업데이트를 방지하기 위해 StoreQueueCollection 객체에서 비관적 잠금을 사용하지만 예상대로 작동하지 않습니다. 추가 로깅, 우리는 다음과 같이 이상한 시퀀스를 참조하십시오

 
- changeTicketStatus() starts 
- lock the queue using entityManager.refresh() 
- ticket X removed from front of queue A 
- queue is entityManager.flush()ed 

* newTicket() starts 
* creates a new ticket Y, locks the StoreQueueCollection using entityManager.refresh(); 
* ticket Y added to the end of queue A 
* ticket Y has fields initialized and is save()d 
* call refresh(), method is blocked 

- changeTicketStatus() resumes 
    (printing in-memory queue shows that ticket X is not in queue A) 
- ticket X added to another queue B 
- ticket X has some fields modified 
- ticket X is saved using repository.save() 
    (printing in-memory queue shows that ticket X is not in queue A) 
- changeTicketStatus() completes 

* newTicket() resumes 
* refresh() returns 
    (printing in-memory queue A shows that ticket X is still in queue!) 
* ticket Y is added to end of queue A 
    (printing in-memory queue A shows that ticket X and Y are in the queue) 
* queues are save()d 

모든 잠금은 LockModeType.PESSIMISTIC_WRITE을하고 범위 PessimisticLockScope.EXTENDED입니다.

이 실행 순서 후에, 어설 션은 다른 스레드에서 큐에있는 null 항목을 검사합니다. 신비하게도 대기열은 기본적으로 정확합니다 (X가 삭제되고 끝에 Y가 추가됩니다). 그러나 Y 이전에 주문 열에 틈이 있습니다.

잘못된 점에 대한 제안은 대단히 감사합니다!

+0

로크에 문제가있는 것 같습니다. 잠금 및 트랜잭션 경계 코드의 간단한 샘플을 제공 할 수 있습니까? – gerrytan

+0

답장을 보내 주셔서 감사합니다. 불행히도 그것은 약간 크고 방법에 걸쳐 펼쳐져 있습니다. – bsa

+0

SQL 수준에서 로깅 (log4jdbc-log4j2 사용), 확실히 잠금이 예상대로 작동하지 않는 것처럼 보입니다. 문서를 다시 읽어야하는 것 같습니다. – bsa

답변

0

단일 행을 잠 그거나 모든 '대기열'행을 잠그고있는 모든 스레드가 있습니까? 후자의 경우 잠금을 유지하려는 전체 시간 동안 트랜잭션을 유지해야한다는 점에 유의하십시오. 이전의 경우, 단일 (다른) 행을 잠그면 인터리브 작업을 방지하기 위해 아무 것도하지 않습니다.

당신은 어떤 백엔드를 사용하는지 언급하지 않았지만, 때로는 SQL Server의 프로파일 러 및 활동 모니터와 같은 데이터베이스 로깅 도구가 모든 SQL의 순서 목록을 얻을 수 있기 때문에 상황을 파악하는 데 도움이 될 수 있습니다 데이터베이스에 발행 된 명령문.

+0

Tyler, 우리는 EntityManager.refresh (storeQueueCollection, ...) 및 EntityManager.flush()를 사용하여 StoreQueueCollection 엔티티 클래스의 인스턴스를 잠급니다. 낮은 수준의 SQL 모니터링은 좋은 제안입니다 (우리는 MySQL을 사용하고 있습니다). – bsa

+0

잠금을 획득 한 후에 중단 점을 설정하고 다른 스레드에서 획득하려고 시도하여 잠금이 실제로 작동하는지 확인할 수 있습니까? Hibernate에 대한 나의 경험에서, 나는 모든 당사자들이 그것이 효과적이기 위해서는 자물쇠를 반드시 얻어야한다는 것을 발견했다. 한 트랜잭션이 잠금을 가지지 만 다른 트랜잭션이 방해를받지 않으면 다른 트랜잭션이 진행될 수 있습니다. 이는 파일 잠금으로 인해 다른 모든 쓰기가 실패하는 Windows의 파일 잠금과 다릅니다. –

+0

Tyler, SQL 수준에서 log4jdbc-log4j2를 사용하여 로깅했습니다. 잠금이 예상대로 작동하지 않는 것처럼 보입니다. 문서를 다시 읽어야하는 것 같습니다. – bsa