From 8c6380c2967c964270faf4403a1da205ccb42275 Mon Sep 17 00:00:00 2001 From: Zaptoss Date: Fri, 23 Aug 2024 18:05:46 +0300 Subject: [PATCH] Worker fix. Endpoint fix --- config.yaml | 4 +- internal/cli/workers.go | 16 +++-- internal/data/daily_questions.go | 6 ++ internal/data/events.go | 1 + internal/data/pg/events.go | 4 ++ .../service/handlers/daily_question_check.go | 12 ++-- .../service/handlers/daily_question_get.go | 20 +++--- .../handlers/daily_questions_status.go | 16 +++-- .../workers/cleanquestiondeadlines/main.go | 66 ++++++++++--------- 9 files changed, 85 insertions(+), 60 deletions(-) diff --git a/config.yaml b/config.yaml index 71e0ee0..be66885 100644 --- a/config.yaml +++ b/config.yaml @@ -101,7 +101,7 @@ event_types: no_auto_open: true - name: daily_question title: Get daily question - reward: 0 + reward: 5 frequency: unlimited description: This event is used when a user get daily question short_description: event for get daily question @@ -109,7 +109,7 @@ event_types: no_auto_open: true daily_questions: - timezone: +4 + timezone: 4 levels: downgradeable: false diff --git a/internal/cli/workers.go b/internal/cli/workers.go index 28f308f..ac49fcd 100644 --- a/internal/cli/workers.go +++ b/internal/cli/workers.go @@ -7,6 +7,7 @@ import ( "github.com/rarimo/geo-points-svc/internal/config" "github.com/rarimo/geo-points-svc/internal/data/evtypes" "github.com/rarimo/geo-points-svc/internal/service" + "github.com/rarimo/geo-points-svc/internal/service/workers/cleanquestiondeadlines" "github.com/rarimo/geo-points-svc/internal/service/workers/expirywatch" "github.com/rarimo/geo-points-svc/internal/service/workers/leveljustice" "github.com/rarimo/geo-points-svc/internal/service/workers/nooneisforgotten" @@ -17,12 +18,12 @@ import ( func runServices(ctx context.Context, cfg config.Config, wg *sync.WaitGroup) { // signals indicate the finished initialization of each worker var ( - reopenerSig = make(chan struct{}) - expiryWatchSig = make(chan struct{}) - evTypesSig = make(chan struct{}) - noOneIsForgottenSig = make(chan struct{}) - levelJustice = make(chan struct{}) - // cleanDQuestionDeadlines = make(chan struct{}) + reopenerSig = make(chan struct{}) + expiryWatchSig = make(chan struct{}) + evTypesSig = make(chan struct{}) + noOneIsForgottenSig = make(chan struct{}) + levelJustice = make(chan struct{}) + cleanDQuestionDeadlines = make(chan struct{}) ) run := func(f func()) { @@ -50,7 +51,8 @@ func runServices(ctx context.Context, cfg config.Config, wg *sync.WaitGroup) { run(func() { leveljustice.Run(cfg, levelJustice) }) //service for cleaning daily question deadlines after day - // run(func() { cleanquestiondeadlines.Run(ctx, cfg, cleanDQuestionDeadlines) }) + run(func() { cleanquestiondeadlines.Run(ctx, cfg, cleanDQuestionDeadlines) }) + <-cleanDQuestionDeadlines // service depends on all the workers for good UX <-expiryWatchSig diff --git a/internal/data/daily_questions.go b/internal/data/daily_questions.go index 66cd15d..dcbccd2 100644 --- a/internal/data/daily_questions.go +++ b/internal/data/daily_questions.go @@ -9,6 +9,12 @@ import ( "github.com/rarimo/geo-points-svc/resources" ) +const ( + ColCorrectAnswers = "num_correct_answers" + ColIncorrectAnswers = "num_incorrect_answers" + ColAllParticipants = "num_all_participants" +) + type DailyQuestion struct { ID int64 `db:"id"` Title string `db:"title"` diff --git a/internal/data/events.go b/internal/data/events.go index 34beb24..38a07c8 100644 --- a/internal/data/events.go +++ b/internal/data/events.go @@ -58,6 +58,7 @@ type EventsQ interface { SelectAbsentTypes(allTypes ...string) ([]ReopenableEvent, error) FilterByID(...string) EventsQ + FilterByQuestionID(int) EventsQ FilterByNullifier(...string) EventsQ FilterByStatus(...EventStatus) EventsQ FilterByType(...string) EventsQ diff --git a/internal/data/pg/events.go b/internal/data/pg/events.go index 30da39e..88e54df 100644 --- a/internal/data/pg/events.go +++ b/internal/data/pg/events.go @@ -245,6 +245,10 @@ func (q *events) FilterTodayEvents(offset int) data.EventsQ { return res } +func (q *events) FilterByQuestionID(id int) data.EventsQ { + return q.applyCondition(squirrel.Eq{"meta->>'question_id'": id}) +} + func (q *events) FilterInactiveNotClaimed(types ...string) data.EventsQ { if len(types) == 0 { return q diff --git a/internal/service/handlers/daily_question_check.go b/internal/service/handlers/daily_question_check.go index e4b37ef..303e357 100644 --- a/internal/service/handlers/daily_question_check.go +++ b/internal/service/handlers/daily_question_check.go @@ -124,11 +124,13 @@ func CheckDailyQuestion(w http.ResponseWriter, r *http.Request) { ape.Render(w, newDailyAnswer(question)) } -func newDailyAnswer(question *data.DailyQuestion) resources.DailyQuestionAnswers { - return resources.DailyQuestionAnswers{ - Key: resources.NewKeyInt64(question.ID, resources.DAILY_QUESTIONS), - Attributes: resources.DailyQuestionAnswersAttributes{ - Answer: question.CorrectAnswer, +func newDailyAnswer(question *data.DailyQuestion) resources.DailyQuestionAnswersResponse { + return resources.DailyQuestionAnswersResponse{ + Data: resources.DailyQuestionAnswers{ + Key: resources.NewKeyInt64(question.ID, resources.DAILY_QUESTIONS), + Attributes: resources.DailyQuestionAnswersAttributes{ + Answer: question.CorrectAnswer, + }, }, } } diff --git a/internal/service/handlers/daily_question_get.go b/internal/service/handlers/daily_question_get.go index 092276e..29e6b0d 100644 --- a/internal/service/handlers/daily_question_get.go +++ b/internal/service/handlers/daily_question_get.go @@ -73,16 +73,18 @@ func GetDailyQuestion(w http.ResponseWriter, r *http.Request) { ape.Render(w, newDailyQuestions(question, options)) } -func newDailyQuestions(question *data.DailyQuestion, options []resources.DailyQuestionOptions) resources.DailyQuestions { +func newDailyQuestions(question *data.DailyQuestion, options []resources.DailyQuestionOptions) resources.DailyQuestionsResponse { - return resources.DailyQuestions{ - Key: resources.Key{ - ID: strconv.Itoa(int(question.ID)), - Type: resources.DAILY_QUESTIONS, - }, - Attributes: resources.DailyQuestionsAttributes{ - Options: options, - Title: question.Title, + return resources.DailyQuestionsResponse{ + Data: resources.DailyQuestions{ + Key: resources.Key{ + ID: strconv.Itoa(int(question.ID)), + Type: resources.DAILY_QUESTIONS, + }, + Attributes: resources.DailyQuestionsAttributes{ + Options: options, + Title: question.Title, + }, }, } } diff --git a/internal/service/handlers/daily_questions_status.go b/internal/service/handlers/daily_questions_status.go index 97a04a5..3a2370e 100644 --- a/internal/service/handlers/daily_questions_status.go +++ b/internal/service/handlers/daily_questions_status.go @@ -56,13 +56,15 @@ func GetDailyQuestionsStatus(w http.ResponseWriter, r *http.Request) { ape.Render(w, newDailyQuestionsStatus(question)) } -func newDailyQuestionsStatus(question *data.DailyQuestion) resources.DailyQuestionsStatus { - return resources.DailyQuestionsStatus{ - Key: resources.NewKeyInt64(question.ID, resources.DAILY_QUESTIONS), - Attributes: resources.DailyQuestionsStatusAttributes{ - NextQuestionDate: question.StartsAt.Unix(), - Reward: int64(question.Reward), - TimeForAnswer: question.TimeForAnswer, +func newDailyQuestionsStatus(question *data.DailyQuestion) resources.DailyQuestionsStatusResponse { + return resources.DailyQuestionsStatusResponse{ + Data: resources.DailyQuestionsStatus{ + Key: resources.NewKeyInt64(question.ID, resources.DAILY_QUESTIONS_STATUS), + Attributes: resources.DailyQuestionsStatusAttributes{ + NextQuestionDate: question.StartsAt.Unix(), + Reward: int64(question.Reward), + TimeForAnswer: question.TimeForAnswer, + }, }, } } diff --git a/internal/service/workers/cleanquestiondeadlines/main.go b/internal/service/workers/cleanquestiondeadlines/main.go index 861fbc7..87d06ac 100644 --- a/internal/service/workers/cleanquestiondeadlines/main.go +++ b/internal/service/workers/cleanquestiondeadlines/main.go @@ -7,52 +7,58 @@ import ( "github.com/go-co-op/gocron/v2" "github.com/rarimo/geo-points-svc/internal/config" + "github.com/rarimo/geo-points-svc/internal/data" + "github.com/rarimo/geo-points-svc/internal/data/pg" "github.com/rarimo/geo-points-svc/internal/service/workers/cron" ) func Run(ctx context.Context, cfg config.Config, sig chan struct{}) { cron.Init(cfg.Log()) + log := cfg.Log().WithField("who", "daily-questions-cleaner") + + eventsQ := pg.NewEvents(cfg.DB().Clone()) + questionsQ := pg.NewDailyQuestionsQ(cfg.DB().Clone()) offset := cfg.DailyQuestions().LocalTime(atDayStart(time.Now().UTC())).Hour() _, err := cron.NewJob( gocron.DailyJob(1, gocron.NewAtTimes(gocron.NewAtTime(uint(offset), 0, 0))), gocron.NewTask(func() { - cfg.DailyQuestions() + counts := cfg.DailyQuestions().ClearDeadlines() + if len(counts) == 0 { + log.Infof("Questions absent") + return + } + + err := eventsQ.New().Transaction(func() error { + for k := range counts { + count, err := eventsQ.New().FilterByQuestionID(k).Count() + if err != nil { + return fmt.Errorf("failed to get count events by question id: %w", err) + } + + err = questionsQ.FilterByID(int64(k)).Update(map[string]any{ + data.ColCorrectAnswers: count, + data.ColAllParticipants: counts[k], + }) + if err != nil { + return fmt.Errorf("failed to update daily question: %w", err) + } + log.WithField("question_id", k).Infof("Correct answers: %d; Total participants: %d", count, counts[k]) + } + return nil + }) + if err != nil { + log.WithError(err).Error("Failed to correct update question statistic") + } }), gocron.WithName("daily-questions-cleaner"), ) if err != nil { - panic(fmt.Errorf(": failed to initialize daily job: %w", err)) + panic(fmt.Errorf("failed to initialize daily job: %w", err)) } + sig <- struct{}{} - for { - now := time.Now().UTC().Add(time.Duration(offset) * time.Hour) - cfg.Log().Info("Daily Question cleaning start") - nextMidnight := time.Date(now.Year(), now.Month(), now.Day()+1, 0, 0, 0, 0, time.UTC). - Add(time.Duration(offset) * time.Hour) - - durationUntilNextTick := nextMidnight.Sub(now) - - timer := time.NewTimer(durationUntilNextTick) - - select { - case <-timer.C: - res := cfg.DailyQuestions().ClearDeadlines() - cfg.Log().Infof("Cleared daily questions quantity: %v", res) - - timer.Stop() - - case <-sig: - cfg.Log().Info("Daily Question cleaning stop") - timer.Stop() - return - - case <-ctx.Done(): - cfg.Log().Info("Daily Question cleaning stop") - timer.Stop() - return - } - } + cron.Start(ctx) } func atDayStart(date time.Time) time.Time {