Skip to content

Commit

Permalink
tmp unsafe waiting code
Browse files Browse the repository at this point in the history
  • Loading branch information
J12934 committed Nov 24, 2024
1 parent 2833a4e commit 6304567
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 2 deletions.
35 changes: 35 additions & 0 deletions balancer/pkg/scoring/scoring.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ type ScoringService struct {
currentScoresSorted []*TeamScore
currentScoresMutex *sync.Mutex

lastSeenUpdate time.Time
newUpdate sync.Cond

challengesMap map[string](bundle.JuiceShopChallenge)
}

Expand All @@ -55,6 +58,9 @@ func NewScoringServiceWithInitialScores(b *bundle.Bundle, initialScores map[stri
currentScoresSorted: sortTeamsByScoreAndCalculatePositions(initialScores),
currentScoresMutex: &sync.Mutex{},

lastSeenUpdate: time.Now(),
newUpdate: *sync.NewCond(&sync.Mutex{}),

challengesMap: cachedChallengesMap,
}
}
Expand All @@ -67,6 +73,33 @@ func (s *ScoringService) GetTopScores() []*TeamScore {
return s.currentScoresSorted
}

func (s *ScoringService) WaitForUpdatesNewerThan(ctx context.Context, lastSeenUpdate time.Time) []*TeamScore {
if s.lastSeenUpdate.After(lastSeenUpdate) {
// the last update was after the last seen update, so we can return the current scores without waiting
return s.currentScoresSorted
}

const maxWaitTime = 25 * time.Second
done := make(chan struct{})
go func() {
s.newUpdate.Wait()
close(done)
}()

select {
// check for update by subscribing to the newUpdate condition
case <-done:
// new update was received
return s.currentScoresSorted
case <-time.After(maxWaitTime):
// timeout was reached
return nil
case <-ctx.Done():
// request was aborted
return nil
}
}

// StartingScoringWorker starts a worker that listens for changes in JuiceShop deployments and updates the scores accordingly
func (s *ScoringService) StartingScoringWorker(ctx context.Context) {
watcher, err := s.bundle.ClientSet.AppsV1().Deployments(s.bundle.RuntimeEnvironment.Namespace).Watch(ctx, metav1.ListOptions{
Expand Down Expand Up @@ -101,13 +134,15 @@ func (s *ScoringService) StartingScoringWorker(ctx context.Context) {
s.currentScoresMutex.Lock()
s.currentScores[score.Name] = score
s.currentScoresSorted = sortTeamsByScoreAndCalculatePositions(s.currentScores)
s.newUpdate.Broadcast()
s.currentScoresMutex.Unlock()
case watch.Deleted:
deployment := event.Object.(*appsv1.Deployment)
team := deployment.Labels["team"]
s.currentScoresMutex.Lock()
delete(s.currentScores, team)
s.currentScoresSorted = sortTeamsByScoreAndCalculatePositions(s.currentScores)
s.newUpdate.Broadcast()
s.currentScoresMutex.Unlock()
default:
s.bundle.Log.Printf("Unknown event type: %v", event.Type)
Expand Down
18 changes: 17 additions & 1 deletion balancer/routes/score-board.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package routes
import (
"encoding/json"
"net/http"
"time"

b "github.com/juice-shop/multi-juicer/balancer/pkg/bundle"
"github.com/juice-shop/multi-juicer/balancer/pkg/scoring"
Expand All @@ -16,7 +17,22 @@ type ScoreBoardResponse struct {
func handleScoreBoard(bundle *b.Bundle, scoringService *scoring.ScoringService) http.Handler {
return http.HandlerFunc(
func(responseWriter http.ResponseWriter, req *http.Request) {
totalTeams := scoringService.GetTopScores()
var totalTeams []*scoring.TeamScore

if req.URL.Query().Get("wait-for-update-after") != "" {
lastSeenUpdate, err := time.Parse(time.RFC3339, req.URL.Query().Get("wait-for-update-after"))
if err != nil {
http.Error(responseWriter, "Invalid time format", http.StatusBadRequest)
return
}
totalTeams = scoringService.WaitForUpdatesNewerThan(req.Context(), lastSeenUpdate)
if totalTeams == nil {
responseWriter.WriteHeader(http.StatusNoContent)
return
}
} else {
totalTeams = scoringService.GetTopScores()
}

var topTeams []*scoring.TeamScore
// limit score-board to calculate score for the top 24 teams only
Expand Down
36 changes: 35 additions & 1 deletion balancer/ui/src/pages/ScoreOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ interface Team {
}

async function fetchTeams(): Promise<Team[]> {
const response = await fetch("/balancer/api/score-board/top");
const response = await fetch(
`/balancer/api/score-board/top?wait-for-update-after=${new Date().toISOString()}`
);
const { teams } = await response.json();
return teams;
}
Expand All @@ -62,6 +64,38 @@ export function ScoreOverviewPage({
clearInterval(timer);
};
}, []);
const [lastUpdateStarted, setLastUpdateStarted] = useState(Date.now());

let timeout: number | null = null;
async function updateScoreData() {
try {
setLastUpdateStarted(Date.now());
const status = await fetchTeams();
setTeams(status);

// the request is using a http long polling mechanism to get the updates as soon as possible
// in case the request returns immediatly we wait for at least 3 seconds to ensure we aren't spamming the server
const waitTime = Math.max(3000, 5000 - (Date.now() - lastUpdateStarted));
console.log(
"Waited for",
Date.now() - lastUpdateStarted,
"ms for status update"
);
console.log("Waiting for", waitTime, "ms until starting next request");
timeout = window.setTimeout(() => updateScoreData(), waitTime);
} catch (err) {
console.error("Failed to fetch current teams!", err);
}
}

useEffect(() => {
updateScoreData();
return () => {
if (timeout !== null) {
clearTimeout(timeout);
}
};
}, []);

return (
<>
Expand Down

0 comments on commit 6304567

Please sign in to comment.