ETags demystified
From overlooked to essential path for improved performance
In the fast-paced world of technology development, certain foundational elements often get overlooked, and ETags are no exception. At Upstox, we realized the importance of ETags as a caching implementation that could significantly improve our service’s efficiency. In this article, we’ll take you through our journey of evaluating and implementing ETags, demonstrating how this seemingly simple addition made a remarkable impact on our platform.
Understanding ETags
Before diving into our experience, let’s briefly understand what ETags are and how they work. ETags, short for Entity Tags, serve as unique identifiers for resources on a web server. When a client requests a resource, the server responds with both the resource and an ETag identifying its current version. The client can then locally cache this resource along with its associated ETag. In subsequent requests, the client includes the ETag in the headers. If the resource hasn’t changed on the server, the server responds with a 304 Not Modified
status, saving bandwidth and reducing network costs. If there are changes, the server sends the updated resource.
Identifying the need for ETags
Our journey began by identifying which parts of our platform could benefit the most from ETag implementation. We analyzed the usage patterns of various entities and compared the frequency of read operations to write operations. It became evident that several entities had a high rate of read usage compared to writes, making them prime candidates for ETag integration. Additionally, we examined the bandwidth statistics for the corresponding entity APIs, confirming that adding ETags would significantly reduce bandwidth usage and associated network costs.
In addition to ETags, we also evaluated CDN caching, frontend caching, and used them for various use-cases, but that’s a topic for another article. This holistic approach to optimizing our caching strategies allowed us to achieve even greater efficiency and performance across our platform.
Below is a report on the bandwidth usage of an API following the implementation of ETag. As you’ll see, there’s a noticeable uptick in the 304
response trend, accompanied by a corresponding decrease in data transfer.
With our target entities identified, the next challenge was to integrate ETags into our existing services and frontends. Upstox primarily uses Java and Node.js for backend services, relying on the Spring framework for Java projects and Express for Node.js projects — both of which offer robust support for ETags.
However, during this process, an interesting point of discussion arose. In RESTful APIs, to determine if a resource has changed, the service must first fetch the latest state of the resource, compute the ETag for it, and compare it with the requesting ETag. If this operation is not optimized, the advantages of adding ETags may be diminished. While ETags help save bandwidth and prevent unnecessary UI refreshes, we explored whether there was more we could do for APIs where obtaining the latest resource state was not perfectly optimized.
Two options emerged: generating ETags by hashing the resource content or maintaining/persisting resource versions. While resource versions could provide a solution, implementing them into an existing ecosystem posed challenges. Another option was to revisit and optimize the respective APIs or introduce caching layers to speed up resource retrieval. However, this task could be time-consuming and would vary for each service or API.
A Creative Twist: Stateful ETags
These challenges led us to an experiment: What if we made ETags stateful? What if we cached ETags in Redis and introduced pre and post middlewares to abstract ETag evaluation? With this line of thinking, we developed two libraries in Java and Node.js.
In the stateful flow, the pre-middleware compares the requesting ETag with the cached ETag. If they match, it resolves the API with a 304 Not Modified
response. In the post-middleware, a new ETag is generated, saved in Redis, and attached to the response header. We ensured that our libraries were highly configurable, allowing each service to choose between stateful and stateless ETag flows. We also implemented a circuit breaker mechanism to fallback to the standard stateless flow if issues arose in the stateful flow.
It’s essential to note that the Stateful ETags approach we adopted has specific use-case limitations. There are guidelines documented on when and how to consider this approach. For instance, it would be inappropriate to use Stateful ETags if the API response is created by aggregating or combining data from multiple services. In such cases, the cache invalidation process becomes complex when resources are updated.
Conclusion
In conclusion, the addition of ETags to our services at Upstox has proven to be a game-changer. By carefully evaluating our needs and implementing stateful ETags with Redis caching, we were able to optimize resource retrieval, reduce bandwidth usage, save network cost and enhance overall caching efficiency. This journey demonstrates that even seemingly small improvements can have a substantial impact on the performance and cost-effectiveness of a tech stack. So, while everyone may be writing about AI and other cutting-edge technologies, let’s not forget to celebrate the simple yet powerful solutions like ETags that continue to shape the world of web development.