Optionaleasy0-2 years

A profile endpoint reads `Optional<Profile> maybe = cache.find(id); return maybe.orElse(repository.loadAndCache(id));`. The cache hit rate is 97%, but the database shows a query on every single request, and it's writing to the cache on every one too. What's actually happening, and what's the fix?

orElse is an ordinary method, and Java evaluates a method's arguments before the call happens — always, no matter what the receiver turns out to be. So maybe.orElse(repository.loadAndCache(id)) runs loadAndCache(id) first, every time, and only afterward checks whether maybe actually had a value; on a cache hit, the freshly loaded result is simply thrown away. orElseGet fixes this because it doesn't take a value, it takes a Supplier — a piece of behaviour, not a computed answer — and only calls that supplier when the Optional is actually empty. The rule that avoids the bug going forward: orElse for something already computed or free (a constant, a cached instance), orElseGet for anything that does real work, especially a method call.

The lesson behind it →