Why You Should Limit Lombok Use: A Live Payment Incident Exposes Idempotency Risks

A midnight payment outage caused a user to be charged three times because a Lombok‑generated @Data class ignored the superclass requestId in its equals method, breaking idempotency checks; the article explains the root cause, reproduces the bug, and offers concrete guidelines for safe Lombok usage.

Coder Life Journal
Coder Life Journal
Coder Life Journal
Why You Should Limit Lombok Use: A Live Payment Incident Exposes Idempotency Risks

Incident Overview

At 02:17 AM the payment service crashed, a user (User A) was charged three times, the system showed a refund but the account balance was short 6000 CNY.

Root Cause Discovery

Scanning the logs revealed a @Data annotation on the RefundRequest class. Lombok’s @Data generates getters, setters, equals(), hashCode() and toString(), which is handy for simple CRUD classes.

Problem with Inheritance and Idempotency

The class hierarchy is:

public class BaseRequest implements Serializable {
    private String requestId; // unique per request, used for idempotency
    private long timestamp;
}

@Data
public class RefundRequest extends BaseRequest {
    private String orderId;
    private BigDecimal amount;
    private String reason;
}

Lombok’s generated equals() only compares the three fields declared in RefundRequest and, by default, @EqualsAndHashCode(callSuper = false), it ignores requestId. The idempotency check uses a Set<RefundRequest> that relies on equals() and hashCode():

Set<RefundRequest> refundCache = new HashSet<>();
if (!refundCache.add(request)) {
    log.info("Duplicate refund request, ignore");
    return;
}

Two refund requests with identical orderId and amount but different requestId (“req‑001” and “req‑002”) are considered equal, so the second request is silently dropped, leading to a double charge.

Empirical Test

A small benchmark compared hand‑written code with Lombok‑generated code under four scenarios:

Inheritance equals : hand‑written super.equals() works; @Data does not – you need @EqualsAndHashCode(callSuper=true).

Serialization compatibility : hand‑written explicit serialVersionUID stays stable; Lombok lets the JVM compute it, which changes when fields or Lombok version change and can break deserialization.

Debug line numbers : without Lombok the stack trace points to your source; with Lombok it points to generated code, adding a debugging step.

Compile‑time errors : Lombok catches some mistakes at compile time, but others surface only at runtime.

Test environment: JDK 11, Spring Boot 2.7, 1 000 000 requests, 50 threads.

Conclusion

Combining inheritance, @Data, and a Set for deduplication destroys idempotency.

Why It Happens

Lombok’s default equals() and hashCode() ignore superclass fields; serialVersionUID is auto‑generated and can change with class modifications; generated code shifts stack‑trace line numbers.

Practical Guidelines

If a class participates in inheritance or idempotency checks, write equals(), hashCode() and serialVersionUID manually.

If Lombok must be used, prefer @Getter and @Setter instead of @Data, or add @EqualsAndHashCode(callSuper=true) when superclass fields are needed.

Always declare a constant serialVersionUID for serializable classes.

Reserve Lombok for simple POJOs, DTOs or VO objects that are not part of core business logic.

Future work will explore alternative idempotency strategies.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaserializationSpring BootidempotencyLombokequals and hashCode
Coder Life Journal
Written by

Coder Life Journal

An ordinary programmer sharing tech and life.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.