January 12, 2023 - 3 Min Read

Database caching: Improved hit rates courtesy of DynamoDB Streams

We used DynamoDB Streams to make Momento Cache even better.

Ellery Addington-White


Cache invalidation is a tough problem! It’s made more painful because most cache invalidation is done via good intentions—i.e. non-deterministically. By default, most caches don’t even require TTLs (time to live), leading to latent and hard-to-diagnose bugs caused by stale values. There are two techniques that can meaningfully add rigor to your stack.

  1. Require TTLs as part of each PUT—this is something Momento Cache has by default. It encourages developers to think hard about how much staleness they can tolerate and internalize that they may in fact get stale values.
  2. The best invalidations are the ones that happen automatically and reliably. This blog covers a simple approach to use our favorite feature of DynamoDB (Streams) to automatically invalidate and pre-fetch the latest entries from DynamoDB!
If you just want to dive right in, check out the full demo repo.

Let’s make database caching easier

All it takes is five lines of code to integrate Momento in your application—but we’re always driving to make it even smoother! Using our middleware for AWS V3 JavaScript SDK, you can add Momento Cache seamlessly to your DynamoDB stack with just one line of code: 

import {NewCachingMiddleware} from '@gomomento-poc/aws-cache-helpers';

const authToken = 'REPLACE_ME';
const db = DynamoDBDocumentClient.from(new DynamoDBClient({}));
    tableName: 'my-ddb-table',
    momentoAuthToken: authToken,
    defaultCacheTtl: 86400,
    cacheName: 'default'

This integrates Momento Cache with every GetItem call across your application. It’s the closest thing to a serverless DynamoDB Accelerator (DAX) but without the DAX price tag or operational burden. 2 lines of code can help you drop your DynamoDB p999 latencies by 60%. But we knew we could still do better! 

Dun da da daaa! Cache invalidation. If items in DynamoDB get updated (from anywhere), we can proactively erase stale values or replace them with the latest value from DynamoDB automatically—with DynamoDB Streams!

Now let’s make database caching better with DynamoDB Streams

DynamoDB Streams is an ordered transactional log of the database. If you create, update, or delete any item in DynamoDB, you will see the event in DynamoDB Streams. This is a powerful tool that you can use to make materialized views or to hydrate your cache. 

Using DynamoDB Streams to improve your caching is a known pattern—but we wanted you to try it without having to write the code! 

We added a component called Stream-Cacher to our simple architecture that will handle these stream events and proactively update Momento Severless Cache. 

So we’re going from a simple read-aside cache which looks like this:

A simple read-aside cache flow chart featuring Momento Serverless Cache and Amazon DynamoDB.

To an architecture like the following, with our Stream-Cacher Lambda added in: 

A flow chart showing Stream-Cacher Lambda, Momento Serverless Cache, DynamoDB Stream and Amazon DynamoDB.

With our aws-cache-helpers library it ends up being very simple to enable this. You can enable it by just dropping in the Momento NewStreamCacheHandler as the main handler for your DynamoDB stream lambda.

import {NewStreamCacheHandler} from '@gomomento-poc/aws-cache-helpers';

const authToken = 'REPLACE_ME';
export const handler = NewStreamCacheHandler({
  tableName: 'my-ddb-table',
  momentoAuthToken: authToken,
  defaultCacheTtl: 86400,
  cacheName: 'default' 

There you have it: near-real time cache updates with very minimal effort thanks to DynamoDB Streams (and Momento Cache 😊). I have the full demo of this setup in this repo, which is just building on top of this simple AWS CDK DDB CRUD example.

It’s super easy to quickly spin up in your own AWS account. Tweet at me (@elleryaw) with any feedback and let me know if you try it out!