How to Build a Multi-Zone Java App in One Day

How to Build a Multi-Zone Java App in One Day

Creating the first version of a Slack-like messenger that runs across multiple availability zones

Featured on Hashnode

Ahoy, matey! At last, the time has come to build and launch the first version of my geo-distributed Java application.

It took me around 24 hours in total to create this version. The app currently runs on Vaadin and Spring, it can use PostgreSQL or YugabyteDB as a database, and it either works locally or can be deployed in Heroku.

Don’t have a clue of what I'm talking about? Welcome to my dev journal where I’ve been documenting my experience of building a geo-distributed app in Java from scratch. In my two previous articles, I talked about geo-distributed apps and what their architecture looks like. In this blog, I’ll actually get things done, sharing my first results and any challenges.

So, if you’re with me on this journey, then, as the pirates used to say - “All Hand Hoy!” which means “Everyone on deck!”

My App - The Big Picture

Some of you might have noticed that I keep talking about building a generic geo-distributed Java app. But, what is my app gonna do? Well, I’m building a Slack-like corporate messenger.

Yep, it may sound like I’m reinventing the wheel, but perhaps I have a killer feature in mind, my special secret sauce that will overturn the dominance of Slack!

On a serious note, I’m just curious about how to design and build a geo-distributed corporate messenger (like Slack) that functions with low latency across the globe, tolerates cloud outages, and honors GDPR-like requirements. So, I thought I would try to make my own! Thus, at the end of my journey, I’m expecting to have this:

image7.png

The messenger application instances and database nodes will be spread worldwide and run in the cloud regions closest to most users. The global load balancer will intercept user traffic and route requests to the application instances closest to the user.

But, that’s the big picture. The finish line, so they say. However, we start with baby steps…

My App - Day 1

While I have an ambitious goal for my geo-distributed messenger, the first version will be much more modest.

I bet that on day 1, my Slack-killer, like most startup companies out there, will not be a big success. In fact, it might be barely noticed. If I get 100 active users daily within the first two months, then it’s already a big win.

So, if I expect to have around 100 active users daily, why should I burn the money on infrastructure that spans the globe? It makes little sense. It’s far more prudent to start small and then grow big. That’s why the first version of my app looks like this:

image6.png

My messenger will be deployed within a single cloud region in the US. It’s going to be a monolith running in Heroku. There will be a three-node YugabyteDB cluster running across three availability zones. With such architecture, I can withstand zone-level outages and serve the user requests of Ms. Blue and other US-based users at low latency.

Unfortunately, the latency for Mr. Green from Berlin and Mr. Red from Sydney will be high. But, as a startup, I can live with that, especially if I put most of my marketing dollars into growing the US-based user base.

Once the time comes, I can easily scale my current multi-zone geo-distributed app to a multi-region one. At least, I hope that it’s going to be easy. Time will tell…

Let me walk you through the main components of the first version of my messenger application. Here are its GitHub coordinates.

Vaadin: Backend and Frontend

As an engineer, I always look for shortcuts when creating web applications, especially on the frontend side. I can’t be called a web developer, because the Web is not what I’m nerding on daily.

It’s more like this… Every two years I join or start a web project and learn something new. I still remember creating my first web app in raw JavaScript with HTML+CSS, then jQuery was launched and was a big thing. Later, I used GWT for a few projects and during the last project got a chance to work on Angular.

Why did I pick Vaadin for this project? Well, even though I’m a polyglot, Java is my mother tongue. I built a special bond with Java after spending many years at Sun Microsystems and Oracle building JVM and JDK. Thus, as a guy who knows Java inside out, it’s always my first choice.

But, putting my personal attachment to Java aside, what do I like about Vaadin after using it for 4 days in a row? Vaadin is a full-stack framework. You can use it to build both the backend and frontend logic.

Firstly, as with many other frontend frameworks, you create/design pages and views using essential building blocks likes Button, Label, HorizontalLayout, etc. Then the framework translates your code, written in Java, to the JavaScript counterpart with a nice-looking UI. This is what I created:

image4.png

Not bad for a guy whose design and front-end skills require some work, right? Right?!

Secondly, Vaadin doesn’t require me to run a separate backend instance and implement APIs for the frontend there. Instead, I just create my views (displayed in the browser) and add the server-side logic that the views then ask the backend to execute. For instance, this is the Button that sends a message on a button-click event:

sendMessageButton = new Button("Send");
        sendMessageButton.addClickListener(e -> {
            …. 
                Profile user = userOptional.get();

                Message newMessage = new Message();

                newMessage.setChannelId(currentChannel.getId());
                newMessage.setCountryCode(currentChannel.getCountryCode());
                newMessage.setSenderId(user.getId());
                newMessage.setSenderCountryCode(user.getCountryCode());

                newMessage.setMessage(newMessageArea.getValue().trim());

                newMessage = messagingService.addMessage(newMessage);
}

The listener logic is implemented in my custom UI component - MessageView. The listener gets triggered in the browser end, it’s obvious. Then Vaadin sends this event for processing to the backend side where a new Message is constructed and added to the database via the call to a Spring Framework’s Service entity - messagingService.addMessage(newMessage).

Nice! No need to create a separate REST API layer. Vaadin can do this for me.

PostgreSQL and YugabyteDB: Database

My database choice was driven by past experience and personal preferences.

PostgreSQL needs no introduction. This is my default go-to database for any project for the last 15+ years.

YugabyteDB might still be a dark horse for many. That’s the database I’m nerding on these days. It’s a distributed SQL database built on PostgreSQL. Basically, it’s a distributed PostgreSQL that can work across geographies. Exactly what I need for my geo-distributed app.

As long as YugabyteDB is Postgres-compliant, the app supports both databases out of the box. I use Postgres for my development environment and can always switch to a multi-node YugabyteDB cluster by tweaking the connectivity settings.

Wanna try? No problem, matey, follow these steps:

  1. Launch a Postgres instance locally and create the messenger’s schema following these instructions
  2. Then start the app using a familiar syntax: mvn spring-boot:run
  3. Open localhost:8080 and enjoy this view

image5.png

It takes time to start the app for the first time as the DataGenerator generates hundreds of Workspaces and Channels with thousands of Messages. But, after that, the startup time will be fast.

Next, if you want to use YugabyteDB for the dev environment as well just:

  1. Start YugabyteDB locally
  2. Tweak connectivity settings in the application-dev.properties file
  3. Start the app! Enjoy!

Heroku and YugabyteDB Managed: Deploying to Prod

The deployment to the prod was the most straightforward part for me. I’m a lazy guy who uses cloud services whenever possible (or reasonable). My geo-messenger was no exception.

As you remember, the first version of the app is supposed to work within a single cloud region with database nodes at least in three availability zones. Zones fail frequently and I need to be ready for that. So, what have I done?

First, I deployed a multi-node YugabyteDB Managed cluster in South Carolina (us-east1) region with nodes in three availability zones - us-east1-b, us-east1-c and us-east1-d. Just in case, each node handles both reads and writes, there is no such a thing like “active and standby” nodes:

image3.png

Next, after creating a Heroku project, I deployed my Java app somewhere in the USA:

image1.png

Currently, Heroku is not transparent about the cloud region my app is assigned to. I can only hope that the app instance will be running close enough to my YugabyteDB cluster and that Heroku will take care of the app’s availability in case of cloud outages. I probably need to dig into Heroku internals here. Please share your thoughts in the comments if you know Heroku better than I do!

What’s On the Horizon?

The development of the first version of my geo-distributed messenger was a swift and fun experience. It’s nice to get a multi-zone app functioning within just four days of coding.

What do we have coming up next?

Performance in prod sucks. It takes 3-6 seconds to load messages when I switch between channels. I can’t blame Heroku or YugabyteDB for that as I was coding too fast and used Spring Data in a less than optimal way.

So, let me work on the performance issues next and then scale the app across multiple regions. Until I do that, you can deploy the current version of the app in Heroku and see how “fast” it is.