Saturday, August 30, 2025

Cache in front of a slow database ?

 

Should You Front a Slow Database with a Cache?

Most of us have been there: a slow database query is dragging down response times, dashboards are red, and someone says, “Let’s put Redis in front of it.”

I have done it myself for an advertising system that needed response times of less than 30 ms. It worked very well.

It’s a tried-and-true trick. Caching can take a query that costs hundreds of milliseconds and make it return in single-digit milliseconds. It reduces load on your database and makes your system feel “snappy.” But caching isn’t free — it introduces its own problems that engineers need to be very deliberate about.




Good Use Cases for Caching

  • Read-heavy workloads
    When the same data is read far more often than it’s written. For example, product catalogs, user profiles, or static metadata.

  • Expensive computations
    Search queries, aggregated analytics, or personalized recommendations where computing results on the fly is costly.

  • Burst traffic
    Handling sudden spikes (sales events, sports highlights, viral posts) where the database alone cannot keep up.

  • Low latency requirements
    Some systems have low latency requirements. Clients need a response is say less than 50 ms or client aborts.


The Catch: Cache Consistency

The hardest part of caching isn’t adding Redis or Memcached — it’s keeping the cache in sync with the database.

Here are the main consistency issues you’ll face:

  1. Stale Data
    If the cache isn’t updated when the database changes, users may see outdated results.
    Example: A user updates their shipping address, but the checkout flow still shows the old one because it’s cached.

  2. Cache Invalidation
    The classic hard problem: When do you expire cache entries? Too soon → database load spikes. Too late → users see stale values.

  3. Race Conditions
    Writes may hit the database while another process is still serving old cache data. Without careful ordering, you risk “losing” updates.


Common Strategies

  • Cache Aside (Lazy Loading)
    Application checks cache → if miss, fetch from DB → populate cache.
    ✅ Simple, common.
    ❌ Risk of stale data unless you also invalidate on updates.

  • Write-Through
    Writes always go through the cache → cache updates DB.
    ✅ Consistency is better.
    ❌ Higher write latency, more complexity.

  • Write-Behind
    Writes update the cache, and DB updates happen asynchronously.
    ✅ Fast writes.
    ❌ Risk of data loss if cache fails before DB is updated.

  • Time-to-Live (TTL)
    Expire cache entries after a set period.
    ✅ Easy safety net.
    ❌ Not precise; stale reads possible until expiry.


So, Is It Worth It?

If your workload is read-heavy, latency-sensitive, and relatively tolerant of eventual consistency, caching is usually a big win.

But if your workload is write-heavy or requires strict consistency (think payments, inventory, or medical records), caching can create more problems than it solves.

The lesson: don’t add Redis or Memcached just because they’re shiny tools. Add them because you’ve carefully measured your system, know where the bottleneck is, and can live with the consistency trade-offs.


Takeaway:
Caching is like nitrous oxide for your system — it can make things blazing fast, but you need to handle it with care or you’ll blow the engine.

Thursday, August 28, 2025

The Unsung Heroes Behind Your AI Coding Assistant

While everyone's talking about ChatGPT and tools like Cursor, Windsurf, and GitHub Copilot transforming how we code, let's shine a light on the specialized models that actually power these coding experiences.


Meet the Code Generation Champions:


  • StarCoder - Trained on 80+ programming languages from GitHub repos, this open-source model excels at code completion and generation

  • CodeT5 - Google's encoder-decoder model that understands code structure and can translate between languages

  • InCoder - Meta's bidirectional model that can fill in code gaps, not just complete from left to right

  • CodeGen - Salesforce's autoregressive model trained on both natural language and code

  • Codex (OpenAI) - The foundation behind GitHub Copilot, though now evolved into GPT-4 variants


What makes these different from general LLMs?

  • Trained on massive code repositories (billions of lines)
  • Understand syntax, semantics, and programming patterns
  • Can maintain context across entire codebases
  • Specialized in code-specific tasks like debugging, refactoring, and documentation


The magic isn't just in having "AI that codes" - it's in having models that truly understand the intricacies of software development. They aren’t just regurgitating text—they’re tuned for the nuances of programming, which makes them invaluable for developers. These specialized architectures are why your AI assistant can suggest that perfect function name or catch that subtle bug you've been hunting for hours.


The real game-changer? Most of these models are open-source, democratizing access to powerful coding assistance beyond just the big tech companies.

Sunday, August 24, 2025

JDK 21 Virtual threads: The end of regular threads ? Not quite.

 A question I get asked all the time: If JDK 21 supports virtual threads, do I ever need to use regular threads ?

Java 21 brought us virtual threads, a game-changer for writing highly concurrent applications. Their lightweight nature and massive scalability are incredibly appealing. It's natural to wonder: do we even need regular platform (OS) threads anymore?

While virtual threads are fantastic for many I/O-bound workloads, there are still scenarios where platform threads remain relevant. Here's why:

1. CPU-Bound Tasks:

Virtual threads yield the carrier thread when they perform blocking I/O operations. However, for purely CPU-bound tasks, they don't offer a significant advantage over platform threads in terms of raw processing power. In fact, the context switching involved might introduce a tiny bit of overhead.

Consider a computationally intensive task like calculating factorials:

Virtual threads example:


// A CPU-intensive task
Runnable cpuBoundTask = () -> {
    long result = 1;
    for (int i = 1; i <= 10000; i++) {
        result *= i;
    }
    System.out.println("Virtual thread task finished.");
};

// Start a virtual thread for the task
Thread.startVirtualThread(cpuBoundTask);


Platform threads example:

Runnable cpuBoundTask = () -> {
    long result = 1;
    for (int i = 1; i <= 10000; i++) {
        result *= i;
    }
    System.out.println("Platform thread task finished.");
};

// Start a regular platform thread
new Thread(cpuBoundTask).start();

For sustained CPU-bound work, managing a smaller pool of platform threads might still be a more efficient approach to leverage the underlying hardware.

2. Integration with Native Code and External Libraries:

Some native libraries or older Java APIs might have specific requirements or behaviors when used with threads. Virtual threads, being a newer abstraction, might not be fully compatible or optimally performant with all such integrations. Platform threads, being closer to the operating system's threading model, often provide better compatibility in these scenarios.

3. Thread-Local Variables with Care:

While virtual threads support thread-local variables, their potentially large number can lead to increased memory consumption if thread-locals are heavily used and store significant data. With platform threads, you typically have a smaller, more controlled number of threads, making it easier to reason about thread-local usage. However, it's crucial to manage thread-locals carefully in both models to avoid memory leaks.

4. Profiling and Debugging:

The tooling around thread analysis and debugging is more mature for platform threads. While support for virtual threads is rapidly improving, there might be cases where existing profiling tools offer more in-depth insights for platform threads.

5. Backward compatibility

If you want you library or server to be available to users who are on JDKs earlier than JDK21, then you have no choice but to use regular threads. Virtual threads are not just a new library; they are a fundamental change to the Java Virtual Machine's threading model (part of Project Loom). The underlying code that manages and schedules virtual threads on top of carrier threads is not present in older JVMs. This can be one of the most important reasons for using platform threads.

In Conclusion:

Virtual threads are a powerful addition to the Java concurrency landscape and will undoubtedly become the default choice for many concurrent applications, especially those with high I/O. However, platform threads still have their place, particularly for CPU-bound tasks, legacy integrations, and situations requiring fine-grained control over thread management.

Understanding the nuances of both models will allow you to make informed decisions and build more efficient and robust Java applications.