OxDEAD Unicornz

Have you ever seen so many?

Go Gopher Go or Tale of One-time URLs

As I wrote before I’m playing with Google Cloud services and Golang. In order to get more of a hands-on expirience I’ve decided to create a simple app, providing one-time URLs for supplied input.

Long time ago when I was working with Ruby on Rails projects I’ve made a RoR based one now it’s time for a Golang based version.

App is available as https://onetimergo.deadunicornz.org as well, but the SSL certificate for the custom domain is self signed so you’ll get a certificate warning unless this URL marked as an exception in your browser.

Code is here, this is default module. And here, this is the module syncronizing Memcache with Data Storage and performing garbage collection.

Few words about goals

So far my goal is to learn how to create Golang based web services. App as for now is quite messy – everything is handled within the single function, no tests, form validation is far from perfect, but from functional perspective app does what it is supposed to do – it gets an input and provides you with one-time URL.

As I clean the mess up I’ll possibly post some findings.

App architecture

App consists of two modules, I try to use ‘microservices’ approach. Default module is basically a middleware between data storage and web server, fetching data with key matching one provided as URI. It checks Memcache first, then persistent Data Store. Sync module iterates over data stored in Data Storage, removes expired entries and re-creates entries in Memcache if they gone.

Deploying application

Note on deploying microservices in Google Cloud. It’s not clear from documentation how exactily you configure several applications within the same project.

You’re supposed to use goapp command to deploy your application:

1
goapp deploy -application <project_id> -version <version_number> app.yaml

The confusing part is -application option. It does not take application id or name. It takes Google Cloud project id. So in general you’ll use the same command to deploy all of microservices. The question is how GAE differentiates between modules? The answer – module key in app.yaml

Here is app.yaml for the default service:

1
2
3
4
5
6
runtime: go
api_version: go1

handlers:
...
blah-blah-blah

Since there is no ‘module’ key this application will be treated as default.

Here is app.yaml for sync service:

1
2
3
4
5
6
7
module: sync
runtime: go
api_version: go1

handlers:
...
blah-blah-blah

If you deploy this app using the same goapp command GAE will place it separately, not overwriting default service and allowing parallel operation.

Module-specific cron

Here is cron.yaml file for sync module.

1
2
3
4
5
6
7
8
cron:
- description: Sync and garbage collect Data Storage
  url: /sync
  schedule: every 15 minutes
  target: sync
  retry_parameters:
    min_backoff_seconds: 5
    max_doublings: 5

Note url key. You can’t put URL with FQDN here, you can use only relative path. If you don’t supply target key pointing to the correct module cron will query /sync on the default application. I’ve spent some time trying to determine why syncronization does not work and the reason was that cron was pulling the default module instead of sync.