Skip to content

Commit

Permalink
Merge pull request #3 from rjojjr/minor-refactoring
Browse files Browse the repository at this point in the history
Release v1.1.2
  • Loading branch information
rjojjr authored Sep 14, 2024
2 parents 41bdb38 + acb938c commit e7c9d1a
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 52 deletions.
3 changes: 1 addition & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
FROM gradle:7.6.4-jdk17 AS builder
COPY . /project
WORKDIR /project
RUN echo 'removing build' && (rm -rf build || echo 'no previous build exists')
RUN gradle bootJar
RUN gradle clean bootJar

FROM openjdk:17-jdk-slim-bullseye
ARG JAR_FILE=/project/build/libs/*.jar
Expand Down
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ plugins {
id 'java-library'
}

def build = '11'
def build = '14'

group = 'rjojjr.com.github'
//version = "1.1.1.$build-SNAPSHOT"
version = "1.1.1-RELEASE"
//version = "1.1.2.$build-SNAPSHOT"
version = "1.1.2-RELEASE"
sourceCompatibility = '17'


Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package rjojjr.com.github.taskslock;

import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import rjojjr.com.github.taskslock.exception.TasksLockShutdownFailure;
import rjojjr.com.github.taskslock.models.TaskLock;
Expand All @@ -11,6 +12,7 @@ public DestroyableTasksLockService() {
super();
}

@PreDestroy
@Override
public void onDestroy() {
log.info("Shutting down TasksLockService and releasing task-locks");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,29 @@

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import rjojjr.com.github.taskslock.entity.TaskLockEntity;
import rjojjr.com.github.taskslock.entity.TaskLockEntityRepository;
import rjojjr.com.github.taskslock.entity.TaskLockRepository;
import rjojjr.com.github.taskslock.exception.AcquireLockFailureException;
import rjojjr.com.github.taskslock.exception.ReleaseLockFailureException;
import rjojjr.com.github.taskslock.models.TaskLock;
import rjojjr.com.github.taskslock.util.HostUtil;
import rjojjr.com.github.taskslock.util.ThreadUtil;
import org.hibernate.exception.DataException;
import java.util.Date;

@ConditionalOnProperty(name = "tasks-lock.client.enabled", havingValue = "false", matchIfMissing = true)
@Service
@Slf4j
public class EmbeddedTasksLockService extends DestroyableTasksLockService {

private static final long RETRY_INTERVAL = 50;
@Value("${tasks-lock.retry-interval.ms:50}")
private long retryInterval;

private final TaskLockEntityRepository taskLockEntityRepository;
private final TaskLockRepository taskLockRepository;

@Autowired
public EmbeddedTasksLockService(TaskLockEntityRepository taskLockEntityRepository) {
this.taskLockEntityRepository = taskLockEntityRepository;
public EmbeddedTasksLockService(TaskLockRepository taskLockRepository) {
this.taskLockRepository = taskLockRepository;
}

@Override
Expand All @@ -38,35 +37,13 @@ public TaskLock acquireLock(String taskName, String hostName, String contextId,
try {
log.debug("attempting to acquire lock for task {}, waiting for lock: {} contextId: {}", taskName, waitForLock, contextId);
synchronized (dbLock) {
// TODO - Synchronize at this level across module instances(maybe some kind of db table lock?)
var lockedAt = new Date();
var entity = taskLockEntityRepository.findById(taskName).orElseGet(() -> new TaskLockEntity(taskName, false, hostName, contextId, new Date()));
try {
if (!entity.getIsLocked()) {
entity.setIsLocked(true);
entity.setLockedAt(lockedAt);
entity.setIsLockedByHost(hostName);
entity.setContextId(contextId);
taskLockEntityRepository.save(entity);
var taskLock = new TaskLock(
taskName,
contextId,
true,
lockedAt,
() -> releaseLock(taskName)
);
cacheLock(taskLock);
log.debug("acquired lock for task {} contextId: {}", taskName, contextId);
return taskLock;
}
} catch (DataException e) {
log.debug("Task lock not acquired for task {} because this worker lost in a race condition", taskName);
}
var taskLock = taskLockRepository.getTaskLock(taskName, hostName, contextId, this::releaseLock, this::cacheLock);
if (taskLock != null) return taskLock;
}

if (waitForLock) {
log.debug("task lock not acquired for task {}, retrying in {}ms contextId: {}", taskName, RETRY_INTERVAL, contextId);
ThreadUtil.sleep(RETRY_INTERVAL);
log.debug("task lock not acquired for task {}, retrying in {}ms contextId: {}", taskName, retryInterval, contextId);
ThreadUtil.sleep(retryInterval);
return acquireLock(taskName, hostName, contextId, true);
}
log.debug("did not acquire lock for task {} contextId: {}", taskName, contextId);
Expand All @@ -83,16 +60,7 @@ public String releaseLock(String taskName) {
try {
String contextId;
synchronized (dbLock) {
var entity = taskLockEntityRepository.findById(taskName).orElseGet(() -> new TaskLockEntity(taskName, false, null, null, new Date()));
contextId = entity.getContextId();
if (entity.getIsLocked()) {
entity.setIsLocked(false);
entity.setLockedAt(null);
entity.setIsLockedByHost(null);
entity.setContextId(null);

taskLockEntityRepository.save(entity);
}
contextId = taskLockRepository.releaseLock(taskName);
}
removeLock(taskName, contextId);
log.debug("released lock for task {}, contextId: {}", taskName, contextId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import rjojjr.com.github.taskslock.EmbeddedTasksLockService;
import rjojjr.com.github.taskslock.TasksLockService;
import rjojjr.com.github.taskslock.entity.TaskLockEntityRepository;
import rjojjr.com.github.taskslock.entity.TaskLockRepository;

@ConditionalOnProperty(name = "tasks-lock.client.enabled", havingValue = "false", matchIfMissing = true)
@Configuration
Expand All @@ -21,10 +21,10 @@
public class EmbeddedTasksLockConfiguration {

@Autowired
private TaskLockEntityRepository taskLockEntityRepository;
private TaskLockRepository taskLockRepository;

@Bean
public TasksLockService tasksLockService() {
return new EmbeddedTasksLockService(taskLockEntityRepository);
return new EmbeddedTasksLockService(taskLockRepository);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,77 @@
package rjojjr.com.github.taskslock.entity;

import jakarta.persistence.LockModeType;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.stereotype.Repository;
import rjojjr.com.github.taskslock.models.TaskLock;

import java.util.Date;
import java.util.function.Consumer;

@ConditionalOnProperty(name = "tasks-lock.client.enabled", havingValue = "false", matchIfMissing = true)
@Repository
public interface TaskLockEntityRepository extends JpaRepository<TaskLockEntity, String> {

/**
* Attempts to acquire lock & locks 'task_locks' table while doing so.
* @param taskName unique task identifier.
* @param hostName hostname of requesting service/container.
* @param contextId a tracing ID provided by request.
* @param releaseLock Consumer function that releases lock.
* @param cacheLock Consumer function that adds TaskLock object to cache.
* @return resulting TaskLock object.
*/
@Lock(LockModeType.PESSIMISTIC_READ)
default TaskLock tryToAcquireLock(String taskName, String hostName, String contextId, Consumer<String> releaseLock, Consumer<TaskLock> cacheLock){
var lockedAt = new Date();

var entity = findById(taskName).orElseGet(() -> new TaskLockEntity(taskName, false, hostName, contextId, new Date()));
if (!entity.getIsLocked()) {
entity.setIsLocked(true);
entity.setLockedAt(lockedAt);
entity.setIsLockedByHost(hostName);
entity.setContextId(contextId);
save(entity);
var taskLock = new TaskLock(
taskName,
contextId,
true,
lockedAt,
() -> releaseLock.accept(taskName)
);
cacheLock.accept(taskLock);
return taskLock;
}
flush();
return new TaskLock(
taskName,
contextId,
false,
lockedAt,
() -> {}
);
}

/**
* Removes lock for task if it exists
* @param taskName unique task identifier
* @return contextId
*/
@Lock(LockModeType.PESSIMISTIC_READ)
default String releaseLock(String taskName){
var entity = findById(taskName).orElseGet(() -> new TaskLockEntity(taskName, false, null, null, new Date()));
var contextId = entity.getContextId();
if (entity.getIsLocked()) {
entity.setIsLocked(false);
entity.setLockedAt(null);
entity.setIsLockedByHost(null);
entity.setContextId(null);

save(entity);
}
flush();
return contextId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package rjojjr.com.github.taskslock.entity;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.exception.DataException;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import rjojjr.com.github.taskslock.models.TaskLock;

import java.util.function.Consumer;

@Component
@RequiredArgsConstructor
@Slf4j
@ConditionalOnProperty(name = "tasks-lock.client.enabled", havingValue = "false", matchIfMissing = true)
public class TaskLockRepository {

private final TaskLockEntityRepository taskLockEntityRepository;

@Transactional
public TaskLock getTaskLock(String taskName, String hostName, String contextId, Consumer<String> releaseLock, Consumer<TaskLock> cacheLock) {
try {
var taskLock = taskLockEntityRepository.tryToAcquireLock(taskName, hostName, contextId, releaseLock, cacheLock);
if (taskLock.getIsLocked()) {
log.debug("acquired lock for task {} contextId: {}", taskName, contextId);
return taskLock;
}
} catch (DataException e) {
log.debug("Task lock not acquired for task {} because this worker lost in a race condition", taskName);
}
return null;
}

@Transactional
public String releaseLock(String taskName) {
return taskLockEntityRepository.releaseLock(taskName);
}
}

0 comments on commit e7c9d1a

Please sign in to comment.