Skip to content

Commit c7a476a

Browse files
author
Sommer Matthias (IFAG IT DSA RD CM / External)
committed
Add graceful shutdown
1 parent 3d25f61 commit c7a476a

File tree

5 files changed

+43
-13
lines changed

5 files changed

+43
-13
lines changed

README.md

+7-4
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,22 @@ Uses a controller/repository pattern to handle typical CRUD actions.
55
Exposes the controller methods via HTTP endpoints.
66

77

8-
## Install dependencies
8+
## Dependencies
99

1010
```bash
11-
go get -d ./...
11+
go mod download
1212
```
1313

14-
## Generate mocks
14+
## Mocks
15+
16+
Mocks are generated with (Mockery)[https://github.com/vektra/mockery].
17+
Put the executable under $GOPATH/bin/mockery and create mocks for interfaces with
1518

1619
```bash
1720
%GOPATH%/bin/mockery -all -case=underscore -inpkg
1821
```
1922

20-
## Go Lint
23+
## Lint
2124

2225
1. Get golangci-lint from [Github](https://github.com/golangci/golangci-lint).
2326
2. Follow the steps under [Editor Integration](https://github.com/golangci/golangci-lint#editor-integration).

api/router.go

+13
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package api
22

33
import (
4+
"context"
45
"golang-microservice-template/pizza"
56
. "golang-microservice-template/utils"
67
"net/http"
8+
"time"
79

810
"github.com/labstack/echo"
911
"github.com/labstack/echo/middleware"
@@ -17,6 +19,8 @@ type Router interface {
1719
Index(echo.Context) error
1820
// Start starts listening for incoming requests on the specified address/port.
1921
Start(address string) error
22+
// Shutdown is waiting some seconds to stop the server gracefully and to release resources.
23+
Shutdown(ctx context.Context) error
2024
}
2125

2226
type router struct {
@@ -73,3 +77,12 @@ func (r *router) setRoutes(echo *echo.Echo) {
7377
pizza.PATCH("/:name", controller.Update)
7478
pizza.DELETE("/:name", controller.Delete)
7579
}
80+
81+
func (router *Router) Shutdown() {
82+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
83+
defer cancel()
84+
85+
if err := router.echo.Shutdown(ctx); err != nil {
86+
Log.Fatal(err)
87+
}
88+
}

main.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ package main
33
import (
44
"golang-microservice-template/api"
55
. "golang-microservice-template/utils"
6+
"os"
7+
"os/signal"
68
"strconv"
9+
"syscall"
710
)
811

912
const (
@@ -21,7 +24,13 @@ func main() {
2124
port = localPort
2225
}
2326

24-
Log.Fatal(router.Start(":" + strconv.Itoa(port)))
27+
go func() {
28+
Log.Fatal(router.Start(":" + strconv.Itoa(port)))
29+
}()
2530

26-
Log.Infof("[PizzaService] Started")
31+
done := make(chan os.Signal, 1)
32+
signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGABRT)
33+
<-done
34+
35+
router.Shutdown()
2736
}

pizza/controller.go

+10-5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ const (
1212
PathParamName = "name"
1313
)
1414

15+
// errors
16+
var (
17+
ErrParamNameMissing = "missing pizza name in path"
18+
)
19+
1520
// Controller handles all requests related to pizza data.
1621
type Controller interface {
1722
// Add creates a new pizza.
@@ -50,7 +55,7 @@ func (c *controller) Add(ctx echo.Context) error {
5055

5156
found, err := c.repository.FindByName(dto.Name)
5257
if found != nil {
53-
return Errorf(ErrorTypeConflict, "a pizza with the name '%s' already exists", dto.Name)
58+
return Errorf(ErrorTypeConflict, ErrPizzaNameTaken, dto.Name)
5459
} else if err != nil {
5560
Log.Debug(err)
5661
}
@@ -101,7 +106,7 @@ func (c *controller) GetByName(ctx echo.Context) error {
101106
if err != nil {
102107
return Error(err, ErrorTypeDatabase)
103108
} else if pizza == nil {
104-
return Errorf(ErrorTypeResourceNotFound, "pizza '%s' could not be found", name)
109+
return Errorf(ErrorTypeResourceNotFound, ErrPizzaNotFound, name)
105110
}
106111

107112
dto, err := pizza.ConvertToDto()
@@ -129,7 +134,7 @@ func (c *controller) Update(ctx echo.Context) error {
129134

130135
pizza, _ := c.repository.FindByName(name)
131136
if pizza == nil {
132-
return Errorf(ErrorTypeResourceNotFound, "there is no pizza with name '%s'", name)
137+
return Errorf(ErrorTypeResourceNotFound, ErrPizzaNotFound, name)
133138
}
134139

135140
pizza, err = dto.ConvertToModel()
@@ -160,7 +165,7 @@ func (c *controller) Delete(ctx echo.Context) error {
160165
if err != nil {
161166
return Error(err, ErrorTypeDatabase)
162167
} else if pizza == nil {
163-
return Errorf(ErrorTypeResourceNotFound, "a pizza with the name '%s' does not exist", name)
168+
return Errorf(ErrorTypeResourceNotFound, ErrPizzaNotFound, name)
164169
}
165170

166171
if err := c.repository.Delete(pizza.Name); err != nil {
@@ -173,7 +178,7 @@ func (c *controller) Delete(ctx echo.Context) error {
173178
func checkNameInPath(ctx echo.Context) (string, error) {
174179
name := ctx.Param(PathParamName)
175180
if name == "" {
176-
return "", Error("missing pizza name in path", ErrorTypeBadRequest)
181+
return "", Error(ErrParamNameMissing, ErrorTypeBadRequest)
177182
}
178183
return name, nil
179184
}

pizza/repository.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import (
77

88
// errors
99
var (
10-
ErrPizzaNotFound = "pizza named %s not found"
11-
ErrPizzaNameTaken = "there is already a pizza named %s"
10+
ErrPizzaNotFound = "pizza %s not found"
11+
ErrPizzaNameTaken = "pizza '%s' already exists"
1212
)
1313

1414
// Repository used to persist pizza data.

0 commit comments

Comments
 (0)