DEV Community


Posted on • Originally published at


How I blundered with enum's hashCode

I was implementing an HTTP caching for one of our APIs, based on "ETag" and "If-None-Match" headers. The entity I was trying to cache — Feed — was simple:

data class Feed(
    val deviceId: String,
    val platform: Platform,
    val items: List<FeedItem>

enum class Platform {

data class FeedItem(
    val id: Int
    // Integers and strings
Enter fullscreen mode Exit fullscreen mode

I decided to use hashCode() for ETags.

I knew that both Integer and String have a well-defined and stable hash code: Integer#hashCode() is equal to the primitive value represented by that Integer object, and String#hashCode() uses a known formula. List#hashCode() is stable as well as long as it’s elements have stable hash codes.

yeah, we happy

Of course not, we’re not happy! I forgot to check the details about Enum#hashCode() method.

And here be dragons: enum’s hash code is not stable across different JVM instances. I assumed wrongly that it is calculated as value’s ordinal(), which is Integer, which, we know, has a stable hash code, equal to itself. But it’s not. Enum’s hash code is defined in Java (Kotlin is virtually the same) as:

public final int hashCode() {
    return super.hashCode();
Enter fullscreen mode Exit fullscreen mode

Here, super is an Object, and, as we know, Object's hash code is stable unless the object itself is changed. It’s generally the case with enums: there is only one instance of each of enum’s values per JVM and they are stable. But it’s important to emphasize, that it applies to a single JVM. Enum’s on different JVMs will have different hash codes.

And that’s how our ETags were broken: every time the request landed on a new server, the ETag was different, even if the objects stored in the database were the same. The situation was worsened by the fact that we were using short-lived JVMs (AWS Lambda functions), so the caching was completely broken for a while until I tracked down this issue.


Top comments (0)

An Animated Guide to Node.js Event Loop

Node.js doesn’t stop from running other operations because of Libuv, a C++ library responsible for the event loop and asynchronously handling tasks such as network requests, DNS resolution, file system operations, data encryption, etc.

What happens under the hood when Node.js works on tasks such as database queries? We will explore it by following this piece of code step by step.