Toggle bug in ProductCatalogService for demos (#85)

Fixes #84.
This commit is contained in:
Théo Chamley 2018-12-17 20:06:49 +01:00 committed by Ahmet Alp Balkan
parent 5272a4d821
commit c91dca764f
2 changed files with 76 additions and 13 deletions

View file

@ -3,3 +3,29 @@
Run the following command to restore dependencies to `vendor/` directory: Run the following command to restore dependencies to `vendor/` directory:
dep ensure --vendor-only dep ensure --vendor-only
## Dynamic catalog reloading / artificial delay
This service has a "dynamic catalog reloading" feature that is purposefully
not well implemented. The goal of this feature is to allow you to modify the
`products.json` file and have the changes be picked up without having to
restart the service.
However, this feature is bugged: the catalog is actually reloaded on each
request, introducing a noticeable delay in the frontend. This delay will also
show up in profiling tools: the `parseCatalog` function will take more than 80%
of the CPU time.
You can trigger this feature (and the delay) by sending a `USR1` signal and
remove it (if needed) by sending a `USR2` signal:
```
# Trigger bug
kubectl exec \
$(kubectl get pods -l app=productcatalogservice -o jsonpath='{.items[0].metadata.name}') \
-c server -- kill -USR1 1
# Remove bug
kubectl exec \
$(kubectl get pods -l app=productcatalogservice -o jsonpath='{.items[0].metadata.name}') \
-c server -- kill -USR2 1
```

View file

@ -22,7 +22,10 @@ import (
"io/ioutil" "io/ioutil"
"net" "net"
"os" "os"
"os/signal"
"strings" "strings"
"sync"
"syscall"
"time" "time"
pb "github.com/GoogleCloudPlatform/microservices-demo/src/productcatalogservice/genproto" pb "github.com/GoogleCloudPlatform/microservices-demo/src/productcatalogservice/genproto"
@ -41,18 +44,16 @@ import (
) )
var ( var (
catalogJSON []byte cat pb.ListProductsResponse
catalogMutex *sync.Mutex
log *logrus.Logger log *logrus.Logger
port = flag.Int("port", 3550, "port to listen at") port = flag.Int("port", 3550, "port to listen at")
reloadCatalog bool
) )
func init() { func init() {
c, err := ioutil.ReadFile("products.json")
if err != nil {
log.Fatalf("failed to open product catalog json file: %v", err)
}
catalogJSON = c
log = logrus.New() log = logrus.New()
log.Formatter = &logrus.JSONFormatter{ log.Formatter = &logrus.JSONFormatter{
FieldMap: logrus.FieldMap{ FieldMap: logrus.FieldMap{
@ -63,7 +64,11 @@ func init() {
TimestampFormat: time.RFC3339Nano, TimestampFormat: time.RFC3339Nano,
} }
log.Out = os.Stdout log.Out = os.Stdout
log.Info("successfully parsed product catalog json") catalogMutex = &sync.Mutex{}
err := readCatalogFile(&cat)
if err != nil {
log.Warnf("could not parse product catalog")
}
} }
func main() { func main() {
@ -71,6 +76,22 @@ func main() {
go initProfiling("productcatalogservice", "1.0.0") go initProfiling("productcatalogservice", "1.0.0")
flag.Parse() flag.Parse()
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGUSR1, syscall.SIGUSR2)
go func() {
for {
sig := <-sigs
log.Printf("Received signal: %s", sig)
if sig == syscall.SIGUSR1 {
reloadCatalog = true
log.Infof("Enable catalog reloading")
} else {
reloadCatalog = false
log.Infof("Disable catalog reloading")
}
}
}()
log.Infof("starting grpc server at :%d", *port) log.Infof("starting grpc server at :%d", *port)
run(*port) run(*port)
select {} select {}
@ -146,12 +167,28 @@ func initProfiling(service, version string) {
type productCatalog struct{} type productCatalog struct{}
func parseCatalog() []*pb.Product { func readCatalogFile(catalog *pb.ListProductsResponse) error {
var cat pb.ListProductsResponse catalogMutex.Lock()
defer catalogMutex.Unlock()
if err := jsonpb.Unmarshal(bytes.NewReader(catalogJSON), &cat); err != nil { catalogJSON, err := ioutil.ReadFile("products.json")
if err != nil {
log.Fatalf("failed to open product catalog json file: %v", err)
return err
}
if err := jsonpb.Unmarshal(bytes.NewReader(catalogJSON), catalog); err != nil {
log.Warnf("failed to parse the catalog JSON: %v", err) log.Warnf("failed to parse the catalog JSON: %v", err)
return err
}
log.Info("successfully parsed product catalog json")
return nil return nil
}
func parseCatalog() []*pb.Product {
if reloadCatalog || len(cat.Products) == 0 {
err := readCatalogFile(&cat)
if err != nil {
return []*pb.Product{}
}
} }
return cat.Products return cat.Products
} }