Last active
February 18, 2022 10:02
-
-
Save KentaKudo/1b466e9ff508390f3d558a7b4e84d476 to your computer and use it in GitHub Desktop.
Go Best Practice
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func (u *User) Store(ctx context.Context) error { | |
... | |
if err := u.Hash.Store(ctx, k, u); err != nil { | |
return err | |
} | |
... | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func main() { | |
ctx, cancel := context.WithCancel(context.Background()) | |
defer cancel() | |
go func() { | |
sigchan := make(chan os.Signal, 1) | |
signal.Notify(sigchan, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) | |
<-sigchan | |
cancel() | |
}() | |
svr := &ctxpkg.Server{} | |
svr.Run(ctx) // ← long running process | |
log.Println("graceful stop") | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type Server struct{} | |
func (s *Server) Run(ctx context.Context) { | |
for { | |
select { | |
case <-ctx.Done(): | |
log.Println("cancel received, attempting graceful stop...") | |
// clean up process | |
return | |
default: | |
handleRequest() | |
} | |
} | |
} | |
func handleRequest() { | |
time.Sleep(time.Duration(rand.Intn(10)) * time.Second) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func main() { | |
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) | |
defer cancel() // ← cancel should be called even if timeout didn't happen | |
SendRequest(ctx) // ← subroutine that can get stuck | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func SendRequest(ctx context.Context) { | |
respCh := make(chan interface{}, 1) | |
go sendRequest(respCh) | |
select { | |
case <-ctx.Done(): | |
log.Println("operation timed out!") | |
case <-respCh: | |
log.Println("response received") | |
} | |
} | |
func sendRequest(ch chan<- interface{}) { | |
time.Sleep(60 * time.Second) | |
ch <- struct{}{} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var logCtxKey = &struct{}{} | |
func handleRequest(w http.ResponseWriter, r *http.Request) { | |
method, path := r.Method, r.URL.Path | |
logger := log.With(). | |
Str("method", method). | |
Str("path", path). | |
Logger() | |
ctxWithLogger := context.WithValue(r.Context(), logCtxKey, logger) | |
... | |
accessDatabase(ctxWithLogger) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func accessDatabase(ctx context.Context) { | |
logger := ctx.Value(logCtxKey).(zerolog.Logger) | |
logger.Debug().Msg("accessing database") | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package bestpractice | |
type User struct {} | |
type UserService interface { | |
User(context.Context, string) (*User, error) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package redis | |
import ( | |
"github.com/thirdfort/go-bestpractice" | |
"github.com/thirdfort/go-redis" | |
) | |
type UserService struct { | |
... | |
} | |
func (s *UserService) User(ctx context.Context, id string) (*bestpractice.User, error) { | |
... | |
err := redis.RetrieveHash(ctx, k) | |
... | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package user | |
func GetUser(ctx context.Context, id string) (*User, error) { | |
... | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type Restaurant struct { | |
openAt time.Time | |
closeAt time.Time | |
} | |
func (r Restaurant) IsOpen(at time.Time) bool { | |
return (at.Equal(r.openAt) || at.After(r.openAt)) && | |
(at.Equal(r.closeAt) || at.Before(r.closeAt)) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func TestRestaurantBeforeOpen(t *testing.T) { | |
r := Restaurant{ | |
openAt: time.Date(2022, time.January, 17, 12, 0, 0, 0, time.UTC), | |
closeAt: time.Date(2022, time.January, 17, 22, 0, 0, 0, time.UTC), | |
} | |
input := r.openAt.Add(-1 * time.Second) | |
got := r.IsOpen(input) | |
assert.False(t, got) | |
} | |
func TestRestaurantBeforeClose(t *testing.T) { | |
r := Restaurant{ | |
openAt: time.Date(2022, time.January, 17, 12, 0, 0, 0, time.UTC), | |
closeAt: time.Date(2022, time.January, 17, 22, 0, 0, 0, time.UTC), | |
} | |
input := r.closeAt | |
got := r.IsOpen(input) | |
assert.True(t, got) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func TestRestaurantJustOpened(t *testing.T) { | |
r := Restaurant{ | |
openAt: time.Date(2022, time.January, 17, 12, 0, 0, 0, time.UTC), | |
closeAt: time.Date(2022, time.January, 17, 22, 0, 0, 0, time.UTC), | |
} | |
input := r.openAt | |
got := r.IsOpen(input) | |
assert.True(t, got) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func TestRestaurantTableDriven(t *testing.T) { | |
r := Restaurant{ | |
openAt: time.Date(2022, time.January, 17, 12, 0, 0, 0, time.UTC), | |
closeAt: time.Date(2022, time.January, 17, 22, 0, 0, 0, time.UTC), | |
} | |
// test cases | |
cases := map[string]struct { | |
input time.Time | |
want bool | |
}{ | |
"before open": { | |
input: r.openAt.Add(-1 * time.Second), | |
want: false, | |
}, | |
"just opened": { | |
input: r.openAt, | |
want: true, | |
}, | |
"before close": { | |
input: r.closeAt, | |
want: true, | |
}, | |
"just closed": { | |
input: r.closeAt.Add(1 * time.Second), | |
want: false, | |
}, | |
} | |
for name, c := range cases { | |
t.Run(name, func(t *testing.T) { | |
got := r.IsOpen(c.input) | |
assert.Equal(t, c.want, got) | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment