[Golang] Why my go-api was 10x slower than node-api

Jason Choi
2 min readDec 11, 2020

The secret of bcrypt(which only I didn’t know)

Problem

api to sign in user

mutation { signInUser(input: { email: “some_user_email” password: “some_password expoToken: “some_expo_token”}) {tokenString}}

The code to sign in user(Go-version)

var user model.Uservar token model.Tokenvar result pg.Resultvar wg = sync.WaitGroup{}_, err := pgdb.Con.Query(&user, `select id, password from users where email = ?`, input.Email)if user.ID == “” {return &token, errors.New(“No User with such email”)}if err != nil {return &token, err}noMatch := bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(input.Password))if noMatch != nil {return &token, errors.New(“Wrong password”)}// update last_visited_at when signed in_, err = pgdb.Con.Query(&result, `update users set last_visited_at = ? where id = ?`, time.Now(), user.ID)if err != nil {return &token, err}tokenString, err := generatetoken.GenerateToken(user.ID)if err != nil {return err}token.TokenString = &tokenStringreturn &token, nil

time to respond, calculated by Postman

Go-based api: avr. of 923ms

node-based api: avr. of 120ms

The gap was too large to be ignored; performance was the №1 reason I chose Go over NodeJS!

So, I decided to find WHY.

Candidates for the reason of low performance

  1. Compile problem: I ran local Go server using go run command, not using go install or go build command. Maybe the reason my server is slow is because I used Go as an interpreting language.
  • But, this was not the case; I compiled Go and ran that file in my local server, and it made no difference.

2. Go Routine problem: I did not use go routine for that api, so that could be the reason. So I added go routine for token generating process.

  • This was not the reason, either.

3. Token generating might take too much time: To test this hypothesis, I removed password comparing part and ran the server again.

  • To no avail.

The Reason is…

Finally, I thought that password comparing might take much time, maybe because I hashed password too many times when creating user.

So, I tested the hypothesis, and the result was surprising(to me, at least).

bytes, err := bcrypt.GenerateFromPassword([]byte(input.Password), hashCount)

hashCount = 14(status quo): 927ms to respond

hashCount = 12: 247ms to respond

hashCount = 10: 81ms to respond

(* node hashCount = 10: 122ms to respond)

It took almost 3 times more when hashCount was added by 2.

Then, how many times should I hash?

One article I found said thatRecommended # of rounds for bcrypt : The number of iterations that gives at least 250 ms to compute’.

That perfectly fits my condition, so I lowered the round of hashes to 12, and my go-api works 1.5x faster than node-api(at least for this one api, but that’s far advanced than before).

--

--