@Slf4j
public class MyTest {

	@Autowired
	private TestService service;

	@Test
	@Rollback(false)
	public void lockTest() throws Exception {

		val targetId = 100L;

		Thread thread1 = new Thread(() -> { updateWorkerValue(targetId, "worker1");});
		Thread thread2 = new Thread(() -> { updateWorkerValue(targetId, "worker2");});

		thread1.start();
		thread2.start();

		thread1.join();
		thread2.join();
	}

	private void updateWorkerValue(Long id, String worker) {

		log.info("+++ {}: STARTED", worker);

		try {
			service.getLock(id);
		} catch (Exception e) {
			log.info("+++ {}: FAIL TO LOCK", worker);
			return;
		}
		log.info("+++ {}: GOT LOCK", worker);

		var result = ERROR;
		try {
			service.updateWorkerValue(id, worker);
			log.info("+++ VALUE UPDATED: {}", worker);
			result = DONE;
		} finally {
			service.updateLockedJobStatusTo(id, result);
		}

		log.info("+++ DONE: " + worker);
	}
}


@Service
public class TestService {

	@Autowired
	private JobRepository repo;

	@Transactional
	public void getLock(Long id) {
		val affected = repo.updateStatus(id, statuses(WAIT), newStatus(LOCK));
		if (affected != 1)
			throw new RuntimeException("fail to lock: id=" + id);
	}
	
	@Transactional
	public void updateWorkerValue(Long id, String worker) {
		// TODO biz
		repo.updateValue(id, worker, status(LOCK));
	}

	@Transactional
	public void updateLockedJobStatusTo(Long id, JobStatus newStatus) {
		val affected = repo.updateStatus(id, statuses(LOCK), newStatus);
		if (affected != 1)
			throw new RuntimeException("error while updating status. id:" + id + ", status:" + newStatus);
	}
}


@Repository
public interface JobRepository {

	@Modifying
	@Query("update JobItem i set i.status=:newStatus, i.modifiedAt=now() where i.id=:id and i.status in :statuses")
	int updateStatus(
			@Param("id") Long id,
			@Param("statuses") List<JobStatus> statuses,
			@Param("newStatus") JobStatus newStatus);

	@Modifying
	@Query("update JobItem i set i.stringUnitId=:value, i.modifiedAt=now() where i.id=:id and i.status=:status")
	int updateValue(
			@Param("id") Long id,
			@Param("value") String value,
			@Param("status") JobStatus status);
}
Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2019-03-09 23:12:01
Processing time 0.0055 sec