Skip to main content

๐Ÿ” "Billing Gone Wrong: How Lack of Synchronization Led to Swapped Bills (And What I Learned!)"

☕ A Walk Down Memory Lane: My First Bug Nightmare


About 9 years ago, as a fresher, I worked on a billing system for a retail application. The system was accessed both from mobile and web UI, and the actual billing logic was written in stored procedures (SQL-based backend).


Everything looked fine... until customers started reporting weird issues:


๐Ÿ› The Bug: Bills Were Swapping Items! ๐Ÿ˜ฑ

๐Ÿงพ Scenario:

  • Two different users generated separate bills around the same time

  • One from Mobile, the other from Web

  • The output bills had mixed-up items — each invoice had products that belonged to the other one! ๐Ÿงจ

๐Ÿ” Initial Questions:

"Is this a DB issue?"
"Are stored procedures broken?"
"Race condition in app layer?"

Turns out...

๐Ÿšซ The code calling stored procedures wasn’t thread-safe.
There was no synchronized mechanism to ensure each bill transaction was isolated.


๐Ÿง  The Root Cause Analysis (RCA):

We were calling a shared BillingService.calculate() method across different threads, and it wasn’t synchronized. When multiple threads hit the same method at the same time, data leakage occurred between bills.

๐Ÿ“Œ The stored procedure was NOT the problem — the problem was how the Java app was managing calls to it.


๐Ÿคฏ A Doubt I Had Later...

๐Ÿ’ญ "In modern microservices — or even back then — each request runs in its own thread. So why would two requests ever clash? Shouldn’t each request be isolated in memory?"

✅ You're right — each incoming HTTP request is handled by a separate thread. But...

⚠️ Here's What Was Actually Happening:

The Java service class (BillingService) had some shared, mutable fields — like:

public class BillingService {
    private List<Item> items = new ArrayList<>(); // shared state ❌

    public void calculate(BillRequest req) {
        items.clear(); // modifies shared state
        items.addAll(req.getItems());
        billingDao.callStoredProcedure(items);
    }
}

So multiple threads using this shared object were modifying the same list at the same time — causing one bill's data to show up in another. ๐Ÿคฏ


๐Ÿ› ️ How We Fixed It: With synchronized ๐Ÿ’ก

We locked access to the calculate() method so only one thread could execute it at a time:

public class BillingService {
    public synchronized void calculate(BillRequest req) {
        List<Item> items = new ArrayList<>(); // local state ✅
        items.addAll(req.getItems());
        billingDao.callStoredProcedure(items);
    }
}

OR

public class BillingService {
    public void calculate(BillRequest req) {
        synchronized (this) {
            List<Item> items = new ArrayList<>();
            items.addAll(req.getItems());
            billingDao.callStoredProcedure(items);
        }
    }
}

✅ No more shared state.
✅ No more item swapping.
✅ Happy customers.


๐Ÿ” What is synchronized in Java?

synchronized is a Java keyword used to prevent concurrent thread access to a block or method.

It ensures:

  • Only one thread can execute the synchronized code at a time

  • Prevents race conditions and data corruption


๐Ÿงฑ Two Ways to Use synchronized:

1️⃣ Synchronized Method:

public synchronized void processBill(BillRequest req) {
    // entire method is locked
}

2️⃣ Synchronized Block:

public void processBill(BillRequest req) {
    synchronized(this) {
        // only this part is locked
    }
}

๐Ÿ“Œ Block-level is better for performance — it only locks what needs locking.


๐Ÿ”„ Bonus: Should I Use Locks Instead?

Yes, in high-concurrency apps, consider:

  • ReentrantLock

  • ReadWriteLock

  • Optimistic locking in DB (version columns)

But for simple, state-sensitive flows — synchronized works beautifully ๐Ÿ’ฏ


๐Ÿ”š Wrapping Up

This bug taught me a valuable lesson early in my career:

Always think about thread safety — especially when dealing with shared resources like billing engines or inventory systems.

๐Ÿ” Synchronization might seem simple, but when forgotten, it can lead to serious production messes.

Got a wild thread-safety story or billing bug nightmare? Drop it in the comments — I’d love to hear it.


Until next time,
– Anand ☕ @ Java Bean Bag

Comments

Popular posts from this blog

๐Ÿ” Is final Really Final in Java? The Truth May Surprise You ๐Ÿ˜ฒ

๐Ÿ’ฌ “When I was exploring what to do and what not to do in Java, one small keyword caught my eye — final . I thought it meant: locked, sealed, frozen — like my fridge when I forget to defrost it.”   But guess what? Java has its own meaning of final… and it’s not always what you expect! ๐Ÿ˜… Let’s break it down together — with code, questions, confusion, jokes, and everything in between. ๐ŸŽฏ The Confusing Case: You Said It's Final... Then It Changed?! ๐Ÿซ  final List<String> names = new ArrayList <>(); names.add( "Anand" ); names.add( "Rahul" ); System.out.println(names); // [Anand, Rahul] ๐Ÿคฏ Hold on... that’s final , right?! So how on earth is it still changing ? Time to dive deeper... ๐Ÿง  Why Is It Designed Like This? Here’s the key secret: In Java, final applies to the reference , not the object it points to . Let’s decode this like a spy mission ๐Ÿ•ต️‍♂️: Imagine This: final List<String> names = new ArrayList <>(); Be...

๐ŸŒŸ My Journey – From Zero to Senior Java Tech Lead ๐ŸŒŸ

 There’s one thing I truly believe… If I can become a Java developer, then anyone in the world can. ๐Ÿ’ฏ Sounds crazy? Let me take you back. ๐Ÿ•“ Back in 2015… I had zero coding knowledge . Not just that — I had no interest in coding either. But life has its own plans. In 2016, I got a chance to move to Bangalore and joined a Java course at a training center. That’s where it all started — Every day, every session made me feel like: "Ohhh! Even I can be a developer!" That course didn’t just teach Java — it gave me confidence . ๐Ÿงช Two Life-Changing Incidents 1️⃣ The Interview That Wasn't Planned Halfway through my course, I had to urgently travel to Chennai to donate blood to a family member. After that emotional rollercoaster, I found myself reflecting on my skills and the future. The next day, as I was preparing for my move to Bangalore to complete the remaining four months of my course, I randomly thought — "Let me test my skills... let me just see...

๐ŸŽข Java Loops: Fun, Fear, and ForEach() Fails

๐ŸŒ€ Oops, I Looped It Again! — The Ultimate Java Loop Guide You Won't Forget “I remember this question from one of my early interviews — I was just 2 years into Java and the interviewer asked, ‘Which loop do you prefer and why?’” At first, I thought, “Duh! for-each is cleaner.” But then he grilled me with cases where it fails. ๐Ÿ˜ต That led me to explore all loop types, their powers, and their pitfalls. Let’s deep-dive into every major Java loop with examples &  real-world guidance so you'll never forget again. ๐Ÿ” Loop Type #1: Classic For Loop — “The Old Reliable” ✅ When to Use: You need an index You want to iterate in reverse You want full control over loop mechanics ✅ Good Example: List<String> names = List.of("A", "B", "C"); for (int i = 0; i < names.size(); i++) { System.out.println(i + ": " + names.get(i)); } ๐Ÿ”ฅ Reverse + Removal Example: List<String> item...