Build a Gin application on AWS Elastic Beanstalk and scale it with Memcache

We’ll walk through how to create a simple Gin Gonic application, and how to deploy it using Amazon Elastic Beanstalk. Once the application is set up and deployed, we’ll explore ways that using Memcache can provide a solution to some common performance bottlenecks you might come across.

We’ll walk you through creating the application from start to finish, but you can view the finished product source code here.

Memcache is a technology that improves the performance and scalability of web apps and mobile app backends. You should consider using Memcache when your pages are loading too slowly or your app is having scalability issues. Even for small sites, Memcache can make page loads snappy and help future-proof your app.

Prerequisites

Before you complete the steps in this guide, make sure you have all of the following:

  • Familiarity with Go (and ideally Gin)
  • An AWS account. If you haven’t used AWS before, you can set up an account here.
  • The AWS CLI installed and configured on your computer.
  • Go, govendor, and the EB CLI installed on your computer.
  • Make sure the GOPATH environment variable is set. (You can check by running go env GOPATH.)

Deploying a Gin application with Elastic Beanstalk

Gin is a minimalist framework that doesn’t require an application skeleton. Simply create a Go app and add github.com/gin-gonic/gin as a dependency like so:

Now that we’ve installed the Gin framework, we can add our app code. We’ll create a page that calculates the largest prime number that’s smaller than a number a visitor submits.

Create application.go and paste the following code into it:

NOTE: Elastic Beanstalk requires that your main go file be named application.go. It compiles your application using the following command: go build -o bin/application application.go

Now let’s add a corresponding view. Create the file templates/index.tmpl.html and paste the following code into it:

You now have a working app that you can start by running go run application.go.

Deploying with Elastic Beanstalk

To deploy the app to Elastic Beanstalk (EB), you’ll need to create a Git repository. We’ll start by creating a .gitignore file with the following lines in it:

In order to deploy to EB, we’ll need three aditional files: build.sh, Buildfile, and Procfile.

Procfile:

Buildfile:

build.sh

These files are what EB relies on to deploy your application. If you run into issues during deployment later, double check these files. Make sure your port is listening on 5000, and make sure your main file is labeled application.go.

Then, create the repository and commit the initial state of the app:

Next, we’ll need to create a EB CLI repository with enough information so EB knows how to run it. Start by creating the repository using eb init. We’ll walk through this now. NOTE: Don’t copy and paste:

Now that you’ve set up your application repository, we’ll need to create the EB instance.

At this point, EB will go about creating and deploying your application. The first time we deploy it’ll take a couple of minutes. Go grab a coffee and come back. Once it’s done building, you can type eb open to see your new application. If you can view the configuration in the AWS console by using eb console.

Adding caching to Gin

Memcache is an in-memory, distributed cache. Its primary API consists of two operations: SET(key, value) and GET(key). Memcache is like a hashmap (or dictionary) that is spread across multiple servers, where operations are still performed in constant time.

The most common use for Memcache is to cache expensive database queries and HTML renders so that these expensive operations don’t need to happen over and over again.

Set up Memcache

To use Memcache in Gin, you first need to provision an actual Memcache cache. MemCachier provides a fast and flexible multi-tenant cache system that’s compatible with the protcol used by the popular memcached software. When you create a cache with MemCachier, you’re provided with one or more endpoints that you can connect to using the memcached protocol, accessing your cache just as if you had set up your own memcached server. So head over to https://www.memcachier.com, sign up for an account, and create a free development cache. If you need help getting it set up, follow the directions here.

There are three config vars to you’ll need for your application to be able to connect to your cache: MEMCACHIER_SERVERS, MEMCACHIER_USERNAME, and MEMCACHIER_PASSWORD. You can find these on your analytics dashboard. You’ll need to add these variables to EB.

We can confirm that they’ve been set by running:

To use the cache in Gin, we need to install mc with govendor:

Add it to your build.sh file:

and configure it in application.go:

package main

import (
  // ...
  "github.com/memcachier/mc"
)

func main() {
  username := os.Getenv("MEMCACHIER_USERNAME")
  password := os.Getenv("MEMCACHIER_PASSWORD")
  servers := os.Getenv("MEMCACHIER_SERVERS")

  mcClient := mc.NewMC(servers, username, password)
  defer mcClient.Quit()
  // ...
}
// ...

Caching expensive computations

There are two reasons why caching the results of expensive computations is a good idea:

  1. Pulling the results from the cache is much faster, resulting in a better user experience.
  2. Expensive computations use significant CPU resources, which can slow down the rest of your app.

Our prime number calculator doesn’t really have any expensive computations, because we limit the input value to 10000. For the sake of the tutorial, however, let’s assume that calculating the prime is an expensive computation we would like to cache.

To achieve this, let’s modify the GET route in application.go and replace

with

Deploy these changes to Heroku and submit some numbers to find primes:

The page should work just as before. However, under the hood, already calculated primes are now cached. To see what’s going on in your cache, open the MemCachier dashboard (which is where you found your environment variables.)

On the dashboard you can refresh the stats each time you request a prime. The first time you enter a number, the get misses will increase. For any subsequent request of the same number, you should get an additional get hit.

Caching rendered views

Rendering HTML views is generally an expensive computation, and you should cache rendered views whenever possible. In Gin, you can achieve this easily with gin-contrib/cache library. Fetch the library with govendor:

Once again add it to your build.sh file:

Now we can cache rendered views in application.go like so:

This is easy enough and works well. However, if the view ever changes, we need to be careful. To illustrate the case of a changing page, let’s add a “Like” button to each number and its calculated largest prime. Let’s put the button just below the calculated prime in the index.tmpl.html file:

We now need to create a controller for the POST route in application.go and store the posted like in a variable.

Storing likes in a variable is a bad idea. Each time the app restarts, it wipes all likes. We do this here only for convenience. In a production application, you should store such information in a database.

In addition, we also need to make sure the likes are passed to the HTML function in the GET controller:

To illustrate the problem with changing pages, let’s commit our current implementation and test it:

If you submit a number, you will now get the largest prime below it, together with a Like button. However, when you click Like!, the like count doesn’t increase. This is because the view is cached.

To resolve this, we need to invalidate the cached view whenever it is updated:

Deploy again to EB:

Now you can see the number of likes increase.

Session Caching

Memcache works well for storing information for short-lived sessions that time out. However, because Memcache is a cache and therefore not persistent, long-lived sessions are better suited to permanent storage options, such as your database.

To use sessions in Gin, you need `gin-contrib/session:``

build.sh:

The configuration in application.go is easy enough:

Now you can now use sessions as you please. For more information about session usage in Gin, check out the gin-contrib/sessions README.

Further reading & resources