How to mock Grpc Clients inside GRPC server implementation - performance

I have a GRPC server that is talking to many other GRPC servers.
So, my server is client for many other GRPC servers
I would like to test my server implementation but I'm stuck on how to mock the GRPC clients.
I have a method from my GRPC server like this:
func (s UserBackendServer) UpdateUser(ctx context.Context, req *api.UpdateUserRequest) (*api.User, error) {
conn, err := grpc.DialContext(ctx, s.userSvcAddr,
grpc.WithInsecure(),
grpc.WithStatsHandler(&ocgrpc.ClientHandler{}))
if err != nil {
return nil, fmt.Errorf("could not connect shipping service: %+v", err)
}
defer conn.Close()
user, err := api.NewUserServiceClient(conn).
UpdateUser(ctx, &api.UpdateUserRequest{
User: nil,
UserId: "",
})
return user, nil
}
In this type of implementation, I cannot mock the UserServiceClient because I create one inside the method :(
I have thought about two ways to solve this issue
Have the client reference inside the UserBackendServer struct
type UserBackendServer struct {
projectID string
userSvcClient api.UserServiceClient
}
func (s UserBackendServer) UpdateUser(ctx context.Context, req *api.UpdateUserRequest) (*api.User, error) {
conn, err := grpc.DialContext(ctx, s.userSvcAddr,
grpc.WithInsecure(),
grpc.WithStatsHandler(&ocgrpc.ClientHandler{}))
if err != nil {
return nil, fmt.Errorf("could not connect shipping service: %+v", err)
}
defer conn.Close()
user, err := s.userSvcClient.UpdateUser(ctx, &api.UpdateUserRequest{
User: nil,
UserId: "",
})
return user, nil
}
With this implementation, I have the side effect of having a continuously active connection to the clients (I have like 10 clients to communicate with). Is it fine in production? Having always-connected clients can it lead to a memory leak or too much concurrents connections?
Using methods that get the client as an argument
func (s UserBackendServer) UpdateUser(ctx context.Context, req *api.UpdateUserRequest) (*api.User, error) {
conn, err := grpc.DialContext(ctx, s.userSvcAddr,
grpc.WithInsecure(),
grpc.WithStatsHandler(&ocgrpc.ClientHandler{}))
if err != nil {
return nil, fmt.Errorf("could not connect shipping service: %+v", err)
}
defer conn.Close()
client := api.NewUserServiceClient(conn)
updateUser(ctx, req.UserId, req.User, client)
user, err := s.userSvcClient.UpdateUser(ctx, &api.UpdateUserRequest{
User: nil,
UserId: "",
})
return user, nil
}
func updateUser(ctx context.Context, userId string, user *api.User, client api.UserServiceClient) (*api.User, error){
userUpdated, err := client.UpdateUser(ctx, &api.UpdateUserRequest{
User: nil,
UserId: "",
})
return userUpdated, err
}
This seems a little bit cumbersome to write all these little functions and I can't make a unit test of the server function.
The strategy number one is it feasible? do you have experience about limitations or how to retry a failed connections if the connections are maintained in the server struct

Related

how to gomock a server streaming grpc method?

I tried using the following code but the client hangs and never receives from the mock server.
I thought this part: Do(rs.EXPECT().Send(&proto.Response{Result: "steve"})would make the mock server response to the client, it never worked out.
For a server-streaming grpc mock, a typical approach like:
mockServer.EXPECT().FetchResponse(&proto.Request{Id: 123}, rs).Times(1).Return(...)
doesn't work, the return value is expected to be an error, not a streaming message.
Could someone with a kind heart please explain how this problem should be handled properly?
Code
func Test_Streaming(t *testing.T) {
// create listiner
lis, err := net.Listen("tcp", ":50005")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
// create grpc server
s := grpc.NewServer()
c := gomock.NewController(t)
mockServer := mock_proto.NewMockStreamServiceServer(c)
proto.RegisterStreamServiceServer(s, mockServer)
go func() {
log.Println("start server")
// and start...
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}()
rs := mock_proto.NewMockStreamService_FetchResponseServer(c)
mockServer.EXPECT().FetchResponse(&proto.Request{Id: 123}, rs).Times(1).Do(rs.EXPECT().Send(&proto.Response{Result: "steve"}))
// client call FetchResponse
ClientConnect()
}

Google PubSub and Go: create client outside or inside publish-function?

I'm new when it comes to Google PubSub(and pubsub applications in general). I'm also relatively new when it comes to Go.
I'm working on a pretty heavy backend service application that already has too many responsibilities. The service needs to fire off one message for each incoming request to a Google PubSub topic. It only needs to "fire and forget". If something goes wrong with the publishing, nothing will happen. The messages are not crucial(only used for analytics), but there will be many of them. We estimate between 50 and 100 messages per second for most of the day.
Now to the code:
func(p *publisher) Publish(message Message, log zerolog.Logger) error {
ctx := context.Background()
client, err := pubsub.NewClient(ctx, p.project)
defer client.Close()
if err != nil {
log.Error().Msgf("Error creating client: %v", err)
return err
}
marshalled, _ := json.Marshal(message)
topic := client.Topic(p.topic)
result := topic.Publish(ctx, &pubsub.Message{
Data: marshalled,
})
_, err = result.Get(ctx)
if err != nil {
log.Error().Msgf("Failed to publish message: %v", err)
return err
}
return nil
}
Disclaimer: p *publisher only contains configuration.
I wonder if this is the best way? Will this lead to the service creating and closing a client 100 times per second? If so, then I guess I should create the client once and pass it as an argument to the Publish()-function instead?
This is how the Publish()-function gets called:
defer func(publisher publish.Publisher, message Message, log zerolog.Logger) {
err := publisher.Publish(log, Message)
if err != nil {
log.Error().Msgf("Failed to publish message: %v", err)
}
}(publisher, message, logger,)
Maybe the way to go is to hold pubsubClient & pubsubTopic inside struct?
type myStruct struct {
pubsubClient *pubsub.Client
pubsubTopic *pubsub.Topic
logger *yourLogger.Logger
}
func newMyStruct(projectID string) (*myStruct, error) {
ctx := context.Background()
pubsubClient, err := pubusb.NewClient(ctx, projectID)
if err != nil {...}
pubsubTopic := pubsubClient.Topic(topicName)
return &myStruct{
pubsubClient: pubsubClient,
pubsubTopic: pubsubTopic,
logger: Logger,
// and whetever you want :D
}
}
And then for that struct create a method, which will take responsibility of marshalling the msg and sends it to Pub/sub
func (s *myStruct) request(ctx context.Context data yorData) {
marshalled, err := json.Marshal(message)
if err != nil {..}
res := s.pubsubTopic.Publish(ctx, &pubsub.Message{
Data: marshalled,
})
if _, err := res.Get(ctx); err !=nil {..}
return nil
}

Serving graphql server over GRPC server

I have below snippet to server the graphql server over http listener. Could you help me how I can do similar implementation with GRPC server? I mean serving graphQL server over grpc server?
func Run() {
var cfg AppConfig
cfg.ProductURL = "0.0.0.0:8001"
err := envconfig.Process("", &cfg)
if err != nil {
log.Fatal(err)
}
s, err := NewGraphQLServer(cfg.ProductURL)
if err != nil {
log.Fatal(err)
}
http.Handle("/graphql", handler.GraphQL(gql.NewExecutableSchema(gql.Config{
Resolvers: s,
})))
http.Handle("/playground", handler.Playground("test", "/graphql"))
log.Fatal(http.ListenAndServe(":8080", nil))
}
// NewGraphQLServer is to create get the connections to other services
func NewGraphQLServer(productURL string) (*resolvers.Resolver, error) {
// Connect to Product Service
productClient, err := service.NewProductClient(productURL)
if err != nil {
return nil, err
}
return &resolvers.Resolver{
ProductClient: productClient,
}, nil
}```

How to turn DataBase access into a Function idiomatically in Go

I have built a Backend API in Go, it works however I want refactor the code for the DB access layer into a function - idiomatically.
// Get the form data entered by client; FirstName, LastName, phone Number,
// assign the person a unique i.d
// check to see if that user isn't in the database already
// if they are send an error message with the a 'bad' response code
// if they aren't in db add to db and send a message with success
func CreateStudentAccountEndpoint(response http.ResponseWriter, request *http.Request){
client, err := mongo.NewClient("mongodb://localhost:27017")
if err != nil {
log.Fatalf("Error connecting to mongoDB client Host: Err-> %v\n ", err)
}
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
err = client.Connect(ctx)
if err != nil {
log.Fatalf("Error Connecting to MongoDB at context.WtihTimeout: Err-> %v\n ", err)
}
response.Header().Set("Content-Type", "application/json")
studentCollection := client.Database(dbName).Collection("students")
_, err = studentCollection.InsertOne(context.Background(),data)
if err != nil {
response.WriteHeader(501)
response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
}
// encoding json object for returning to the client
jsonStudent, err := json.Marshal(student)
if err != nil {
http.Error(response, err.Error(), http.StatusInternalServerError)
}
response.Write(jsonStudent)
}
I understand that I can create a method which returns (*mongoClient, err) as I utilise the client local variable later on in the code.
However I am lost as to how to implement the defer cancel() part because it executes once the method CreateStudenAccountEndpoint is at the end. But I am at a loss on how to implement this defer section in a method that will recognise that I want the defer to happen at the end of the function that calls the DB access layer method e.g CreateStudentAccountEndpoint not the actual db access method itself.
As I understand it, the connection should be long-lived and set up as a part of a constructor, i.e. not part of the request flow.
This will typically look something like this:
type BackendAPI struct {
client *mongo.Client
}
func NewBackendAPI(mongoURI string) (*BackendAPI, error) {
client, err := mongo.NewClient(mongoURI)
if err != nil {
return nil, err
}
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
err = client.Connect(ctx)
if err != nil {
return nil, err
}
return &BackendAPI{client}, nil
}
func (api *BackendAPI) func CreateStudentAccountEndpoint(response http.ResponseWriter, request *http.Request) {
response.Header().Set("Content-Type", "application/json")
// note the use of the long-lived api.client, which is connected already.
studentCollection := api.client.Database(dbName).Collection("students")
_, err = studentCollection.InsertOne(context.Background() ,data)
if err != nil {
response.WriteHeader(501)
response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
return // at this point, the method should return
}
// encoding json object for returning to the client
jsonStudent, err := json.Marshal(student)
if err != nil {
http.Error(response, err.Error(), http.StatusInternalServerError)
}
response.Write(jsonStudent)
}
If you worry about losing the connection, you could implement a call to api.client.Ping in there, but in my opinion this should only be attempted if you encounter a failure you believe you can recover from by reconnecting.

Golang server "write tcp ... use of closed network connection"

I am beginner at Go, I had wrote small server to testing and deploy it on heroku platform. I have /logout request, which almost works, but sometimes I see something like this:
PANIC: write tcp 172.17.110.94:36641->10.11.189.195:9951: use of closed network connection
I don't know why it happens, and why sometimes it works perfectly.
My steps:
I send 1st POST request to /token-auth with body then generate token and send as response.
At 2nd I do /logout GET request with that token, and set token to Redis store
Here is full code of my redil_cli.go
package store
import (
"github.com/garyburd/redigo/redis"
)
type RedisCli struct {
conn redis.Conn
}
var instanceRedisCli *RedisCli = nil
func Connect() (conn *RedisCli) {
if instanceRedisCli == nil {
instanceRedisCli = new(RedisCli)
var err error
//this is works!!!
instanceRedisCli.conn, err = redis.Dial("tcp", "lab.redistogo.com:9951")
if err != nil {
panic(err)
}
if _, err := instanceRedisCli.conn.Do("AUTH", "password"); err != nil {
//instanceRedisCli.conn.Close()
panic(err)
}
}
return instanceRedisCli
}
func (redisCli *RedisCli) SetValue(key, value string, expiration ...interface{}) error {
_, err := redisCli.conn.Do("SET", key, value)
if err == nil && expiration != nil {
redisCli.conn.Do("EXPIRE", key, expiration[0])
}
return err
}
func (redisCli *RedisCli) GetValue(key string) (interface{}, error) {
data, err := redisCli.conn.Do("GET", key)
if err != nil{
panic(err)
}
return data, err
}
After that my function that checks Authorization header will panic while trying to do GetValue(key string) method
func (redisCli *RedisCli) GetValue(key string) (interface{}, error) {
data, err := redisCli.conn.Do("GET", key)
if err != nil{
panic(err)
}
return data, err
}
Can anyone point me, what I doing wrong?

Resources