Skip to content
Snippets Groups Projects
Commit bca82c85 authored by Ruben Backx's avatar Ruben Backx :coffee:
Browse files

Merge branch...

Merge branch '163-https-sentry-ewi-tudelft-nl-organizations-eip-issues-2351-project-8-query-is-3aunresolved' into 'development'

Resolve "https://sentry.ewi.tudelft.nl/organizations/eip/issues/2351/?project=8&query=is%3Aunresolved"

Closes #163

See merge request !236
parents ac95646d aa9eac65
No related branches found
No related tags found
2 merge requests!241Deploy,!236Resolve "https://sentry.ewi.tudelft.nl/organizations/eip/issues/2351/?project=8&query=is%3Aunresolved"
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
*/ */
package nl.tudelft.submit.model; package nl.tudelft.submit.model;
import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import lombok.*; import lombok.*;
...@@ -31,6 +32,7 @@ import lombok.experimental.SuperBuilder; ...@@ -31,6 +32,7 @@ import lombok.experimental.SuperBuilder;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class CoreSubmission extends AbstractSubmitSubmission { public class CoreSubmission extends AbstractSubmitSubmission {
@Column(unique = true)
private Long coreSubmissionId; private Long coreSubmissionId;
} }
...@@ -24,6 +24,8 @@ import java.nio.file.Path; ...@@ -24,6 +24,8 @@ import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.modelmapper.ModelMapper; import org.modelmapper.ModelMapper;
...@@ -102,6 +104,10 @@ public class SubmissionService { ...@@ -102,6 +104,10 @@ public class SubmissionService {
private final ModelMapper mapper = new ModelMapper(); private final ModelMapper mapper = new ModelMapper();
// Locks for synchronisation
private final ReentrantLock lockMapLock = new ReentrantLock();
private final Map<Long, ReentrantLock> submissionLocks = new ConcurrentHashMap<>();
/** /**
* Returns a SubmissionViewDTO of the Submission of the given ID. * Returns a SubmissionViewDTO of the Submission of the given ID.
* *
...@@ -131,8 +137,15 @@ public class SubmissionService { ...@@ -131,8 +137,15 @@ public class SubmissionService {
@Transactional @Transactional
public Long addSubmission(SubmissionCreateDTO createDTO) { public Long addSubmission(SubmissionCreateDTO createDTO) {
Long id = submissionApi.addSubmission(createDTO).block(); Long id = submissionApi.addSubmission(createDTO).block();
// Be the only one to modify this submission
ReentrantLock lock = acquireSubmissionLock(id);
try {
lock.lock();
submissionRepository.deleteAllByCoreSubmissionId(id); submissionRepository.deleteAllByCoreSubmissionId(id);
submissionRepository.saveAndFlush(CoreSubmission.builder().coreSubmissionId(id).build()); submissionRepository.saveAndFlush(CoreSubmission.builder().coreSubmissionId(id).build());
} finally {
lock.unlock();
}
return id; return id;
} }
...@@ -542,6 +555,28 @@ public class SubmissionService { ...@@ -542,6 +555,28 @@ public class SubmissionService {
return submissions; return submissions;
} }
/**
* Gets a lock for the given core submission. If a lock is returned and locked successfully, the core
* submission cannot be modified by other threads.
*
* @param coreSubmissionId The id of the core submission
* @return The lock to gain access
*/
private ReentrantLock acquireSubmissionLock(Long coreSubmissionId) {
ReentrantLock lock;
try {
lockMapLock.lock();
// Get submission lock for this submission, or create if does not exist
if (!submissionLocks.containsKey(coreSubmissionId)) {
submissionLocks.put(coreSubmissionId, new ReentrantLock());
}
lock = submissionLocks.get(coreSubmissionId);
return lock;
} finally {
lockMapLock.unlock();
}
}
/** /**
* Gets the submit core submission with the corresponding id or creates one. * Gets the submit core submission with the corresponding id or creates one.
* *
...@@ -550,9 +585,18 @@ public class SubmissionService { ...@@ -550,9 +585,18 @@ public class SubmissionService {
*/ */
@Transactional @Transactional
public CoreSubmission getOrCreateSubmitCoreSubmission(Long submissionId) { public CoreSubmission getOrCreateSubmitCoreSubmission(Long submissionId) {
return submissionRepository.findByCoreSubmissionId(submissionId).orElseGet( ReentrantLock lock = acquireSubmissionLock(submissionId);
() -> submissionRepository
.save(CoreSubmission.builder().coreSubmissionId(submissionId).build())); // At this point, another thread might also have gotten the lock from the map
// Be the only one to create this submission
try {
lock.lock();
Optional<CoreSubmission> existing = submissionRepository.findByCoreSubmissionId(submissionId);
return existing.orElseGet(() -> submissionRepository
.saveAndFlush(CoreSubmission.builder().coreSubmissionId(submissionId).build()));
} finally {
lock.unlock();
}
} }
/** /**
......
...@@ -747,3 +747,14 @@ databaseChangeLog: ...@@ -747,3 +747,14 @@ databaseChangeLog:
comment: Delete and drop submit_submission table. comment: Delete and drop submit_submission table.
sql: delete from submit_submission; sql: delete from submit_submission;
drop table submit_submission; drop table submit_submission;
# Prevent duplicate core submissions
- changeSet:
id: 1710252596737-1
author: ruben (generated)
changes:
- addUniqueConstraint:
columnNames: core_submission_id
constraintName: UK_p00vxxysmhvlxbqgkfx93gcvc
tableName: core_submission
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment