diff options
| -rw-r--r-- | backend/api/handler_wrapper.go | 28 | ||||
| -rw-r--r-- | backend/db/models.go | 1 | ||||
| -rw-r--r-- | backend/db/query.sql.go | 6 | ||||
| -rw-r--r-- | backend/game/hub.go | 15 | ||||
| -rw-r--r-- | backend/query.sql | 4 | ||||
| -rw-r--r-- | backend/schema.sql | 1 | ||||
| -rw-r--r-- | backend/taskqueue/processor.go | 15 | ||||
| -rw-r--r-- | backend/taskqueue/queue.go | 12 | ||||
| -rw-r--r-- | backend/taskqueue/tasks.go | 69 | ||||
| -rw-r--r-- | worker/exec.go | 23 | ||||
| -rw-r--r-- | worker/handlers.go | 5 | ||||
| -rw-r--r-- | worker/models.go | 5 |
12 files changed, 105 insertions, 79 deletions
diff --git a/backend/api/handler_wrapper.go b/backend/api/handler_wrapper.go index 69b9baa..9f524e9 100644 --- a/backend/api/handler_wrapper.go +++ b/backend/api/handler_wrapper.go @@ -11,15 +11,15 @@ import ( "github.com/nsfisis/iosdc-japan-2024-albatross/backend/db" ) -var _ StrictServerInterface = (*ApiHandlerWrapper)(nil) +var _ StrictServerInterface = (*HandlerWrapper)(nil) -type ApiHandlerWrapper struct { - innerHandler Handler +type HandlerWrapper struct { + impl Handler } -func NewHandler(queries *db.Queries, hubs GameHubsInterface) *ApiHandlerWrapper { - return &ApiHandlerWrapper{ - innerHandler: Handler{ +func NewHandler(queries *db.Queries, hubs GameHubsInterface) *HandlerWrapper { + return &HandlerWrapper{ + impl: Handler{ q: queries, hubs: hubs, }, @@ -39,7 +39,7 @@ func parseJWTClaimsFromAuthorizationHeader(authorization string) (*auth.JWTClaim return claims, nil } -func (h *ApiHandlerWrapper) GetGame(ctx context.Context, request GetGameRequestObject) (GetGameResponseObject, error) { +func (h *HandlerWrapper) GetGame(ctx context.Context, request GetGameRequestObject) (GetGameResponseObject, error) { user, err := parseJWTClaimsFromAuthorizationHeader(request.Params.Authorization) if err != nil { return GetGame401JSONResponse{ @@ -48,10 +48,10 @@ func (h *ApiHandlerWrapper) GetGame(ctx context.Context, request GetGameRequestO }, }, nil } - return h.innerHandler.GetGame(ctx, request, user) + return h.impl.GetGame(ctx, request, user) } -func (h *ApiHandlerWrapper) GetGames(ctx context.Context, request GetGamesRequestObject) (GetGamesResponseObject, error) { +func (h *HandlerWrapper) GetGames(ctx context.Context, request GetGamesRequestObject) (GetGamesResponseObject, error) { user, err := parseJWTClaimsFromAuthorizationHeader(request.Params.Authorization) if err != nil { return GetGames401JSONResponse{ @@ -60,10 +60,10 @@ func (h *ApiHandlerWrapper) GetGames(ctx context.Context, request GetGamesReques }, }, nil } - return h.innerHandler.GetGames(ctx, request, user) + return h.impl.GetGames(ctx, request, user) } -func (h *ApiHandlerWrapper) GetToken(ctx context.Context, request GetTokenRequestObject) (GetTokenResponseObject, error) { +func (h *HandlerWrapper) GetToken(ctx context.Context, request GetTokenRequestObject) (GetTokenResponseObject, error) { user, err := parseJWTClaimsFromAuthorizationHeader(request.Params.Authorization) if err != nil { return GetToken401JSONResponse{ @@ -72,9 +72,9 @@ func (h *ApiHandlerWrapper) GetToken(ctx context.Context, request GetTokenReques }, }, nil } - return h.innerHandler.GetToken(ctx, request, user) + return h.impl.GetToken(ctx, request, user) } -func (h *ApiHandlerWrapper) PostLogin(ctx context.Context, request PostLoginRequestObject) (PostLoginResponseObject, error) { - return h.innerHandler.PostLogin(ctx, request) +func (h *HandlerWrapper) PostLogin(ctx context.Context, request PostLoginRequestObject) (PostLoginResponseObject, error) { + return h.impl.PostLogin(ctx, request) } diff --git a/backend/db/models.go b/backend/db/models.go index d4cf98b..5bca7b7 100644 --- a/backend/db/models.go +++ b/backend/db/models.go @@ -36,6 +36,7 @@ type Submission struct { UserID int32 Code string CodeSize int32 + CodeHash string CreatedAt pgtype.Timestamp } diff --git a/backend/db/query.sql.go b/backend/db/query.sql.go index 5475067..dc87602 100644 --- a/backend/db/query.sql.go +++ b/backend/db/query.sql.go @@ -34,8 +34,8 @@ func (q *Queries) AggregateTestcaseResults(ctx context.Context, submissionID int } const createSubmission = `-- name: CreateSubmission :one -INSERT INTO submissions (game_id, user_id, code, code_size) -VALUES ($1, $2, $3, $4) +INSERT INTO submissions (game_id, user_id, code, code_size, code_hash) +VALUES ($1, $2, $3, $4, $5) RETURNING submission_id ` @@ -44,6 +44,7 @@ type CreateSubmissionParams struct { UserID int32 Code string CodeSize int32 + CodeHash string } func (q *Queries) CreateSubmission(ctx context.Context, arg CreateSubmissionParams) (int32, error) { @@ -52,6 +53,7 @@ func (q *Queries) CreateSubmission(ctx context.Context, arg CreateSubmissionPara arg.UserID, arg.Code, arg.CodeSize, + arg.CodeHash, ) var submission_id int32 err := row.Scan(&submission_id) diff --git a/backend/game/hub.go b/backend/game/hub.go index ac71152..aa1b9f2 100644 --- a/backend/game/hub.go +++ b/backend/game/hub.go @@ -2,7 +2,9 @@ package game import ( "context" + "crypto/md5" "errors" + "fmt" "log" "strings" "time" @@ -163,11 +165,13 @@ func (hub *gameHub) run() { log.Printf("submit: %v", message.message) code := msg.Data.Code codeSize := len(code) // TODO: exclude whitespaces. + codeHash := calcHash(code) if err := hub.taskQueue.EnqueueTaskCreateSubmissionRecord( hub.game.gameID, message.client.playerID, code, codeSize, + taskqueue.MD5HexHash(codeHash), ); err != nil { // TODO: notify failure to player log.Fatalf("failed to enqueue task: %v", err) @@ -329,7 +333,8 @@ func (hub *gameHub) processTaskResultCreateSubmissionRecord( if err := hub.taskQueue.EnqueueTaskCompileSwiftToWasm( taskResult.TaskPayload.GameID(), taskResult.TaskPayload.UserID(), - taskResult.TaskPayload.Code(), + taskResult.TaskPayload.Code, + taskResult.TaskPayload.CodeHash(), taskResult.SubmissionID, ); err != nil { return &codeSubmissionError{ @@ -371,7 +376,7 @@ func (hub *gameHub) processTaskResultCompileSwiftToWasm( if err := hub.taskQueue.EnqueueTaskCompileWasmToNativeExecutable( taskResult.TaskPayload.GameID(), taskResult.TaskPayload.UserID(), - taskResult.TaskPayload.Code(), + taskResult.TaskPayload.CodeHash(), taskResult.TaskPayload.SubmissionID, ); err != nil { return &codeSubmissionError{ @@ -429,7 +434,7 @@ func (hub *gameHub) processTaskResultCompileWasmToNativeExecutable( if err := hub.taskQueue.EnqueueTaskRunTestcase( taskResult.TaskPayload.GameID(), taskResult.TaskPayload.UserID(), - taskResult.TaskPayload.Code(), + taskResult.TaskPayload.CodeHash(), taskResult.TaskPayload.SubmissionID, int(testcase.TestcaseID), testcase.Stdin, @@ -644,3 +649,7 @@ func isTestcaseResultCorrect(expectedStdout, actualStdout string) bool { actualStdout = strings.TrimSpace(actualStdout) return actualStdout == expectedStdout } + +func calcHash(code string) string { + return fmt.Sprintf("%x", md5.Sum([]byte(code))) +} diff --git a/backend/query.sql b/backend/query.sql index e767746..f0e4034 100644 --- a/backend/query.sql +++ b/backend/query.sql @@ -59,8 +59,8 @@ SET WHERE game_id = $1; -- name: CreateSubmission :one -INSERT INTO submissions (game_id, user_id, code, code_size) -VALUES ($1, $2, $3, $4) +INSERT INTO submissions (game_id, user_id, code, code_size, code_hash) +VALUES ($1, $2, $3, $4, $5) RETURNING submission_id; -- name: ListTestcasesByGameID :many diff --git a/backend/schema.sql b/backend/schema.sql index 74d1202..64642bc 100644 --- a/backend/schema.sql +++ b/backend/schema.sql @@ -59,6 +59,7 @@ CREATE TABLE submissions ( user_id INT NOT NULL, code TEXT NOT NULL, code_size INT NOT NULL, + code_hash CHAR(32) NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW(), CONSTRAINT fk_game_id FOREIGN KEY(game_id) REFERENCES games(game_id), CONSTRAINT fk_user_id FOREIGN KEY(user_id) REFERENCES users(user_id) diff --git a/backend/taskqueue/processor.go b/backend/taskqueue/processor.go index 149ab67..5678cc8 100644 --- a/backend/taskqueue/processor.go +++ b/backend/taskqueue/processor.go @@ -29,8 +29,9 @@ func (p *processor) doProcessTaskCreateSubmissionRecord( submissionID, err := p.q.CreateSubmission(ctx, db.CreateSubmissionParams{ GameID: int32(payload.GameID()), UserID: int32(payload.UserID()), - Code: payload.Code(), + Code: payload.Code, CodeSize: int32(payload.CodeSize), + CodeHash: string(payload.CodeHash()), }) if err != nil { return nil, err @@ -49,6 +50,7 @@ func (p *processor) doProcessTaskCompileSwiftToWasm( type swiftcRequestData struct { MaxDuration int `json:"max_duration_ms"` Code string `json:"code"` + CodeHash string `json:"code_hash"` } type swiftcResponseData struct { Status string `json:"status"` @@ -57,7 +59,8 @@ func (p *processor) doProcessTaskCompileSwiftToWasm( } reqData := swiftcRequestData{ MaxDuration: 5000, - Code: payload.Code(), + Code: payload.Code, + CodeHash: string(payload.CodeHash()), } reqJSON, err := json.Marshal(reqData) if err != nil { @@ -99,7 +102,7 @@ func (p *processor) doProcessTaskCompileWasmToNativeExecutable( ) (*TaskResultCompileWasmToNativeExecutable, error) { type wasmcRequestData struct { MaxDuration int `json:"max_duration_ms"` - Code string `json:"code"` + CodeHash string `json:"code_hash"` } type wasmcResponseData struct { Status string `json:"status"` @@ -108,7 +111,7 @@ func (p *processor) doProcessTaskCompileWasmToNativeExecutable( } reqData := wasmcRequestData{ MaxDuration: 5000, - Code: payload.Code(), + CodeHash: string(payload.CodeHash()), } reqJSON, err := json.Marshal(reqData) if err != nil { @@ -150,7 +153,7 @@ func (p *processor) doProcessTaskRunTestcase( ) (*TaskResultRunTestcase, error) { type testrunRequestData struct { MaxDuration int `json:"max_duration_ms"` - Code string `json:"code"` + CodeHash string `json:"code_hash"` Stdin string `json:"stdin"` } type testrunResponseData struct { @@ -160,7 +163,7 @@ func (p *processor) doProcessTaskRunTestcase( } reqData := testrunRequestData{ MaxDuration: 5000, - Code: payload.Code(), + CodeHash: string(payload.CodeHash()), Stdin: payload.Stdin, } reqJSON, err := json.Marshal(reqData) diff --git a/backend/taskqueue/queue.go b/backend/taskqueue/queue.go index 515a406..30fe265 100644 --- a/backend/taskqueue/queue.go +++ b/backend/taskqueue/queue.go @@ -25,12 +25,14 @@ func (q *Queue) EnqueueTaskCreateSubmissionRecord( userID int, code string, codeSize int, + codeHash MD5HexHash, ) error { task, err := newTaskCreateSubmissionRecord( gameID, userID, code, codeSize, + codeHash, ) if err != nil { return err @@ -43,12 +45,14 @@ func (q *Queue) EnqueueTaskCompileSwiftToWasm( gameID int, userID int, code string, + codeHash MD5HexHash, submissionID int, ) error { task, err := newTaskCompileSwiftToWasm( gameID, userID, code, + codeHash, submissionID, ) if err != nil { @@ -61,13 +65,13 @@ func (q *Queue) EnqueueTaskCompileSwiftToWasm( func (q *Queue) EnqueueTaskCompileWasmToNativeExecutable( gameID int, userID int, - code string, + codeHash MD5HexHash, submissionID int, ) error { task, err := newTaskCompileWasmToNativeExecutable( gameID, userID, - code, + codeHash, submissionID, ) if err != nil { @@ -80,7 +84,7 @@ func (q *Queue) EnqueueTaskCompileWasmToNativeExecutable( func (q *Queue) EnqueueTaskRunTestcase( gameID int, userID int, - code string, + codeHash MD5HexHash, submissionID int, testcaseID int, stdin string, @@ -89,7 +93,7 @@ func (q *Queue) EnqueueTaskRunTestcase( task, err := newTaskRunTestcase( gameID, userID, - code, + codeHash, submissionID, testcaseID, stdin, diff --git a/backend/taskqueue/tasks.go b/backend/taskqueue/tasks.go index cbe83b1..d5f2993 100644 --- a/backend/taskqueue/tasks.go +++ b/backend/taskqueue/tasks.go @@ -8,6 +8,9 @@ import ( type TaskType string +// MD5 hash in hexadecimal format +type MD5HexHash string + const ( TaskTypeCreateSubmissionRecord TaskType = "create_submission_record" TaskTypeCompileSwiftToWasm TaskType = "compile_swift_to_wasm" @@ -16,13 +19,14 @@ const ( ) type TaskPayloadBase struct { - GameID int - UserID int - Code string + GameID int + UserID int + CodeHash MD5HexHash } type TaskPayloadCreateSubmissionRecord struct { TaskPayloadBase + Code string CodeSize int } @@ -31,13 +35,15 @@ func newTaskCreateSubmissionRecord( userID int, code string, codeSize int, + codeHash MD5HexHash, ) (*asynq.Task, error) { payload, err := json.Marshal(TaskPayloadCreateSubmissionRecord{ TaskPayloadBase: TaskPayloadBase{ - GameID: gameID, - UserID: userID, - Code: code, + GameID: gameID, + UserID: userID, + CodeHash: codeHash, }, + Code: code, CodeSize: codeSize, }) if err != nil { @@ -46,28 +52,31 @@ func newTaskCreateSubmissionRecord( return asynq.NewTask(string(TaskTypeCreateSubmissionRecord), payload), nil } -func (t *TaskPayloadCreateSubmissionRecord) GameID() int { return t.TaskPayloadBase.GameID } -func (t *TaskPayloadCreateSubmissionRecord) UserID() int { return t.TaskPayloadBase.UserID } -func (t *TaskPayloadCreateSubmissionRecord) Code() string { return t.TaskPayloadBase.Code } +func (t *TaskPayloadCreateSubmissionRecord) GameID() int { return t.TaskPayloadBase.GameID } +func (t *TaskPayloadCreateSubmissionRecord) UserID() int { return t.TaskPayloadBase.UserID } +func (t *TaskPayloadCreateSubmissionRecord) CodeHash() MD5HexHash { return t.TaskPayloadBase.CodeHash } type TaskPayloadCompileSwiftToWasm struct { TaskPayloadBase SubmissionID int + Code string } func newTaskCompileSwiftToWasm( gameID int, userID int, code string, + codeHash MD5HexHash, submissionID int, ) (*asynq.Task, error) { payload, err := json.Marshal(TaskPayloadCompileSwiftToWasm{ TaskPayloadBase: TaskPayloadBase{ - GameID: gameID, - UserID: userID, - Code: code, + GameID: gameID, + UserID: userID, + CodeHash: codeHash, }, SubmissionID: submissionID, + Code: code, }) if err != nil { return nil, err @@ -75,9 +84,9 @@ func newTaskCompileSwiftToWasm( return asynq.NewTask(string(TaskTypeCompileSwiftToWasm), payload), nil } -func (t *TaskPayloadCompileSwiftToWasm) GameID() int { return t.TaskPayloadBase.GameID } -func (t *TaskPayloadCompileSwiftToWasm) UserID() int { return t.TaskPayloadBase.UserID } -func (t *TaskPayloadCompileSwiftToWasm) Code() string { return t.TaskPayloadBase.Code } +func (t *TaskPayloadCompileSwiftToWasm) GameID() int { return t.TaskPayloadBase.GameID } +func (t *TaskPayloadCompileSwiftToWasm) UserID() int { return t.TaskPayloadBase.UserID } +func (t *TaskPayloadCompileSwiftToWasm) CodeHash() MD5HexHash { return t.TaskPayloadBase.CodeHash } type TaskPayloadCompileWasmToNativeExecutable struct { TaskPayloadBase @@ -87,14 +96,14 @@ type TaskPayloadCompileWasmToNativeExecutable struct { func newTaskCompileWasmToNativeExecutable( gameID int, userID int, - code string, + codeHash MD5HexHash, submissionID int, ) (*asynq.Task, error) { payload, err := json.Marshal(TaskPayloadCompileWasmToNativeExecutable{ TaskPayloadBase: TaskPayloadBase{ - GameID: gameID, - UserID: userID, - Code: code, + GameID: gameID, + UserID: userID, + CodeHash: codeHash, }, SubmissionID: submissionID, }) @@ -104,9 +113,11 @@ func newTaskCompileWasmToNativeExecutable( return asynq.NewTask(string(TaskTypeCompileWasmToNativeExecutable), payload), nil } -func (t *TaskPayloadCompileWasmToNativeExecutable) GameID() int { return t.TaskPayloadBase.GameID } -func (t *TaskPayloadCompileWasmToNativeExecutable) UserID() int { return t.TaskPayloadBase.UserID } -func (t *TaskPayloadCompileWasmToNativeExecutable) Code() string { return t.TaskPayloadBase.Code } +func (t *TaskPayloadCompileWasmToNativeExecutable) GameID() int { return t.TaskPayloadBase.GameID } +func (t *TaskPayloadCompileWasmToNativeExecutable) UserID() int { return t.TaskPayloadBase.UserID } +func (t *TaskPayloadCompileWasmToNativeExecutable) CodeHash() MD5HexHash { + return t.TaskPayloadBase.CodeHash +} type TaskPayloadRunTestcase struct { TaskPayloadBase @@ -119,7 +130,7 @@ type TaskPayloadRunTestcase struct { func newTaskRunTestcase( gameID int, userID int, - code string, + codeHash MD5HexHash, submissionID int, testcaseID int, stdin string, @@ -127,9 +138,9 @@ func newTaskRunTestcase( ) (*asynq.Task, error) { payload, err := json.Marshal(TaskPayloadRunTestcase{ TaskPayloadBase: TaskPayloadBase{ - GameID: gameID, - UserID: userID, - Code: code, + GameID: gameID, + UserID: userID, + CodeHash: codeHash, }, SubmissionID: submissionID, TestcaseID: testcaseID, @@ -142,9 +153,9 @@ func newTaskRunTestcase( return asynq.NewTask(string(TaskTypeRunTestcase), payload), nil } -func (t *TaskPayloadRunTestcase) GameID() int { return t.TaskPayloadBase.GameID } -func (t *TaskPayloadRunTestcase) UserID() int { return t.TaskPayloadBase.UserID } -func (t *TaskPayloadRunTestcase) Code() string { return t.TaskPayloadBase.Code } +func (t *TaskPayloadRunTestcase) GameID() int { return t.TaskPayloadBase.GameID } +func (t *TaskPayloadRunTestcase) UserID() int { return t.TaskPayloadBase.UserID } +func (t *TaskPayloadRunTestcase) CodeHash() MD5HexHash { return t.TaskPayloadBase.CodeHash } type TaskResult interface { Type() TaskType diff --git a/worker/exec.go b/worker/exec.go index bd49162..9e937f7 100644 --- a/worker/exec.go +++ b/worker/exec.go @@ -3,7 +3,6 @@ package main import ( "bytes" "context" - "crypto/md5" "fmt" "os" "os/exec" @@ -36,10 +35,6 @@ func prepareDirectories() error { return nil } -func calcHash(code string) string { - return fmt.Sprintf("%x", md5.Sum([]byte(code))) -} - func calcFilePath(hash, ext string) string { return fmt.Sprintf("%s/%s/%s.%s", dataRootDir, ext, hash, ext) } @@ -85,11 +80,11 @@ func convertCommandErrorToResultType(err error) string { func execSwiftCompile( ctx context.Context, code string, + codeHash string, maxDuration time.Duration, ) swiftCompileResponseData { - hash := calcHash(code) - inPath := calcFilePath(hash, "swift") - outPath := calcFilePath(hash, "wasm") + inPath := calcFilePath(codeHash, "swift") + outPath := calcFilePath(codeHash, "wasm") if err := os.WriteFile(inPath, []byte(code), 0644); err != nil { return swiftCompileResponseData{ @@ -122,12 +117,11 @@ func execSwiftCompile( func execWasmCompile( ctx context.Context, - code string, + codeHash string, maxDuration time.Duration, ) wasmCompileResponseData { - hash := calcHash(code) - inPath := calcFilePath(hash, "wasm") - outPath := calcFilePath(hash, "cwasm") + inPath := calcFilePath(codeHash, "wasm") + outPath := calcFilePath(codeHash, "cwasm") stdout, stderr, err := execCommandWithTimeout( ctx, @@ -154,12 +148,11 @@ func execWasmCompile( func execTestRun( ctx context.Context, - code string, + codeHash string, stdin string, maxDuration time.Duration, ) testRunResponseData { - hash := calcHash(code) - inPath := calcFilePath(hash, "cwasm") + inPath := calcFilePath(codeHash, "cwasm") stdout, stderr, err := execCommandWithTimeout( ctx, diff --git a/worker/handlers.go b/worker/handlers.go index e7ceef6..ac9701f 100644 --- a/worker/handlers.go +++ b/worker/handlers.go @@ -23,6 +23,7 @@ func handleSwiftCompile(c echo.Context) error { res := execSwiftCompile( c.Request().Context(), req.Code, + req.CodeHash, req.maxDuration(), ) @@ -40,7 +41,7 @@ func handleWasmCompile(c echo.Context) error { res := execWasmCompile( c.Request().Context(), - req.Code, + req.CodeHash, req.maxDuration(), ) @@ -58,7 +59,7 @@ func handleTestRun(c echo.Context) error { res := execTestRun( c.Request().Context(), - req.Code, + req.CodeHash, req.Stdin, req.maxDuration(), ) diff --git a/worker/models.go b/worker/models.go index c60002c..9f60eb0 100644 --- a/worker/models.go +++ b/worker/models.go @@ -19,6 +19,7 @@ var ( type swiftCompileRequestData struct { MaxDurationMilliseconds int `json:"max_duration_ms"` Code string `json:"code"` + CodeHash string `json:"code_hash"` } func (req *swiftCompileRequestData) maxDuration() time.Duration { @@ -40,7 +41,7 @@ type swiftCompileResponseData struct { type wasmCompileRequestData struct { MaxDurationMilliseconds int `json:"max_duration_ms"` - Code string `json:"code"` + CodeHash string `json:"code_hash"` } type wasmCompileResponseData struct { @@ -62,7 +63,7 @@ func (req *wasmCompileRequestData) validate() error { type testRunRequestData struct { MaxDurationMilliseconds int `json:"max_duration_ms"` - Code string `json:"code"` + CodeHash string `json:"code_hash"` Stdin string `json:"stdin"` } |
