19 Ekim 2023 Perşembe

SpringData JPA @Lock Anotasyonu - PESSIMISTIC_WRITE İle Dağıtık Ortamda Scheduling

Giriş
LockModeType = PESSIMISTIC_WRITE kullanınca şöyle bir SQL üretiyor.
'select id from table where id = ? for update wait 5'
Örnek
Şöyle yaparız.
interface WidgetRepository extends Repository<Widget, Long> {

  @Lock(LockModeType.PESSIMISTIC_WRITE)
  Widget findOne(Long id);
}
Timeout 
Açıklaması şöyle
To lock entities pessimistically, set the lock mode to PESSIMISTIC_READPESSIMISTIC_WRITE, or PESSIMISTIC_FORCE_INCREMENT.

If a pessimistic lock cannot be obtained, but the locking failure doesn’t result in a transaction rollback, a LockTimeoutException is thrown.

Pessimistic Locking Timeouts

The length of time in milliseconds the persistence provider should wait to obtain a lock on the database tables may be specified using the javax.persistence.lock.timeout property. If the time it takes to obtain a lock exceeds the value of this property, a LockTimeoutException will be thrown, but the current transaction will not be marked for rollback. If this property is set to 0, the persistence provider should throw a LockTimeoutException if it cannot immediately obtain a lock.

If javax.persistence.lock.timeout is set in multiple places, the value will be determined in the following order:

1. The argument to one of the EntityManager or Query methods.
2. The setting in the @NamedQuery annotation.
3. The argument to the Persistence.createEntityManagerFactory method.
4. The value in the persistence.xml deployment descriptor.
Dağıtık Ortamda Scheduling
Örnek
Elimizde şöyle bir kod olsun
@Repository
public interface TaskLockRepository extends JpaRepository<TaskLockEntity, String> {

  @Lock(LockModeType.PESSIMISTIC_WRITE)
  @QueryHints({
    @QueryHint(name = "jakarta.persistence.lock.timeout", value = "1000")
  })
  Optional<ProviderLockEntity> findByTaskIdAndLastExecutionLessThan(String taskId,
    long timestamp);
}
Veri tabanı şöyle olsun
CREATE TABLE task_lock
(
    task_id         VARCHAR(64)       NOT NULL,
    last_execution   bigint DEFAULT 0 NOT NULL,
    PRIMARY KEY (task_id)
);
INSERT INTO task_lock (task_id) VALUES ('scrap_website');
INSERT INTO task_lock (task_id) VALUES ('move_to_cold');
Bu kodu şöyle kullanırız
@Configuration
@EnableScheduling
public class TaskScheduler {

  private final TaskLockRepository taskLockRepository;
  // Other dependencies

  @Transactional
  @Scheduled(fixedDelay = 5, timeUnit = TimeUnit.MINUTES)
  public void scrapSourceWebsite() {
    long currentTime = System.currentTimeMillis();
    long scheduledRate = Duration.of(1, ChronoUnit.HOURS).toMillis(); // 1h
    taskLockRepository.findByTaskIdAndLastExecutionLessThan("scrap_website",
      currentTime - scheduledRate)
      .ifPresent(scrapingTask -> {
        // Execute scraping task...
        scrapingTask.setLastExecution(System.currentTimeMillis());
      });
  }
  // Other tasks
}
1. Burada @Transactional ile scrap_website satırı  eğer last_execution değeri son 1 saatten eski ise kilitleniyor. 
2. LockModeType.PESSIMISTIC_WRITE kullanıldığı için  dağıtık ortamda sadece bir iş bu satırı kilitleyebilir
3. Eğer scrapingTask exception fırlatırsa Transaction rollback edilir. Yani satır kilitli kalmaz
4. Eğer servis çökerse Transaction bu sefer veri tabanı tarafından rollback edilir. Yani satır kilitli kalmaz





Hiç yorum yok:

Yorum Gönder