Skip to content

feat: question matching #50

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

Merged
merged 38 commits into from
Oct 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
a483202
feat: use redis pubsub and transactions
tituschewxj Oct 22, 2024
9834600
feat: handle duplicate connection
tituschewxj Oct 22, 2024
8c373f2
feat: add keys to watch
tituschewxj Oct 22, 2024
55a16a0
docs: update comment
tituschewxj Oct 22, 2024
2ba631b
chore: clean up
tituschewxj Oct 22, 2024
c730342
chore: cleanup
tituschewxj Oct 22, 2024
444eb80
fix: update log ouput
tituschewxj Oct 23, 2024
1f07dc6
feat: use redis locks
tituschewxj Oct 23, 2024
f80eb2e
style: remove redundant log of user found
tituschewxj Oct 23, 2024
512ad31
fix: remove sleep
tituschewxj Oct 23, 2024
0fe7a7c
feat: update models
tituschewxj Oct 24, 2024
06eeb1d
feat: validate not duplicate user
tituschewxj Oct 24, 2024
5f10257
feat: update matching algorithm
tituschewxj Oct 24, 2024
7f767b0
fix json name
tituschewxj Oct 24, 2024
816f398
feat: add grpc proto files
tituschewxj Oct 24, 2024
ba57e31
chore: update go dependencies
tituschewxj Oct 24, 2024
c1f98ee
chore: clean up dependencies
tituschewxj Oct 24, 2024
6696635
fix: grpc server
tituschewxj Oct 24, 2024
0cd12f8
refactor: move to main.go
tituschewxj Oct 24, 2024
dbcdc69
feat: implement grpc client
tituschewxj Oct 25, 2024
8ba4883
chore: remove deprecated funcs
tituschewxj Oct 25, 2024
922f336
feat: grpc client-server connection
tituschewxj Oct 25, 2024
c040b36
feat: improve logs
tituschewxj Oct 25, 2024
e98e0bd
feat: improve logs
tituschewxj Oct 25, 2024
cf1417d
feat: add client to server struct
tituschewxj Oct 25, 2024
b9050cc
feat: find random question
tituschewxj Oct 25, 2024
edab9a6
feat: add stubs
tituschewxj Oct 25, 2024
1248651
refactor: findmachingquestion
tituschewxj Oct 25, 2024
7b37824
fix: complexity matching
tituschewxj Oct 25, 2024
16a2023
feat: return docrefid
tituschewxj Oct 25, 2024
fcd1bed
feat: update test file
tituschewxj Oct 25, 2024
125ce82
fix: proto dependency
tituschewxj Oct 25, 2024
c2ad342
feat: add simulation test
tituschewxj Oct 25, 2024
8aabda6
fix: remove h1
tituschewxj Oct 25, 2024
47eb5f0
fix: margin
tituschewxj Oct 25, 2024
2ff03c3
chore: move proto files into individual services
tituschewxj Oct 25, 2024
f268332
feat: update docker and env
tituschewxj Oct 25, 2024
e58e4af
fix: return error if unable to lock
tituschewxj Oct 26, 2024
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 .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
MATCHING_SERVICE_PORT: ${{ vars.MATCHING_SERVICE_PORT }}
MATCHING_SERVICE_TIMEOUT: ${{ vars.MATCHING_SERVICE_TIMEOUT }}
REDIS_URL: ${{ vars.REDIS_URL }}
QUESTION_SERVICE_GRPC_URL: ${{ vars.QUESTION_SERVICE_GPRC_URL }}
run: |
cd ./apps/frontend
echo "NEXT_PUBLIC_QUESTION_SERVICE_URL=$QUESTION_SERVICE_URL" >> .env
Expand All @@ -56,6 +57,7 @@ jobs:
echo "MATCH_TIMEOUT=$MATCHING_SERVICE_TIMEOUT" >> .env
echo "JWT_SECRET=$JWT_SECRET" >> .env
echo "REDIS_URL=$REDIS_URL" >> .env
echo "QUESTION_SERVICE_GRPC_URL=$QUESTION_SERVICE_GRPC_URL" >> .env

- name: Create Database Credential Files
env:
Expand Down
2 changes: 1 addition & 1 deletion apps/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Once running, you can access:

- The **frontend** at http://localhost:3000
- The **user service** at http://localhost:3001
- The **question service** at http://localhost:8080
- The **question service** at http://localhost:8080 (REST) and http://localhost:50051 (gRPC)
- The **matching service** at http://localhost:8081
- The **redis service** at http://localhost:6379

Expand Down
1 change: 1 addition & 0 deletions apps/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ services:
dockerfile: Dockerfile
ports:
- 8080:8080
- 50051:50051
env_file:
- ./question-service/.env
networks:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ const DifficultySelector: React.FC<DifficultySelectorProps> = ({ selectedDifficu
<Tag.CheckableTag
className={`difficulty-tag ${difficultyOption.value}-tag`}
key={difficultyOption.value}
checked={selectedDifficulties.includes(difficultyOption.label)}
onChange={() => handleChange(difficultyOption.label)}
checked={selectedDifficulties.includes(difficultyOption.value)}
onChange={() => handleChange(difficultyOption.value)}
>
{difficultyOption.label}
</Tag.CheckableTag>
Expand Down
41 changes: 14 additions & 27 deletions apps/frontend/src/app/services/use-matching.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,15 @@ export type MatchRequestParams = {
}

export type MatchFoundResponse = {
type: "match_found",
matchId: number,
partnerId: number,
partnerName: string,
} | {
type: "match_found",
matchId: string,
type: "match_question_found",
match_id: string,
user: string,
matchedUser: string,
topic: string | string[],
difficulty: string
matched_user: string,
matched_topics: string[],
question_doc_ref_id: string,
question_name: string,
question_difficulty: string,
question_topics: string[],
}

export type MatchTimeoutResponse = {
Expand Down Expand Up @@ -61,7 +59,7 @@ export default function useMatching(): MatchState {
return;
}

if (responseJson.type == "match_found") {
if (responseJson.type == "match_question_found") {
setIsSocket(false);

const info: MatchInfo = parseInfoFromResponse(responseJson);
Expand Down Expand Up @@ -136,20 +134,9 @@ export default function useMatching(): MatchState {
}

function parseInfoFromResponse(responseJson: MatchFoundResponse): MatchInfo {
// test whether old or new
if ("partnerId" in responseJson) {
return {
matchId: responseJson.matchId?.toString() ?? "unknown",
partnerId: responseJson.partnerId?.toString() ?? "unknown",
partnerName: responseJson.partnerName ?? "unknown",
myName: "unknown",
};
} else {
return {
matchId: responseJson.matchId?.toString() ?? "unknown",
partnerId: "unknown",
partnerName: responseJson.matchedUser ?? "unknown",
myName: responseJson.user ?? "unknown",
};
}
return {
matchId: responseJson.match_id?.toString() ?? "unknown",
partnerName: responseJson.matched_user ?? "unknown",
myName: responseJson.user ?? "unknown",
};
}
1 change: 0 additions & 1 deletion apps/frontend/src/contexts/websocketcontext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export type SocketState = {
};
export type MatchInfo = {
matchId: string;
partnerId: string;
myName: string;
partnerName: string;
}
Expand Down
6 changes: 4 additions & 2 deletions apps/matching-service/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ PORT=8081
MATCH_TIMEOUT=30
JWT_SECRET=you-can-replace-this-with-your-own-secret

# if you are NOT USING docker, use the below url
# If you are NOT USING docker, use the below variables
REDIS_URL=localhost:6379
QUESTION_SERVICE_GRPC_URL=localhost:50051

# if you are USING docker, use the below url
# If you are USING docker, use the below variables
# REDIS_URL=redis-container:6379
# QUESTION_SERVICE_GRPC_URL=question-service:50051
25 changes: 17 additions & 8 deletions apps/matching-service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,24 @@ Client sends matching parameters:
{
"type": "match_request",
"topics": ["Algorithms", "Arrays"],
"difficulties": ["Easy", "Medium"],
"username": "Jane Doe"
"difficulties": ["easy", "medium"],
"username": "1f0myn"
}
```

Server response on successful match:

```json
{
"type": "match_found",
"matchId": "1c018916a34c5bee21af0b2670bd6156",
"user": "zkb4px",
"matchedUser": "JohnDoe",
"topic": "Algorithms",
"difficulty": "Medium"
"type": "match_question_found",
"match_id": "c377f463d380a9bd1dd03242892ef32e",
"user": "1f0myn",
"matched_user": "jrsznp",
"matched_topics": ["Graphs", "Bit Manipulation", "Databases"],
"question_doc_ref_id": "5lObMfyyKPgNXSuLcGEm",
"question_name": "Repeated DNA Sequences",
"question_difficulty": "medium",
"question_topics": ["Algorithms", "Bit Manipulation"]
}
```

Expand Down Expand Up @@ -128,21 +131,27 @@ Before running the following commands, ensure that the URL for the Redis server
To run the application via Docker, run the following command:

1. Set up the Go Docker container for the matching service

```bash
docker build -f Dockerfile -t match-go-app .
```

2. Create the Docker network for Redis and Go

```bash
docker network create redis-go-network
```

3. Start a new Redis container in detached mode using the Redis image from Docker Hub

```bash
docker run -d --name redis-container --network redis-go-network redis
```

4. Run the Go Docker container for the matching-service

```bash
docker run -d -p 8081:8081 --name go-app-container --network redis-go-network match-go-app
```

**NOTE:** As there is a dependency on the question-service to return the found questions, the matching-service does not work fully unless the question-service is present.
36 changes: 36 additions & 0 deletions apps/matching-service/databases/topic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package databases

import (
"context"
"log"
"matching-service/models"
"strings"

"github.com/redis/go-redis/v9"
)

// Add user into each specified topic set based on the topics selected by users
func AddUserToTopicSets(tx *redis.Tx, request models.MatchRequest, ctx context.Context) {
for _, topic := range request.Topics {
err := tx.SAdd(ctx, strings.ToLower(topic), request.Username).Err()
if err != nil {
log.Println("Error adding user to topic set:", err)
}
}
}

// Remove user from each specified topic set based on the topics selected by users
func RemoveUserFromTopicSets(tx *redis.Tx, username string, ctx context.Context) {
request, err := GetUserDetails(tx, username, ctx)
if err != nil {
log.Println("Error retrieving user from hashset:", err)
return
}

for _, topic := range request.Topics {
err := tx.SRem(ctx, strings.ToLower(topic), request.Username).Err()
if err != nil {
log.Println("Error removing user from topic set:", err)
}
}
}
22 changes: 22 additions & 0 deletions apps/matching-service/databases/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package databases

import (
"context"
"matching-service/models"

"github.com/redis/go-redis/v9"
)

// Clean up queue, sets and hashset in Redis
func CleanUpUser(tx *redis.Tx, username string, ctx context.Context) {
DequeueUser(tx, username, ctx)
RemoveUserFromTopicSets(tx, username, ctx)
RemoveUserDetails(tx, username, ctx)
}

// Adds the user to the queue, sets and hashsets in Redis
func AddUser(tx *redis.Tx, matchRequest models.MatchRequest, ctx context.Context) {
EnqueueUser(tx, matchRequest.Username, ctx)
AddUserToTopicSets(tx, matchRequest, ctx)
StoreUserDetails(tx, matchRequest, ctx)
}
Loading