Feat(*): Retry for error case, Go 1.17 beta compiler.

This commit is contained in:
fshee 2021-06-15 01:03:31 +00:00
parent e39a843f18
commit 15d3d2837f
2 changed files with 51 additions and 26 deletions

67
main.go
View file

@ -40,13 +40,29 @@ import (
//go:embed index.html //go:embed index.html
var indexTemplate string var indexTemplate string
type Retry struct {
retryAttemptCount int
}
func (r Retry) Do(f func() error) (err error) {
for i := 0; i < r.retryAttemptCount; i++ {
err = f()
if err == nil {
return nil
}
}
return err
}
type DB struct { type DB struct {
*gorm.DB *gorm.DB
log *log.Logger log *log.Logger
hashSeed string hashSeed string
retry Retry
} }
func NewDB(l *log.Logger, dbFilePath, hashSeed string) (DB, error) { func NewDB(l *log.Logger, dbFilePath, hashSeed string, retry Retry) (DB, error) {
_, err := os.Stat(dbFilePath) _, err := os.Stat(dbFilePath)
if os.IsNotExist(err) { if os.IsNotExist(err) {
err := ioutil.WriteFile(dbFilePath, []byte{}, 0600) err := ioutil.WriteFile(dbFilePath, []byte{}, 0600)
@ -61,7 +77,7 @@ func NewDB(l *log.Logger, dbFilePath, hashSeed string) (DB, error) {
if err != nil { if err != nil {
return DB{}, err return DB{}, err
} }
return DB{db, l, hashSeed}, db.AutoMigrate(&Link{}) return DB{db, l, hashSeed, retry}, db.AutoMigrate(&Link{})
} }
type Link struct { type Link struct {
@ -71,10 +87,15 @@ type Link struct {
Del string `gorm:"unique"` Del string `gorm:"unique"`
} }
func (db DB) getHashShortLink(s fmt.Stringer) string { func (db DB) getHashShortLink(s fmt.Stringer) (string, error) {
h := maphash.Hash{} var (
h.WriteString(s.String()) h = maphash.Hash{}
return strings.TrimSpace(strings.TrimLeft(fmt.Sprintf("%#x\n", h.Sum64()), "0x")) _, err = h.WriteString(s.String())
)
if err != nil {
return "", err
}
return strings.TrimSpace(strings.TrimLeft(fmt.Sprintf("%#x\n", h.Sum64()), "0x")), nil
} }
func (db DB) getHashDeleteKey(s fmt.Stringer) string { func (db DB) getHashDeleteKey(s fmt.Stringer) string {
@ -82,20 +103,20 @@ func (db DB) getHashDeleteKey(s fmt.Stringer) string {
} }
func (db DB) NewLink(u *url.URL) (Link, error) { func (db DB) NewLink(u *url.URL) (Link, error) {
return db.NewLinkWithShortLink(u, db.getHashShortLink(u)) h, err := db.getHashShortLink(u)
}
func (db DB) NewLinkWithShortLink(u *url.URL, hash string) (Link, error) {
var (
link = Link{Big: u.String(), Smol: hash, Del: db.getHashDeleteKey(u)}
err = db.Create(&link).Error
)
// TODO: If error due to unique issue should attempt to retry a few times.
if err != nil { if err != nil {
db.log.Println(err)
return Link{}, err return Link{}, err
} }
return link, nil return db.NewLinkWithShortLink(u, h)
}
func (db DB) NewLinkWithShortLink(u *url.URL, hash string) (link Link, err error) {
// Retry for unique errors.
err = db.retry.Do(func() error {
link = Link{Big: u.String(), Smol: hash, Del: db.getHashDeleteKey(u)}
return db.Create(&link).Error
})
return
} }
func (db DB) GetLink(smol string) (l Link, e error) { func (db DB) GetLink(smol string) (l Link, e error) {
@ -130,12 +151,12 @@ func NewController(logger *log.Logger, db DB, demo bool, url, copy string, tmpl
func (c controller) Err(rw http.ResponseWriter, r *http.Request, err error) { func (c controller) Err(rw http.ResponseWriter, r *http.Request, err error) {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
rw.WriteHeader(http.StatusNotFound) rw.WriteHeader(http.StatusNotFound)
rw.Write([]byte(err.Error())) fmt.Fprintf(rw, "%s", err)
return return
} }
c.log.Println(err) c.log.Println(err)
rw.WriteHeader(http.StatusInternalServerError) rw.WriteHeader(http.StatusInternalServerError)
rw.Write([]byte(err.Error())) fmt.Fprintf(rw, "%s", err)
} }
func (c controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) { func (c controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
@ -184,7 +205,7 @@ func (c controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
} }
if u.Scheme != "http" && u.Scheme != "https" { if u.Scheme != "http" && u.Scheme != "https" {
rw.WriteHeader(http.StatusBadRequest) rw.WriteHeader(http.StatusBadRequest)
rw.Write([]byte("URL must contain scheme. E.G. missing `http://` or `https://`.")) fmt.Fprintf(rw, "URL must contain scheme. E.G. missing `http://` or `https://`.")
return return
} }
var ( var (
@ -203,7 +224,7 @@ func (c controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
} }
rw.Header().Set("X-Delete-With", link.Del) rw.Header().Set("X-Delete-With", link.Del)
rw.WriteHeader(http.StatusFound) rw.WriteHeader(http.StatusFound)
rw.Write([]byte(fmt.Sprintf("%s/%s", c.url, link.Smol))) fmt.Fprintf(rw, "%s/%s", c.url, link.Smol)
return return
case http.MethodDelete: case http.MethodDelete:
@ -214,7 +235,7 @@ func (c controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
} }
if len(b) < 1 { if len(b) < 1 {
rw.WriteHeader(http.StatusBadRequest) rw.WriteHeader(http.StatusBadRequest)
rw.Write([]byte("Must include deletion key in DELETE body.")) fmt.Fprintf(rw, "Must include deletion key in DELETE body.")
return return
} }
var ( var (
@ -254,7 +275,7 @@ func main() {
if *v { if *v {
applicationLogger = log.New(os.Stdout, logPrefix, 0) applicationLogger = log.New(os.Stdout, logPrefix, 0)
} }
db, err := NewDB(applicationLogger, *dbFilePath, *hashSeed) db, err := NewDB(applicationLogger, *dbFilePath, *hashSeed, Retry{3})
if err != nil { if err != nil {
startupLogger.Fatal(err) startupLogger.Fatal(err)
return return

View file

@ -1,11 +1,12 @@
BIN=out BIN=out
CC=go1.16 CC=go1.17beta1
all: setup vendor gen build all: setup vendor gen build
setup: setup:
@env GOOD=off go get golang.org/dl/go1.16 @env GOOD=off go get golang.org/dl/$(CC)
@env GOOD=off $(CC) download @env GOOD=off $(CC) download
#@env GO111MODULE=off $(CC) get github.com/golangci/golangci-lint/cmd/golangci-lint
vendor: go.mod go.sum vendor: go.mod go.sum
@$(CC) mod tidy @$(CC) mod tidy
@ -20,9 +21,12 @@ gen:
test: test:
@env $(ENV) $(CC) test ./... -cover -count 1 @env $(ENV) $(CC) test ./... -cover -count 1
run: gen build run: lint build
@clear @clear
@env $(ENV) ./$(BIN) -v -demo -copy "2021 i@fsh.ee" -url https://dev.fsh.ee -port 8080 -db /tmp/link_test_db_1.sql -seed secret @env $(ENV) ./$(BIN) -v -demo -copy "2021 i@fsh.ee" -url https://dev.fsh.ee -port 8080 -db /tmp/link_test_db_1.sql -seed secret
dev: dev:
@find . -type f | grep -E '(.*)\.(go|html)' | entr -cr make run @find . -type f | grep -E '(.*)\.(go|html)' | entr -cr make run
lint:
@golangci-lint run ./...