Project

General

Profile

Actions

개선(improvement) #2627

open

개선(improvement) #2310: [BE] Optimize Memory, CPU API search list flight

[BE] CRITICAL-3: Repeated Deep Copy Operations

Added by Dante Le 3 months ago. Updated 3 months ago.

Status:
신규(New)
Priority:
긴급(Emergency)
Assignee:
Start date:
Due date:
12/15/2025 (about 3 months late)
% Done:

0%

Estimated time:
6.00 h
Part:
Build env.:

Description

Parent Issue: #2310 [BE] Optimize Memory, CPU API search list flight

  1. Summary

Multiple `deepCopy()` operations throughout the service creating 100-500 KB garbage per request, triggering excessive GC pressure under concurrent load.

  1. Location

File: `web-api/src/main/java/com/ohmy/api/service/flight/FlightListItineraryService.java`

All deepCopy() Locations:

Line Context Impact per Call
------ --------- -----------------
585 `alliance.deepCopy()` in triple nested loop 5-10 KB × loop iterations
1162 `fareJsonNode.deepCopy()` in `onewayFaresToFlightsFare()` 2-5 KB per fare
1242 `x.deepCopy()` in stream map 2-5 KB per item
1543, 1548, 1597, 1620 Multiple deepCopy in `getSelectedJsonNodeCombinedFares()` 10-20 KB per call
1683, 1760, 1793 Multiple deepCopy in `getSelectedJsonNodeOnewayFares()` 10-20 KB per call
1999 `flightJsonNode.deepCopy()` in `makeFareRequestBody()` 5-10 KB
2792 `requestBody.get("condition").deepCopy()` in `getListItinerary()` 1-2 KB
  1. Problem Code Examples
    // Line 585: Deep copy in triple nested loop
    alliance.deepCopy()
    
    // Line 1162: Deep copy in fare transformation
    JsonNode copiedFareJsonNode = fareJsonNode.deepCopy();
    
    // Lines 1543-1548: Multiple deep copies
    flightFareJsonNode = flightJsonNode.deepCopy();
    // ... more processing ...
    flightFareJsonNode = flightFareJsonNode.deepCopy();
    
    // Line 2792: Deep copy in request processing
    ObjectNode conditionJsonNode = requestBody.get("condition").deepCopy();
    
  1. Performance Impact

Memory Allocation:
- Total per request: 100-500 KB garbage objects
- Under 50 concurrent requests: 5-25 MB additional garbage/second
- Each deepCopy() recursively copies entire JSON tree structure

GC Pressure:
- Triggers Young GC every 2-3 seconds under load
- Contributes to Full GC cycles when Old Gen fills
- Part of the GC death spiral causing OutOfMemoryError

CPU Impact:
- Object allocation overhead
- Recursive tree traversal for each copy
- GC CPU time increases proportionally

  1. Required Fix
  1. 1. Use Immutable Patterns
    Instead of defensive copying, design methods to not modify input:
    // BEFORE: defensive copy
    JsonNode modified = original.deepCopy();
    ((ObjectNode) modified).put("field", value);
    
    // AFTER: create new node with specific changes
    ObjectNode modified = JsonNodeFactory.instance.objectNode();
    original.fields().forEachRemaining(entry -> {
        if (!entry.getKey().equals("field")) {
            modified.set(entry.getKey(), entry.getValue());
        }
    });
    modified.put("field", value);
    

    ```
  1. 2. Copy Only Modified Fields
    Build new nodes with only changed fields instead of full tree copy:
    ```java
    // BEFORE: full deep copy
    JsonNode copy = fareJsonNode.deepCopy();
    ((ObjectNode) copy).put("itemCode", newValue);

// AFTER: selective copy
ObjectNode fare = JsonNodeFactory.instance.objectNode();
fare.setAll((ObjectNode) fareJsonNode);
fare.put("itemCode", newValue);
```

  1. 3. In-Place Mutation Where Safe
    Use ObjectNode mutation when the object is not shared:
    // If fareJsonNode is owned by this method only
    ((ObjectNode) fareJsonNode).put("field", value);  // No copy needed
    
  1. 4. Analyze Copy Necessity
    Audit each deepCopy() to determine:
    - Is this protecting against mutation that actually occurs?
    - Can we refactor to avoid mutation entirely?
    - Is this node shared across threads or requests?
  1. Acceptance Criteria

MUST maintain 100% identical API response (no logic changes)
✅ Reduce garbage allocation from 100-500 KB to < 50 KB per request
✅ Eliminate deepCopy() in hot paths (triple nested loops)
✅ Keep only deepCopy() calls that are truly necessary for thread safety
✅ Verify GC reduction with profiling tools (VisualVM, JFR)
✅ Measure GC pause time improvement under 50 concurrent requests

  1. Constraints

⚠️ NO LOGIC CHANGES ALLOWED - This is a performance optimization only
- Do not modify calculation results or data transformations
- Do not alter API response structure or values
- Maintain thread safety where required

  1. Related Issues

- #2610 - CRITICAL-1: Excessive JsonNode Traversal (Fixed ✅)
- #2611 - AtomicReference Memory Retention (Fixed ✅)
- #2626 - CRITICAL-2: O(n³) Nested Loop Anti-Pattern
- Part of Phase 3: Major Refactoring in optimization roadmap

  1. Reference

See `docs/flight-list-itinerary/VERIFIED_ISSUES.md` - CRITICAL-3 section for complete analysis.

  1. Testing Requirements

1. Before/after memory profiling with identical request load
2. JFR recording comparing GC activity
3. Response validation: byte-for-byte comparison of API output
4. Load test: 50 concurrent requests, measure GC pause time

Actions

Also available in: Atom PDF

Add picture from clipboard (Maximum size: 50 MB)