Skip to content

Conversation

ykhfree
Copy link
Contributor

@ykhfree ykhfree commented Oct 6, 2025

No description provided.

@ykhfree ykhfree requested a review from Copilot October 6, 2025 13:47
@ykhfree ykhfree self-assigned this Oct 6, 2025
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR addresses critical issues identified in issue #42 by implementing comprehensive fixes across the codebase. The changes focus on improving Docker port management, test reliability, and code formatting while maintaining functionality.

  • Replaces fixed port bindings with dynamic port allocation in Redis Testcontainers to prevent conflicts
  • Adds conditional test execution using environment variables to make Redis tests optional
  • Improves code formatting and error message clarity throughout the codebase

Reviewed Changes

Copilot reviewed 39 out of 39 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
support/src/testFixtures/kotlin/com/linecorp/cse/reqshield/support/redis/RedisContainer.kt Replaces fixed port binding with dynamic port exposure for Redis container
support/src/testFixtures/kotlin/com/linecorp/cse/reqshield/support/redis/AbstractRedisTest.kt Adds environment variable condition to control Redis test execution
Multiple test files Adds @EnabledIfEnvironmentVariable annotations to make Redis integration tests conditional
Multiple core modules Improves code formatting, error messages, and lock implementation fixes
README.md / AGENTS.md Adds documentation for testing practices and repository guidelines

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +166 to +183
val scheduled: ScheduledFuture<*> =
executor.scheduleAtFixedRate({
if (future.isDone) {
return@scheduleAtFixedRate
}
}, GET_CACHE_INTERVAL_MILLIS, TimeUnit.MILLISECONDS)

val scheduleFuture = schedule()
val funcResult = executeGetCacheFunction(cacheGetter, key)
if (funcResult != null) {
future.complete(funcResult.value)
return@scheduleAtFixedRate
}

if (counter.incrementAndGet() >= reqShieldConfig.maxAttemptGetCache) {
future.complete(executeCallable({ callable.call() }, false))
}
}, GET_CACHE_INTERVAL_MILLIS, GET_CACHE_INTERVAL_MILLIS, TimeUnit.MILLISECONDS)

future.whenComplete { _, _ -> scheduleFuture.cancel(false) }
future.whenComplete { _, _ -> scheduled.cancel(false) }
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change from schedule() to scheduleAtFixedRate() creates a potential memory leak. If the future completes quickly, the scheduled task will continue running at fixed intervals indefinitely until manually cancelled, consuming unnecessary resources.

Copilot uses AI. Check for mistakes.

Comment on lines +68 to +72
val before = lockMap.size
lockMap.entries.removeIf { now > it.value.expiresAt }
val after = lockMap.size
if (log.isTraceEnabled && before > after) {
log.trace("Cleaned up {} expired locks, {} remaining", before - after, after)
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cleanup logic captures before and after sizes but only uses them for logging. Consider moving the size calculations inside the if (log.isTraceEnabled) block to avoid unnecessary operations when trace logging is disabled.

Suggested change
val before = lockMap.size
lockMap.entries.removeIf { now > it.value.expiresAt }
val after = lockMap.size
if (log.isTraceEnabled && before > after) {
log.trace("Cleaned up {} expired locks, {} remaining", before - after, after)
val removed = lockMap.entries.removeIf { now > it.value.expiresAt }
if (log.isTraceEnabled) {
val before = lockMap.size + (if (removed) 1 else 0) // Approximate, since removeIf returns true if any were removed
val after = lockMap.size
if (before > after) {
log.trace("Cleaned up {} expired locks, {} remaining", before - after, after)
}

Copilot uses AI. Check for mistakes.

Comment on lines +73 to +89
return when (annotation.nullHandling) {
com.linecorp.cse.reqshield.spring.webflux.annotation.NullHandling.EMIT_EMPTY ->
resultMono.flatMap { value ->
if (value == null) {
Mono.empty()
} else {
Mono.just(value)
}
}
com.linecorp.cse.reqshield.spring.webflux.annotation.NullHandling.ERROR ->
resultMono.flatMap { value ->
if (value == null) {
Mono.error(IllegalStateException("ReqShieldCacheable returned null for key=$cacheKey"))
} else {
Mono.just(value)
}
}
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The null handling logic is duplicated across both enum cases. Consider extracting this into a helper method or using switchIfEmpty() and handle() operators to reduce code duplication.

Suggested change
return when (annotation.nullHandling) {
com.linecorp.cse.reqshield.spring.webflux.annotation.NullHandling.EMIT_EMPTY ->
resultMono.flatMap { value ->
if (value == null) {
Mono.empty()
} else {
Mono.just(value)
}
}
com.linecorp.cse.reqshield.spring.webflux.annotation.NullHandling.ERROR ->
resultMono.flatMap { value ->
if (value == null) {
Mono.error(IllegalStateException("ReqShieldCacheable returned null for key=$cacheKey"))
} else {
Mono.just(value)
}
}
return resultMono.handle { value, sink ->
when (annotation.nullHandling) {
com.linecorp.cse.reqshield.spring.webflux.annotation.NullHandling.EMIT_EMPTY -> {
if (value != null) sink.next(value)
// else emit nothing (empty)
}
com.linecorp.cse.reqshield.spring.webflux.annotation.NullHandling.ERROR -> {
if (value == null) sink.error(IllegalStateException("ReqShieldCacheable returned null for key=$cacheKey"))
else sink.next(value)
}
}

Copilot uses AI. Check for mistakes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant