diff options
Diffstat (limited to 'ws.go')
-rw-r--r-- | ws.go | 34 |
1 files changed, 33 insertions, 1 deletions
@@ -52,6 +52,7 @@ import ( "net/http" "strconv" "sync" + "time" "github.com/coder/websocket" "github.com/jackc/pgx/v5" @@ -326,6 +327,18 @@ func handleConn( } courseID := int(_courseID) course := courses[courseID] + /* + * TODO: Ensure that the user is not already enrolled in this course + * and pay attention to relevant race conditions. It might be useful + * to restructure this part, to begin a transaction that adds the user + * to the database (and check the (currently not existing) uniqueness) + * constraint at that exact moment, and abort the transaction if the + * course limit is exceeded. + * Or perhaps choices should be also stored in an internal data + * structure, though that requires extra attention on consistency + * issues between the internal data structure and the database. + * (Sometime I should really go fix the LMDB bindings...) + */ ok := false func() { course.SelectedLock.Lock() @@ -339,7 +352,26 @@ func handleConn( ok = false }() if ok { - /* TODO: Add it to the database */ + _, err = db.Exec( + ctx, /* TODO: Do we really want this to be in a request context? */ + "INSERT INTO choices (seltime, userid, courseid) VALUES ($1, $2, $3)", + time.Now().UnixMicro(), + userID, + courseID, + /* TODO: Set uniqueness constraint for each (userid, courseid) pair */ + ) + if err != nil { + go func() { /* Separate goroutine because we don't need a response from this operation */ + course.SelectedLock.Lock() + defer course.SelectedLock.Unlock() + course.Selected-- + propagateCouldFail(fmt.Sprintf("N %d %d", courseID, course.Selected)) + }() + err := writeText(ctx, c, "R "+mar[1]+" :Database error") /* TODO: Handle this better */ + if err != nil { + return fmt.Errorf("error rejecting course choice: %w", err) + } + } err := writeText(ctx, c, "Y "+mar[1]) if err != nil { return fmt.Errorf("error affirming course choice: %w", err) |