Online-Judge
Contents
Online-Judge-Project
前言
紀錄一下學習強者的project過程,目前完成了前端以外的部分。 先把預計要做的事情寫下來,也算是給自己一個動力完成吧!
- 把project的語言從kotlin轉成go
- deploy到AWS-ec2上面
- 做個壓力測試跟監控
雖然整體架構跟API、資料庫都已經開好了,但技術選型(go framework, orm)可能還是要斟酌一下,加油吧!
Day 3
- routing: route requests by two thing
- Method
- URI(Uniform Resource Identifier)
- URI 可以:
- 單獨表示 URL(locator), subset of URI
- 單獨表示 URN(name), subset of URI
- 兩者兼具 (identified by locator and name)
- request format: [Method] [URI] HTTP/1.1
- response format: HTTP/1.1 [HTTP Status Code] [該 HTTP Status Code 的英文名稱]
- header(parameters) + body(parameters value)
Day 4
- data schema
- ktor-jackson: convert data structure to JSON format
- Gradle: 處理專案建置的輔助工具
Day 5
- RESTful API: 題目資源被 /problems 來代表
Day 6
- Http Request:
- Accept: which format client receive
- Content-Type: which format client send(body)
Day 7
- Error Handling:
- no correspoding route to handle request -> 404 Not Found
- Exception -> 500 Internal Server Error
- create problem with invalid body -> 400 Bad Request
Day 8
password: 秘密 port: 5432
- auto increment title id(SERIAL), date persistency -> database
- Database normalization: Testcases Table use problemId as foreign key
Day 9
- object 這個關鍵字是用在如果你要定義的 class 只會生出一個物件的話,你可以直接用 object 簡寫即可,最後宣告出來的結果會類似於其他語言中的 static class 的使用方式,可以直接將該類別當成一個物件來使用。
- DTO(Data Transfer Object):
- PUT /problems/{id}:
- testcases in body: {A, B, C}, testcases in database: {A, B, D}
- testcase exist{A, B} -> override it
- testcase not exist{C} -> create it
- testcase disapper{D} -> delete it
- DELETE /problems/{id}: A <- B, B depend on A, if you want to delete A, delete B first
TestCases Table裡的val problem
應該要改成val problemId
才能跟POST /problems裡的變數名稱對起來?
Day 10
fun verifyPassword(password: String, passwordHash: String) =
BCrypt.verifyer().verify(
password.toCharArray(),
passwordHash
).verified
少了最後面的.verified
- 驗證機制:
- Session-Based Authentication: store Session on browser, and put Session Id on cookie(http request header) to authenticate when doing http request
- Token-Based Authentication: similar to Session-Based Authentication, but server doesn’t need to store Session
- number of users can be served: Session-Based loss(server need space to store session)
- frequency of users to re-login: Token-Based loss(token need to expire quickly to prevent from stealing)
- 會員系統:
- UserIdAuthorityPrincipal: use userId and authority as session
- 設定 Session 的值為 userId 和 authority 形成的 UserIdAuthorityPrincipal 物件值,同時設定完也會讓 HTTP response 加上一段告訴客戶端之後要帶什麼樣的 Cookie 值回來。
- after doing logout, cookie on client will also be cleared
Day 11
- Submission API
- Authority(change problem operations, submit operation)
Day 12
- Execution of Program
- 透過編譯器將程式碼變成執行檔後,在該編譯出的執行擋可執行之平台執行
- 透過編譯器變成中間碼檔案,此中間碼檔案可利用各平台能夠執行該中間碼的程式進行執行。
- 直接用直譯器去執行程式碼。
- Build environment for Kotlin code to be compiled and executed
Day 13
- Runner proceed submissions process:
- Architecture:
hierarchy:
Application.kt(submissionSource, Judger)
/ \
ISubmissionSource Judger
1. ISubmissionSource(FileSubmissionSource, DatabaseSubmissionSource)
1-1. functions: getNextSubmission(), setSubmissionResult()
2. Judger:
2-1. components: ICompiler(KotlinCompiler), IExecutor(JVMExecutor)
Day 14
- connect with postgreSQL, same as Day9
- implement “DatabaseSubmissionSource” class
- Runner will take unexecuted submission, judge it and then set result
Day 15
- result of runner compile and execute:
- Accepted (AC):程式通過審核。
- Wrong Answer (WA):程式輸出的結果有誤。
- Compile Error (CE):程式在編譯的時候出現編譯錯誤。
- Runtime Error (RE):程式在執行時壞掉。
- Time Limit Exceeded (TLE):程式執行時間超過規定。
Day 16
- 容器就是基於原本的作業系統,想辦法在主作業系統上,分配部分的資源去建立起一個與外部隔離的環境。
- Docker
- Image: 環境設定檔
- Container: Image的實體
- Repository: 放Image的地方
- goal: 為了要讓容器裡面的環境能夠讀取到我們的程式碼,我們會需要讓主體作業系統分享一個資料夾位置給容器,讓容器可以透過自己的檔案系統讀取到分享的資料夾內的內容。
- compile:
docker run --rm -v ${System.getProperty("user.dir").appendPath(workspace)}:/$workspace
zenika/kotlin kotlinc /$codeFilePath -include-runtime -d /$executableFilePath
docker run [映像檔名稱] [指令] -> 建立並進入container後執行指令
--rm -> container執行完後自動刪除container
-v [主系統目錄位置]:[Docker 內容器檔案系統目錄位置] -> 兩個位址的內容同步
compileProcess.redirectError(ProcessBuilder.Redirect.INHERIT) -> 執行指令時產生的錯誤會導到runner的console上
- execute:
docker run --rm --name DOCKER_CONTAINER_NAME -v ${System.getProperty("user.dir").appendPath(workspace)}:/$workspace
zenika/kotlin sh -c 'java -jar /$executableFilename < /$inputFilePath > /$outputFilePath'
java -jar [執行檔於 Docker 容器內的路徑] < [輸入檔於 Docker 容器內的路徑] > [輸出檔於 Docker 容器內的路徑]
sh -c -> 我們想要導向的是 Docker 容器內執行的指令,而非執行 Docker 的指令
--name DOCKER_CONTAINER_NAME -> 當執行executableFilename產生TLE時,
就需要先kill該container(DOCKER_CONTAINER_NAME),才能把執行這個command的Process(executeProcess) destroy。
- 先是架構不對 amd64 -> arm64
- image跟host同一個架構後(arm64) -> Unable to find image ‘dyninka/kotlin:latest’ locally, 一直想用最新版的image卻不用現有的image?
- 改成用image id取代image名稱&tag -> docker: Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: “kotlinc”: executable file not found in $PATH: unknown. ???
Day 17
- -p -> 這裡是將主機的 6379 號碼的 port 與 Docker 容器內的 6379 號碼的 port 連結在了一起,這樣我們才能在 Docker 容器外連進去 Docker 容器內的 Redis 資料庫所預設監聽的 6379 號的 port。
- JudgerSubmissionData(for redis)跟SubmissionData結構相同 -> runner可以直接取用redis的資料不用再做轉換
- 由於 submissionId 和 testCaseData 這兩個變數是在 transaction 裡面的 Lambda Function 被修改,導致後面使用 if 語句去判斷是否為 null 時,編譯器不知道該 Lambda Function 實際會在什麼時候執行,故無法保證這兩個值是否真的不為 null -> use two other variables to store
- Jedis.rpush([Queue 的名稱], [要丟進 Queue 內的內容]) -> redis裡面可以有很多個task queue,這裡使用code lauguage做為queue的種類。
- 推進去的 JudgerSubmissionData 資料要是 JSON 的格式字串 -> writeValueAsString([資料物件])
- readValue炸裂??? None of the following functions can be called with the arguments supplied.
return jacksonObjectMapper().readValue(data, SubmissionData::class.java)
- POST /submissions/restart: super user, unjudged submission will be judge
- POST /submissions/{id}/restart: submission belongs to that user, unjudged/judged submission will be judge
- 因為剛遞交的程式碼是在 Redis 資料庫斷線的時候遞交的,所以 Redis 資料庫並沒有收到,也就讓審核程式沒辦法拿到那些程式碼資料去進行批改。 -> call submissions/restart api -> 會把postgreSQL未judge的submission丟到redis裡
Day 18
- support multiple languages
- image for compile & execute
- implement ICompiler & IExecutor for multiple languages
雖然沒有講到c++的作法,不過因為image可以跟c的共用,
所以只要在跟language相關的地方加上c++的東西即可。