Skip to content
Merged

Dev #81

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.teamEWSN.gitdeun.common.fastapi;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.teamEWSN.gitdeun.common.converter.IsoToLocalDateTimeDeserializer;
Expand Down Expand Up @@ -249,7 +250,7 @@ public SuggestionAutoResponse createAutoSuggestions(
}
}

public String getFileRaw(String repoUrl, String filePath, String authHeader) {
/* public String getFileRaw(String repoUrl, String filePath, String authHeader) {
return getFileRaw(repoUrl, filePath, null, null, null, authHeader);
}

Expand Down Expand Up @@ -309,52 +310,50 @@ public String getFileRaw(String repoUrl, String filePath, Integer startLine, Int
log.error("FastAPI 파일 λ‚΄μš© 쑰회 μ‹€νŒ¨ - repoId: {}, filePath: {}", repoId, filePath, e);
return ""; // 빈 λ¬Έμžμ—΄ λ°˜ν™˜ (null λŒ€μ‹ )
}
}
}*/

public String getFileRawFromNode(String nodeKey, String filePath, String authHeader) {
public String getCodeFromNode(String nodeKey, String filePath, String authHeader) {
String fileName = extractFileName(filePath);
try {
log.debug("FastAPI 파일 λ‚΄μš© 쑰회 μ‹œμž‘ - nodeKey: {}, filePath: {}", nodeKey, filePath);
log.debug("FastAPI λ…Έλ“œ 기반 μ½”λ“œ 쑰회 μ‹œμž‘ - nodeKey: {}, filePath: {}", nodeKey, fileName);

// URI 생성 (쿼리 νŒŒλΌλ―Έν„° 포함)
UriComponentsBuilder uriBuilder = UriComponentsBuilder
.fromPath("/content/file/nodes")
.fromPath("/content/file/by-node") // FastAPI의 ν•΄λ‹Ή μ—”λ“œν¬μΈνŠΈ
.queryParam("node_key", nodeKey)
.queryParam("file_path", filePath);
.queryParam("file_path", fileName);
// 'prefer' νŒŒλΌλ―Έν„°λŠ” μƒλž΅ν•˜μ—¬ FastAPI의 κΈ°λ³Έκ°’(auto)을 λ”°λ₯΄λ„둝 함

String uri = uriBuilder.build().toUriString();

String response = webClient.get()
NodeCodeResponse response = webClient.get()
.uri(uri)
.headers(headers -> {
if (authHeader != null && !authHeader.trim().isEmpty()) {
headers.set("Authorization", authHeader);
}
headers.set("Accept", "text/plain");
})
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, clientResponse -> {
log.warn("FastAPI 파일 쑰회 4xx 였λ₯˜ - nodeKey: {}, filePath: {}, status: {}",
nodeKey, filePath, clientResponse.statusCode());
return clientResponse.bodyToMono(String.class)
.map(errorBody -> new RuntimeException("νŒŒμΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€: " + errorBody));
})
.onStatus(HttpStatusCode::is5xxServerError, serverResponse -> {
log.error("FastAPI 파일 쑰회 5xx 였λ₯˜ - nodeKey: {}, filePath: {}, status: {}",
nodeKey, filePath, serverResponse.statusCode());
return Mono.error(new RuntimeException("FastAPI μ„œλ²„ 였λ₯˜"));
})
.bodyToMono(String.class)
.onStatus(HttpStatusCode::is4xxClientError, clientResponse ->
clientResponse.bodyToMono(String.class)
.map(errorBody -> {
log.warn("FastAPI λ…Έλ“œ μ½”λ“œ 쑰회 4xx 였λ₯˜ - nodeKey: {}, fileName: {}, status: {}, body: {}",
nodeKey, fileName, clientResponse.statusCode(), errorBody);
return new RuntimeException("FastAPI ν΄λΌμ΄μ–ΈνŠΈ 였λ₯˜: " + errorBody);
})
)
.bodyToMono(NodeCodeResponse.class)
.timeout(Duration.ofSeconds(30))
.block();

log.debug("FastAPI 파일 λ‚΄μš© 쑰회 μ™„λ£Œ - nodeKey: {}, filePath: {}, 길이: {}",
nodeKey, filePath, response != null ? response.length() : 0);
String codeContent = (response != null) ? response.getCode() : "";
log.debug("FastAPI λ…Έλ“œ 기반 μ½”λ“œ 쑰회 성곡 - nodeKey: {}, fileName: {}, 길이: {}",
nodeKey, fileName, codeContent != null ? codeContent.length() : 0);

return response != null ? response : "";
return codeContent;

} catch (Exception e) {
log.error("FastAPI 파일 λ‚΄μš© 쑰회 μ‹€νŒ¨ - nodeKey: {}, filePath: {}", nodeKey, filePath, e);
return ""; // 빈 λ¬Έμžμ—΄ λ°˜ν™˜ (null λŒ€μ‹ )
log.error("FastAPI λ…Έλ“œ 기반 μ½”λ“œ 쑰회 μ‹€νŒ¨ - nodeKey: {}, fileName: {}", nodeKey, fileName, e);
return ""; // μ˜ˆμ™Έ λ°œμƒ μ‹œ 빈 λ¬Έμžμ—΄ λ°˜ν™˜
}
}

Expand All @@ -366,6 +365,11 @@ private String extractMapId(String repoUrl) {
return segments[segments.length - 1].replaceAll("\\.git$", "");
}

// 파일λͺ… μΆ”μΆœ
public String extractFileName(String filePath) {
return filePath.substring(filePath.lastIndexOf('/') + 1);
}

private AnalysisResultDto buildAnalysisResultDto(
RepoInfoResponse repoInfo
) {
Expand All @@ -388,6 +392,13 @@ private AnalysisResultDto buildRefreshResultDto(

// === Response DTOs ===

@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true) // code ν•„λ“œ μ™Έ λ‹€λ₯Έ ν•„λ“œλŠ” λ¬΄μ‹œ
private static class NodeCodeResponse {
private String code;
}

@Getter
@Setter
public static class FetchResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ public class FileContentCache {

@Component
public static class FileContentL1Cache {
@Cacheable(value = "FILE_CONTENT_L1", key = "#key")
@Cacheable(value = "FILE_CONTENT_L1", key = "#key",
unless = "#result == null || #result.isEmpty()")
public String getFromL1Cache(String key) {
return null;
return null; // 미슀면 null
}

@CachePut(value = "FILE_CONTENT_L1", key = "#key")
@CachePut(value = "FILE_CONTENT_L1", key = "#key",
condition = "#content != null && !#content.isBlank()")
public String cacheToL1(String key, String content) {
return content;
}
Expand All @@ -46,7 +48,6 @@ public void evictL1Cache() {


public String getFileContentWithCacheFromNode(String repoUrl, String nodeKey, String filePath, LocalDateTime lastCommit, String authHeader) {
// μΊμ‹œ 킀에 repoUrl을 ν¬ν•¨μ‹œμΌœ μ–΄λ–€ λ¦¬ν¬μ§€ν† λ¦¬μ˜ μΊμ‹œμΈμ§€ λͺ…ν™•νžˆ ν•©λ‹ˆλ‹€.
String cacheKey = "file-content:" + repoUrl + ":node:" + nodeKey + ":" + filePath + ":" + lastCommit.toString();

// 1. L1 μΊμ‹œ 확인
Expand All @@ -59,23 +60,27 @@ public String getFileContentWithCacheFromNode(String repoUrl, String nodeKey, St
// 2. L2 μΊμ‹œ 확인
try {
content = (String) redisTemplate.opsForValue().get(cacheKey);
if (content != null) {
if (content != null && !content.isBlank()) {
log.debug("파일 λ‚΄μš© L2 μΊμ‹œ 히트 - key: {}", cacheKey);
l1Cache.cacheToL1(cacheKey, content); // L1에 μ €μž₯
return content;
} else if (content != null && content.isBlank()) {
log.warn("L2 μΊμ‹œμ—μ„œ 빈 λ¬Έμžμ—΄ 발견 - key: {} (μ „νŒŒ/재적재 ν•˜μ§€ μ•ŠμŒ)", cacheKey);
// μΊμ‹œμ— 남겨두지 말고 μ¦‰μ‹œ μ‚­μ œ
try { redisTemplate.delete(cacheKey); } catch (Exception ignore) {}
}
} catch (Exception e) {
log.warn("Redis 쑰회 μ‹€νŒ¨, API 직접 호좜 - key: {}", cacheKey, e);
}

// 3. FastAPI μ‹€μ‹œκ°„ 쑰회
content = fastApiClient.getFileRawFromNode(nodeKey, filePath, authHeader);
// 3. FastAPI μ‹€μ‹œκ°„ 쑰회 (μƒˆλ‘œ λ§Œλ“  λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜λ„λ‘ λ³€κ²½)
content = fastApiClient.getCodeFromNode(nodeKey, filePath, authHeader);

// 4. L1, L2 μΊμ‹œμ— μ €μž₯
if (content != null) {
if (content != null && !content.isBlank()) {
l1Cache.cacheToL1(cacheKey, content);
try {
redisTemplate.opsForValue().set(cacheKey, content, Duration.ofHours(2)); // L2: 2μ‹œκ°„
redisTemplate.opsForValue().set(cacheKey, content, Duration.ofHours(2));
} catch (Exception e) {
log.warn("Redis μ €μž₯ μ‹€νŒ¨ - key: {}", cacheKey, e);
}
Expand All @@ -88,7 +93,7 @@ public String getFileContentWithCacheFromNode(String repoUrl, String nodeKey, St
public void evictFileCacheForRepo(String repoUrl) {
// L1 μΊμ‹œλŠ” 전체 μ‚­μ œ
l1Cache.evictL1Cache();
deleteRedisKeysByPattern("file-content:repo:" + repoUrl + ":*");
deleteRedisKeysByPattern("file-content:" + repoUrl + ":*");
}

private void deleteRedisKeysByPattern(String pattern) {
Expand Down