Go vs Rust: Performance comparison for JWT verify and MySQL query

Mayank C
Tech Tonic

--

Introduction

After publishing a record number of articles on comparing performance of various technologies like Node.js, Deno, Bun, Rust, Go, Spring, Python, etc. for a simple hello world case, I consistently got comments that the articles were good, but weren’t applicable directly for real-world use cases. I was asked to do the same for more ‘real-world’ cases. The articles also (and still) attracted a record number of views. However, the point was well taken. Hello world was the best starting point, but definitely not a ‘real-world’ case.

Real-world use case

With this article, I’m starting a new series where I’m going to compare a number of technologies for a real-world case:

  • Get JWT from the authorization header
  • Verify JWT & get email from claims
  • Perform a MySQL query with email
  • Return the user record

This is a very common real-world case. For the ‘Hello world’ case, I’ve seen the technologies offering somewhere between 70K to 200K RPS. The RPS was high because all the app was doing was returning a simple string. Of course, we won’t expect a 50–100K RPS for the JWT + MySQL use case. How much we’ll get is yet to be seen.

This article compares Go & Rust for this use-case. This is an interesting comparison because both are compiled to machine code languages, that runs directly.

Due to the large number of articles that will get published, I’ll also be creating an article to index all the real-world cases. At the end of this article, you’ll find a link to that article. Let’s get started.

Test setup

All tests are executed on MacBook Pro M1 with 16G of RAM.

The software versions are:

  • Go v1.20.5
  • Rust v1.70.0

On the Go side, I’m using Gin web framework. The other frameworks that I’m using are: golang-jwt for verifying and decoding JWTs, and go-sql-driver for performing MySQL queries.

On the Rust side, I’m using Actix web framework. The other frameworks that I’m using are: jsonwebtoken for verifying and decoding JWTs, and sqlx for performing MySQL queries.

The HTTP load tester is built over libcurl. There is a pre-created list of 100K JWTs. The tester picks random JWTs and sends it in the Authorization header of the HTTP request.

The MySQL database contains a table called users, which has 6 columns:

mysql> desc users;
+--------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+--------------+------+-----+---------+-------+
| email | varchar(255) | NO | PRI | NULL | |
| first | varchar(255) | YES | | NULL | |
| last | varchar(255) | YES | | NULL | |
| city | varchar(255) | YES | | NULL | |
| county | varchar(255) | YES | | NULL | |
| age | int | YES | | NULL | |
+--------+--------------+------+-----+---------+-------+
6 rows in set (0.00 sec)

The users table is prepopulated with 100K records:

mysql> select count(*) from users;
+----------+
| count(*) |
+----------+
| 99999 |
+----------+
1 row in set (0.01 sec)

For each email present in the JWT, there is a corresponding user record in the MySQL database.

Code

Go

Rust

The Rust code is compiled in release mode using — release option.

Results

Each test is executed for 1M requests in total. The concurrency levels are 10, 50, and 100 connections. A warm-up of 1K requests is given before taking measurements.

Here are the charts with the results:

Conclusion

A scorecard is generated from the results using the following formula. For each measurement, get the winning margin. If the winning margin is:

  • < 5%, no points are given
  • between 5% and 20%, 1 point is given to the winner
  • between 20% and 50%, 2 points are given to the winner
  • > 50%, 3 points are given to the winner

A list of all real-world performance comparisons is here.

--

--