Skip to content

Instantly share code, notes, and snippets.

@KentaKudo
Last active February 18, 2022 10:02
Show Gist options
  • Save KentaKudo/1b466e9ff508390f3d558a7b4e84d476 to your computer and use it in GitHub Desktop.
Save KentaKudo/1b466e9ff508390f3d558a7b4e84d476 to your computer and use it in GitHub Desktop.
Go Best Practice
func (u *User) Store(ctx context.Context) error {
...
if err := u.Hash.Store(ctx, k, u); err != nil {
return err
}
...
}
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")
}
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)
}
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
}
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{}{}
}
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)
}
func accessDatabase(ctx context.Context) {
logger := ctx.Value(logCtxKey).(zerolog.Logger)
logger.Debug().Msg("accessing database")
}
package bestpractice
type User struct {}
type UserService interface {
User(context.Context, string) (*User, error)
}
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)
...
}
package user
func GetUser(ctx context.Context, id string) (*User, error) {
...
}
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))
}
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)
}
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)
}
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