Skip to content

Commit

Permalink
Merge pull request #121 from LucienShui/feature/delete_temporary_afte…
Browse files Browse the repository at this point in the history
…r_expire_minute

add delete after expire minute
  • Loading branch information
LucienShui authored Oct 6, 2021
2 parents 31dddb5 + 5bcae5c commit 6087d98
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 25 deletions.
3 changes: 2 additions & 1 deletion gotest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
BASE=github.com/PasteUs/PasteMeGoBackend/

PACKAGE_LISTS="
model/paste
handler/paste
router
"
Expand Down Expand Up @@ -33,7 +34,7 @@ for PACKAGE in ${PACKAGE_LISTS}; do
exit 1
fi
else
if ! go test -count=1 -cover "${BASE}${PACKAGE}" -args -c "${PWD}/config.json"; then
if ! go test -count=1 -cover "${BASE}${PACKAGE}" -args -c "${PWD}/config.json" --debug; then
echo "test ${PACKAGE} failed"
exit 1
fi
Expand Down
1 change: 0 additions & 1 deletion model/dao/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"github.com/PasteUs/PasteMeGoBackend/common/logging"
"go.uber.org/zap"
"gorm.io/driver/mysql"
_ "gorm.io/driver/mysql"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/schema"
Expand Down
10 changes: 5 additions & 5 deletions model/paste/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func getOne(cs []rune) rune {
return cs[rand.Intn(len(cs))]
}

func generator(length int, zeroFirst bool) string {
func _generator(length int, zeroFirst bool) string {
ret := make([]rune, length)

if zeroFirst {
Expand All @@ -33,10 +33,10 @@ func generator(length int, zeroFirst bool) string {
return string(ret)
}

func Generator(length int, zeroFirst bool, model interface{}) string {
str := generator(length, zeroFirst)
for Exist(str, model) {
str = generator(length, zeroFirst)
func generator(length int, zeroFirst bool, model interface{}) string {
str := _generator(length, zeroFirst)
for exist(str, model) {
str = _generator(length, zeroFirst)
}
return str
}
2 changes: 1 addition & 1 deletion model/paste/paste.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (paste *AbstractPaste) checkPassword(password string) *common.ErrorResponse
return common.ErrWrongPassword
}

func Exist(key string, model interface{}) bool {
func exist(key string, model interface{}) bool {
count := int64(0)
dao.DB.Model(model).Where("`key` = ?", key).Count(&count)
return count > 0
Expand Down
2 changes: 1 addition & 1 deletion model/paste/permanent.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type Permanent struct {

// Save 成员函数,创建
func (paste *Permanent) Save() error {
paste.Key = Generator(8, false, &paste)
paste.Key = generator(8, false, &paste)
paste.Password = hash(paste.Password)
return dao.DB.Create(&paste).Error
}
Expand Down
49 changes: 33 additions & 16 deletions model/paste/temporary.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package paste

import (
"github.com/PasteUs/PasteMeGoBackend/common/logging"
"github.com/PasteUs/PasteMeGoBackend/model/dao"
"go.uber.org/zap"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"time"
)

Expand All @@ -26,45 +29,59 @@ type Temporary struct {

// Save 成员函数,保存
func (paste *Temporary) Save() error {
paste.Key = Generator(8, true, &paste)
paste.Key = generator(8, true, &paste)
paste.Password = hash(paste.Password)
return dao.DB.Create(&paste).Error
err := dao.DB.Create(&paste).Error
if err == nil {
key := paste.Key
time.AfterFunc(time.Minute*time.Duration(paste.ExpireMinute), func() {
if e := dao.DB.Delete(&Temporary{AbstractPaste: &AbstractPaste{Key: key}}).Error; e != nil {
logging.Error("delete paste failed", zap.String("key", paste.Key), zap.String("err", e.Error()))
}
})
}
return err
}

// Delete 成员函数,删除
func (paste *Temporary) Delete() error {
return dao.DB.Delete(&paste).Error
}

func (paste *Temporary) Expired() bool {
duration := time.Minute * time.Duration(paste.ExpireMinute)
if time.Now().After(paste.CreatedAt.Add(duration)) {
return true
}
if paste.ExpireCount < 1 {
return true
}
return false
}

// Get 成员函数,查看
func (paste *Temporary) Get(password string) error {
if err := dao.DB.Transaction(func(tx *gorm.DB) error {
if e := tx.Take(&paste).Error; e != nil {
if e := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Take(&paste).Error; e != nil {
return e
}

duration := time.Minute * time.Duration(paste.ExpireMinute)
if time.Now().After(paste.CreatedAt.Add(duration)) {
if e := tx.Delete(&paste).Error; e != nil {
return e
}
if paste.Expired() {
paste.CreatedAt = nilTime // 通过此字段标记为非法,transaction 生效后再 return error
return nil
return tx.Delete(&paste).Error
}

if e := paste.checkPassword(password); e != nil {
return e
}

if paste.ExpireCount <= 1 {
if e := tx.Delete(&paste).Error; e != nil {
return e
}
paste.ExpireCount -= 1

if paste.Expired() {
return tx.Delete(&paste).Error
} else {
return tx.Model(&paste).Update("expire_count", paste.ExpireCount-1).Error
return tx.Save(&paste).Error
}

return nil
}); err != nil {
return err
} else if paste.CreatedAt.IsZero() {
Expand Down
84 changes: 84 additions & 0 deletions model/paste/temporary_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package paste

import (
"fmt"
"gorm.io/gorm"
"testing"
"time"
)

func assertNil(t *testing.T, err error) {
if err != nil {
t.Fatal(err.Error())
}
}

func assertEqual(t *testing.T, expect interface{}, got interface{}) {
if expect != got {
t.Fatal(fmt.Sprintf("expect %+v, got %+v", expect, got))
}
}

func TestTemporaryGet(t *testing.T) {
paste := Temporary{AbstractPaste: &AbstractPaste{}}
paste.ExpireCount = 2
paste.ExpireMinute = 10086

assertNil(t, paste.Save())
assertNil(t, paste.Get(""))
assertNil(t, paste.Get(""))
assertEqual(t, gorm.ErrRecordNotFound, paste.Get(""))
}

func TestTemporaryAutoDelete(t *testing.T) {
paste := Temporary{AbstractPaste: &AbstractPaste{}}
paste.ExpireCount = 10086
paste.ExpireMinute = 1

assertNil(t, paste.Save())
assertNil(t, paste.Get(""))
key := paste.Key
paste.Key = "a1b2c3d4"
time.Sleep(time.Minute*1 + time.Second)
assertEqual(t, false, exist(key, &paste))
assertEqual(t, gorm.ErrRecordNotFound, (&Temporary{AbstractPaste: &AbstractPaste{Key: key}}).Get(""))
}

func TestTemporaryConcurrentGet(t *testing.T) {
var (
expireCount uint64 = 3
concurrentCnt = 20
getCnt uint64 = 0
)

paste := Temporary{AbstractPaste: &AbstractPaste{}}
paste.ExpireCount = expireCount
paste.ExpireMinute = 1
assertNil(t, paste.Save())

key := paste.Key

ch := make([]chan bool, concurrentCnt)

for i := 0; i < concurrentCnt; i++ {
ch[i] = make(chan bool)
go func(i int) {
err := (&Temporary{AbstractPaste: &AbstractPaste{Key: key}}).Get("")
ch[i] <- err == nil
}(i)
}

for i := 0; i < concurrentCnt; i++ {
get := <-ch[i]
if get {
getCnt += 1
t.Logf("coroutine %d got the paste", i)
}
}

assertEqual(t, expireCount, getCnt)
}

func TestMain(m *testing.M) {
m.Run()
}

0 comments on commit 6087d98

Please sign in to comment.