Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development: Improve git operations performance #9809

Open
wants to merge 35 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
bb4e6c8
refactored query usage, SSH works nicely, for HTTPS state keeping is …
SimonEntholzer Nov 17, 2024
cc8d497
fixed access log again
SimonEntholzer Nov 18, 2024
f945d1e
more fixes of access log
SimonEntholzer Nov 18, 2024
483d9fd
cleanup 1
SimonEntholzer Nov 18, 2024
5e5705e
handle aux repos correctly
SimonEntholzer Nov 18, 2024
ce69c49
fix retrieval of test repository solution participation
SimonEntholzer Nov 18, 2024
c8e3bcc
cleanup 2
SimonEntholzer Nov 18, 2024
19e4d8c
Merge branch 'develop' into chore/programming-exercises/improve-perfo…
SimonEntholzer Nov 19, 2024
10f2260
cleanup 3
SimonEntholzer Nov 19, 2024
7812d47
cleanup 4
SimonEntholzer Nov 19, 2024
ed27a1b
cleanup 5
SimonEntholzer Nov 19, 2024
48f3255
fix tests
SimonEntholzer Nov 19, 2024
59d437f
Merge branch 'develop' into chore/programming-exercises/improve-perfo…
SimonEntholzer Nov 19, 2024
5bdfea2
fix server style
SimonEntholzer Nov 19, 2024
fe504dd
Merge branch 'develop' into chore/programming-exercises/improve-perfo…
SimonEntholzer Nov 23, 2024
3c9a2a9
Merge branch 'develop' into chore/programming-exercises/improve-perfo…
SimonEntholzer Nov 26, 2024
fb6e69d
Merge branch 'develop' into chore/programming-exercises/improve-perfo…
SimonEntholzer Nov 26, 2024
2994293
Merge branch 'develop' into chore/programming-exercises/improve-perfo…
SimonEntholzer Nov 28, 2024
ba93fa7
Merge branch 'develop' into chore/programming-exercises/improve-perfo…
SimonEntholzer Nov 29, 2024
e3ecc78
Merge branch 'develop' into chore/programming-exercises/improve-perfo…
SimonEntholzer Nov 30, 2024
51a4514
Merge branch 'develop' into chore/programming-exercises/improve-perfo…
SimonEntholzer Dec 6, 2024
24109d4
Merge branch 'develop' into chore/programming-exercises/improve-perfo…
SimonEntholzer Dec 7, 2024
9a41a29
Merge branch 'develop' into chore/programming-exercises/improve-perfo…
SimonEntholzer Dec 8, 2024
06c290d
Merge branch 'develop' into chore/programming-exercises/improve-perfo…
SimonEntholzer Dec 10, 2024
fb4c947
Merge branch 'develop' into chore/programming-exercises/improve-perfo…
SimonEntholzer Dec 10, 2024
ff15cf3
merge develop
SimonEntholzer Dec 21, 2024
72fb576
Merge branch 'develop' into chore/programming-exercises/improve-perfo…
SimonEntholzer Dec 22, 2024
40be7f9
add suggestions
SimonEntholzer Dec 22, 2024
b3099da
Merge branch 'develop' into chore/programming-exercises/improve-perfo…
SimonEntholzer Dec 22, 2024
c42ad29
Merge branch 'develop' into chore/programming-exercises/improve-perfo…
SimonEntholzer Dec 23, 2024
7d954a6
Merge branch 'develop' into chore/programming-exercises/improve-perfo…
SimonEntholzer Dec 23, 2024
e6bfc2b
Merge branch 'develop' into chore/programming-exercises/improve-perfo…
SimonEntholzer Dec 29, 2024
6ecf6c3
Merge branch 'develop' into chore/programming-exercises/improve-perfo…
SimonEntholzer Jan 2, 2025
bf2a651
Improve intendation
SimonEntholzer Jan 3, 2025
1175f8f
merge develop and resolve merge conflicts
SimonEntholzer Jan 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,11 @@ Optional<ProgrammingExerciseStudentParticipation> findByIdWithAllResultsAndRelat

List<ProgrammingExerciseStudentParticipation> findAllByExerciseIdAndStudentLogin(long exerciseId, String username);

default ProgrammingExerciseStudentParticipation findByExerciseIdAndStudentLoginOrThrow(long exerciseId, String username) {
return getValueElseThrow(findByExerciseIdAndStudentLogin(exerciseId, username));
@EntityGraph(type = LOAD, attributePaths = { "submissions" })
Optional<ProgrammingExerciseStudentParticipation> findWithSubmissionsByRepositoryUri(String repositoryUri);

default ProgrammingExerciseStudentParticipation findWithSubmissionsByRepositoryUriElseThrow(String repositoryUri) {
return getValueElseThrow(findWithSubmissionsByRepositoryUri(repositoryUri));
}

Optional<ProgrammingExerciseStudentParticipation> findByRepositoryUri(String repositoryUri);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ public interface SolutionProgrammingExerciseParticipationRepository
""")
Optional<SolutionProgrammingExerciseParticipation> findByBuildPlanIdWithResults(@Param("buildPlanId") String buildPlanId);

@EntityGraph(type = LOAD, attributePaths = { "submissions" })
Optional<SolutionProgrammingExerciseParticipation> findWithSubmissionsByRepositoryUri(String repositoryUri);

default SolutionProgrammingExerciseParticipation findWithSubmissionsByRepositoryUriElseThrow(String repositoryUri) {
return getValueElseThrow(findWithSubmissionsByRepositoryUri(repositoryUri));
}

Optional<SolutionProgrammingExerciseParticipation> findByRepositoryUri(String repositoryUri);

default SolutionProgrammingExerciseParticipation findByRepositoryUriElseThrow(String repositoryUri) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ default TemplateProgrammingExerciseParticipation findWithEagerResultsAndSubmissi
return getValueElseThrow(findWithEagerResultsAndSubmissionsByProgrammingExerciseId(exerciseId));
}

@EntityGraph(type = LOAD, attributePaths = { "submissions" })
Optional<TemplateProgrammingExerciseParticipation> findWithSubmissionsByRepositoryUri(String repositoryUri);

default TemplateProgrammingExerciseParticipation findWithSubmissionsByRepositoryUriElseThrow(String repositoryUri) {
return getValueElseThrow(findWithSubmissionsByRepositoryUri(repositoryUri));
}

Optional<TemplateProgrammingExerciseParticipation> findByRepositoryUri(String repositoryUri);

default TemplateProgrammingExerciseParticipation findByRepositoryUriElseThrow(String repositoryUri) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,36 @@
public interface VcsAccessLogRepository extends ArtemisJpaRepository<VcsAccessLog, Long> {

/**
* Find the access log entry which does not have any commit hash yet
* Retrieves the most recent {@link VcsAccessLog} for a given participation ID.
*
* @param participationId The id of the participation the repository belongs to
* @return a log entry belonging to the participationId, which has no commit hash
* @param participationId the ID of the participation to filter by.
* @return an {@link Optional} containing the newest {@link VcsAccessLog}, or empty if none exists.
*/
@Query("""

SELECT vcsAccessLog
SELECT vcsAccessLog
FROM VcsAccessLog vcsAccessLog
WHERE vcsAccessLog.participation.id = :participationId
ORDER BY vcsAccessLog.timestamp DESC
ORDER BY vcsAccessLog.id DESC
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
LIMIT 1
""")
Optional<VcsAccessLog> findNewestByParticipationId(@Param("participationId") long participationId);

/**
* Retrieves the most recent {@link VcsAccessLog} for a specific repository URI of a participation.
*
* @param repositoryUri the URI of the participation to filter by.
* @return an Optional containing the newest {@link VcsAccessLog} of the participation, or empty if none exists.
*/
@Query("""
SELECT log
FROM VcsAccessLog log
LEFT JOIN TREAT (log.participation AS ProgrammingExerciseStudentParticipation) participation
WHERE participation.repositoryUri = :repositoryUri
ORDER BY log.id DESC
LIMIT 1
""")
Optional<VcsAccessLog> findNewestByRepositoryUri(@Param("repositoryUri") String repositoryUri);

/**
* Retrieves a list of {@link VcsAccessLog} entities associated with the specified participation ID.
* The results are ordered by the log ID in ascending order.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,25 +127,18 @@ public TemplateProgrammingExerciseParticipation findTemplateParticipationByProgr
/**
* Tries to retrieve a team participation for the given exercise and team short name.
*
* @param exercise the exercise for which to find a participation.
* @param teamShortName of the team to which the participation belongs.
* @param withSubmissions true if the participation should be fetched with its submissions.
* @param exercise the exercise for which to find a participation.
* @param teamShortName of the team to which the participation belongs.
* @return the participation for the given exercise and team.
* @throws EntityNotFoundException if the team participation was not found.
*/
public ProgrammingExerciseStudentParticipation findTeamParticipationByExerciseAndTeamShortNameOrThrow(ProgrammingExercise exercise, String teamShortName,
boolean withSubmissions) {
public ProgrammingExerciseStudentParticipation findTeamParticipationByExerciseAndTeamShortNameOrThrow(ProgrammingExercise exercise, String teamShortName) {

Optional<ProgrammingExerciseStudentParticipation> participationOptional;

// It is important to fetch all students of the team here, because the local VC and local CI system use this participation to check if the authenticated user is part of the
// team.
if (withSubmissions) {
participationOptional = studentParticipationRepository.findWithSubmissionsAndEagerStudentsByExerciseIdAndTeamShortName(exercise.getId(), teamShortName);
}
else {
participationOptional = studentParticipationRepository.findWithEagerStudentsByExerciseIdAndTeamShortName(exercise.getId(), teamShortName);
}
participationOptional = studentParticipationRepository.findWithSubmissionsAndEagerStudentsByExerciseIdAndTeamShortName(exercise.getId(), teamShortName);

if (participationOptional.isEmpty()) {
throw new EntityNotFoundException("Participation could not be found by exerciseId " + exercise.getId() + " and team short name " + teamShortName);
Expand All @@ -169,25 +162,19 @@ public Optional<ProgrammingExerciseStudentParticipation> findTeamParticipationBy
/**
* Tries to retrieve a student participation for the given exercise and username and test run flag.
*
* @param exercise the exercise for which to find a participation.
* @param username of the user to which the participation belongs.
* @param isTestRun true if the participation is a test run participation.
* @param withSubmissions true if the participation should be loaded with its submissions.
* @param exercise the exercise for which to find a participation.
* @param username of the user to which the participation belongs.
* @param isTestRun true if the participation is a test run participation.
* @return the participation for the given exercise and user.
* @throws EntityNotFoundException if there is no participation for the given exercise and user.
*/
@NotNull
public ProgrammingExerciseStudentParticipation findStudentParticipationByExerciseAndStudentLoginAndTestRunOrThrow(ProgrammingExercise exercise, String username,
boolean isTestRun, boolean withSubmissions) {
boolean isTestRun) {

Optional<ProgrammingExerciseStudentParticipation> participationOptional;

if (withSubmissions) {
participationOptional = studentParticipationRepository.findWithSubmissionsByExerciseIdAndStudentLoginAndTestRun(exercise.getId(), username, isTestRun);
}
else {
participationOptional = studentParticipationRepository.findByExerciseIdAndStudentLoginAndTestRun(exercise.getId(), username, isTestRun);
}
participationOptional = studentParticipationRepository.findWithSubmissionsByExerciseIdAndStudentLoginAndTestRun(exercise.getId(), username, isTestRun);

if (participationOptional.isEmpty()) {
throw new EntityNotFoundException("Participation could not be found by exerciseId " + exercise.getId() + " and user " + username);
Expand Down Expand Up @@ -443,63 +430,25 @@ public void resetRepository(VcsRepositoryUri targetURL, VcsRepositoryUri sourceU
}

/**
* Get the participation for a given exercise and a repository type or user name. This method is used by the local VC system and by the local CI system to get the
* participation.
* Get the participation for a given repository url and a repository type or user name. This method is used by the local VC system to get the
* participation for logging operations on the repository.
*
* @param exercise the exercise for which to get the participation.
* @param repositoryTypeOrUserName the repository type ("exercise", "solution", or "tests") or username (e.g. "artemis_test_user_1") as extracted from the repository URI.
* @param isPracticeRepository whether the repository is a practice repository, i.e. the repository URI contains "-practice-".
* @param withSubmissions whether submissions should be loaded with the participation. This is needed for the local CI system.
* @return the participation.
* @throws EntityNotFoundException if the participation could not be found.
* @param repositoryTypeOrUserName the name of the user or the type of the repository
* @param repositoryURI the participation's repository URL
* @return the participation belonging to the provided repositoryURI and repository type or username
*/
public ProgrammingExerciseParticipation retrieveParticipationForRepository(ProgrammingExercise exercise, String repositoryTypeOrUserName, boolean isPracticeRepository,
boolean withSubmissions) {

boolean isAuxiliaryRepository = auxiliaryRepositoryService.isAuxiliaryRepositoryOfExercise(repositoryTypeOrUserName, exercise);

// For pushes to the tests repository, the solution repository is built first, and thus we need the solution participation.
// Can possibly be used by auxiliary repositories
if (repositoryTypeOrUserName.equals(RepositoryType.SOLUTION.toString()) || repositoryTypeOrUserName.equals(RepositoryType.TESTS.toString()) || isAuxiliaryRepository) {
if (withSubmissions) {
return solutionParticipationRepository.findWithEagerResultsAndSubmissionsByProgrammingExerciseIdElseThrow(exercise.getId());
}
else {
return solutionParticipationRepository.findByProgrammingExerciseIdElseThrow(exercise.getId());
}
public ProgrammingExerciseParticipation retrieveParticipationWithSubmissionsByRepository(String repositoryTypeOrUserName, String repositoryURI) {
if (repositoryTypeOrUserName.equals(RepositoryType.SOLUTION.toString()) || repositoryTypeOrUserName.equals(RepositoryType.TESTS.toString())) {
return solutionParticipationRepository.findWithSubmissionsByRepositoryUriElseThrow(repositoryURI);
}

if (repositoryTypeOrUserName.equals(RepositoryType.TEMPLATE.toString())) {
if (withSubmissions) {
return templateParticipationRepository.findWithEagerResultsAndSubmissionsByProgrammingExerciseIdElseThrow(exercise.getId());
}
else {
return templateParticipationRepository.findByProgrammingExerciseIdElseThrow(exercise.getId());
}
}

if (exercise.isTeamMode()) {
// The repositoryTypeOrUserName is the team short name.
// For teams, there is no practice participation.
return findTeamParticipationByExerciseAndTeamShortNameOrThrow(exercise, repositoryTypeOrUserName, withSubmissions);
return templateParticipationRepository.findWithSubmissionsByRepositoryUriElseThrow(repositoryURI);
}

// If the exercise is an exam exercise and the repository's owner is at least an editor, the repository could be a test run repository, or it could be the instructor's
// assignment repository.
// There is no way to tell from the repository URI, and only one participation will be created, even if both are used.
// This participation has "testRun = true" set if the test run was created first, and "testRun = false" set if the instructor's assignment repository was created first.
// If the exercise is an exam exercise, and the repository's owner is at least an editor, get the participation without regard for the testRun flag.
boolean isExamEditorRepository = exercise.isExamExercise()
&& authorizationCheckService.isAtLeastEditorForExercise(exercise, userRepository.getUserByLoginElseThrow(repositoryTypeOrUserName));
if (isExamEditorRepository) {
if (withSubmissions) {
return studentParticipationRepository.findWithSubmissionsByExerciseIdAndStudentLoginOrThrow(exercise.getId(), repositoryTypeOrUserName);
}

return studentParticipationRepository.findByExerciseIdAndStudentLoginOrThrow(exercise.getId(), repositoryTypeOrUserName);
if (repositoryTypeOrUserName.equals(RepositoryType.AUXILIARY.toString())) {
throw new EntityNotFoundException("Auxiliary repositories do not have participations.");
}
return studentParticipationRepository.findWithSubmissionsByRepositoryUriElseThrow(repositoryURI);

return findStudentParticipationByExerciseAndStudentLoginAndTestRunOrThrow(exercise, repositoryTypeOrUserName, isPracticeRepository, withSubmissions);
}

/**
Expand All @@ -510,7 +459,7 @@ public ProgrammingExerciseParticipation retrieveParticipationForRepository(Progr
* @param repositoryURI the participation's repository URL
* @return the participation belonging to the provided repositoryURI and repository type or username
*/
public ProgrammingExerciseParticipation retrieveParticipationForRepository(String repositoryTypeOrUserName, String repositoryURI) {
public ProgrammingExerciseParticipation retrieveParticipationByRepository(String repositoryTypeOrUserName, String repositoryURI) {
if (repositoryTypeOrUserName.equals(RepositoryType.SOLUTION.toString()) || repositoryTypeOrUserName.equals(RepositoryType.TESTS.toString())) {
return solutionParticipationRepository.findByRepositoryUriElseThrow(repositoryURI);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseParticipation;
import de.tum.cit.aet.artemis.programming.domain.Repository;
import de.tum.cit.aet.artemis.programming.domain.RepositoryType;
import de.tum.cit.aet.artemis.programming.domain.VcsAccessLog;
import de.tum.cit.aet.artemis.programming.domain.VcsRepositoryUri;
import de.tum.cit.aet.artemis.programming.dto.FileMove;
import de.tum.cit.aet.artemis.programming.service.localvc.VcsAccessLogService;
Expand Down Expand Up @@ -454,12 +455,10 @@ public void pullChanges(Repository repository) {
* @param domainId the id of the domain Object (participation) owning the repository
* @throws GitAPIException if the staging/committing process fails.
*/
public void commitChanges(Repository repository, User user, Long domainId) throws GitAPIException {
public Optional<VcsAccessLog> commitChanges(Repository repository, User user, Long domainId) throws GitAPIException {
SimonEntholzer marked this conversation as resolved.
Show resolved Hide resolved
gitService.stageAllChanges(repository);
gitService.commitAndPush(repository, "Changes by Online Editor", true, user);
if (vcsAccessLogService.isPresent()) {
vcsAccessLogService.get().storeCodeEditorAccessLog(repository, user, domainId);
}
return vcsAccessLogService.isPresent() ? vcsAccessLogService.get().createPreliminaryCodeEditorAccessLog(repository, user, domainId) : Optional.empty();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ public void init() {
this.setReceivePackFactory((request, repository) -> {
ReceivePack receivePack = new ReceivePack(repository);
// Add a hook that prevents illegal actions on push (delete branch, rename branch, force push).
receivePack.setPreReceiveHook(new LocalVCPrePushHook(localVCServletService, (User) request.getAttribute("user")));
// user is always null here
receivePack.setPreReceiveHook(new LocalVCPrePushHook(localVCServletService, (User) request.getSession().getAttribute("user")));
// Add a hook that triggers the creation of a new submission after the push went through successfully.
receivePack.setPostReceiveHook(new LocalVCPostPushHook(localVCServletService));
return receivePack;
Expand All @@ -72,4 +73,5 @@ public void init() {
return uploadPack;
});
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package de.tum.cit.aet.artemis.programming.service.localvc;

public class HttpsConstants {

public static final String USER_KEY = "user";

public static final String PARTICIPATION_KEY = "participation";

public static final String VCS_ACCESS_LOG_KEY = "vcs_access_log";

public static final String EXERCISE_KEY = "exercise";
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ public LocalVCFetchPreUploadHook(LocalVCServletService localVCServletService, Ht

@Override
public void onBeginNegotiateRound(UploadPack uploadPack, Collection<? extends ObjectId> collection, int clientOffered) {
localVCServletService.updateVCSAccessLogForCloneAndPullHTTPS(request, clientOffered);
String authorizationHeader = request.getHeader(LocalVCServletService.AUTHORIZATION_HEADER);
localVCServletService.updateAndStoreVCSAccessLogForCloneAndPullHTTPS(request, authorizationHeader, clientOffered);

}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package de.tum.cit.aet.artemis.programming.service.localvc;

import java.nio.file.Path;
import java.util.Collection;

import org.apache.sshd.server.session.ServerSession;
Expand All @@ -14,24 +13,23 @@ public class LocalVCFetchPreUploadHookSSH implements PreUploadHook {

private final ServerSession serverSession;

private final Path rootDir;

public LocalVCFetchPreUploadHookSSH(LocalVCServletService localVCServletService, ServerSession serverSession, Path rootDir) {
public LocalVCFetchPreUploadHookSSH(LocalVCServletService localVCServletService, ServerSession serverSession) {
this.localVCServletService = localVCServletService;
this.serverSession = serverSession;
this.rootDir = rootDir;
}

@Override
public void onBeginNegotiateRound(UploadPack uploadPack, Collection<? extends ObjectId> collection, int clientOffered) {
localVCServletService.updateVCSAccessLogForCloneAndPullSSH(serverSession, rootDir, clientOffered);
localVCServletService.updateAndStoreVCSAccessLogForCloneAndPullSSH(serverSession, clientOffered);
}

@Override
public void onEndNegotiateRound(UploadPack uploadPack, Collection<? extends ObjectId> collection, int i, int i1, boolean b) {
int bs = 4;
}

@Override
public void onSendPack(UploadPack uploadPack, Collection<? extends ObjectId> collection, Collection<? extends ObjectId> collection1) {
int ss = 4;
}
}
Loading
Loading