Use case that Fiber is not as I expected

Thiện Trần
4 min readJul 17, 2022

Fiber has been known for a long time as a high-performance server framework for Golang, so when thinking about building a heavy load service, Fiber appears immediately in my thought. However, it turns out that my experience this time with Fiber was not as I expected, and that is why I had to rebuild my server from scratch.

1. Explaining my use case

For some reasons and businesses, I must build a server that serves files to clients. The components diagram of my server is just as simple as the diagram below:

Streaming requested files to clients

In this article, we will focus only on the cached file use case to see only the performance and what happened on the inside to compare.

Additionally, I will use docker to limit resources when testing. If you’re concerned about my configuration, here it is:

  • Machine: i5 8th gen with 6 core, 32GB of RAM, 250 SSD
  • Resource limit on docker running container: 1 CPU + 500Mb of RAM

2. Play with Fiber

As shown in the document of Fiber, this framework allows us to set stream writer as the body of a request. By using that, we can stream a large file from our storage to client chunk by chunk.

Yes! The code works well, and the performance is okay too. However, the CPU work really hard 😮.

After running performance testing, I realized Fiber is not working as I think it should be. Let's see the flame graph:

flame graph of writing to stream (left) and sending data to client (right)

Actually, in the writing flow, I didn’t send data straight to client’s response. Instead of that, writing to stream writer just writes into a buffer channel so Fiber can read and pass it to client.

I had a doubt and check the source code of Fiber myself, and yes, the conclusion above is right. Inside the function SetBodyStreamWriter, 2 channels are created along with buffio.Writer to allow us to write data. Then on the other thread, the data will be read again and sent to client.

Stream writing flow by using Fiber

Each time we call to write on writer, a command will be created for our CPU. More commands are created, more pressure is pushed to our CPU. This explains why the CPU is always at high-load state. Based on that, to improve my service, reducing CPU pressure is one of the solutions. I do some research to know whether can I change the streaming flow of Fiber framework or somehow call read and write less. But nah 😕, that is the architect of framework, you can’t change too much except using which has been provided. You can change WriteBufferSize of Fiber config, it allows us to call read write a little less, but the performance is changed not too much.

So I decided to rewrite my service from scratch to take control fully of the streaming flow.

2. net/http implementation

The new flow will be straight like this:

New flow

The flame graph of streaming flow is shown below:

Flame graph of new writing stream flow

Just only one thread and send data straightly. The CPU pressure of my server is reduced nearly 2 times, and the performance was boosted merely double too (100 concurrent connections to download a 170MB file from server, ~52s with Fiber, and ~28s with my custom net/http server).

3. Conclusion

In summary, Fiber is still a high-performance framework for those who work with Golang, just be careful when making a decision. A framework always has its own strong and weak points, making a good choice depends on us.

For me, if have to build a fast restful API, Fiber will be my top choice 🙌 😋.

Hope this article will provide something that is helpful to you ✌️.

Thank you for reading and have a good day!!!

--

--