Независимо от того, какой тип и уровень блокировок вы собираетесь использовать, и что будет с другими работниками, долгосрочная блокировка, а также долгосрочная транзакция не являются хорошим решением.ИМХО в вашем случае лучше использовать другой подход без каких-либо блокировок, например, дополнительная таблица для записи «блокировок» принтера:
create table printer_locks (
printer_id bigint not null constraint printer_locks_pkey primary key,
created_at timestamp not null,
worker_id bigint not null constraint fk_printer_locks_worker_id references workers,
constraint fk_printer_locks_printer_id foreign key (printer_id) references printers(id)
);
Когда работник хочет начать работу с какого-то принтера, сначалаон пытается вставить запись в эту таблицу.Затем, в случае успеха, он начинает работу.Когда работа завершена, работник удаляет эту запись.
Поскольку столбец printer_id
уникален - другие работники не смогут одновременно работать с одним и тем же принтером.
Реализация:
@Entity
@Table(name = "workers")
public class Worker {
@Id
@GeneratedValue(...)
private Long id;
// other stuff...
}
@Entity
@Table(name = "printers")
public class Printer {
@Id
@GeneratedValue(...)
private Long id;
// other stuff...
}
@Entity
@Table(name = "printer_locks")
public class PrinterLock {
@Id
private Long id;
private Instant createdAt = Instant.now();
@OneToOne(fetch = FetchType.LAZY)
@MapsId
private Printer printer;
@ManyToOne(fetch = FetchType.LAZY)
private Worker worker;
public PrinterLock(Printer printer, Worker worker) {
this.printer = printer;
this.worker = worker;
}
// other stuff...
}
public void execute(...) {
Worker worker = ...;
Long printerId = ...;
printerRepo.findById(printerId)
.map(printer -> {
try {
printerLockRepo.save(new PrinterLock(printer, worker));
try {
// do the work here (30 sec to 1 min)
} finally {
printerLockRepo.deleteById(printerId);
}
} catch(Exception e) {
log.warn("Printer {} is busy", printerId);
}
})
.orElseThrow(() -> new PrinterNotFoundException(printerId));
}
Обратите внимание, что метод execute
даже не имеет аннотации @Transactional
.
Дополнительным преимуществом этого подхода является столбец createdAt
, который позволяет контролировать зависаниезадания.
Дополнительная литература: