Perplexity 연구를 사용했습니다.
Singleton, Scoped, Transient을 넘어서는 서비스 수명주기 - 테넌트, 풀링, 드리프터
Andrew Lock의 블로그 글 "Going beyond singleton, scoped, and transient lifetimes"는 .NET Core의 표준 의존성 주입(DI) 수명주기를 넘어서는 새로운 개념적 수명주기에 대해 탐구합니다. 이 글은 The Breakpoint Show 팟캐스트에서 언급된 추가적인 서비스 수명주기 개념을 실제로 구현해보는 과정을 담고 있습니다1.
.NET Core DI 컨테이너의 표준 서비스 수명주기
.NET Core의 의존성 주입 컨테이너는 세 가지 기본 서비스 수명주기를 제공합니다1.
Singleton 서비스
Singleton 서비스는 가장 단순한 수명주기로, 애플리케이션 전체에서 단 한 번만 생성되고 모든 요청에서 동일한 인스턴스가 사용됩니다1.
csharp
// 명시적 인스턴스 제공
builder.Services.AddSingleton(new SingletonClass1());
// 또는 DI 컨테이너가 인스턴스 생성
builder.Services.AddSingleton<SingletonClass2>();
장점:
- 리소스 효율성: 인스턴스가 한 번만 생성됨
- 애플리케이션 전체에서 상태 공유 가능
단점:
- 스레드 안전성을 고려해야 함
- 전역 상태로 인한 테스트 복잡성 증가
Scoped 서비스
Scoped 서비스는 하나의 요청(스코프) 내에서는 동일한 인스턴스가 사용되지만, 다른 요청에서는 새로운 인스턴스가 생성됩니다1. ASP.NET Core에서는 일반적으로 하나의 HTTP 요청이 하나의 스코프를 형성합니다.
csharp
builder.Services.AddScoped<ScopedClass>();
장점:
- 요청별 상태 격리 제공
- Singleton보다 유연하면서도 Transient보다 리소스 효율적
단점:
- 스코프 개념 이해 필요
- 잘못 사용 시 의도치 않은 동작 발생 가능
Transient 서비스
Transient 서비스는 요청할 때마다 새로운 인스턴스가 생성됩니다1. 동일한 요청 내에서도 서비스를 여러 번 요청하면 매번 새로운 인스턴스가 제공됩니다.
csharp
builder.Services.AddTransient<TransientClass>();
장점:
- 완전한 인스턴스 격리 제공
- 상태 관리 단순화
단점:
- 리소스 사용량 증가
- 불필요한 인스턴스 생성으로 성능 저하 가능성
추가적인 서비스 수명주기 개념
The Breakpoint Show 팟캐스트에서는 표준 수명주기를 넘어서는, 세 가지 추가적인 서비스 수명주기 개념이 논의되었습니다1.
Tenant-scoped 서비스
테넌트 스코프 서비스는 멀티테넌트 애플리케이션에서 특히 유용한 개념으로, 각 테넌트별로 “싱글톤” 인스턴스를 제공합니다1. 즉, 전체 애플리케이션에서 공유되는 것이 아니라 특정 테넌트에 대해서만 싱글톤으로 동작합니다.
장점:
- 테넌트 간 서비스 인스턴스 격리 보장
- 테넌트별 최적화 가능
- 리소스 효율성 제공
단점:
- 구현 복잡성 증가
- 테넌트 식별 및 관리 오버헤드
Pooled 서비스
풀링된 서비스는 성능 향상을 위해 서비스 인스턴스의 풀을 관리하는 개념입니다1. Entity Framework Core의 DbContext 풀링 기능에서 영감을 받았으며, 할당(allocation)을 줄여 성능을 개선할 수 있습니다.
장점:
- 메모리 할당 감소로 성능 향상 가능
- 리소스 재사용 최적화
단점:
- 풀에서 객체를 반환할 때 상태 초기화 필요
- 풀 관리의 복잡성
- 모든 상황에서 성능 향상을 보장하지 않음
Time-based(drifter) 서비스
시간 기반 서비스는 일정 시간 동안만 같은 인스턴스를 사용하고, 시간이 지나면 새로운 인스턴스를 생성하는 개념입니다1. 특정 시간 동안은 scoped 서비스처럼 동작하지만, 시간이 초과되면 새 인스턴스가 생성됩니다.
장점:
- 시간 기반 데이터 갱신 메커니즘
- 캐시와 유사한 동작
단점:
- IDisposable 처리 문제 발생 가능
- 여러 인스턴스가 동시에 활성화될 수 있는 상황 발생
- 실제 사용 사례가 제한적
시간 기반 서비스 구현
Andrew Lock은 시간 기반 서비스를 구현하기 위해 TimedDependencyFactory 클래스를 설계했습니다1. 이 구현은 지정된 시간 동안 같은 인스턴스를 반환하고, 시간이 초과되면 새 인스턴스를 생성합니다.
csharp
public static IServiceCollection AddTimed<T>(this IServiceCollection services, TimeSpan lifetime)
where T : class
{
services.AddSingleton(provider => new TimedDependencyFactory<T>(
TimeProvider.System, lifetime, provider));
services.AddScoped(provider => provider
.GetRequiredService<TimedDependencyFactory<T>>()
.GetInstance());
return services;
}
이 구현에는 두 가지 접근 방식이 있습니다1:
- Lazy와 Interlocked.CompareExchange()를 사용한 락 없는 스레드 안전 구현
- lock(_lock)을 사용한 단순화된 구현
그러나 이 구현에는 IDisposable 인터페이스를 구현하는 서비스에 대한 처리 문제와 같은 중요한 제한사항이 있습니다1.
결론
Andrew Lock의 글은 .NET Core의 표준 서비스 수명주기를 넘어서 더 특수한 시나리오에 맞는 수명주기 개념을 탐구합니다1. 이러한 확장된 수명주기 개념은 특정 사용 사례에서 유용할 수 있지만, 구현 복잡성과 제한사항을 고려해야 합니다.
멀티테넌트 애플리케이션에서는 테넌트 스코프 서비스가 실용적인 이점을 제공하며, 고성능이 필요한 시나리오에서는 풀링된 서비스가 도움이 될 수 있습니다1. 시간 기반 서비스는 흥미로운 개념이지만 실제 사용 사례가 제한적이며 구현 시 여러 복잡한 문제를 해결해야 합니다.
표준 및 확장 서비스 수명주기 중 선택할 때는 애플리케이션의 요구사항, 성능 특성, 그리고 개발 및 유지보수 복잡성을 종합적으로 고려해야 합니다1.
Citations:
- Going beyond singleton, scoped, and transient lifetimes—tenant, pooled, and drifter
- https://www.reddit.com/r/csharp/comments/1acwtar/can_someone_explain_when_to_use_singleton_scoped/
- https://www.youtube.com/watch?v=RK7gmONMlVM
- When to Use Transient, Scoped, or Singleton in .NET Apps - Understanding Service Lifetimes - codewithmukesh
- https://www.aoml.noaa.gov/phod/dac/LumpkinPazos.pdf
- Going beyond singleton, scoped, and transient lifetimes—tenant, pooled… | Andrew Lock
- Dependency Injected pooled instances · Issue #48172 · dotnet/aspnetcore · GitHub
- javascript - can setInterval drift over time? - Stack Overflow
- Implementing ASP.NET Identity for a Multi-Tenant Application: Best Practices - DEV Community
- Going beyond singleton, scoped, and transient lifetimes | daily.dev
- 14 to 2 seconds: how I improved the performance of an endpoint by 82% - part 2 | Code4IT
- Resolve scoped services based on current tenant from Blazor - Stack Overflow
- Make HTTP requests using IHttpClientFactory in ASP.NET Core | Microsoft Learn
- Dependency Injection in ASP.NET Core Attributes
- @andrewlock.bsky.social on Bluesky
- https://dotnetkicks.com
- c# - How to Convert RegisterDecorator from Simple Injector to .NET Core's IServiceCollection? - Stack Overflow
- https://x.com/andrewlocknet/status/1914720398110818559
- Going beyond singleton, scoped, and transient lifetimes—tenant, pooled… | Andrew Lock
- https://x.com/andrewlocknet
- Historical Drifter Data and Statistical Prediction of Particle Motion: A Case Study in the Central Adriatic Sea in: Journal of Atmospheric and Oceanic Technology Volume 24 Issue 2 (2007)
- Andrew Lock (@andrewlock@hachyderm.io) - Hachyderm.io
- Multi-tenancy in ASP.NET Core 8 - Dependency Injection & Tenant Specific Services - Michael McKenna
- Pooled Instances — Autofac 7.0.0 documentation
- https://www.joet.org/include/download.php?filedata=3110|KSOE-2023-018.pdf
- Dependency injection in ASP.NET Core | Microsoft Learn
- c# - Dependency Injection - What If Dependency Lifetimes Are Shorter Than The Dependent Object? - Stack Overflow
- https://www.mdpi.com/2077-1312/8/3/205
- Multi-tenant Dependency Injection in ASP.NET Core - Ben Foster
- Andy Butland
- @andrewlock.bsky.social on Bluesky
- Setting Cached Time Base on a function instead of based on a period · TanStack/query · Discussion #7113 · GitHub
- https://x.com/_unaizc_
- Andrew Lock | .NET Escapades
- Local caching | Flutter
- Use PostConfigure For Default Configuration of Collections in ASP.NET
- Time-Based Anti-Patterns for Caching Time-Series Data - ScyllaDB
- c# - Tenant specific containers in ASP.NET Core 6 - Stack Overflow
- Blog Post - ASP.NET Core 3 Things I Learned / Pro Tips / Migration to ASP.NET Core 3
- Going beyond singleton, scoped, and transient lifetimes—tenant, pooled… | Mirco Vanini
- Using SQL Profiler To Identify Repeated SQL Calls
- https://www.alvinashcraft.com