Skip to content

Commit

Permalink
DMP-4653 Fixing courtroom name unique constraint violation (#2536)
Browse files Browse the repository at this point in the history
  • Loading branch information
davet1985 authored Feb 5, 2025
1 parent 8ccea56 commit d3abf51
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package uk.gov.hmcts.darts.noderegistration.controller;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
Expand All @@ -10,7 +12,6 @@
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.transaction.annotation.Transactional;
import uk.gov.hmcts.darts.authorisation.component.UserIdentity;
import uk.gov.hmcts.darts.common.entity.CourthouseEntity;
import uk.gov.hmcts.darts.common.entity.CourtroomEntity;
Expand All @@ -34,7 +35,6 @@

@Slf4j
@AutoConfigureMockMvc
@Transactional
class NodeRegistrationControllerTest extends IntegrationBase {

@Autowired
Expand All @@ -43,6 +43,15 @@ class NodeRegistrationControllerTest extends IntegrationBase {
@MockitoBean
private UserIdentity mockUserIdentity;

@BeforeEach
void startHibernateSession() {
openInViewUtil.openEntityManager();
}

@AfterEach
void closeHibernateSession() {
openInViewUtil.closeEntityManager();
}

@Test
void testPostRegisterDevices() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import uk.gov.hmcts.darts.common.entity.CourtCaseEntity;
import uk.gov.hmcts.darts.common.entity.CourthouseEntity;
import uk.gov.hmcts.darts.common.entity.CourtroomEntity;
Expand All @@ -16,39 +18,109 @@

public interface RetrieveCoreObjectService {

@Retryable(backoff = @Backoff(delay = 50), retryFor = {DataIntegrityViolationException.class, PSQLException.class})
/**
* This method is used when events are received and processed.
* By creating a new transaction, along with using the @Retryable annotation, we can ensure that concurrent requests to create the
* same hearing can be processed without causing a constraint violation.
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Retryable(backoff = @Backoff(delay = 50), retryFor = {DataIntegrityViolationException.class, PSQLException.class}, maxAttempts = 10)
HearingEntity retrieveOrCreateHearing(String courthouseName, String courtroomName, String caseNumber, LocalDateTime hearingDate);

@Retryable(backoff = @Backoff(delay = 50), retryFor = {DataIntegrityViolationException.class, PSQLException.class})
/**
* This method is used for the following
* - daily list processing
* - adding audio using metadata
* By creating a new transaction, along with using the @Retryable annotation, we can ensure that concurrent requests to create the
* same hearing can be processed without causing a constraint violation.
* Concurrency isn't a concern for daily list processing.
*/
@Retryable(backoff = @Backoff(delay = 50), retryFor = {DataIntegrityViolationException.class, PSQLException.class}, maxAttempts = 10)
HearingEntity retrieveOrCreateHearing(String courthouseName, String courtroomName, String caseNumber, LocalDateTime hearingDate,
UserAccountEntity userAccount);

@Retryable(backoff = @Backoff(delay = 50), retryFor = {DataIntegrityViolationException.class, PSQLException.class})
/**
* Retrieve or create a case and link to media.
* @deprecated This method is only used by tests. Tests should be refactored and this method should be removed.
*/
@SuppressWarnings("java:S1133") // suppress sonar warning about deprecated methods
@Deprecated(since = "04/02/2025")
HearingEntity retrieveOrCreateHearingWithMedia(String courthouseName, String courtroomName, String caseNumber, LocalDateTime hearingDate,
UserAccountEntity userAccount, MediaEntity mediaEntity);

@Retryable(backoff = @Backoff(delay = 50), retryFor = {DataIntegrityViolationException.class, PSQLException.class})
/**
* Retrieve or create a courtroom.
* @deprecated This method is only used by tests.
* Tests should be refactored to use the other `retrieveOrCreateCourtroom` method, and this method should be removed.
*/
@SuppressWarnings("java:S1133") // suppress sonar warning about deprecated methods
@Deprecated(since = "04/02/2025")
CourtroomEntity retrieveOrCreateCourtroom(CourthouseEntity courthouse, String courtroomName, UserAccountEntity userAccount);

@Retryable(backoff = @Backoff(delay = 50), retryFor = {DataIntegrityViolationException.class, PSQLException.class})
/**
* This method is used for the following
* - node register
* - events
* - get cases
* - adding audio
* - audio requests
* By creating a new transaction, along with using the @Retryable annotation, we can ensure that concurrent requests that attempt to create the
* same courtroom can be processed without causing a constraint violation.
* Concurrency isn't a concern for adding audio or audio requests. Due to the nature of these calls, it is extremely unlikely that the courtroom
* will not exist.
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Retryable(backoff = @Backoff(delay = 50), retryFor = {DataIntegrityViolationException.class, PSQLException.class}, maxAttempts = 10)
CourtroomEntity retrieveOrCreateCourtroom(String courthouseName, String courtroomName, UserAccountEntity userAccount);

@Retryable(backoff = @Backoff(delay = 50), retryFor = {DataIntegrityViolationException.class, PSQLException.class})
/**
* This method is used for the following
* - add case
* - events
* By creating a new transaction, along with using the @Retryable annotation, we can ensure that concurrent requests that attempt to create the
* same case can be processed without causing a constraint violation.
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Retryable(backoff = @Backoff(delay = 50), retryFor = {DataIntegrityViolationException.class, PSQLException.class}, maxAttempts = 10)
CourtCaseEntity retrieveOrCreateCase(String courthouseName, String caseNumber);

@Retryable(backoff = @Backoff(delay = 50), retryFor = {DataIntegrityViolationException.class, PSQLException.class})
/**
* Retrieve or create a case.
* @deprecated This method is only used by tests.
* Tests should be refactored to use the other `retrieveOrCreateCase` method, and this method should be removed.
*/
@SuppressWarnings("java:S1133") // suppress sonar warning about deprecated methods
@Deprecated(since = "04/02/2025")
CourtCaseEntity retrieveOrCreateCase(String courthouseName, String caseNumber, UserAccountEntity userAccount);

@Retryable(backoff = @Backoff(delay = 50), retryFor = {DataIntegrityViolationException.class, PSQLException.class})
/**
* This method is used during add audio when retrieving/creating cases used in the metadata before adding to the media_linked_case table.
* It's possible that the same case could be sent in concurrent add audio requests.
* By creating a new transaction, along with using the @Retryable annotation, we can ensure that concurrent requests that attempt to create the
* same case can be processed without causing a constraint violation.
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Retryable(backoff = @Backoff(delay = 50), retryFor = {DataIntegrityViolationException.class, PSQLException.class}, maxAttempts = 10)
CourtCaseEntity retrieveOrCreateCase(CourthouseEntity courthouse, String caseNumber, UserAccountEntity userAccount);

@Retryable(backoff = @Backoff(delay = 50), retryFor = {DataIntegrityViolationException.class, PSQLException.class})
/**
* Used to retrieve the courthouse during add audio validation.
*/
CourthouseEntity retrieveCourthouse(String courthouseName);

@Retryable(backoff = @Backoff(delay = 50), retryFor = {DataIntegrityViolationException.class, PSQLException.class})
/**
* This method is used for add case, it's possible that the same judge could be sent in concurrent add case requests.
* By creating a new transaction, along with using the @Retryable annotation, we can ensure that concurrent requests that attempt to create the
* same judge can be processed without causing a constraint violation.
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Retryable(backoff = @Backoff(delay = 50), retryFor = {DataIntegrityViolationException.class, PSQLException.class}, maxAttempts = 10)
JudgeEntity retrieveOrCreateJudge(String judgeName);

@Retryable(backoff = @Backoff(delay = 50), retryFor = {DataIntegrityViolationException.class, PSQLException.class})
/**
* This method is only used to add judges during daily list processing. This task processed data in serial and therefore
* there are no concurrent write concerns.
*/
JudgeEntity retrieveOrCreateJudge(String judgeName, UserAccountEntity userAccount);

}

0 comments on commit d3abf51

Please sign in to comment.