AWS Database Blog

Amazon ElastiCache: Utilizing Redis Geospatial Capabilities

Amazon ElastiCache makes it easy to deploy and manage a highly available and scalable in-memory data store in the cloud. Among the open source in-memory engines available to you for use with ElastiCache is Redis, which added powerful geospatial capabilities in version 3.2. This post explores using these capabilities to build an app that locates nearby bike share stations in Chicago. You can easily extend the app to other cities or to use other geo datasets.

Benefits of Amazon ElastiCache
You can use Amazon ElastiCache to accelerate your high volume application workloads by caching your data in-memory providing sub-millisecond data retrieval performance. When used in conjunction with any database including Amazon RDS or Amazon DynamoDB, ElastiCache can alleviate the pressure associated with heavy request loads, increase overall application performance and reduce costs associated with scaling for throughput on other databases.

As an AWS managed service, Amazon ElastiCache also removes the undifferentiated heavy lifting involved with administrating Redis. There are a variety of application patterns that can be used to leverage ElastiCache with your data, some of which will be discussed in this post.

Our Bike Share Application
Bike sharing programs are becoming increasingly popular in major cities throughout the world. Our app will enable users to search for nearby Divvy bike share locations in Chicago. In this post, we will explore the associated backend services, as shown in the following image.

BikeShareApp

We’ll use the AWS Serverless Application Model (SAM) to manage and deploy our AWS resources, including supporting storage (DynamoDB and ElastiCache). SAM extends AWS CloudFormation to simplify deployment of serverless functionality, including AWS Lambda and Amazon API Gateway. SAM also eases deployment using specialized tasks available in the AWS CLI.

Reviewing the architecture above, our SAM template will create two public-facing API actions, one to prime our data store (SetupStationFunction) and one to find nearby stations (ListStationsFunctions). It will also create an ElastiCache cluster, DynamoDB table (our system of record), and DynamoDB Stream as well as a Lambda function to process that stream (StreamProcessorFunction).

Complete sample code for our example is available at https://github.com/awslabs/elasticache-geospatial-public-bikes. The README provides detailed instruction on how to deploy the application in your own environment, making use of AWS CloudFormation.

Integrating with ElastiCache
When working with ElastiCache, you can use familiar libraries to connect to and interact with your Redis and Memcached clusters. Here, we will demonstrate an integration with Redis using a popular open source library for Node.js (redis). The version of the redis library used in this sample (version 2.6.5) doesn’t yet directly support Redis GEO commands, so here we make use the generic send_command function.

Loading Bike Share Stations
Like many other bike share programs, Divvy exposes a complete listing and current availability by using a public API. We’ll use this API to load our station data into DynamoDB and, in turn, ElastiCache by using a DynamoDB Stream. If you have deployed our sample code, it includes a setup function that you can call to add station data to DynamoDB:

$ curl –L 'https://<YOUR_API_DOMAIN>/Prod/stations/setup'

As stations are added to the DynamoDB table, we use a write-through pattern in which an AWS Lambda function is triggered by a DynamoDB Stream to add the station’s geo data to ElastiCache. To store geospatial information, we use the Redis GEOADD command, which can be called in our Node.js example as follows:

const redis = require('redis');

// first, we connect to our Redis cluster
var client = redis.createClient({
  host: ELASTICACHE_HOST,
  port: ELASTICACHE_PORT
});

// next, we can store the user's most recent location
client.send_command('GEOADD',
  [ 'public-bikes:stations',
  latitude,
  longitude,
  stationId ], (error, reply) => {
  client.quit();
  if (error) { ... }
  else { ... }
});

Querying for Nearby Stations
The major benefit of our app is that you can use it to search for nearby bike share stations. With the station data already stored in ElastiCache, we will make use of the powerful GEORADIUS Redis command. In our Node.js sample, this is as follows:

const redis = require('redis');

// first, we’ll setup a few search options
var searchOptions = {
  radius: 2,
  unit: 'mi',
  count: 10
};

// then, we connect to our Redis cluster
var client = redis.createClient({
  host: ELASTICACHE_HOST,
  port: ELASTICACHE_PORT
});

// finally, we can query for nearby users
client.send_command('GEORADIUS',
  ['public-bikes:stations',
  longitude,
  latitude,
  searchOptions.radius,
  searchOptions.unit, // m | mi | km | ft
  'WITHDIST',
  'WITHCOORD',
'  COUNT',
  searchOptions.count ], (error, reply) => {
  client.quit();
  if (error) { ... }
  else { ... }
});

In the preceding sample, Redis will return up to 10 stations within a 2 mile radius of the current user. The response will also include the distance (in miles) and the coordinates of the station. For our example, station name, coordinates, and distance will be the extent of the data that we return to the user. You may wish to append additional data in some manner, for example, via another query against ElastiCache or DynamoDB.

If you have deployed our sample code, you can query for nearby stations utilizing the following (note the query string parameters already include valid coordinates for downtown Chicago):

$ curl –L 'https://<YOUR_API_DOMAIN>/Prod/stations?latitude=41.8802596&longitude=-87.6346818'
The resulting output will look like the following (note that we have massaged the response from Redis):

[{
  "name": "Wacker Dr & Washington St-Chicago",
  "distance": "0.2484 mi",
  "coordinates": {
  "latitude": 41.88327238502640881,
  "longitude": -87.63731449842453003
}
}, {
  "name": "State St & Harrison St-Chicago",
  "distance": "0.5589 mi",
  "coordinates": {
  "latitude": 41.87405360416989453,
  "longitude": -87.62771755456924438
  }
...
]

We’ve now demonstrated how to add geospatial data to Amazon ElastiCache for Redis and query that data to identify nearby bike share stations. Now you can easily use this data in a mobile app or website.

Removing Old Stations
Finally, we will also want to add the capability to remove stations from the cache if, for example, no bikes are available or a station is moved. To remove an item from the Geo index, we can use the ZREM Redis command as shown following:

const redis = require('redis');

// first, we connect to our Redis cluster
var client = redis.createClient({
  host: ELASTICACHE_HOST,
  port: ELASTICACHE_PORT
});

// we can now remove the user’s location from the cache
client.send_command('ZREM',
  [ 'public-bikes:stations',
  stationId ], (error, reply) => {
  client.quit()
  if (error) { ... }
  else { ... }
});

In examining the sample code, you will notice that this code is part of our DynamoDB stream processor function. To remove a station, we remove it from the source data (in this case, DynamoDB) and the processor propagates that change to ElastiCache. Notice that our stream handler contains logic that examines the type of operation (record.eventName) and then perform the necessary action as described in the preceding sections.

Powering a Mobile App
With our API complete, we can use it to power a mobile application such as the React Native sample included in our same code.  The mobile application can pass the user’s current location to our backend API, which returns a listing of nearby bikes stations.  The app then parses and displays these stations, along with each station’s distance from the user’s current location.

App

Extending Our Example
Although our goal in this post is to describe a general approach for managing and querying geospatial data in Amazon ElastiCache, you can also extend the sample code in several meaningful ways. First, you can load the full set of Divvy bikes locations (note that DynamoDB’s batchWrite function supports a maximum of 25 records at a time). You can also load bike stations in other cities, such as New York, San Francisco, or Melbourne, Australia. Or you can modify the sample further to build a restaurant finder or plot local points of interest.

Amazon ElastiCache for Redis can enable advanced geospatial capabilities by using the Redis 3.2 engine for numerous use cases that require low latency and high performance. Redis Geospatial capabilities are available in ElastiCache today!


About the Author

Josh Kahn is a solutions architect at Amazon Web Services. He works with the AWS customers to provide guidance and technical assistance on database projects, helping them improve the value of their solutions when using AWS.