REST based Embedded Tiny URL server

Motivation

If applications, online services or websites have a rich query functionality, URL may be too long to share in a messenger and mail. In this case you may want to support Tiny URL service. If you can’t use an external service like tinyurl.com then internal or even embedded Tiny URL service may solve the problem.

For an instance the image of this article is a “Share Query” screenshot of VMWare vRealize Log Insight product.

Requirements

  • Reasonable resource footprint 
  • Persistence
  • Scalability
  • Availability
  • Replication. If some part of cluster is down, we still should be able to read all existing data. Replication Factor should be configurable.
  • Load Balancing. Load and replication should be balanced between available nodes.
  • Strong consistency
  • Rest API
  • Expiration Time or Time-To-Live

Appeared that Xenon has it all out of the box!

What is Xenon?

Xenon is a framework for building REST based micro-services https://github.com/vmware/xenon. It supports Persistence, Replication and Strong Consistency.

Before we discuss Tiny URL Service Design and Sequence Diagrams, let’s have a look at how to build, run and test it. It will provide a better understanding of the goal.

Implementation is 200 Lines Of Code!

Full code https://github.com/kuzminva/tinyurlservice.

Build

This is a maven project

mvn package

builds jar file with dependencies.

Run Host with Services

java -jar tinyurlservice-host-0.0.1-SNAPSHOT-jar-with-dependencies.jar

[0][I][2017-05-23T06:07:30.995Z][1][8000][startImpl][ServiceHost/577c94d8 listening on http://127.0.0.1:8000]
[1][I][2017-05-23T06:07:33.014Z][15][8000/core/node-groups/default][mergeRemoteAndLocalMembership][State updated, merge with 925654b1-7ba8-4d40-a38e-31d49d613f68, self 925654b1-7ba8-4d40-a38e-31d49d613f68, 1495519653013014]
[2][I][2017-05-23T06:07:37.017Z][22][8000/core/node-selectors/default-3x][checkAndScheduleSynchronization][Scheduling synchronization (1 nodes)]
[3][I][2017-05-23T06:07:37.017Z][22][8000][scheduleNodeGroupChangeMaintenance][/core/node-selectors/default-3x 1495519657017001]
[4][I][2017-05-23T06:07:37.017Z][24][8000/core/node-selectors/default][checkAndScheduleSynchronization][Scheduling synchronization (1 nodes)]
[5][I][2017-05-23T06:07:37.018Z][21][8000/core/node-selectors/default-1x][checkAndScheduleSynchronization][Scheduling synchronization (1 nodes)]
[6][I][2017-05-23T06:07:37.018Z][24][8000][scheduleNodeGroupChangeMaintenance][/core/node-selectors/default 1495519657018001]
[7][I][2017-05-23T06:07:37.019Z][21][8000][scheduleNodeGroupChangeMaintenance][/core/node-selectors/default-1x 1495519657019001]

Test

Now you may use curl to send POST and GET requests

Generate Tiny URL alias

curl -X GET http://localhost:8000/tinyurlgenerate

Response Body is alias string
4t2j0w

Create Tiny URL

Use a given Tiny URL alias to perform POST request to Stateful service

curl -X POST -H “Content-type: application/json” -d ‘{“documentSelfLink”:”4t2j0w”,”url”:”kuzminva.wordpress.com”}’ http://localhost:8000/tinyurl

JSON Response

{
"url":"kuzminva.wordpress.com",
"documentVersion":0,
"documentEpoch":0,
"documentKind":"TinyUrlService:UrlState",
"documentSelfLink":"/tinyurl/4t2j0w",
"documentUpdateTimeMicros":1492673138367000,
"documentUpdateAction":"POST",
"documentExpirationTimeMicros":0,
"documentOwner":"7f598d08-4be3-4be6-80dd-53fe86d5fa0e"
}

If you try to perform POST request with the same documentSelfLink but different URL it will fail. PUT request isn’t accepted at all.

Read Long URL using Tiny URL

curl -X GET http://localhost:8000/tinyurl/4t2j0w

{
"url": "kuzminva.wordpress.com",
"documentVersion": 0,
"documentEpoch": 0,
"documentKind": "TinyUrlService:UrlState",
"documentSelfLink": "/tinyurl/4t2j0w",
"documentUpdateTimeMicros": 1492673138367000,
"documentUpdateAction": "POST",
"documentExpirationTimeMicros": 0,
"documentOwner": "7f598d08-4be3-4be6-80dd-53fe86d5fa0e"
}

If you are trying to get non existing Tiny URL it returns error response

{
"message": "Service not found: http://localhost:8000/tinyurl/5t2j0w",
"statusCode": 404,
"documentKind": "com:vmware:xenon:common:ServiceErrorResponse",
"errorCode": -2147483648
}

High Level Design

Xenon Service is an instance that exposes a REST API endpoint. There are 2 different types of Xenon services: StatelessService and StatefulService. We need both of them. We use StatelessService only as a generator of Tiny URL aliases and StatefulService as an indexed persistent storage with replication and strong consistency.

Sequence Diagram

Screen Shot 2017-05-23 at 10.55.38 PM

TinyUrlGeneratorService – Xenon StatelessService

We use Xenon Stateless service only to generate Tiny URL Alias. We need to implement GET handler only. It returns us alias as a response body.

curl -X GET http://127.0.0.1:8000/tinyurlgenerate

In fact, a client can decide to generate its own alias and use the same or other algorithm

// Settings for building Tiny Alias
private final String ALIAS_CHAR_SET = "abcdefghijklmnopqrstuvwxyz0123456789";
private final int ALIAS_URL_LEN = 6;

private String generateAlias() {
    String alias = "";
    Random random = new Random();
    for (int i = 0; i < ALIAS_URL_LEN; ++i) {
        alias += ALIAS_CHAR_SET.charAt(random.nextInt(ALIAS_CHAR_SET.length()));
    }

    return alias;
}

We use the given alias to create a POST request to Tiny URL StatefulService.

TinyUrlService – Xenon StatefulService

Internally Xenon StatefulService operates on documents. Service document has unique identifier documentSelfLink. This can be provided by a user or Xenon generates an UUID. Every document is reachable through URI that includes its documentSelfLink – this is going to be our Tiny URL.

curl -X POST -H "Content-type: application/json" -d ‘{"documentSelfLink":"4t2j0w","url":"kuzminva.wordpress.com"}’ http://localhost:8000/tinyurl
curl -X GET http://127.0.0.1:8000/tinyurl/4t2j0w

For Tiny URL we need only POST and GET. POST call is used to create xenon document and GET is used to read it.

Xenon Service has Options that can be used to configure desired behavior of Service. Tiny URL Service requires
ServiceOption.PERSISTENCE
ServiceOption.REPLICATION
ServiceOption.OWNER_SELECTION - guarantees Strong Consistency

Our Tiny URL service needs to subclass Xenon StatefulService with REPLICATION and OWNER options, PERSISTANCE has been already set by StatefulService. See presentation for details: https://github.com/vmware/xenon/wiki/workshop/StatefulServices.pptx.

So, Xenon StatefulService gives us Persistence, Replication and Strong Consistency out of the box.

We describe State that we want to write/read as a Java class.

Tiny URL State

class TinyURLState {
    String url;
}

Then we need to implement POST handler that checks that state is valid and saves it as a Service Document. That’s it! Everything else is implemented by StatefulService.

Class Diagram

Screen Shot 2017-05-24 at 12.01.37 PM

Extra Features

Expiration or Time-To-Live

Every service document has also Expiration Time field that can be set and updated by client in a POST request. See example

curl -X POST -H “Content-type: application/json” -d ‘{“documentSelfLink”:”4t2j0w”,”url”:”kuzminva.wordpress.com”, “documentExpirationTimeMicros”:1503673138367000}’ http://localhost:8000/tinyurl

Note that value of documentExpirationTimeMicros must be in future.

documentExpirationTimeMicros = Utils.fromNowMicrosUtc(TimeUnit.SECONDS.toMicros(tinyUrlLifetimeInSeconds));

In some cases you may want to update this attribute with every GET request. In this case TinyUrlService should send internal POST request to itself with the same state but with new expiration time.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s