Moving Visualizer. Again.

I haven’t posted an update for almost a month, but that doesn’t mean nothing’s been happening. On October 12th, a significant change occurred: I migrated Visualizer to a new server provider. If you haven’t muted #extensions on Discord or if you’re subscribed to the status page, you probably saw the update.

The Story So Far

Visualizer started way back in 2020 and, like most hobby Ruby projects at the time, was deployed to Heroku. It was a great fit back then, and with a simple git push I could see my changes reflected almost immediately. However, Heroku’s focus shifted towards large companies / enterprises, and their pricing reflected that. Additionally, as Visualizer grew, I became increasingly frustrated by the performance I got there.

In November 2022, I moved Visualizer to Fly.io. It was like a glass of ice-cold water in hell. Fly was this cool upstart that promised the same developer ergonomics, but with better performance at a reasonable price. I tried it out, and it worked really well. I was very happy with it and loved the Fly Community. Sure, the support left something to be desired, but that was to be expected from a small company. Once I switched to the Launch Plan, that improved.

The Fly Experience

But for the past couple of months I had a feeling that performance was getting worse. I upgraded web and database servers and it did improve things slightly, but it also got more expensive. In the beginning of October, I started seeing some weird errors in the logs about database malfunctioning. I contacted support, but didn’t hear back. Then the first random downtime occurred. For a minute. And in a day, another one. 3 minutes. So I’ve contacted them again and selected a higher priority.

Would you mind letting us know what app you’re seeing this on? This might help us better pinpoint the issue.

Are you kidding me? There’s only one app!

Taking a look at the recent health checks, it looks like the issue has gone away? Do you have any sense regarding how long the original issue lasted?

None of the issues went away. All the errors were still there and happening every 5 to 10 minutes.

I just took a look at your logs and haven’t seen the error you shared in quite some time. Di (sic) you manage to fix the issue you were running into by chance?

Again, all the errors were still there. Did I mention there were days between these (non-)responses?

Visualizer Downtime

The Kamal Experience

Still impressed by the Rails World 2024 opening keynote I mentioned in the previous post, I decided to try moving my other project, the Business European Coffee Trip from Fly to Hetzner with Kamal 2.

A couple of hours later, it was up and running. I was blown away by the speed. It’s a small project so I only need a single server. I got roughly 2-3× performance boost on CAX21 (ARM64, 4 cores, 8GB @ €5.99/month) compared to shared-cpu-2x on Fly.io (x86, 2 cores, 4GB @ $24.69/month). And…it…just works? 🤔

A couple of days later, while working, I get an SMS from my uptime monitor. Visualizer is down. Ugh, again? What’s going on now? I log into Fly and I see both database instances are down. No warning, no emails from Fly, no nothing. Just down. I run a restart command and one instance comes back up, the other one forever dead. 11 minutes of downtime and Visualizer is hobbling along. Several hours pass before everything is back up and running somewhat normally.

Then I see their newest post on Community: We’re making pricing simpler!. Basically, they’re removing the Launch Plan which includes the service and support, and are splitting support into a separate paid add-on: $29/month for Standard, $199/month for Premium, or Enterprise starting at $2500. It’s clear I’m no longer their target audience. And by that point I was convinced: I will be moving Visualizer to Hetzner too.

Friend’s Experience

As if to confirm my decision, my friend attempted to deploy a simple app using the fly launch, and I’ll let the chat speak for itself:

WTF is going on at Fly?!? “To launch an app just run fly launch!” I do, it runs, suggest FRA region and then tells me I can’t use FRA region on my plan. The links it provides give no information on this, nor on the price of other plans nor on how to switch, nor what my plan is, nor what regions I can use (all except Frankfurt and Mumbai for some reason), and there’s no way to sort regions by price.

I explained that Frankfurt was previously only available on Launch plans and above, and that now they’re removing those plans.

Wait, what? You can’t deploy to FRA without a Launch plan but you can’t get a Launch plan any more? I can’t even report this bug to support because I’d have to pay to be able to contact support. 😂

The Hetzner Experience

I knew moving Visualizer was going to be a bit more difficult than ECT. For starters its database size is ~35 GB while European Coffee Trip’s is ~150 MB. With ECT it was as simple as stopping fly, pg_dump, rsync to the new server, pg_restore, switch DNS records, done. But Visualizer needed a bit more thought and planning.

I looked into and trialed many different options: pg_auto_failover, pglogical, pg_easy_replicate, but they all had their own issues. Mainly of various limitations because of the way Fly runs Postgres.

Then I came across pgsync. It’s a fairly simple tool, that basically utilizes pg_dump and pg_restore under the hood, but provides a much better developer experience. Namely, it allowed me to easily select which tables to sync. This allowed me to make the following plan:

  1. Do a sync of ShotInformation (at ~30 GB by far the biggest table that holds, well, shot information)
  2. Stop fly web server
  3. Sync all tables except ShotInformation
  4. Start web server on Hetzner
  5. Switch DNS records
  6. Connect to Fly database from Hetzner and sync the missing rows in ShotInformation

With the plan in place I went through the entire thing except stopping the server and switching DNS records. The whole thing took about 2 hours. But the crucial bit: without step 1 (which I could do before stopping the Fly server) the entire process took just over 5 minutes.

I announced the migration on the status page and Discord with the planned downtime of ~15 minutes and started the migration on a Saturday morning. It went as smoothly as the test run, and we were back up and running on Hetzner in 6 minutes. As expected, there were some minor hiccups, but in a span of about half an hour everything was back to normal and better than ever.

The Story Now

Similar to ECT, I’m seeing 2-3× performance improvements across the board. Together with speedups last month, the progress really feels incredible. The database calls are faster, the page rendering is faster, and shot parsing is much faster. Most importantly: so far there have been 0 reliability issues, server errors, or any downtime whatsoever.

There’s still a lot of work to be done. I want to improve database resilience and backup strategy, I want to improve server monitoring, I want to add some other bells and whistles. But already I feel much more confident in this setup than I did with Fly for the last couple of months.

Thanks for reading, and I hope you now know a bit more about the recent instability and downtimes and what I did about it. Feel free to email me or ping me on Discord if you have any questions.

And, as always, you can check the entire diff since the last update. Which again includes some breadcrumbs about future features, and some smaller features like adding support for roasting date formats.

Have a great day! ☕


Rails 8, Native Auth, and Speed Boosts! 🚀

This time it’s more of a backend-work kind of upgrade.

Living on the Edge 😎

So, I watched the Rails World 2024 opening keynote and got way too excited. Next thing you know, I’m upgrading Visualizer to Rails 8 beta 1. The upgrade went surprisingly smoothly, but I did stumble upon a bug with the Devise dependency. And that’s when a truly dangerous idea struck me: “Hey, why not replace Devise with Rails native authentication?”. Several hours later, there was a PR. 😬

I also wrote a technical post on Radan’s blog. Why there? Well, here I don’t want to be too dev focused, and my website gets 0 traffic. Also, the previous post I wrote there on how to implement infinite pagination with Turbo was very well received, so there’s a precedence. 😅

Goodbye Redis, Hello Solid 👋

Given how well all that went I was further inspired to get rid of another dependency: Redis. So I’ve added Solid Cable and Solid Cache, and just like that, I could remove Redis. Fewer service dependencies -> fewer moving parts -> fewer things can break -> happier life. 🥳

Just Thrust it ⚡

But then I added another new thing: Thruster. It’s basically a tiny go server in front of Rails server (Puma in my case) that speeds up static file serving. There was an issue getting it to work, but the lovely people at Fly helped me out.

I also brought most JavaScript files in-house, reducing my reliance on another dependency: CDN. Highcharts is still being a bit…difficult, so it’s staying on CDN for now. But mark my words, one day I’ll conquer that beast! 🐉

Community Search Speedup 🏎️

There was another thing that I mentioned in Discord: I made a big speedup of community/search for all non-premium users. Since I am premium myself (shocking, I know) I did not realize how slow it was for the majority of the users. But one morning I stumbled into the performance tab and saw mean and p90 response times lounging around 600ms. For context, that was about 15 times slower than what I see. But then I found that the issue was the count of non-premium shots for the banner. I extracted it to an async call, and voilà! Lightning-fast page for everyone.

Why Stop at Fast? 🚀

Today, I went even further and added trigram indexes on the Roaster and Coffee fields, speeding those queries massively. Don’t believe me? Try it out yourself. It’s practically instant. You might even think it’s broken at first. It’s not, I promise.

A Tiny but Important Change 🔍

Oh, and one more thing: Decent profiles downloaded from Visualizer will no longer be prefixed with Visualizer/. I saw Damian’s Basecamp post about this breaking D-Flow profile editor, so it had to go. If I remember correctly, I did this because some people wanted to differentiate between their own profiles vs. the ones downloaded from Visualizer. I never really liked that, so now it’s gone. ✨

What’s Next? 👀

As always, you can check out all the changes in the diff. And if you look closely, you might spot some seeds of a super-secret, very exciting new project. Stay tuned!

Enjoy the rest of your weekend, and remember: life’s too short for bad coffee…and slow websites. ☕️


September? When did that happen?

What was that sound? Oh, I think the summer just ended. What? How did that happen so fast? 🙈

In the last update at the beginning of July, I mentioned that I was going to Indonesia for 3 weeks and that I’d enable coffee management as soon as I got back. Well, life had other plans. 😂

It’s September now, and after some more polish, I finally feel ready to actually enable it. There still might be some bugs (especially when it comes to syncing with Airtable), so if you find any, please let me know.

The feature is now live for all Premium users! Head to your profile settings to turn it on. Once enabled, Visualizer will start processing your shots. For most, this’ll happen faster than you can say “crema”, but for our data-rich friends with tens of thousands of Airtable-synced shots, it might take a bit longer. You know who you are. 👀

Coffee Management

As per usual, there were many more changes since the last update. Most notably, you can now copy details from your recent shots to the shot you’re editing. While most people use DYE (Describe Your Espresso) extension on Decent, not everyone uses Decent.

Another tiny change is that if you have only 2 lines visible in a chart, you will now see a delta between the values. Both of these features came from GitHub issues, so if you have other ideas, feel free to open a new issue and describe what you’d like to see.

I’ve also switched from SendGrid to Postmark after hearing several complaints over the past months about password reset emails not being delivered. Since the switch, I haven’t received any emails about this not working, so unless my email is broken, I think I finally fixed it. 😅

That’s it for now. I’m looking forward to seeing how this goes. And again, if you find any bugs, please let me know.

Happy weekend! ☕