개선(improvement) #2627
Updated by Dante Le 3 months ago
**Parent Issue**: #2310 [BE] Optimize Memory, CPU API search list flight
## Summary
Multiple `deepCopy()` operations throughout the service creating 100-500 KB garbage per request, triggering excessive GC pressure under concurrent load.
## 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 |
## Problem Code Examples
```java
<pre><code class="java">
// 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();
</code></pre> ```
## 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
## Required Fix
### 1. Use Immutable Patterns
Instead of defensive copying, design methods to not modify input:
<pre><code class="java"> ```java
// 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);
</code></pre>
```
### 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);
```
### 3. In-Place Mutation Where Safe
Use ObjectNode mutation when the object is not shared:
<pre><code class="java"> ```java
// If fareJsonNode is owned by this method only
((ObjectNode) fareJsonNode).put("field", value); // No copy needed
</code></pre>
```
### 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?
## 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
## 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
## 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
## Reference
See `docs/flight-list-itinerary/VERIFIED_ISSUES.md` - CRITICAL-3 section for complete analysis.
## 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