From ab881956070e5611cd82f1ffed9c0985c040ce14 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 2 Sep 2022 14:08:03 +0200 Subject: [PATCH] Introduce survey and question cache --- api.go | 10 ++++++ questions.go | 37 ++++++++++++++++++++++ surveys.go | 40 ++++++++++++++++++++++-- ui/src/routes/surveys/[sid]/admin.svelte | 8 +++++ 4 files changed, 93 insertions(+), 2 deletions(-) diff --git a/api.go b/api.go index 6dca204..111c090 100644 --- a/api.go +++ b/api.go @@ -28,6 +28,16 @@ func declareAPIRoutes(router *gin.Engine) { apiAdminRoutes := router.Group("/api") apiAdminRoutes.Use(authMiddleware(adminRestricted)) + apiAdminRoutes.DELETE("/cache", func(c *gin.Context) { + _surveys_cache_mutex.Lock() + _surveys_cache = map[int64]*Survey{} + _surveys_cache_mutex.Unlock() + + _questions_cache_mutex.Lock() + _questions_cache = map[int64]*Question{} + _questions_cache_mutex.Unlock() + }) + declareAPIAdminAsksRoutes(apiAdminRoutes) declareAPIAuthGradesRoutes(apiAdminRoutes) declareAPIAdminHelpRoutes(apiAdminRoutes) diff --git a/questions.go b/questions.go index 9d20f4e..3087001 100644 --- a/questions.go +++ b/questions.go @@ -4,12 +4,18 @@ import ( "log" "net/http" "strconv" + "sync" "time" "github.com/gin-gonic/gin" "github.com/russross/blackfriday/v2" ) +var ( + _questions_cache = map[int64]*Question{} + _questions_cache_mutex = sync.RWMutex{} +) + func declareAPIAuthQuestionsRoutes(router *gin.RouterGroup) { router.GET("/questions", func(c *gin.Context) { var s *Survey @@ -252,16 +258,38 @@ func (s *Survey) GetQuestions() (questions []*Question, err error) { } func getQuestion(id int) (q *Question, err error) { + _questions_cache_mutex.RLock() + question, ok := _questions_cache[int64(id)] + _questions_cache_mutex.RUnlock() + if ok { + return question, nil + } + q = new(Question) err = DBQueryRow("SELECT id_question, id_survey, title, description, placeholder, kind FROM survey_quests WHERE id_question=?", id).Scan(&q.Id, &q.IdSurvey, &q.Title, &q.DescriptionRaw, &q.Placeholder, &q.Kind) q.Description = string(blackfriday.Run([]byte(q.DescriptionRaw))) + + _questions_cache_mutex.Lock() + _questions_cache[int64(id)] = q + _questions_cache_mutex.Unlock() return } func (s *Survey) GetQuestion(id int) (q *Question, err error) { + _questions_cache_mutex.RLock() + question, ok := _questions_cache[int64(id)] + _questions_cache_mutex.RUnlock() + if ok { + return question, nil + } + q = new(Question) err = DBQueryRow("SELECT id_question, id_survey, title, description, placeholder, kind FROM survey_quests WHERE id_question=? AND id_survey=?", id, s.Id).Scan(&q.Id, &q.IdSurvey, &q.Title, &q.DescriptionRaw, &q.Placeholder, &q.Kind) q.Description = string(blackfriday.Run([]byte(q.DescriptionRaw))) + + _questions_cache_mutex.Lock() + _questions_cache[int64(id)] = q + _questions_cache_mutex.Unlock() return } @@ -283,6 +311,9 @@ func (q *Question) Update() (*Question, error) { if _, err := DBExec("UPDATE survey_quests SET id_survey = ?, title = ?, description = ?, placeholder = ?, kind = ? WHERE id_question = ?", q.IdSurvey, q.Title, q.DescriptionRaw, q.Placeholder, q.Kind, q.Id); err != nil { return nil, err } else { + _questions_cache_mutex.Lock() + _questions_cache[q.Id] = q + _questions_cache_mutex.Unlock() return q, err } } @@ -293,6 +324,9 @@ func (q *Question) Delete() (int64, error) { } else if nb, err := res.RowsAffected(); err != nil { return 0, err } else { + _questions_cache_mutex.Lock() + delete(_questions_cache, q.Id) + _questions_cache_mutex.Unlock() return nb, err } } @@ -303,6 +337,9 @@ func ClearQuestions() (int64, error) { } else if nb, err := res.RowsAffected(); err != nil { return 0, err } else { + _questions_cache_mutex.Lock() + _questions_cache = map[int64]*Question{} + _questions_cache_mutex.Unlock() return nb, err } } diff --git a/surveys.go b/surveys.go index 1c907e8..63e514f 100644 --- a/surveys.go +++ b/surveys.go @@ -6,13 +6,17 @@ import ( "net/http" "strconv" "strings" + "sync" "time" "github.com/gin-gonic/gin" ) var ( - _score_cache = map[int64]map[int64]*float64{} + _score_cache = map[int64]map[int64]*float64{} + _score_cache_mutex = sync.RWMutex{} + _surveys_cache = map[int64]*Survey{} + _surveys_cache_mutex = sync.RWMutex{} ) func declareAPISurveysRoutes(router *gin.RouterGroup) { @@ -228,8 +232,19 @@ func getSurveys(cnd string, param ...interface{}) (surveys []*Survey, err error) } func getSurvey(id int) (s *Survey, err error) { + _surveys_cache_mutex.RLock() + survey, ok := _surveys_cache[int64(id)] + _surveys_cache_mutex.RUnlock() + if ok { + return survey, nil + } + s = new(Survey) err = DBQueryRow("SELECT id_survey, title, promo, grp, shown, direct, corrected, start_availability, end_availability FROM surveys WHERE id_survey=?", id).Scan(&s.Id, &s.Title, &s.Promo, &s.Group, &s.Shown, &s.Direct, &s.Corrected, &s.StartAvailability, &s.EndAvailability) + + _surveys_cache_mutex.Lock() + _surveys_cache[int64(id)] = s + _surveys_cache_mutex.Unlock() return } @@ -244,17 +259,29 @@ func NewSurvey(title string, promo uint, group string, shown bool, direct *int64 } func (s Survey) GetScore(u *User) (score *float64, err error) { + _score_cache_mutex.RLock() if _, ok := _score_cache[u.Id]; !ok { + _score_cache_mutex.RUnlock() + _score_cache_mutex.Lock() _score_cache[u.Id] = map[int64]*float64{} + _score_cache_mutex.Unlock() + _score_cache_mutex.RLock() } - if v, ok := _score_cache[u.Id][s.Id]; ok { + + v, ok := _score_cache[u.Id][s.Id] + _score_cache_mutex.RUnlock() + + if ok { score = v } else { err = DBQueryRow("SELECT SUM(score)/COUNT(*) FROM student_scores WHERE id_survey=? AND id_user=?", s.Id, u.Id).Scan(&score) if score != nil { *score = *score / 5.0 } + + _score_cache_mutex.Lock() _score_cache[u.Id][s.Id] = score + _score_cache_mutex.Unlock() } return } @@ -288,6 +315,9 @@ func (s *Survey) Update() (*Survey, error) { if _, err := DBExec("UPDATE surveys SET title = ?, promo = ?, grp = ?, shown = ?, direct = ?, corrected = ?, start_availability = ?, end_availability = ? WHERE id_survey = ?", s.Title, s.Promo, s.Group, s.Shown, s.Direct, s.Corrected, s.StartAvailability, s.EndAvailability, s.Id); err != nil { return nil, err } else { + _surveys_cache_mutex.Lock() + _surveys_cache[s.Id] = s + _surveys_cache_mutex.Unlock() return s, err } } @@ -298,6 +328,9 @@ func (s Survey) Delete() (int64, error) { } else if nb, err := res.RowsAffected(); err != nil { return 0, err } else { + _surveys_cache_mutex.Lock() + delete(_surveys_cache, s.Id) + _surveys_cache_mutex.Unlock() return nb, err } } @@ -308,6 +341,9 @@ func ClearSurveys() (int64, error) { } else if nb, err := res.RowsAffected(); err != nil { return 0, err } else { + _surveys_cache_mutex.Lock() + _surveys_cache = map[int64]*Survey{} + _surveys_cache_mutex.Unlock() return nb, err } } diff --git a/ui/src/routes/surveys/[sid]/admin.svelte b/ui/src/routes/surveys/[sid]/admin.svelte index 7f3756c..c128c30 100644 --- a/ui/src/routes/surveys/[sid]/admin.svelte +++ b/ui/src/routes/surveys/[sid]/admin.svelte @@ -347,6 +347,14 @@ > +