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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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