Geek Culture
Published in

Geek Culture

How to Write a C++ Microservice Using DynamoDB in 20 Minutes

Photo by Anthony Indraus on Unsplash

Writing scalable and robust services and applications requires a scalable and robust solution for storing, searching, and querying data. The database marketplace presents many, many different choices. A few key factors differentiate one database from another, and why a specific application or service picks the database it uses. Most of the time, the most critical factor in choosing a database is how easy it is to deploy it and to scale it. The length of time it takes to deploy a database will limit how quickly a service or application can get off the ground initially, or uptake new versions in the future.

DynamoDB, offered by Amazon Web Services, is a noSQL database that provides dynamic throughput and scaling, consistent reads and writes, as well as easy spin up and tear down of tables. DynamoDB uses a partition and sort key system, that allows operations to behave in a manner half way between a traditional relational SQL database and a noSQL document based database like MongoDB. DynamoDB allows flexibility in an object’s structure and contents, with a wide array of data types such as strings, numbers, sets, maps, and more.

This article will go over how to write a basic micro service using C++, the AWS software development kit, and DynamoDB, in a very short period of time. Fundamental knowledge of both C++ and the CMake language is assumed. Additionally, to run the code presented here, an AWS account is required, with an IAM role that has access and secret keys.

Setup and Installation

In order to build C++ programs, you will need a C++ compiler, as well as a build system generator. For this case, clang and CMake will be used on MacOS. However, the AWS SDK for C++ can compile and run on Windows, Linux, MacOS, or even Android. A repository with all the sample code for this guide is available here.

To begin, the directory used for implementing this project should be laid out like the following :

- <project directory>
|
- CMakeLists.txt
- main.cpp

In this directory, the CMakeLists.txt file will , at build time, download the AWS SDK from Github, build it inside of a local dependency directory, then install the libraries and headers. Because DynamoDB is the only Amazon web service that will be used in this demonstration, we will restrict the options passed in CMake to only build the code relevant for contacting DynamoDB. The AWS SDK is a CMake project that uses components , a CMake feature that allows specification that only certain libraries of a larger project should be found and linked into another target executable.

The major part of the CMake file are the following statements:

The ExternalProject_Add call tells CMake that we want to build and use a project on Github. This function allows us to pass in the CMake options to specify when building that external project, such as not building or running the unit tests, and building static libraries. Depending on your specific operating system distribution, more libraries may be downloaded , built, and installed when the SDK is being built. This is because you may not have commonly used libraries like curl or openssl installed.

To simplify the process of linking the SDK libraries into our simple executable, the libraries are listed explicitly , along with the include directory. If you try this on windows, you will need to remove the pthread library from the list.

Now that we have established how to setup and install the dependencies for the project, let’s understand the SDK that will be used to write a DynamoDB application

The AWS SDK

The AWS SDK for C++ structures the code for each service into three distinct groupings. The first are models. These are objects that store data in a format that relates to the service being used. For example, with DynamoDB, you have an AttributeValue . This is an object that embodies the different data types DynamoDB supports. For example, if you wanted to set the value as a string type, you would use the method SetS . If you wanted to retrieve the string value, you would use GetS .

The second are requests. Requests hold all the information that needs to be packaged into one object before calling an Amazon web service. For DynamoDB, an UpdateItemRequest would require at minimum the partition key of the item being updated, an UpdateItemExpression, a type of query that represents the attributes of the item to update, and the values to supply that update expression.

The third are outcomes and results. Every response from an Amazon web service in the SDK gives you an outcome. An outcome contains a result, and a potential error object. A result object contains everything you would expect to find in a response for a particular API you might be using. With DynamoDB, a ScanResult will have packaged with it the items that are a “result” of scanning the table.

Creating and Deleting Tables

Tables are the backbone and biggest component of using DynamoDB. You must create a table before you are able to perform any operations or store data in DynamoDB. To do this, you could create a table through the AWS console. However, to simplify the executable written in this tutorial, let’s create and delete a table through the AWS SDK code instead.

In order to use the SDK in a C++ program, the SDK must first be initialized. This is required because the SDK uses the curl library to form connection pools it can use to make connections to AWS. For simplicity, the initialization and shutdown of the SDK will be encapsulated in an RAII object, as follows:

Warning: Failing to initialize the SDK prior to making requests to AWS will result in segmentation faults.

The code we will write to communicate with DynamoDB will be encapsulated in a ScopedDynamoTable class. This will be an RAII style object, where the existence of the table is limited to a C++ scope. The constructor of the class will create the table, and wait a bit for it become active, while the destructor will delete the table.

To start, let’s look at what the constructor of the class does to create the table:

First, the DynamoDB client class is initialized with all default configurations. This is stored in a special unique pointer that the SDK provides, which is recommended to use over the std::unique_ptr for compatibility with the SDK’s custom memory management system. After that, a CreateTableRequest is built with the specified name, hash (partition) key, as well as the throughput we want to specify for our table. Although DynamoDB supports both on demand and provisioned throughput, for making this sample program cost virtually nothing to run, we will use a small provisioned throughput. More information on DynamoDB throughput and provisioning can be found here. Similar to this, the destructor of this class would use a DeleteTableRequest, only needing to specify the name of the table to delete.

It’s important to note the use of IsSuccess() on the result of the request. This is the method use for virtually all SDK requests, and is a universal boolean method to determine if they succeeded or not.

Adding Items

Now that we can create DynamoDB tables, we need to be able to add items into the tables. Adding an item to a table in DynamoDB is accomplished through the PutItem operation, encapsulated in the PutItemRequest object. An item added to a table must at least contain the partition key and a corresponding value. Besides that, items in the same table can have different keys and values. Since our partition key is a string, we will just add an item that contains the partition key and a value. As such, a method to put items into the table should look like this:

In this code, the PutItemRequest object is used to format an item to send to DynamoDB to add to our table. However, a ConditionExpression is used here. In DynamoDB, every item based request may also have a condition expression . This condition permits that the request will only succeed if the condition is true, or otherwise, it will fail with a specific failure code indicating it only failed due to the condition being false. Condition expressions are a very useful part of DynamoDB because they permit requests to DynamoDB to be transactional. Requests with condition expressions have read-modify-write semantics that are atomic with respect to other requests acting on the table.

Reading and Printing Items

In addition to adding items to our table, we want to be able to read those items and do something with them in our service. DynamoDB supports two different operations that read items from the table. The first and most straight forward is scanning. Scanning reads all the items from the table, with the option to provide a filtering expression as well as limits on the attributes of the items returned. The other method, querying, is more efficient in that it can take an expression that only reads from specific portions of the table based on either the partition key or an additional secondary index. The filtering expression for scanning the table only filters items after the entire table is read. For large tables, scanning is usually not recommended. However, for simplicity, we will scan the table to read the items.

In the SDK code, the ScanRequest object, similar to other request objects used thus far, can be constructed and built to scan the table. To demonstrate, we can implement a method called printItems , that scans the table and prints out the key and value of each item.

For any result object in the SDK for DynamoDB that contains items read from a table, those items are given in the form of a map, between key names and attribute values. Though the auto type is used here to simplify the code and names of types, the GetItems() call will return an const Aws::Vector<Aws::Map<Aws::String, AttributeValue>>& , a vector of maps, where each map represents one item. For any platform except android, Aws::Vector and Aws::Map are the same as std::vector and std::map , respectively.

If we put together all the code we have so far, compile it, and run it, with a valid set of AWS credentials, you should see the following output on your terminal. This uses the overall repo project located here.

Table: 'schedule' created successfully
Waiting until table is ready....
Waiting until table is ready....
Waiting until table is ready....
Waiting until table is ready....
Waiting until table is ready....
Waiting until table is ready....
Waiting until table is ready....
Waiting until table is ready....
Waiting until table is ready....
Waiting until table is ready....
Scanned a total of 3 items
{event : lunch}
{event : morning}
{event : basketball}
Table "schedule" has been deleted
* Closing connection 0

Overall, DynamoDB is a great option for C++ programmers to create micro services and applications with. It’s scalable, easy to provision, and can be setup to work with existing C++ projects in a very short amount of time.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Josh Weinstein

Josh Weinstein

I’m an engineer on a mission to write the fastest software in the world.