[Memoize] Part 1: Request-level Cache for Spring applications. The WHY and the HOW.

James Tran
4 min readJul 26, 2019

I. Introduction

This is a 3-part series in which I will discuss the Memoization technique within the context of Spring. In this article, I will discuss the WHY and the HOW of this amazing technique. In the next article, I will show you different patterns to apply this technique to vastly improve the performance and maintainability of your application. Finally, in the last article, I will give you the tool to analyze your current code base as well as your future code to find out exactly where to apply this technique to maximize ROI for your efforts.

  • Part 1: The WHY and the HOW.
  • Part 2: Practical repeatable patterns. (in progress)
  • Part 3: Code analysis to identify bottleneck. (in progress)

II. The HOW

In case you’ve already nailed down your WHY thanks to some specific requirements in your application, I will first dive into the implementation details of this technique. Technically, it’s as easy as pie but as the old saying goes — true genius lies in simplicity.

In order to fully realize this feature, we need to complete 3 tasks.

  1. Define a custom class to distinguish different method calls within a single HTTP request.
  2. Define a cache to store the calculated results and perform lookup in subsequent invocations.
  3. Define a marker annotation to declare which methods or classes should be memoized and a corresponding AOP Aspect to intercept all calls and return the cached results if available.

Without further ado, let’s dive into the code! The 1st task is relatively simple. We will rely on Apache Commons Lang to implement equals and hashCode.

IMPORTANT: If you plan to memoize a method with Object class parameters, don’t forget to implement equals() and hashCode() in your classes.

The 2nd task is not difficult as well as long as we take proper care of the corner cases for NULL and NONE. Since the cache is put in @RequestScope, it will be purged immediately once the request is completed, wasting no valuable resources of our servers.

Finally, at the heart of this technique is the AOP Aspect for intercepting method calls. We will allow annotating on both class and method levels to support different practical patterns, which will be discussed in more details in Part 2 of this series.

To activate the cache, simply add @Memoize to your classes or methods as shown below.

III. The WHY

Caching is a popular solution to a wide sort of performance issues. While Spring Cache Abstraction is the most obvious option, it tends to be an overkill as we don’t always need to cache results beyond the scope of a single request. In addition, we’d have to configure cache manager, cache regions, manage application-level cache updates and evictions which take time to implement properly and also take up server resources unnecessarily.

If that sounds like cracking a nut with a sledgehammer to you, it actually is. Fortunately, we now have the simple Memoization technique in our tool bag which can be fully implemented with only 3 classes.

In case that is not appealing enough to you, by caching results of method calls at request-level, this technique will also eliminate the needs for passing down arguments to those methods with 13 parameters that make your eyes sore every time you see them. For example, consider the following scenario where you’re creating an Invoice for a Customer.

All is good until you also need to create a Shipping Label for the given order in the same request. One possible approach is to create a higher level entry method which looks for the Customer record and pass it down to child methods for creating Invoice and Shipping Label.

While this approach is easy and straight-forward, it comes with some obvious as well as subtle drawbacks. For instance, as your application grows, the number of parameters of a method may keep growing as the logic gets more complicated. In addition, both child methods are now relying on other methods to provide them with the Customer record. As a consequence, they may become increasingly error-prone and difficult to reuse over time.

While those might not look like serious problems to you due to the simplicity of the example, I have personally seen many code base in which there are duplicate methods with only tiny differences in the signature.

Instead, with the superpower of Memoization, my suggestion is to memoize the result of customerRepo.findByUsername() to make your code more concise.

In case you didn’t notice, all of your methods are now perfectly independent. They require less inputs from higher level methods and are much easier to test as well as to compose to satisfy new business requirements.

If you’re familiar with React then you knew how the Context system helps child components gain access to the some properties exposed by the parent without having to manually pass down via attributes. In our case, we have a Context system in steroid where a method just need to be invoked once anywhere within our application and for the duration of that request, the result will be available to all other callers. Besides, as soon as the request is completed, the cache is discarded immediately to free up resources for other requests. How neat is that!

IV. To be continued…

In this article, I’ve shown you how to fully implement this technique in Spring applications with minimal efforts. In addition, I’ve also discussed about the benefits of Memoization with a simple use case. Still, a technique is only as good as the hands that apply it. To help you realize the full potentials of Memoization, I will lay out a number of practical repeatable patterns in the very next article. Read on…

--

--