Skip to content

Commit 2df7f0b

Browse files
committed
perf(pipeline): UsecasePipelineBase 핸들러명 추출 결과 generic static 캐싱
GetRequestHandler/GetRequestHandlerLower/GetRequestHandlerPath은 매 요청 hot path에서 호출되지만 결과는 generic 인자 TRequest별 고정값. 매번 3회 Regex 매칭 + string indexing 재실행 중이었음. UsecasePipelineBase<TRequest>가 이미 generic class라 .NET 런타임이 인자별 별도 static 영역을 자동 분리. ConcurrentDictionary 같은 동적 lookup 자료구조는 불필요 — static readonly field 패턴으로 lookup 0, 타입별 boundedness 자연 보장, 24/365 가동에서 메모리 누수 없음 (TRequest 종류는 컴파일 타임 결정, 100 타입 기준 약 30KB 상수 누적). 변경: - _cachedHandlerPath, _cachedHandler, _cachedHandlerLower 3개 static readonly field 추가 - GetRequestHandler*는 캐시 참조로 단순화 (lookup 0) - 기존 본문은 ComputeRequestHandler private 메서드로 이전 - 보너스: ToLower() → ToLowerInvariant() (culture-aware bug 위험 제거) 검증: Functorium.slnx 빌드 0 오류, 1554/1582 테스트 통과(28 skip). Public API 영향 0 (protected 메서드 시그니처 동일). 후속(별도 평가): GetRequestCategoryType 두 오버로드는 TRequest와 다른 타입을 받을 수 있어 generic static field 부적합. 호출 빈도 측정 후 판단.
1 parent 93d9647 commit 2df7f0b

1 file changed

Lines changed: 20 additions & 14 deletions

File tree

Src/Functorium.Adapters/Pipelines/UsecasePipelineBase.cs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,17 @@ protected static string GetRequestCategoryType(Type requestType)
9292
return ObservabilityNaming.CategoryTypes.Unknown;
9393
}
9494

95-
protected static string GetRequestHandlerPath()
96-
{
97-
return typeof(TRequest).FullName!;
98-
}
95+
// ----------------------------------------
96+
// Generic 인자(TRequest)별 static 캐시.
97+
// .NET 런타임은 generic 인자별로 별도 static 영역을 할당하므로
98+
// ConcurrentDictionary 같은 동적 lookup 자료구조 없이도 자연스럽게
99+
// 타입별 boundedness가 보장되고 lookup 비용이 0입니다.
100+
// ----------------------------------------
101+
private static readonly string _cachedHandlerPath = typeof(TRequest).FullName ?? string.Empty;
102+
private static readonly string _cachedHandler = ComputeRequestHandler();
103+
private static readonly string _cachedHandlerLower = _cachedHandler.ToLowerInvariant();
104+
105+
protected static string GetRequestHandlerPath() => _cachedHandlerPath;
99106

100107
// - "+"가 존재할 경우
101108
// - 가장 첫 번째 "+" 기준으로 처리
@@ -115,7 +122,15 @@ protected static string GetRequestHandlerPath()
115122
// `domain.name` | `name` | `"+"` 없음 → 마지막 `"."` 이후
116123
// `no.plus.or.dot` | `dot` | `"+"` 없음 → 마지막 `"."` 이후
117124
// `no_dot_or_plus` | (빈 문자열) | `"."`, `"+"` 모두 없음
118-
protected static string GetRequestHandler()
125+
protected static string GetRequestHandler() => _cachedHandler;
126+
127+
/// <summary>
128+
/// GetRequestHandler()의 소문자 버전.
129+
/// 메트릭 네이밍 등 소문자가 필요한 경우 사용합니다.
130+
/// </summary>
131+
protected static string GetRequestHandlerLower() => _cachedHandlerLower;
132+
133+
private static string ComputeRequestHandler()
119134
{
120135
string input = typeof(TRequest).FullName!;
121136

@@ -146,15 +161,6 @@ protected static string GetRequestHandler()
146161
return string.Empty;
147162
}
148163

149-
/// <summary>
150-
/// GetRequestHandler()의 소문자 버전.
151-
/// 메트릭 네이밍 등 소문자가 필요한 경우 사용합니다.
152-
/// </summary>
153-
protected static string GetRequestHandlerLower()
154-
{
155-
return GetRequestHandler().ToLower();
156-
}
157-
158164
/// <summary>
159165
/// 에러에서 타입과 코드 정보를 추출합니다.
160166
/// </summary>

0 commit comments

Comments
 (0)