Giriş
LockModeType = PESSIMISTIC_WRITE kullanınca şöyle bir SQL
üretiyor.
'select id from table where id = ? for update wait 5'
Örnek
interface WidgetRepository extends Repository<Widget, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
Widget findOne(Long id);
}
Timeout
To lock entities pessimistically, set the lock mode to PESSIMISTIC_READ, PESSIMISTIC_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);
}
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');
@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