Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

calculate user visit count and save to redis #333

Merged
merged 6 commits into from
Nov 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions api/api_login_required_path.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ package api
import (
"strings"

"github.com/Ptt-official-app/go-openbbsmiddleware/types"

"github.com/Ptt-official-app/go-openbbsmiddleware/schema"

pttbbsapi "github.com/Ptt-official-app/go-pttbbs/api"
"github.com/Ptt-official-app/go-pttbbs/bbs"
"github.com/gin-gonic/gin"
Expand Down Expand Up @@ -49,6 +53,12 @@ func loginRequiredPathProcess(theFunc LoginRequiredPathAPIFunc, params interface
if err != nil {
userID = bbs.UUserID(pttbbsapi.GUEST)
}
userVisit := &schema.UserVisit{
UserID: userID,
Action: c.Request.Method + ":" + c.Request.URL.Path,
UpdateNanoTS: types.NowNanoTS(),
}
_ = schema.UpdateUserVisit(userVisit)

result, statusCode, err := theFunc(remoteAddr, userID, params, path, c)
processResult(c, result, statusCode, err)
Expand Down
33 changes: 33 additions & 0 deletions cron/calculate_user_visit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package cron

import (
"time"

"github.com/Ptt-official-app/go-openbbsmiddleware/schema"

"github.com/sirupsen/logrus"
)

// RetryCalculateUserVisit make loop job to call CalculateUserVisit per 10 mins
func RetryCalculateUserVisit() {
for {
logrus.Infof("RetryCalculateUserVisit: to calculate user visit")
CalculateUserVisit()
logrus.Infof("RetryCalculateUserVisit: to sleep 10 mins")
time.Sleep(10 * time.Minute)
}
}

// CalculateUserVisit get user visit count from db
// and set to redis
func CalculateUserVisit() {
count, err := schema.CalculateAllUserVisitCounts()
if err != nil {
logrus.Printf("get error in calculate user visit count %v", err)
}
// set to redis
err = schema.SetUserVisitCount(count)
if err != nil {
logrus.Printf("set to redis %v", err)
}
}
2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ func main() {
// retry load full class boards
go cron.RetryLoadFullClassBoards()

// retry to calculate user visit count
go cron.RetryCalculateUserVisit()
s := &http.Server{
Addr: types.HTTP_HOST,
Handler: router,
Expand Down
5 changes: 4 additions & 1 deletion schema/const.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package schema

import (
"time"

"github.com/Ptt-official-app/go-openbbsmiddleware/db"
redis "github.com/go-redis/redis/v8"
)

const (
TITLE_REGEX_N_GRAM = 5
TITLE_REGEX_N_GRAM = 5
TIME_CALC_ALL_USER_VISIT_COUNTS = -10 * time.Minute
)

var (
Expand Down
9 changes: 9 additions & 0 deletions schema/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,15 @@ func Init() (err error) {
// UserReject
UserReject_c = client.Collection("user_reject")

// UserVisit
UserVisit_c = client.Collection("user_visit")
keys = &bson.D{
{Key: USER_READ_BOARD_USER_ID_b, Value: 1},
}
err = UserVisit_c.CreateIndex(keys, nil)
if err != nil {
return err
}
// userIDEmail
UserIDEmail_c = client.Collection("user_id_email")
keys = &bson.D{
Expand Down
55 changes: 55 additions & 0 deletions schema/user_visit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package schema

import (
"time"

"github.com/Ptt-official-app/go-openbbsmiddleware/db"
"github.com/Ptt-official-app/go-openbbsmiddleware/types"
"github.com/Ptt-official-app/go-pttbbs/bbs"
"go.mongodb.org/mongo-driver/bson"
)

var UserVisit_c *db.Collection

type UserVisit struct {
UserID bbs.UUserID `bson:"user_id"`
Action string `bson:"action"`
UpdateNanoTS types.NanoTS `bson:"update_nano_ts"`
}

var EMPTY_USER_VISIT = &UserVisit{}

var (
USER_VISIT_USER_ID_b = getBSONName(EMPTY_USER_VISIT, "UserID")
USER_VISIT_ACTION_b = getBSONName(EMPTY_USER_VISIT, "Action")
USER_VISIT_UPDATE_NANO_TS_b = getBSONName(EMPTY_USER_VISIT, "UpdateNanoTS")
)

// UpdateUserVisit updates user_visit's document data
func UpdateUserVisit(userVisit *UserVisit) (err error) {
query := bson.M{
USER_VISIT_USER_ID_b: userVisit.UserID,
}

_, err = UserVisit_c.Update(query, userVisit)
if err != nil {
return err
}
return
}

// CalculateAllUserVisitCounts count recent 10 mins users who are calling login_required_api
func CalculateAllUserVisitCounts() (int64, error) {
query := bson.M{
USER_VISIT_UPDATE_NANO_TS_b: bson.M{
"$gte": types.TimeToNanoTS(time.Now().Add(TIME_CALC_ALL_USER_VISIT_COUNTS)),
},
}

count, err := UserVisit_c.Count(query, 0)
if err != nil {
return 0, err
}

return count, nil
}
21 changes: 21 additions & 0 deletions schema/user_visit_count.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package schema

import (
"context"
"time"
)

var (
REDIS_USER_VISIT_COUNT = "user_visit_count"
REDIS_USER_VISIT_COUNT_EXP = 10 * time.Minute
)

// SetUserVisitCount try to set user visit count to redis
func SetUserVisitCount(count int64) error {
ctx := context.Background()
err := rdb.Set(ctx, REDIS_USER_VISIT_COUNT, count, REDIS_USER_VISIT_COUNT_EXP).Err()
if err != nil {
return err
}
return nil
}
41 changes: 41 additions & 0 deletions schema/user_visit_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package schema

import (
"testing"

"github.com/Ptt-official-app/go-openbbsmiddleware/types"
)

func TestCalculateAllUserVisitCounts(t *testing.T) {
setupTest()
defer teardownTest()
defer UserVisit_c.Drop()

userVisit := &UserVisit{UserID: "", Action: "test", UpdateNanoTS: types.NowNanoTS()}
_ = UpdateUserVisit(userVisit)

tests := []struct {
name string
want int64
wantErr bool
}{
// TODO: Add test cases.
{
"test get 1 user visit count",
1,
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := CalculateAllUserVisitCounts()
if (err != nil) != tt.wantErr {
t.Errorf("CalculateAllUserVisitCounts() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("CalculateAllUserVisitCounts() got = %v, want %v", got, tt.want)
}
})
}
}