Skip to content

test: Phase 1 로드 테스트 관측 결과 정리#33

Merged
ohhalim merged 7 commits into
developfrom
chore/32/phase1-concurrency-load-test
May 18, 2026
Merged

test: Phase 1 로드 테스트 관측 결과 정리#33
ohhalim merged 7 commits into
developfrom
chore/32/phase1-concurrency-load-test

Conversation

@ohhalim
Copy link
Copy Markdown
Owner

@ohhalim ohhalim commented May 18, 2026

Summary

  • 30초 k6 주문/조회 부하 테스트 기준선 결과를 문서화했습니다.
  • Prometheus/Grafana 관측 smoke 결과를 정리했습니다.
  • Grafana 대시보드에서 actuator scrape 요청을 제외하고, 5xx가 없을 때 0으로 표시되도록 개선했습니다.

Test

  • ./gradlew test
  • DURATION=30s ORDER_RATE=10 QUERY_RATE=20 BUYER_COUNT=8 SELLER_COUNT=8 k6 run k6/order-flow-load-test.js
  • Prometheus target up
  • Grafana CoinFlow Overview 대시보드 확인

관련 이슈

closes #32

Summary by CodeRabbit

  • Documentation

    • Added comprehensive test results and expanded test plan documentation covering concurrency, load testing, and observability procedures.
    • Updated README with local execution guidelines, monitoring setup, and testing procedures.
  • New Features

    • Introduced observability stack with pre-configured monitoring dashboards and metrics collection.
  • Tests

    • Added concurrency integration tests validating order matching, concurrent operations, and race condition handling.
    • Added load testing capabilities for performance validation.
  • Chores

    • Updated security and metrics configuration.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

📝 Walkthrough

Walkthrough

This PR adds comprehensive concurrency testing and observability infrastructure to validate Phase 1 core transaction flows. It includes JUnit integration tests for concurrent order scenarios, a k6 load test script, Prometheus/Grafana monitoring stack, and supporting documentation with test results template.

Changes

Concurrency Testing & Observability

Layer / File(s) Summary
Test planning and documentation
.docs/TestPlan.md, .docs/TEST_RESULTS.md, README.md
TestPlan expands with Concurrency (CON-001..005) and Local Load Test sections; TEST_RESULTS template records execution metadata and per-test outcomes; README clarifies test scope (balance safety, no over-fill, stable reads, cancel/fill consistency) and adds k6 execution command.
Concurrency integration test suite
src/test/java/com/coinflow/integration/ConcurrencyIntegrationTest.java, .gitignore
Four JUnit test cases validate concurrent order safety: CON-001 (same-user concurrent BUY within balance), CON-002 (concurrent takers don't over-fill maker), CON-003 (orderbook reads stable under writes), CON-004 (cancel/fill race yields consistent final state). Helpers coordinate concurrent tasks via ExecutorService/CountDownLatch and assert wallet/ledger invariants.
K6 load test script
k6/order-flow-load-test.js
Load test with setup() pre-provisioning test users/deposits, orderCreation scenario (alternating BUY/SELL), queryMix scenario (6-endpoint polling), per-endpoint timing metrics, and HTTP 5xx/latency/failure thresholds.
Application metrics instrumentation
src/main/java/com/coinflow/config/SecurityConfig.java, src/main/resources/application.properties
SecurityConfig exposes /actuator/prometheus unauthenticated; application.properties enables Micrometer HTTP server percentiles/SLO buckets and application tag for aggregation.
Observability stack
docker-compose.yml, observability/prometheus/prometheus.yml, observability/grafana/provisioning/*
Docker Compose adds prometheus (5s scrape from host.docker.internal:8080/actuator/prometheus, service:coinflow label) and grafana (provisioned datasource, CoinFlow overview dashboard with HTTP rate/p95/5xx/JVM/Hikari panels, auto-polling from /var/lib/grafana/dashboards).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • ohhalim/CoinFlow#11: The ConcurrencyIntegrationTest asserts ORDER_LOCK ledger entries and wallet balance effects directly produced by order creation/cancel flows from this PR's OrderService and LedgerType.
  • ohhalim/CoinFlow#21: ConcurrencyIntegrationTest's KRW deposit setup depends on the WalletController.deposit and WalletService.deposit behavior with ledger/error handling from this retrieved PR.

Poem

🐰 Four tests race forth to prove the trades hold true,
No wallet minus, no over-fill brew,
K6 loads the market, Prometheus watches near,
Grafana shows the metrics, crystal clear—
Phase One stands steady, concurrency blessed!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title references Phase 1 load test observation results, which aligns with the documented baseline results and observability setup, though it partially describes the changeset—this is a primarily documentation and observability infrastructure PR, not solely about load test results.
Linked Issues check ✅ Passed All coding requirements from issue #32 are addressed: JUnit concurrency tests (CON_001-004) verify wallet balances, order quantity limits, concurrent orderbook reads, and cancel-vs-fill races; k6 load test script with order creation and query scenarios; local Prometheus/Grafana observability setup; and documentation of test execution and interpretation.
Out of Scope Changes check ✅ Passed All changes align with issue #32 scope: concurrency tests, k6 load testing, local observability infrastructure, and documentation updates are in-scope; Kafka/WebSocket/OutboxPublisher are explicitly excluded and not included.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/32/phase1-concurrency-load-test

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/test/java/com/coinflow/integration/ConcurrencyIntegrationTest.java (1)

196-289: 💤 Low value

Consider simplifying the executor shutdown timeout.

Line 287 uses Duration.ofSeconds(5).toMillis() with TimeUnit.MILLISECONDS, which is correct but verbose. Consider using executor.awaitTermination(5, TimeUnit.SECONDS) directly for better readability.

The same pattern appears at line 435.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/java/com/coinflow/integration/ConcurrencyIntegrationTest.java`
around lines 196 - 289, In CON_003_주문_처리_중_오더북_반복_조회는_안정적으로_응답한다() replace the
verbose awaitTermination call that uses Duration.ofSeconds(5).toMillis() with a
direct seconds-based call for readability; update the
executor.awaitTermination(...) invocation in the finally block to use
executor.awaitTermination(5, TimeUnit.SECONDS) (and apply the same change for
the similar call referenced at the other occurrence) so the timeout is passed
directly as seconds rather than converting a Duration to milliseconds.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.docs/TEST_RESULTS.md:
- Line 19: Update the TEST_RESULTS.md to clarify mismatched dates: identify the
"common environment" date entry and the "k6" and "observability" sections and
either align the common environment date to 2026-05-18 or add a short note
explaining that the common environment entry reflects a different run
(2026-05-16) while the k6 and observability sections reflect the later
comprehensive run (2026-05-18); make the change around the table header labeled
"Date" and the k6/observability section headings so readers understand which
date applies to which test results.

In `@docker-compose.yml`:
- Around line 54-55: Remove the insecure fallback defaults for Grafana admin
credentials by eliminating the ":-admin" defaults for GF_SECURITY_ADMIN_USER and
GF_SECURITY_ADMIN_PASSWORD in the docker-compose environment block (the two
environment keys GF_SECURITY_ADMIN_USER and GF_SECURITY_ADMIN_PASSWORD). Change
them to require explicit environment values (e.g. use ${GRAFANA_ADMIN_USER} and
${GRAFANA_ADMIN_PASSWORD} with no defaults), and update any deployment docs or
.env.example to show secure values so the variables are always set explicitly.
Ensure no other code relies on the old defaults before removing them.

In `@src/main/java/com/coinflow/config/SecurityConfig.java`:
- Line 50: The current SecurityConfig exposes /actuator/prometheus publicly;
update the HttpSecurity configuration (in SecurityConfig, e.g., the method that
builds the SecurityFilterChain or configure(HttpSecurity http)) so that
"/actuator/health" and "/actuator/info" remain permitAll() but
"/actuator/prometheus" is not permitAll — instead require authenticated() (or
add a JWT authentication requirement) or apply an IP/network restriction via
hasIpAddress(...) or a custom request matcher; adjust the requestMatchers call
accordingly and ensure any authentication entry point or JWT filter used by the
application (the same one wired by SecurityConfig) will protect the prometheus
endpoint.

---

Nitpick comments:
In `@src/test/java/com/coinflow/integration/ConcurrencyIntegrationTest.java`:
- Around line 196-289: In CON_003_주문_처리_중_오더북_반복_조회는_안정적으로_응답한다() replace the
verbose awaitTermination call that uses Duration.ofSeconds(5).toMillis() with a
direct seconds-based call for readability; update the
executor.awaitTermination(...) invocation in the finally block to use
executor.awaitTermination(5, TimeUnit.SECONDS) (and apply the same change for
the similar call referenced at the other occurrence) so the timeout is passed
directly as seconds rather than converting a Duration to milliseconds.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9ccfa1c0-0ae6-4c08-96a3-08018d8630dd

📥 Commits

Reviewing files that changed from the base of the PR and between 185d903 and ec466b7.

📒 Files selected for processing (15)
  • .docs/TEST_RESULTS.md
  • .docs/TestPlan.md
  • .gitignore
  • README.md
  • docker-compose.yml
  • k6/order-flow-load-test.js
  • observability/grafana/dashboards/coinflow-overview.json
  • observability/grafana/provisioning/alerting/alerting.yml
  • observability/grafana/provisioning/dashboards/dashboards.yml
  • observability/grafana/provisioning/datasources/prometheus.yml
  • observability/grafana/provisioning/plugins/plugins.yml
  • observability/prometheus/prometheus.yml
  • src/main/java/com/coinflow/config/SecurityConfig.java
  • src/main/resources/application.properties
  • src/test/java/com/coinflow/integration/ConcurrencyIntegrationTest.java

Comment thread .docs/TEST_RESULTS.md

| 항목 | 값 |
|---|---|
| Date | 2026-05-16 |
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Consider clarifying the relationship between test run dates.

The common environment date (2026-05-16) differs from the k6 and observability test dates (2026-05-18). If these are separate runs, consider adding a note explaining which sections use which environment, or updating the common environment date to match the latest comprehensive run.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.docs/TEST_RESULTS.md at line 19, Update the TEST_RESULTS.md to clarify
mismatched dates: identify the "common environment" date entry and the "k6" and
"observability" sections and either align the common environment date to
2026-05-18 or add a short note explaining that the common environment entry
reflects a different run (2026-05-16) while the k6 and observability sections
reflect the later comprehensive run (2026-05-18); make the change around the
table header labeled "Date" and the k6/observability section headings so readers
understand which date applies to which test results.

Comment thread docker-compose.yml
Comment on lines +54 to +55
GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER:-admin}
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:-admin}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

# First, find and examine the docker-compose.yml file
find . -name "docker-compose.yml" -type f

Repository: ohhalim/CoinFlow

Length of output: 81


🏁 Script executed:

# Read the docker-compose.yml file to see the actual content
cat -n docker-compose.yml | head -60

Repository: ohhalim/CoinFlow

Length of output: 2475


🌐 Web query:

docker-compose environment variable expansion syntax ${VAR?message} support

💡 Result:

Docker Compose supports the ${VAR?message} syntax for environment variable interpolation [1][2]. This syntax is used to enforce that a required variable is set and non-empty; if the variable is unset or empty, Docker Compose will exit with an error message containing the specified message [1][3]. The following variations of this syntax are supported: - ${VAR:?message}: Exits with an error if VAR is unset or empty [1][2]. - ${VAR?message}: Exits with an error only if VAR is unset [1][2]. If the variable is set and non-empty (or set, depending on the syntax used), Compose substitutes the value of the variable [1][4]. This feature is part of the standard variable interpolation supported in Compose files, which follows a Bash-like syntax [1][2]. For cases where you need to use a literal dollar sign in your configuration (e.g., to pass a variable to the container's shell rather than having Compose interpolate it), you should use the double-dollar sign ($$) syntax [1][5].

Citations:


🏁 Script executed:

# Check if there's an .env file or env_file configuration
ls -la | grep -E "\.env|env_file"
cat docker-compose.yml | grep -A 5 -B 5 "env_file"

Repository: ohhalim/CoinFlow

Length of output: 42


🏁 Script executed:

# Check for .env files in the repository
find . -name ".env*" -type f
git ls-files | grep -E "\.env"

Repository: ohhalim/CoinFlow

Length of output: 42


🏁 Script executed:

# Check the full docker-compose.yml for any env_file references or comments about environment setup
cat docker-compose.yml | grep -i "env"

Repository: ohhalim/CoinFlow

Length of output: 111


🏁 Script executed:

# Look for any documentation about setting up environment variables
find . -name "README*" -o -name "SETUP*" -o -name "INSTALL*" | head -5

Repository: ohhalim/CoinFlow

Length of output: 72


🏁 Script executed:

# Read README to check for environment setup instructions
cat README.md

Repository: ohhalim/CoinFlow

Length of output: 4548


🏁 Script executed:

# Check if there's documentation about default credentials or security
grep -r "admin" . --include="*.md" --include="*.txt" 2>/dev/null | head -20

Repository: ohhalim/CoinFlow

Length of output: 314


Remove insecure default Grafana admin credentials.

admin/admin fallback defaults are risky and frequently leaked into shared environments. Require explicit env vars instead of permissive defaults.

Suggested change
-      GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER:-admin}
-      GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:-admin}
+      GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER?set GRAFANA_ADMIN_USER}
+      GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD?set GRAFANA_ADMIN_PASSWORD}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER:-admin}
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:-admin}
GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER?set GRAFANA_ADMIN_USER}
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD?set GRAFANA_ADMIN_PASSWORD}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose.yml` around lines 54 - 55, Remove the insecure fallback
defaults for Grafana admin credentials by eliminating the ":-admin" defaults for
GF_SECURITY_ADMIN_USER and GF_SECURITY_ADMIN_PASSWORD in the docker-compose
environment block (the two environment keys GF_SECURITY_ADMIN_USER and
GF_SECURITY_ADMIN_PASSWORD). Change them to require explicit environment values
(e.g. use ${GRAFANA_ADMIN_USER} and ${GRAFANA_ADMIN_PASSWORD} with no defaults),
and update any deployment docs or .env.example to show secure values so the
variables are always set explicitly. Ensure no other code relies on the old
defaults before removing them.

.requestMatchers("/api/v1/auth/signup", "/api/v1/auth/login").permitAll()
.requestMatchers("/api/v1/markets", "/api/v1/markets/*/orderbook", "/api/v1/markets/*/trades").permitAll()
.requestMatchers("/actuator/health", "/actuator/info").permitAll()
.requestMatchers("/actuator/health", "/actuator/info", "/actuator/prometheus").permitAll()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

# First, let's verify the file exists and check the relevant code
ls -la src/main/java/com/coinflow/config/SecurityConfig.java

Repository: ohhalim/CoinFlow

Length of output: 163


🏁 Script executed:

# Get the content around line 50 to see the full context
cat -n src/main/java/com/coinflow/config/SecurityConfig.java | sed -n '40,60p'

Repository: ohhalim/CoinFlow

Length of output: 1316


🏁 Script executed:

# Check if there are any other references to actuator or prometheus in the codebase
rg -i "prometheus|actuator" --type java -B 2 -A 2

Repository: ohhalim/CoinFlow

Length of output: 1554


Restrict /actuator/prometheus access to authenticated users or internal networks.

Line 50 exposes detailed runtime and system metrics without authentication, including JVM statistics, request latencies, and application-specific metrics that can enable reconnaissance. Keep /actuator/health and /actuator/info public if needed for monitoring, but protect /actuator/prometheus via authentication (e.g., JWT token requirement) or restrict it to internal network access only.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/coinflow/config/SecurityConfig.java` at line 50, The
current SecurityConfig exposes /actuator/prometheus publicly; update the
HttpSecurity configuration (in SecurityConfig, e.g., the method that builds the
SecurityFilterChain or configure(HttpSecurity http)) so that "/actuator/health"
and "/actuator/info" remain permitAll() but "/actuator/prometheus" is not
permitAll — instead require authenticated() (or add a JWT authentication
requirement) or apply an IP/network restriction via hasIpAddress(...) or a
custom request matcher; adjust the requestMatchers call accordingly and ensure
any authentication entry point or JWT filter used by the application (the same
one wired by SecurityConfig) will protect the prometheus endpoint.

@ohhalim ohhalim merged commit c40cd48 into develop May 18, 2026
2 checks passed
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.

[chore]: Phase 1 주문 흐름 동시성 및 k6 부하 테스트 추가

1 participant