Skip to content

Commit fea8537

Browse files
committed
refactor: clean up files in matching-service
1 parent fc0fdac commit fea8537

File tree

9 files changed

+217
-236
lines changed

9 files changed

+217
-236
lines changed

apps/matching-service/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ go mod tidy
2929
- `MATCH_TIMEOUT`: The time in seconds to wait for a match before timing out.
3030
- `REDIS_URL`: The URL for the Redis server. Default is `localhost:6379`.
3131

32-
4. Start a local redis server:
32+
4. Start a local Redis server:
3333

3434
```bash
3535
docker run -d -p 6379:6379 redis

apps/matching-service/handlers/websocket.go

Lines changed: 29 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -123,44 +123,42 @@ func createTimeoutContext() (context.Context, context.CancelFunc, error) {
123123
return ctx, cancel, nil
124124
}
125125

126+
// Cleans up the data associated with the user before ending the websocket connection.
127+
// If user is already removed, then nothing happens.
128+
func cleanUpUser(username string) {
129+
// Cleanup Redis
130+
processes.CleanUpUser(processes.GetRedisClient(), username, context.Background())
131+
132+
// Removes the match context and active
133+
if cancelFunc, exists := matchContexts[username]; exists {
134+
cancelFunc()
135+
delete(matchContexts, username)
136+
}
137+
if _, exists := activeConnections[username]; exists {
138+
delete(activeConnections, username)
139+
}
140+
if _, exists := matchFoundChannels[username]; exists {
141+
delete(matchFoundChannels, username)
142+
}
143+
}
144+
126145
// waitForResult waits for a match result, timeout, or cancellation.
127146
func waitForResult(ws *websocket.Conn, ctx, timeoutCtx, matchCtx context.Context, matchFoundChan chan models.MatchFound, username string) {
128147
select {
129148
case <-ctx.Done():
130149
log.Println("Matching cancelled")
131-
// Cleanup Redis
132-
processes.CleanUpUser(processes.GetRedisClient(), username, context.Background())
133-
// Remove the match context and active
134-
if _, exists := matchContexts[username]; exists {
135-
delete(matchContexts, username)
136-
}
137-
if _, exists := activeConnections[username]; exists {
138-
delete(activeConnections, username)
139-
}
140-
if _, exists := matchFoundChannels[username]; exists {
141-
delete(matchFoundChannels, username)
142-
}
143-
150+
cleanUpUser(username)
144151
return
145152
case <-timeoutCtx.Done():
146153
log.Println("Connection timed out")
147-
// Cleanup Redis
148-
processes.CleanUpUser(processes.GetRedisClient(), username, context.Background())
149-
// Remove the match context and active
150-
if _, exists := matchContexts[username]; exists {
151-
delete(matchContexts, username)
152-
}
153-
if _, exists := activeConnections[username]; exists {
154-
delete(activeConnections, username)
155-
}
156-
if _, exists := matchFoundChannels[username]; exists {
157-
delete(matchFoundChannels, username)
158-
}
159-
160154
sendTimeoutResponse(ws)
155+
cleanUpUser(username)
161156
return
162157
case <-matchCtx.Done():
163158
log.Println("Match found for user: " + username)
159+
160+
// NOTE: user is already cleaned-up in the other process,
161+
// so there is no need to clean up again.
164162
return
165163
case result, ok := <-matchFoundChan:
166164
if !ok {
@@ -170,7 +168,10 @@ func waitForResult(ws *websocket.Conn, ctx, timeoutCtx, matchCtx context.Context
170168
}
171169
log.Println("Match found for user: " + username)
172170
// Notify the users about the match
173-
notifyMatch(result.User, result.MatchedUser, result)
171+
notifyMatches(result.User, result.MatchedUser, result)
172+
173+
// NOTE: user and other user are already cleaned up in a separate matching algorithm process
174+
// so no clean up is required here.
174175
return
175176
}
176177
}
@@ -186,7 +187,7 @@ func sendTimeoutResponse(ws *websocket.Conn) {
186187
}
187188
}
188189

189-
func notifyMatch(username, matchedUsername string, result models.MatchFound) {
190+
func notifyMatches(username, matchedUsername string, result models.MatchFound) {
190191
mu.Lock()
191192
defer mu.Unlock()
192193

@@ -204,27 +205,4 @@ func notifyMatch(username, matchedUsername string, result models.MatchFound) {
204205
log.Printf("Error sending message to user %s: %v\n", username, err)
205206
}
206207
}
207-
208-
// Remove the match context for both users and cancel for matched user
209-
if cancelFunc, exists := matchContexts[username]; exists {
210-
cancelFunc()
211-
delete(matchContexts, username)
212-
}
213-
214-
if cancelFunc2, exists := matchContexts[matchedUsername]; exists {
215-
cancelFunc2()
216-
delete(matchContexts, matchedUsername)
217-
}
218-
219-
// Remove the match channels
220-
if _, exists := matchFoundChannels[username]; exists {
221-
delete(matchFoundChannels, username)
222-
}
223-
if _, exists := matchFoundChannels[matchedUsername]; exists {
224-
delete(matchFoundChannels, matchedUsername)
225-
}
226-
227-
// Remove users from the activeConnections map
228-
delete(activeConnections, username)
229-
delete(activeConnections, matchedUsername)
230208
}

apps/matching-service/main.go

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package main
22

33
import (
4-
"context"
54
"fmt"
65
"log"
76
"matching-service/handlers"
@@ -10,7 +9,6 @@ import (
109
"os"
1110

1211
"github.com/joho/godotenv"
13-
"github.com/redis/go-redis/v9"
1412
)
1513

1614
func main() {
@@ -19,33 +17,17 @@ func main() {
1917
if err != nil {
2018
log.Fatalf("err loading: %v", err)
2119
}
22-
port := os.Getenv("PORT")
23-
24-
// Retrieve redis url env variable and setup the redis client
25-
redisAddr := os.Getenv("REDIS_URL")
26-
client := redis.NewClient(&redis.Options{
27-
Addr: redisAddr,
28-
Password: "", // no password set
29-
DB: 0, // use default DB
30-
})
31-
32-
// Ping the redis server
33-
_, err = client.Ping(context.Background()).Result()
34-
if err != nil {
35-
log.Fatalf("Could not connect to Redis: %v", err)
36-
} else {
37-
log.Println("Connected to Redis at the following address: " + redisAddr)
38-
}
39-
40-
// Set redis client
41-
processes.SetRedisClient(client)
20+
21+
// Setup redis client
22+
processes.SetupRedisClient()
4223

4324
// Run a goroutine that matches users
44-
25+
4526
// Routes
4627
http.HandleFunc("/match", handlers.HandleWebSocketConnections)
47-
28+
4829
// Start the server
30+
port := os.Getenv("PORT")
4931
log.Println(fmt.Sprintf("Server starting on :%s", port))
5032
err = http.ListenAndServe(fmt.Sprintf(":%s", port), nil)
5133
if err != nil {
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package models
2+
3+
import (
4+
"log"
5+
"strings"
6+
)
7+
8+
// Get the highest common difficulty (aka complexity) between two users,
9+
// If no common difficulty found, choose the min of the 2 arrays.
10+
func GetCommonDifficulty(userArr []string, matchedUserArr []string) string {
11+
commonDifficulties := make([]int, 3)
12+
for i := range commonDifficulties {
13+
commonDifficulties[i] = 0
14+
}
15+
16+
for _, difficulty := range userArr {
17+
formattedDifficulty := strings.ToLower(difficulty)
18+
switch formattedDifficulty {
19+
case "easy":
20+
commonDifficulties[0]++
21+
case "medium":
22+
commonDifficulties[1]++
23+
case "hard":
24+
commonDifficulties[2]++
25+
default:
26+
log.Println("Unknown difficulty specified: " + difficulty)
27+
}
28+
}
29+
30+
for _, difficulty := range matchedUserArr {
31+
formattedDifficulty := strings.ToLower(difficulty)
32+
switch formattedDifficulty {
33+
case "easy":
34+
commonDifficulties[0]++
35+
case "medium":
36+
commonDifficulties[1]++
37+
case "hard":
38+
commonDifficulties[2]++
39+
default:
40+
log.Println("Unknown difficulty specified: " + difficulty)
41+
}
42+
}
43+
44+
lowest := "Hard"
45+
for i := 2; i >= 0; i-- {
46+
if commonDifficulties[i] == 2 {
47+
switch i {
48+
case 0:
49+
return "Easy"
50+
case 1:
51+
return "Medium"
52+
case 2:
53+
return "Hard"
54+
}
55+
} else if commonDifficulties[i] > 0 {
56+
switch i {
57+
case 0:
58+
lowest = "Easy"
59+
case 1:
60+
lowest = "Medium"
61+
case 2:
62+
lowest = "Hard"
63+
}
64+
}
65+
}
66+
return lowest
67+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package processes
2+
3+
import (
4+
"context"
5+
"sync"
6+
7+
"github.com/redis/go-redis/v9"
8+
)
9+
10+
const matchmakingQueueRedisKey = "matchmaking_queue"
11+
12+
var (
13+
redisClient *redis.Client
14+
matchingRoutineMutex sync.Mutex // Mutex to ensure only one matchmaking goroutine is running
15+
redisAccessMutex sync.Mutex // Mutex for Redis access for concurrency safety
16+
ctx = context.Background()
17+
)

apps/matching-service/processes/match.go

Lines changed: 17 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,62 +2,26 @@ package processes
22

33
import (
44
"context"
5-
"fmt"
65
"log"
76
"matching-service/models"
8-
"strconv"
9-
"strings"
10-
"sync"
7+
"matching-service/utils"
118
"time"
129

1310
"github.com/redis/go-redis/v9"
1411
)
1512

16-
var (
17-
redisClient *redis.Client
18-
mu sync.Mutex // Mutex to ensure only one matchmaking goroutine is running
19-
ctx = context.Background()
20-
)
21-
22-
// SetRedisClient sets the Redis client to a global variable
23-
func SetRedisClient(client *redis.Client) {
24-
redisClient = client
25-
}
26-
27-
// Get redisclient
28-
func GetRedisClient() *redis.Client {
29-
return redisClient
30-
}
31-
32-
func getPortNumber(addr string) (int64, error) {
33-
// Split the string by the colon
34-
parts := strings.Split(addr, ":")
35-
if len(parts) < 2 {
36-
return 0, fmt.Errorf("no port number found")
37-
}
38-
39-
// Convert the port string to an integer
40-
port, err := strconv.ParseInt(parts[len(parts)-1], 10, 64)
41-
if err != nil {
42-
return 0, err // Return an error if conversion fails
43-
}
44-
45-
return port, nil
46-
}
47-
4813
func PerformMatching(matchRequest models.MatchRequest, ctx context.Context, matchFoundChannels map[string]chan models.MatchFound) {
4914
// Acquire mutex
50-
mu.Lock()
15+
matchingRoutineMutex.Lock()
5116
// Defer unlocking the mutex
52-
defer mu.Unlock()
17+
defer matchingRoutineMutex.Unlock()
5318

5419
for {
55-
5620
// Log queue before matchmaking
5721
// PrintMatchingQueue(redisClient, "Before Matchmaking", context.Background())
5822

5923
// Check if the queue is empty
60-
queueLength, err := redisClient.LLen(context.Background(), "matchmaking_queue").Result()
24+
queueLength, err := redisClient.LLen(context.Background(), matchmakingQueueRedisKey).Result()
6125
if err != nil {
6226
log.Println("Error checking queue length:", err)
6327
time.Sleep(1 * time.Second)
@@ -71,7 +35,7 @@ func PerformMatching(matchRequest models.MatchRequest, ctx context.Context, matc
7135
}
7236

7337
// Peek at the user queue
74-
username, err := redisClient.LIndex(context.Background(), "matchmaking_queue", 0).Result()
38+
username, err := redisClient.LIndex(context.Background(), matchmakingQueueRedisKey, 0).Result()
7539
if err != nil {
7640
log.Println("Error peeking user from queue:", err)
7741
time.Sleep(1 * time.Second)
@@ -93,19 +57,15 @@ func PerformMatching(matchRequest models.MatchRequest, ctx context.Context, matc
9357
// Log down which users got matched
9458
log.Printf("Users %s and %s matched on the topic: %s with difficulty: %s", username, matchedUsername, matchedTopic, matchedDifficulty)
9559

96-
// Clean up queue, sets and hashset in Redis
97-
DequeueUser(redisClient, username, ctx)
98-
DequeueUser(redisClient, matchedUsername, ctx)
99-
RemoveUserFromTopicSets(redisClient, username, ctx)
100-
RemoveUserFromTopicSets(redisClient, matchedUsername, ctx)
101-
RemoveUserDetails(redisClient, username, ctx)
102-
RemoveUserDetails(redisClient, matchedUsername, ctx)
103-
10460
// Log queue after matchmaking
10561
PrintMatchingQueue(redisClient, "After Matchmaking", context.Background())
10662

63+
// Clean up redis for this match
64+
cleanUp(redisClient, username, ctx)
65+
cleanUp(redisClient, matchedUsername, ctx)
66+
10767
// Generate a random match ID
108-
matchId, err := GenerateMatchID()
68+
matchId, err := utils.GenerateMatchID()
10969
if err != nil {
11070
log.Println("Unable to randomly generate matchID")
11171
}
@@ -128,3 +88,10 @@ func PerformMatching(matchRequest models.MatchRequest, ctx context.Context, matc
12888
}
12989
}
13090
}
91+
92+
// Clean up queue, sets and hashset in Redis
93+
func cleanUp(redisClient *redis.Client, username string, ctx context.Context) {
94+
DequeueUser(redisClient, username, ctx)
95+
RemoveUserFromTopicSets(redisClient, username, ctx)
96+
RemoveUserDetails(redisClient, username, ctx)
97+
}

0 commit comments

Comments
 (0)