badge-size icon indicating copy to clipboard operation
badge-size copied to clipboard

Rewrite in Rust

Open ngryman opened this issue 2 years ago • 10 comments

tl;dr This PR is a complete rewrite in Rust. It shouldn't change anything for end-users.

👋 Could you help me try the new version?

Please try https://img.runbots.io instead of https://img.badgesize.io and let me know if it works fine. Take a look at the README for the API.

I'm using this temporary domain for testing purposes. I will shut it down when this new version is deployed. For the story, the new solution needs to sit behind a CDN for caching content. For this to work, the CDN needs to manage the domain. But the current badgesize.io domain is already managed by Vercel, so I had to use another temporary domain. I happened to have runbots.io (yet another unborn project) free 🤷‍♂️

Motivation

The first reason for this change is that I wanted to learn Rust. That's a good reason by itself 😀

But the main reason is, as often, pasta (💰). badgesize.io is directly serving 1.5+ million badges per month (~50k per day). Badges are cached for 1 hour. So the traffic is largely absorbed by Vercel's CDN and other caches like Camo when accessed via GitHub. In theory, it's serving dozens of millions of badges without having to generate them 👌

However, despite this cache, the current hosting on Vercel + Logflare cost me respectively $60 + $10 per month. This is not sustainable for me. I need to change of hosting solution to something cheaper and make sure to use every atom of available hardware to its best. Rust is the perfect candidate for that.

My end goal is to divide costs per 10, from$70 to $7. Spoiler, I got down to $6 😎

Breaking changes

There is only one major breaking change: PNG is officially dropped. It was broken in the current version anyway since Shields.io changed the subdomain for raster image formats (ie. png, jpg). I think it makes sense since SVG is superior in every aspect and from what I see in my logs, everybody is using SVG anyways.

That's good news for the wallet 💰 since it allows me to drastically change the implementation to something more efficient (cf. section below). SVG is a text-based image format that is cheap to generate compared to rater images.

New implementation

I mostly ported the existing code to Rust with some tweaks. The major change is that I don't depend on Shields.io anymore. The current version was redirecting the user to Shields.io for the badge generation itself. It happened to be brittle on some occasions and to slow things down.

The new implementation proudly generates SVG badges itself 💪 It was a fun and interesting task to do, not that hard in the end. The most challenging part was to handle kernings (ie. space between characters) to compute the final badge width.


Fixes #94, #97

ngryman avatar Nov 09 '21 16:11 ngryman

WOW! This is really awesome! Sincere thanks for your contributions!

I haven't learned Rust at all so I only took some simple tests. It seems the computed sizes are increased by the changes, e.g., the represented size of a sample file before, after.

PaperStrike avatar Nov 10 '21 12:11 PaperStrike

@PaperStrike Thanks for testing this out!

I should have mentioned that but I changed Brotli's compression quality in the new version. It explains the differences.

Node.js defaults it to the maximum value (11). It produces a smaller file at the expense of much more CPU cycles. That's why I think servers out there have lower defaults. For example, Apache sets it to 5 and Nginx is 6 by default. The reason is that the additional compression time would likely be slower than transferring a slightly bigger file, which would defeat the purpose of speeding transfers.

If we take unpkg as an example. By inspecting the transferred size with Chrome DevTools, we can see that it takes 4.1kb and not 3.45kb as advertised by the older version. So it's likely unpkg uses something close to the configuration of the new badgesize version.

After your comment, I dug a little deeper, and I adjusted Brotli's options a little bit to try to match a reasonable default configuration (cf. c4a934d). The reported size of this new version is still above the older (eg. 3.9kb for your library), but I think it's for the best. I believe it's better to advertise something close to real-world sizes rather than optimal sizes.

ngryman avatar Nov 10 '21 16:11 ngryman

@ngryman I realize you've already done a lot of work on the Rust implementation here but I wanted to reach out because the $60/month for Vercel doesn't sound right. The Pro plan is $20/month and should offer plenty of bandwidth and function execution without hitting any overages.

That being said, Vercel offers sponsorships to cover the cost of open source projects so you don't need to worry about billing at all 😃

Take a look at https://vercel.com/support/articles/can-vercel-sponsor-my-open-source-project

styfle avatar Nov 10 '21 16:11 styfle

Hey @styfle, thanks for the heads up, I appreciate it.

I already have the pro plan, but unfortunately, I'm reaching the Execution quota. I'm consuming 2 × 100 GB-Hours which costs me an additional $40.

For the sponsorship, I already asked last year but got the following response:

At the moment, we are sponsoring OSS packages that fit the criteria here.
Serverless Functions are indeed a premium offering in our platform and that is why we are not prepared
to sponsor projects heavily using this type of resource.
If you don't want to use Vercel to deploy this service, perhaps a serverful environment may suit your needs.

So I figured out I should use a different hosting solution for this.

ngryman avatar Nov 10 '21 16:11 ngryman

@ngryman definitely happy to help out out here

rauchg avatar Nov 10 '21 19:11 rauchg

@ngryman Yes, I agree real-world sizes are better.

The transferred size shown in Chrome DevTools contains everything in the response including the headers, which will increase the size by around 0.4 kB. You may take that into account to be more accurate.-)

PaperStrike avatar Nov 11 '21 09:11 PaperStrike

@PaperStrike Oh good catch for the headers. My example with unpkg is probably not the best then 😅 I'll keep the current settings for now and if folks complain about it, I'll revisit it.

ngryman avatar Nov 11 '21 11:11 ngryman

@rauchg Oh cool 🎉 I guess I'll ask directly, could this project be eligible for your OSS sponsorship? If so, I can port this PR to use the Vercel Rust runtime.

ngryman avatar Nov 11 '21 11:11 ngryman

@ngryman Yes, I confirmed with @rauchg that Vercel will sponsor this project 👍

Once you add a logo and link to your project, contact support and we'll apply the sponsorship to your project! 🎉

I hope that helps you ship faster, thanks! 🤗

styfle avatar Nov 11 '21 16:11 styfle

Oh wow, thank you so much guys! That's just awesome 🎉

ngryman avatar Nov 12 '21 08:11 ngryman