#.NET & Architecture

Before buying a bigger server, check what the app keeps doing twice

Zulu Shack illustration for “Before buying a bigger server, check what the app keeps doing twice”: storage jar and loop arrow

Sometimes an application needs more capacity. Sometimes it keeps asking the same expensive question and looks exhausted by the answers.

Before buying a larger server, I’d check for the second situation.

Caching stores a result so later requests can reuse it instead of repeating the original work. Milan Jovanovic’s June 2024 ASP.NET Core guide provides a useful practical introduction.1 The business decision is which results can safely be reused, for whom and for how long.

Those last questions matter. A cached service description and a cached claim that one seat remains available do not carry the same consequences.

Picture a fictional supplier’s portal. Every page rebuilds the same navigation and repeatedly reads a largely unchanged product category structure. Reusing suitable results could remove unnecessary work.

Now picture the same portal reusing a previous customer’s account-specific prices. That’s not an optimization. That’s a support call with excellent response time.

The cache key and scope need to reflect the information’s boundaries, including user or account differences where relevant. Permission-sensitive responses deserve particular care.

ASP.NET Core supports in-memory and distributed caching approaches. A result stored inside one web process isn’t automatically shared with another. A shared cache can help multiple instances use common results, while adding another service to operate.

Choose according to the actual deployment. The label Distributed in an interface name isn’t evidence that the configured implementation lives outside the application.

When off-the-shelf stops fitting, see how we approach custom development.

Agree on how old is too old

A cache needs a freshness strategy. A time limit can expire a result. An application can also invalidate or replace relevant entries when data changes. The right combination depends on the consequence of stale information.

For marketing content, a modest delay may be acceptable. For stock, availability or standings, decide which views may be approximate and which action must check authoritative state.

A booking page can display recently retrieved availability and still validate capacity when committing the booking. Concurrency protection, such as the approaches discussed in Jovanovic’s locking work, addresses that final decision.3 A cache doesn’t reserve the seat out of gratitude for being fast.

Also test what happens when many requests miss the cache together. Rebuilding an expensive result repeatedly can create the very burst of work the cache was meant to prevent.

Measure before and after using representative activity. Include the cost of storing, refreshing and operating the cache, not only the best-case lookup.

Pairs well with The page can wait a second. The customer should not have to.

.NET 8’s runtime improvements can also help suitable workloads, as Stephen Toub explains through specific benchmarks.2 Doing repeated work faster and avoiding unnecessary work are different opportunities. Neither should be assumed to solve a delay the team hasn’t located.

Start with one expensive, frequently repeated read whose reuse rules are clear. Check correctness after changes and observe the misses as well as the hits.

If the result improves the journey and reduces unnecessary load, lovely. You may still need a bigger server later, but it will be carrying useful work rather than repeatedly rediscovering the same navigation menu.

The best performance improvement is occasionally a very polite reminder that we already know the answer.

Turn reading into results

Like what you’re reading? Let’s put it to work on your brand.