Skip to content

Commit

Permalink
Fixing courtroom name unique constraint violation
Browse files Browse the repository at this point in the history
- plus hearing, case and judge constraint violations
- increasing the attempts made by the Retryable annotation, up to 10 attempts retying every 50ms, spending a total of 500ms before failing
- most of these are used by requests via the SOAP service and if a failure did happen it would be retried by the external system
- creating a new transaction so that the retried method has the latest data, before it was stuck with a stable view when retrying and never doing anything
- adding javadoc for all methods in `RetrieveCoreObjectService` to help explain how they are used
- deprecating some methods as they are only used in tests
- removing the Retryable annotation from some methods because it's not necessary
  • Loading branch information
davet1985 committed Feb 5, 2025
1 parent 6ecad3d commit c858454
Showing 1 changed file with 80 additions and 11 deletions.
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,106 @@

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.
*/
@Deprecated
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.
*/
@Deprecated
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.
*/
@Deprecated
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 c858454

Please sign in to comment.