Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions docs/schema.dbml
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@ Table game_matches {
started_at timestamp
finished_at timestamp

times_scheduled integer [not null, default: 0]

~user_audit_template
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ public GameMatch scheduleMatch(GameMatch match) {

match.setStatus(MatchStatus.waiting);
match.setScheduledAt(Instant.now());
match.setTimesScheduled(match.getTimesScheduled() + 1);

return gameMatchRepository.save(match);
}
Expand Down Expand Up @@ -302,90 +303,12 @@ public Page<GameMatch> getFullPaginatedQueue(Competition competition, Pageable p
page);
}

public GameMatchDto getDTOById(Long id) {
return GameMatchDto.fromEntity(gameMatchRepository.getReferenceById(id));
}

public Optional<GameMatchDto> getDTOByUuid(String uuid) {
Optional<GameMatch> dto = gameMatchRepository.findByUuid(UUID.fromString(uuid));
if (dto.isEmpty()) return Optional.empty();

return Optional.of(GameMatchDto.fromEntity(dto.get()));
}

public GameMatch getReferenceById(Long id) {
return gameMatchRepository.getReferenceById(id);
}

public Optional<GameMatch> getReferenceByUuid(String uuid) {
return gameMatchRepository.findByUuid(UUID.fromString(uuid));
}

public boolean isGameMatchIdExist(Long id) {
return gameMatchRepository.existsById(id);
}

public boolean isGameMatchUuidExist(String uuid) {
return gameMatchRepository.existsByUuid(UUID.fromString(uuid));
}

public boolean isGameMatchWaiting(String uuid) {
return false;
// return gameMatchRepository.findByUuid(UUID.fromString(uuid)).orElseThrow().getStatus()
// == MatchStatus.WAITING;
}

public List<GameMatch> getFailedMatches() {
return null;
// return gameMatchRepository
// .findByStatus(MatchStatus.FAILED)
// .stream()
// .toList();
}

@Transactional
public void rescheduleStaleMatches(boolean isIgnoreLimit) {
LocalDateTime thresholdTime =
LocalDateTime.now(clock).minusMinutes(gameMatchProperties.getStaleThresholdMinutes());

// This atomically marks all stale matches as RESCHEDULING and returns their ids
List<Long> matchesToReschedule = gameMatchRepository.claimAndMarkStaleMatches(thresholdTime);
log.info("Found {} matches to reschedule", matchesToReschedule.size());
matchesToReschedule.forEach(id -> rescheduleMatch(id, isIgnoreLimit));
log.info("Rescheduling completed");
}

@Transactional
public void adminRescheduleMatches(List<Long> matchIds) {
log.info("Admin {} matches to reschedule", matchIds.size());
matchIds.forEach(id -> rescheduleMatch(id, true));
log.info("Rescheduling completed");
}

@Transactional
public GameMatchJob rescheduleMatch(Long gameMatchId, boolean isIgnoreLimit) {
GameMatch gameMatch = gameMatchRepository.getReferenceById(gameMatchId);
// Integer timesQueued = gameMatch.getTimesQueued();
// if (!isIgnoreLimit && timesQueued == 3) {
// throw new IllegalStateException("Match " + gameMatch.getId() + " has exceeded
// maximum retry attempts (3)");
// }
//
// if(gameMatch.getStatus() != MatchStatus.WAITING) {
// throw new IllegalArgumentException("Match " + gameMatch.getId() + " cannot be
// rescheduled.");
// }
//
// gameMatch.setQueuedAt(LocalDateTime.now(clock));
// gameMatch.incrementTimesQueued();
// gameMatch.setStatus(MatchStatus.WAITING);
GameMatchJob job = GameMatchJob.from(gameMatch);
gameMatchRepository.save(gameMatch);
rabbitMQService.enqueueGameMatchJob(job);
log.info("rescheduled match {}", job);
return job;
}

public long countTeamQueuedMatchesByLadder(Team team, Ladder ladder) {
return gameMatchRepository.countTeamMatchesByLadderAndStatus(
team, ladder.getLadder(), Set.of(MatchStatus.waiting, MatchStatus.in_progress));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,7 @@ public class GameMatch extends AuditableEntity {

@Column(name = "finished_at")
private Instant finishedAt;

@Column(name = "times_scheduled", nullable = false)
private Integer timesScheduled = 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import java.util.List;
import java.util.Set;
import java.util.UUID;

import org.bytefight.webserver.common.web.RestPageRequest;
import org.bytefight.webserver.gamematch.application.AdminGameMatchService;
Expand Down Expand Up @@ -40,18 +41,14 @@ public AdminGameMatchController(
this.gameMatchService = gameMatchService;
}

@PostMapping("/reschedule-stale")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<Void> reschduleStaleMatches() {
gameMatchService.rescheduleStaleMatches(false);
@PostMapping("/schedule")
public ResponseEntity<Void> adminScheduleMatches(@RequestBody List<String> matchUuids) {
for(String uuid : matchUuids) {
GameMatch gameMatch = gameMatchService.getGameMatch(UUID.fromString(uuid)).orElse(null);
if(gameMatch == null) continue;

return ResponseEntity.ok().build();
}

@PostMapping("/reschedule")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<Void> adminRescheduleMatches(@RequestBody List<Long> matchIds) {
gameMatchService.adminRescheduleMatches(matchIds);
gameMatchService.scheduleMatch(gameMatch);
}

return ResponseEntity.ok().build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
BEGIN;
ALTER TABLE IF EXISTS public.game_matches
ADD COLUMN times_scheduled integer NOT NULL DEFAULT 0;
END;
Loading