Không biết vì sao code golang chạy chậm

Em chào mọi người ạ, em có đoạn code golang Login function sử dụng grpc, em deploy nó trong kubernetes, và cho chạy benchmark 200 request thì kết quả mỗi request Login mất tận tầm 5s, trong khi chạy 1 request riêng thì chỉ mất tầm 200ms. Em đã đặt tracing nhưng không hiểu kết quả tracing luôn, nhờ mọi người xem giúp em ạ.
Một số config:

  • Os: ubuntu 22.04
  • CPU: 8
  • RAM: 16gb
  • Consumed CPU: ~40%/CPU
  • Comsumed RAM: ~60%
  • Grpc connection pool: 150 connections
  • Mysql max idle connections: 20
  • Mysql max open connection: 150

login rpc


func (x *userGrpc) Login(ctx context.Context, request *service_auth.LoginRequest) (*service_auth.LoginResponse, error) {
	ctx, span := otel.Tracer("userGrpc").Start(ctx, "userGrpc.Login")
	defer span.End()

	propagationCache := util.GetPropgationCacheFromCtx(ctx, x.Config)

	userResult := x.UserMysqlRepository.GetUserByEmail(ctx, &user.MysqlRepositoryOption{
		RoleAccessibility: &service_auth.RoleAccessibility{AccessType: service_auth.RoleAccessibility_PRIVILEGED_ACCESS},
		GetByEmail:        &user.MysqlRepositoryGetByEmailOption{Email: request.Email},
	})
	if userResult.Err != nil {
		return nil, x.Interpreter.TranslateError(ctx, &locale.InterpreterOption{Err: userResult.Err, Locale: propagationCache.Locale})
	}

	err := bcrypt.CompareHashAndPassword([]byte(userResult.GetByEmail.User.Password), []byte(request.Password))
	if err != nil {
		return nil, x.Interpreter.TranslateError(ctx, &locale.InterpreterOption{Err: util.ErrInvalidPassword, Locale: propagationCache.Locale})
	}

	mask, err := util.DetectMask(ctx, &util.FieldmaskOption{Descriptor: request.ProtoReflect().Descriptor()})
	if err != nil {
		return nil, x.Interpreter.TranslateError(ctx, &locale.InterpreterOption{Err: err, Locale: propagationCache.Locale})
	}

	fmutils.Filter(userResult.GetByEmail.User, mask.Paths)

	sessionId := fmt.Sprintf("%s-%s", x.Config.Prefix.SessionIdPrefix, uuid.NewString())

	_, err = x.CictGatewayClient.CictAuthClient.SessionManagerClient.MutateSession(ctx, &service_auth.MutateSessionRequest{
		Operation: &service_auth.MutateSessionRequest_GracefulCreate{
			GracefulCreate: &service_auth.MutateSessionGracefulCreate{UserId: userResult.GetByEmail.User.Id, SessionId: sessionId}},
	})
	if err != nil {
		return nil, x.Interpreter.TranslateError(ctx, &locale.InterpreterOption{Err: err})
	}

	return &service_auth.LoginResponse{User: userResult.GetByEmail.User, SessionId: sessionId}, nil
}

GetUserByEmail:


func (x *mysqlRepository) GetUserByEmail(ctx context.Context, opt *user.MysqlRepositoryOption) *user.MysqlRepositoryResult {
	_, span := otel.Tracer("mysqlRepository").Start(ctx, "mysqlRepository.GetUserByEmail")
	defer span.End()

	if opt.RoleAccessibility == nil || opt.GetByEmail == nil {
		return &user.MysqlRepositoryResult{Err: util.ErrInternalWithInvalidArgument}
	}

	result := &user.MysqlRepositoryResult{GetByEmail: &user.MysqlRepositoryGetByEmailResult{User: &service_auth.User{}}}

	query := gorm_helper.RoleAccessibility(x.GormDB, opt.RoleAccessibility).
		Where("email = ?", opt.GetByEmail.Email).First(&result.GetByEmail.User)

	result.Err = query.Error

	return result
}

MutateSessionClient gọi đến một server gateway khác để push message vào kafka


func (x *sessionManagerClient) MutateSession(ctx context.Context, request *service_auth.MutateSessionRequest) (*service_auth.MutateSessionResponse, error) {
	ctx, span := otel.Tracer("").Start(ctx, "sessionManagerClient.MutateSession")
	defer span.End()

	propagationCache := util.GetPropgationCacheFromCtx(ctx, x.Config)

	md := metadata.Pairs(
		x.Config.Const.Scope, service_auth.EnumScope_AUTH_SCOPE.Enum().String(),
		x.Config.Const.Locale, propagationCache.Locale,
		x.Config.Const.SessionId, propagationCache.Session.Id,
		x.Config.Const.WorkspaceId, propagationCache.WorkspaceId,
	)

	newCtx := metadata.NewOutgoingContext(context.Background(), md)

	cictGatewayEndpoint := fmt.Sprintf("%s:%d", x.Config.Services.ServiceGatewayEndpoint.Address, x.Config.Services.ServiceGatewayEndpoint.Port)

	conn, err := grpc.Dial(cictGatewayEndpoint, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		return nil, err
	}

	defer conn.Close()

	client := service_auth.NewSessionManagerServiceClient(conn)

	return client.MutateSession(newCtx, request)
}

Đây là kết quả tracing:

Từ kết quả tracing, cậu có thể thấy 1 khoảng trống giữa lúc cậu gọi SQL và khi cậu detect mask.
Ở code login của cậu, cậu có thể thấy giữa 2 phần kể ở trên, cậu có sử dụng bcrypt để compare password. Logic mà nói, đó là nơi chạy chậm. Và nếu tớ không nhầm, bcrypt nổi tiếng chạy chậm:

Besides incorporating a salt to protect against rainbow table attacks, bcrypt is an adaptive function: over time, the iteration count can be increased to make it slower , so it remains resistant to brute-force search attacks even with increasing computation power.

Cậu có thể thử xác nhận điều này, chỉ đơn giản in ra log thời gian thực thi của bcrypt.

Hope it helps!

5 Likes

Dạ đúng rồi anh, bcrypt chạy chậm quá, em thấy không ổn lắm.

1 Like
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?