samedi 2 décembre 2023

Design Method for Separating Retry Logic from Transaction Scope

I encountered an 'org.hibernate.AssertionFailure' error in my test for a specific logic.
ERROR 56862 --- [pool-3-thread-9] org.hibernate.AssertionFailure : HHH000099: an assertion failure occurred (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): org.hibernate.AssertionFailure: null id in ... entry (don't flush the Session after an exception occurs)

The issue arises when a data race condition leads to a Constraint Violation Exception due to the 'longUrl' being a unique column. In this scenario

  1. the first committed transaction context returns its 'shortUrl'
  2. while the other failing contexts should find and return the committed entity.
@Transactional
public String
getShortUrl(String longUrl) {
    UrlEntity   url;
    try {
        // Create Entity For Id
        url = this.db.findUrlEntityByLongUrl(longUrl).orElseGet(
                () -> this.db.save(new UrlEntity(longUrl)));
        
        String shortUrl = this.generator.encode(url.getId());
        url.setShortUrl(shortUrl);
        this.db.save(url);
    } catch (DataIntegrityViolationException e) {
        //  if Conflict return after find
        url = this.db.findUrlEntityByLongUrl(longUrl).get();
        if (url.getShortUrl() != null) return url.getShortUrl();
        url.setShortUrl(this.generator.encode(url.getId()));
    }

    return url.getShortUrl();
}

Based on my understanding, exceptions occurring within the scope of a transaction trigger a rollback, rendering the session unstable. In this context, I am attempting to perform entity retrieval using this unstable session :(

How should I approach the design? I am concerned that managing try-catch blocks at the controller level might shift part of the business logic to the controller, which seems inappropriate.

Is there a suitable design pattern for this situation?

Aucun commentaire:

Enregistrer un commentaire