Skip to content

Commit 68b25e0

Browse files
committed
update README.md
1 parent 598fa80 commit 68b25e0

5 files changed

+97
-42
lines changed

README-java-asynchronous-environments-EN.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,21 @@ public class AsyncConfig {
4848
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
4949

5050
// Configure thread pool settings according to your application needs... (skip)
51-
52-
// Set TaskDecorator to copy the request context to the new thread!
51+
5352
executor.setTaskDecorator(task -> {
54-
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
53+
// Get the QueryCountPerRequest from the current thread
54+
QueryCountPerRequest currentQueryCountPerRequest = QueryCountPerRequestHolder.INSTANCE.get();
55+
5556
return () -> {
5657
try {
57-
RequestContextHolder.setRequestAttributes(requestAttributes);
58+
// Propagate the QueryCountPerRequest to the new thread
59+
if (currentQueryCountPerRequest != null) {
60+
QueryCountPerRequestHolder.INSTANCE.set(currentQueryCountPerRequest);
61+
}
5862
task.run();
5963
} finally {
60-
RequestContextHolder.resetRequestAttributes();
64+
// Clear the QueryCountPerRequest after the task is complete
65+
QueryCountPerRequestHolder.INSTANCE.remove();
6166
}
6267
};
6368
});

README-java-asynchronous-environments.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,21 @@ public class AsyncConfig {
4949
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
5050

5151
// 애플리케이션 요구사항에 맞게 스레드 풀 구성... (생략)
52-
53-
// 새 스레드에 요청 컨텍스트를 복사하기 위한 TaskDecorator 설정!
52+
5453
executor.setTaskDecorator(task -> {
55-
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
54+
// 현재 스레드의 QueryCountPerRequest 획득
55+
QueryCountPerRequest currentQueryCountPerRequest = QueryCountPerRequestHolder.INSTANCE.get();
56+
5657
return () -> {
5758
try {
58-
RequestContextHolder.setRequestAttributes(requestAttributes);
59+
// 새로운 스레드에 QueryCountPerRequest 전파
60+
if (currentQueryCountPerRequest != null) {
61+
QueryCountPerRequestHolder.INSTANCE.set(currentQueryCountPerRequest);
62+
}
5963
task.run();
6064
} finally {
61-
RequestContextHolder.resetRequestAttributes();
65+
// 스레드 종료 시 QueryCountPerRequest 제거
66+
QueryCountPerRequestHolder.INSTANCE.remove();
6267
}
6368
};
6469
});

README-kotlin-coroutine-environments-EN.md

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,34 +27,59 @@ Therefore, it is difficult to properly use `QueryCountPerRequest` by default.
2727

2828
## 🖥️ Solution: Coroutine Context Element
2929

30-
### 1. Using CoroutineQueryCountContextElement
30+
### 1. Utilizing CoroutineQueryCountContextElement
3131

32-
`CoroutineQueryCountContextElement` implements the [`ThreadContextElement` interface](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-thread-context-element/) to propagate `RequestAttributes` to the branching Coroutine contexts.
32+
`CoroutineQueryCountContextElement` implements the [`ThreadContextElement` interface](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-thread-context-element/) as a propagating context element.
33+
It propagates the `QueryCountPerRequest` state through the branching Coroutine contexts.
3334

3435
```kotlin
3536
class CoroutineQueryCountContextElement(
36-
private val requestAttributes: RequestAttributes = RequestContextHolder.currentRequestAttributes(),
37-
) : ThreadContextElement<RequestAttributes> {
37+
var queryCountPerRequest: QueryCountPerRequest? = null,
38+
) : ThreadContextElement<QueryCountPerRequest?> {
3839

3940
companion object Key : CoroutineContext.Key<CoroutineQueryCountContextElement>
4041

4142
override val key: CoroutineContext.Key<CoroutineQueryCountContextElement>
4243
get() = Key
4344

44-
override fun updateThreadContext(context: CoroutineContext): RequestAttributes {
45-
RequestContextHolder.setRequestAttributes(requestAttributes)
46-
return requestAttributes
45+
override fun updateThreadContext(context: CoroutineContext): QueryCountPerRequest? {
46+
return queryCountPerRequest
4747
}
4848

49-
override fun restoreThreadContext(context: CoroutineContext, oldState: RequestAttributes) {
50-
RequestContextHolder.setRequestAttributes(oldState)
49+
override fun restoreThreadContext(context: CoroutineContext, oldState: QueryCountPerRequest?) {
50+
queryCountPerRequest = oldState
5151
}
5252
}
5353
```
5454

55-
The actual implementation is included in the library, so you can use it immediately.
55+
### 2. Obtaining QueryCountPerRequest from CoroutineQueryCountContextElement
5656

57-
### 2. Usage Example
57+
Acquire the `QueryCountPerRequest` by calling `CoroutineQueryCountContextElement` through the `QueryCountPerRequestHolder`.
58+
59+
```kotlin
60+
object QueryCountPerRequestHolder {
61+
62+
private val queryCountPerRequestHolder = ThreadLocal<QueryCountPerRequest?>()
63+
private val coroutineContextElement = CoroutineQueryCountContextElement()
64+
65+
fun set(queryCountPerRequest: QueryCountPerRequest) {
66+
queryCountPerRequestHolder.set(queryCountPerRequest)
67+
coroutineContextElement.queryCountPerRequest = queryCountPerRequest
68+
}
69+
70+
fun get(): QueryCountPerRequest? = queryCountPerRequestHolder.get()
71+
?: coroutineContextElement.queryCountPerRequest
72+
73+
fun remove() {
74+
queryCountPerRequestHolder.remove()
75+
coroutineContextElement.queryCountPerRequest = null
76+
}
77+
}
78+
```
79+
80+
### 3. Usage Example
81+
82+
Since it naturally acquires the `QueryCountPerRequest` in its own Coroutine context, no additional code modification is required.
5883

5984
```kotlin
6085
@RestController
@@ -64,10 +89,7 @@ class UserController(
6489
@CountQueries
6590
@GetMapping("/users/coroutine")
6691
fun getUsers() {
67-
// Now this Coroutine and its branching Coroutines maintain the same RequestAttributes
68-
val element = CoroutineQueryCountContextElement()
69-
val threadPool = ForkJoinPool(2)
70-
return runBlocking(threadPool.asCoroutineDispatcher() + element) {
92+
return runBlocking {
7193
val usersDeferred = async {
7294
userRepository.findAllUsers() // Query count point.
7395
}

README-kotlin-coroutine-environments.md

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# 🖥️ Kotlin Coroutine 환경 QueryCountPerRequest 사용 가이드
1+
# 🖥️ Kotlin Coroutine 환경 QueryCountPerRequest 동작 원리 가이드
22

33
**한국어** | [English](README-kotlin-coroutine-environments-EN.md)
44

@@ -9,7 +9,7 @@ Multi-Datasource-Query-Counter 라이브러리는 API 요청별 DB 쿼리 수를
99
그러나 Kotlin Coroutine 환경에서는 Coroutine 이 스레드를 자유롭게 이동(`suspension`, `resumption`)할 수 있기 때문에,
1010
`RequestContextHolder`의 컨텍스트가 유지되지 않는 문제가 발생할 수 있습니다.
1111

12-
이 가이드는 Coroutine 환경에서 `QueryCountPerRequest` 를 올바르게 사용하는 방법을 설명합니다.
12+
이 가이드는 Coroutine 환경에서 `QueryCountPerRequest` 를 올바르게 측정할 수 있는 원리를 설명합니다.
1313

1414
<br>
1515

@@ -29,32 +29,58 @@ Coroutine 은 다음과 같은 특성을 갖습니다.
2929

3030
### 1. CoroutineQueryCountContextElement 활용
3131

32-
`CoroutineQueryCountContextElement`[`ThreadContextElement` 인터페이스](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-thread-context-element/)를 구현하여 분화되는 Coroutine 컨텍스트에 `RequestAttributes` 를 전파합니다.
32+
`CoroutineQueryCountContextElement`[`ThreadContextElement` 인터페이스](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-thread-context-element/)를 구현하여 전파되는 컨텍스트 요소입니다.
33+
분화되는 Coroutine 컨텍스트에 `QueryCountPerRequest` 지닌 상태로 전파됩니다.
3334

3435
```kotlin
3536
class CoroutineQueryCountContextElement(
36-
private val requestAttributes: RequestAttributes = RequestContextHolder.currentRequestAttributes(),
37-
) : ThreadContextElement<RequestAttributes> {
37+
var queryCountPerRequest: QueryCountPerRequest? = null,
38+
) : ThreadContextElement<QueryCountPerRequest?> {
3839

3940
companion object Key : CoroutineContext.Key<CoroutineQueryCountContextElement>
4041

4142
override val key: CoroutineContext.Key<CoroutineQueryCountContextElement>
4243
get() = Key
4344

44-
override fun updateThreadContext(context: CoroutineContext): RequestAttributes {
45-
RequestContextHolder.setRequestAttributes(requestAttributes)
46-
return requestAttributes
45+
override fun updateThreadContext(context: CoroutineContext): QueryCountPerRequest? {
46+
return queryCountPerRequest
4747
}
4848

49-
override fun restoreThreadContext(context: CoroutineContext, oldState: RequestAttributes) {
50-
RequestContextHolder.setRequestAttributes(oldState)
49+
override fun restoreThreadContext(context: CoroutineContext, oldState: QueryCountPerRequest?) {
50+
queryCountPerRequest = oldState
5151
}
5252
}
5353
```
5454

55-
실제 구현체는 라이브러리에 포함되어 있으므로, 곧바로 사용할 수 있습니다.
55+
### 2. CoroutineQueryCountContextElement 로 부터 QueryCountPerRequest 획득
5656

57-
### 2. 사용 예제
57+
`QueryCountPerRequestHolder` 를 통해 `CoroutineQueryCountContextElement` 를 호출하여 `QueryCountPerRequest` 를 획득합니다.
58+
59+
```kotlin
60+
object QueryCountPerRequestHolder {
61+
62+
private val queryCountPerRequestHolder = ThreadLocal<QueryCountPerRequest?>()
63+
private val coroutineContextElement = CoroutineQueryCountContextElement()
64+
65+
fun set(queryCountPerRequest: QueryCountPerRequest) {
66+
queryCountPerRequestHolder.set(queryCountPerRequest)
67+
coroutineContextElement.queryCountPerRequest = queryCountPerRequest
68+
}
69+
70+
fun get(): QueryCountPerRequest? = queryCountPerRequestHolder.get()
71+
?: coroutineContextElement.queryCountPerRequest
72+
73+
fun remove() {
74+
queryCountPerRequestHolder.remove()
75+
coroutineContextElement.queryCountPerRequest = null
76+
}
77+
}
78+
79+
```
80+
81+
### 3. 사용 예제
82+
83+
자신의 Coroutine 컨텍스트에서 `QueryCountPerRequest` 를 자연스럽게 획득하므로, 별도의 코드 수정이 필요 없습니다.
5884

5985
```kotlin
6086
@RestController
@@ -64,10 +90,7 @@ class UserController(
6490
@CountQueries
6591
@GetMapping("/users/coroutine")
6692
fun getUsers() {
67-
// 이제 이 Coroutine 과 분화되는 Coroutine 들은 서로 같은 RequestAttributes 를 유지함
68-
val element = CoroutineQueryCountContextElement()
69-
val threadPool = ForkJoinPool(2)
70-
return runBlocking(threadPool.asCoroutineDispatcher() + element) {
93+
return runBlocking {
7194
val usersDeferred = async {
7295
userRepository.findAllUsers() // 쿼리 카운트 지점.
7396
}

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ ERROR --- 'GET /examples' - totalQueryCount: 2, totalSpendTime: 7ms
118118
## 🖥️ 비동기 환경 지원
119119
120120
- [Java 비동기 환경에 대한 가이드를 읽으려면 여기를 클릭하세요.](README-java-asynchronous-environments.md)
121-
- [Kotlin 코루틴 환경에 대한 가이드를 읽으려면 여기를 클릭하세요.](README-kotlin-coroutine-environments.md)
121+
- [Kotlin 코루틴 환경에 대한 원리를 확인하고 싶다면 여기를 클릭하세요.](README-kotlin-coroutine-environments.md)
122122
123123
<br>
124124

0 commit comments

Comments
 (0)