Skip to content

[Perf] Vector 저장 TPS 및 JDBC Batch Size 비교 - #156

Merged
Gimini-3 merged 5 commits into
developfrom
feature/155
Aug 12, 2026
Merged

[Perf] Vector 저장 TPS 및 JDBC Batch Size 비교#156
Gimini-3 merged 5 commits into
developfrom
feature/155

Conversation

@Gimini-3

@Gimini-3 Gimini-3 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

🔍️ 작업 내용

  • Closes Vector 저장 TPS 및 JDBC Batch Size 비교 지원 추가 구현 #155
  • PostgreSQL 17 + pgvector 0.8.1에서 Vector 저장 규모·JDBC Batch Size별 TPS를 비교합니다.
  • 일반 테스트와 분리된 vectorStoragePerformanceTest 작업을 추가합니다.
  • Row 수·1024차원·HNSW·Batch 실행 횟수와 JDBC 실패 결과를 자동 검증합니다.
  • 설계, 12개 Profile 원본 JSON과 해석 문서를 함께 기록합니다.

✨ 상세 설명

비교 조건

  • 저장 건수: 1,000 / 10,000 / 100,000
  • Batch Size: 1 / 100 / 500 / 1,000
  • Vector: 정규화된 1024차원 Vector
  • Index: HNSW cosine, m=16, ef_construction=64
  • 측정 경계: 첫 Bind 직전부터 HNSW 온라인 갱신과 Commit 완료까지

주요 결과

  • 100,000건 Batch 1: 1,130.06 TPS
  • 100,000건 Batch 1,000: 1,631.31 TPS
  • TPS 44.36% 향상
  • 저장 시간 83.22초 → 60.59초
  • Batch 호출 100,000회 → 100회

작은 1,000건에서는 Batch 100이 가장 빨랐으며, 결과만으로 제품 기본값을 변경하지 않고 실제 Pipeline과 공식 OpenSQL에서 다시 검증하도록 한계를 명시했습니다.

✅ 검증

  • DB_SSLMODE=disable ./gradlew vectorStoragePerformanceTest -Dvector.storage.performance.sizes=1000 -Dvector.storage.performance.batch-sizes=1,100 -Dvector.storage.performance.measured-runs=1
  • DB_SSLMODE=disable JWT_SECRET=<test-only-value> ./gradlew test
  • git diff --check origin/develop...HEAD
  • 실제 전체 측정: 12개 Profile, Profile별 2회
  • 실패·부분 저장·차원 오류 없음

🛠️ 추후 리팩토링 및 고도화 계획

  • 공식 OpenSQL 환경에서 같은 Profile 재측정
  • 실제 Chunk·Embedding Pipeline의 메모리와 Transaction 시간을 포함한 Batch 기본값 검증
  • 반복 횟수를 늘린 안정적인 백분위 측정

💬 리뷰 요구사항

  • HNSW 온라인 갱신과 Commit을 포함한 측정 경계가 적절한지
  • 원본 JSON과 결과 해석에서 운영 SLO로 오해할 표현이 없는지
  • 전용 성능 Tag가 일반 회귀 테스트에서 올바르게 분리되는지

Summary by CodeRabbit

  • 새로운 기능

    • PostgreSQL pgvector의 벡터 저장 성능을 측정하는 전용 벤치마크 작업을 추가했습니다.
    • 데이터 규모와 JDBC Batch Size별 실행 시간, 처리량, 저장 공간 등의 결과를 확인할 수 있습니다.
  • 문서

    • 벤치마크 설계, 실행 방법, 검증 기준 및 재현 절차를 문서화했습니다.
    • PostgreSQL 17.8과 pgvector 0.8.1 환경의 측정 결과를 추가했습니다.
  • 테스트

    • 벤치마크 설정과 결과 계산 로직에 대한 자동 검증을 추가했습니다.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Gimini-3, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 52 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7ff58912-1e73-46f1-a9c0-3c747e5756f1

📥 Commits

Reviewing files that changed from the base of the PR and between 7c8a02d and 7dc31e7.

📒 Files selected for processing (2)
  • docs/design/gimin-#155-vector-storage-batch-benchmark.md
  • docs/test-results/gimin-#155-vector-storage-batch-benchmark.md
📝 Walkthrough

Walkthrough

PostgreSQL pgvector 저장 benchmark를 추가했습니다. 1,000·10,000·100,000행과 JDBC Batch Size 1·100·500·1,000을 측정하고, HNSW 갱신·Commit·저장 공간·검증 결과를 JSON과 문서로 기록합니다.

Changes

Vector storage benchmark

Layer / File(s) Summary
Benchmark 설정과 결과 계약
src/test/java/com/opensource/docgrid/domain/embedding/benchmark/VectorStoragePerformanceBenchmark.java
시스템 속성으로 profile과 출력 경로를 설정합니다. 측정 통계와 JSON 보고서 구조를 정의합니다. 입력값과 측정 표본을 검증합니다.
pgvector 저장 측정 흐름
src/test/java/com/opensource/docgrid/domain/embedding/benchmark/VectorStoragePerformanceBenchmark.java
격리 schema와 HNSW Probe Table을 생성합니다. 고정 vector pool을 사용해 Batch 삽입부터 Commit까지 측정합니다. 행 수, Batch 호출 수, vector 차원과 저장 공간을 검증합니다.
전용 Gradle 실행과 단위 검증
build.gradle, src/test/java/com/opensource/docgrid/domain/embedding/benchmark/VectorStoragePerformanceBenchmarkTest.java
일반 test 태스크에서 성능 태그를 제외합니다. 전용 vectorStoragePerformanceTest 태스크를 추가합니다. profile 순환, Batch 계산, TPS와 설정 검증을 테스트합니다.
Benchmark 설계와 측정 결과
docs/design/gimin-vector-storage-batch-benchmark.md, docs/test-results/gimin-vector-storage-batch-benchmark.md, docs/test-results/gimin-vector-storage-batch-benchmark-data.json
측정 기준과 재현 절차를 문서화합니다. PostgreSQL 17·pgvector 0.8.1의 12개 profile 결과와 검증 데이터를 기록합니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Developer
  participant Gradle
  participant VectorStoragePerformanceBenchmark
  participant PostgreSQL
  participant JSONReport
  Developer->>Gradle: Run vectorStoragePerformanceTest
  Gradle->>VectorStoragePerformanceBenchmark: Pass system properties
  VectorStoragePerformanceBenchmark->>PostgreSQL: Create isolated schema and HNSW table
  VectorStoragePerformanceBenchmark->>PostgreSQL: Insert vectors with JDBC batches and commit
  PostgreSQL-->>VectorStoragePerformanceBenchmark: Return validation and storage metrics
  VectorStoragePerformanceBenchmark->>JSONReport: Write benchmark results
Loading

Possibly related PRs

  • DocGrid/backend#62: 시스템 속성, warm-up, 반복 profile과 결과 문서를 사용하는 성능 benchmark 패턴이 유사합니다.
  • DocGrid/backend#140: 전용 Gradle task, 제외 태그, 결과 파일과 캐시 비활성화 구성이 유사합니다.
  • DocGrid/backend#98: PostgreSQL 17·pgvector 환경과 benchmark 검증 변경의 코드 연결이 있습니다.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed 제목은 Vector 저장 TPS와 JDBC Batch Size 비교라는 PR의 핵심 변경을 간결하게 설명합니다.
Description check ✅ Passed 필수 섹션과 작업 내용, 검증 결과, 향후 계획, 리뷰 요구사항을 대부분 충실하게 작성했습니다.
Linked Issues check ✅ Passed [#155]의 12개 Profile 비교, 측정 경계, 자동 검증, 전용 Gradle 작업, 결과 기록 요구사항을 변경 사항이 충족합니다.
Out of Scope Changes check ✅ Passed 변경 사항은 [#155]의 성능 벤치마크 구현, 검증 테스트, 설계 문서 및 결과 기록 범위에 포함됩니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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 feature/155

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
src/test/java/com/opensource/docgrid/domain/embedding/benchmark/VectorStoragePerformanceBenchmark.java (3)

275-292: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Vector 문자열이 float4 정밀도보다 큰 텍스트를 만들며 Pool 메모리도 커집니다.

values[index] / normfloat / double 연산이므로 결과가 double입니다. StringBuilder.append(double)는 최대 17자리 유효숫자를 씁니다. pgvector vector 타입은 float4로 저장하므로 이 자리수는 저장 정확도에 기여하지 않습니다.

두 가지 영향이 있습니다.

  • 측정 영향: Bind 페이로드가 필요보다 커져 TPS 측정에 편향을 줍니다. 측정 경계가 첫 Bind부터이므로 페이로드 크기는 결과에 직접 반영됩니다.
  • 메모리 영향: 기본 vector-pool-size가 1,024이고 Vector 하나가 약 20K자에 이릅니다. Pool 전체가 수십 MB 힙을 차지합니다.

정규화 결과를 float로 축약하면 두 문제를 함께 줄입니다.

♻️ 제안 수정
-        double norm = Math.sqrt(squaredNorm);
-        StringBuilder vector = new StringBuilder(VECTOR_DIMENSION * 13).append('[');
+        float norm = (float) Math.sqrt(squaredNorm);
+        StringBuilder vector = new StringBuilder(VECTOR_DIMENSION * 13).append('[');
         for (int index = 0; index < VECTOR_DIMENSION; index++) {
             if (index > 0) {
                 vector.append(',');
             }
-            vector.append(values[index] / norm);
+            vector.append(values[index] / norm);
         }

normfloat로 바꾸면 나눗셈 결과가 float가 되고, 출력 자리수가 float4 정밀도에 맞춰집니다.

🤖 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/opensource/docgrid/domain/embedding/benchmark/VectorStoragePerformanceBenchmark.java`
around lines 275 - 292, Update normalizedVector so the computed norm is stored
as a float before dividing the float values, ensuring values[index] / norm uses
float arithmetic and StringBuilder.append emits float4-appropriate precision.
Keep the existing normalization and vector formatting behavior unchanged.

71-79: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

테스트용 JWT 비밀값을 코드에 직접 넣지 않는 방식을 검토하세요.

Line 74는 jwt.secret 값을 Java 코드에 리터럴로 넣습니다. 값 자체는 테스트 전용으로 보이지만, 코딩 가이드라인은 설정 값에 비밀값을 하드코딩하지 않도록 요구합니다. 다른 테스트가 사용하는 공통 테스트 프로퍼티 소스나 환경 변수로 옮기면 규칙과 일치합니다.

가이드라인 근거: "Do not hardcode secrets in application.yml or other application configuration files."

🤖 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/opensource/docgrid/domain/embedding/benchmark/VectorStoragePerformanceBenchmark.java`
around lines 71 - 79, Move the jwt.secret value out of the Java literal in
configureEnvironment and source it from the shared test property source or an
environment variable, while preserving the existing test configuration behavior.

Source: Coding guidelines


150-160: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

저장 공간 측정을 반복 Loop 밖으로 옮기면 의도가 분명해집니다.

Line 158~159는 매 Round마다 pg_table_sizepg_relation_size를 조회하지만, 마지막 Round 값만 남습니다. 각 Round는 동일한 Row 수를 넣으므로 결과 값도 사실상 같습니다. 조회를 Loop 종료 뒤 한 번만 수행하면 불필요한 왕복을 없애고 "최종 Round 기준 저장 공간"이라는 의미를 코드에 드러냅니다.

♻️ 제안 수정
         List<RoundMeasurement> rounds = new ArrayList<>();
-        long tableBytes = 0L;
-        long indexBytes = 0L;
         for (int round = 1; round <= configuration.measuredRuns(); round++) {
             truncateProbeTable();
             RoundMeasurement measurement = insertVectors(rowCount, batchSize, vectorPool);
             assertStoredVectors(rowCount);
             rounds.add(measurement);
-            tableBytes = tableSize(PROBE_TABLE);
-            indexBytes = relationSize(HNSW_INDEX);
         }
+        long tableBytes = tableSize(PROBE_TABLE);
+        long indexBytes = relationSize(HNSW_INDEX);
🤖 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/opensource/docgrid/domain/embedding/benchmark/VectorStoragePerformanceBenchmark.java`
around lines 150 - 160, Move the tableSize(PROBE_TABLE) and
relationSize(HNSW_INDEX) calls out of the measured-runs loop and execute them
once after the loop completes. Preserve the existing final storage-size values
while making them explicitly represent the completed final round.
🤖 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/design/gimin-vector-storage-batch-benchmark.md`:
- Line 1: Use the actual linked issue number, without guessing, to rename both
benchmark documents to the {github-id}-#{issue-number}-{description}.md
convention. Update the design document’s result-document reference at lines
115-116 and the test-results document’s design-document reference at line 6;
affected files are docs/design/gimin-vector-storage-batch-benchmark.md (line 1)
and docs/test-results/gimin-vector-storage-batch-benchmark.md (line 1).

In `@docs/test-results/gimin-vector-storage-batch-benchmark.md`:
- Around line 40-56: 문서의 벤치마크 표에서 p50 TPS와 p50 시간을 동일한 Round의 값처럼 제시하지 않도록
정리하십시오. TimingSummary.from(...)이 두 표본을 독립적으로 요약한다는 전제를 명시하고 두 열이 서로 다른 Round일 수
있음을 설명하거나, 대표 Round를 사용하려면 같은 Round의 Duration과 TPS를 함께 기록하도록 표와 해설을 수정하십시오. 선택한
해석에 맞게 TPS = rowCount / durationSeconds 재계산이 일관되도록 모든 관련 수치를 갱신하십시오.

---

Nitpick comments:
In
`@src/test/java/com/opensource/docgrid/domain/embedding/benchmark/VectorStoragePerformanceBenchmark.java`:
- Around line 275-292: Update normalizedVector so the computed norm is stored as
a float before dividing the float values, ensuring values[index] / norm uses
float arithmetic and StringBuilder.append emits float4-appropriate precision.
Keep the existing normalization and vector formatting behavior unchanged.
- Around line 71-79: Move the jwt.secret value out of the Java literal in
configureEnvironment and source it from the shared test property source or an
environment variable, while preserving the existing test configuration behavior.
- Around line 150-160: Move the tableSize(PROBE_TABLE) and
relationSize(HNSW_INDEX) calls out of the measured-runs loop and execute them
once after the loop completes. Preserve the existing final storage-size values
while making them explicitly represent the completed final round.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5136044c-9339-4d1f-8876-e924403eee13

📥 Commits

Reviewing files that changed from the base of the PR and between 2e45c49 and 7c8a02d.

📒 Files selected for processing (6)
  • build.gradle
  • docs/design/gimin-vector-storage-batch-benchmark.md
  • docs/test-results/gimin-vector-storage-batch-benchmark-data.json
  • docs/test-results/gimin-vector-storage-batch-benchmark.md
  • src/test/java/com/opensource/docgrid/domain/embedding/benchmark/VectorStoragePerformanceBenchmark.java
  • src/test/java/com/opensource/docgrid/domain/embedding/benchmark/VectorStoragePerformanceBenchmarkTest.java

@@ -0,0 +1,124 @@
# Vector 저장 TPS·Batch Size 비교 Benchmark 설계

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

문서 파일명에 실제 이슈 번호를 포함하십시오.

제공된 문맥에는 연결된 이슈 번호가 없으므로 번호를 추측하지 마십시오. 실제 이슈 번호를 사용해 두 파일을 {github아이디}-#{이슈번호}-{설명}.md 형식으로 변경하고 상호 참조를 갱신하십시오.

  • docs/design/gimin-vector-storage-batch-benchmark.md#L1: 파일명을 규칙 형식으로 변경하고 Lines 115-116의 결과 문서 참조를 갱신하십시오.
  • docs/test-results/gimin-vector-storage-batch-benchmark.md#L1: 파일명을 규칙 형식으로 변경하고 Line 6의 설계 문서 참조를 갱신하십시오.

코딩 가이드라인의 docs/design/*.mddocs/test-results/*.md 파일명 규칙을 적용했습니다.

📍 Affects 2 files
  • docs/design/gimin-vector-storage-batch-benchmark.md#L1-L1 (this comment)
  • docs/test-results/gimin-vector-storage-batch-benchmark.md#L1-L1
🤖 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/design/gimin-vector-storage-batch-benchmark.md` at line 1, Use the
actual linked issue number, without guessing, to rename both benchmark documents
to the {github-id}-#{issue-number}-{description}.md convention. Update the
design document’s result-document reference at lines 115-116 and the
test-results document’s design-document reference at line 6; affected files are
docs/design/gimin-vector-storage-batch-benchmark.md (line 1) and
docs/test-results/gimin-vector-storage-batch-benchmark.md (line 1).

Source: Coding guidelines

Comment on lines +40 to +56
TPS와 시간은 2회 표본의 nearest-rank p50이다. 표본 수가 작으므로 절대 성능 SLO가 아니라 같은
로컬 환경의 상대 기준선으로 해석한다.

| 저장 건수 | Batch Size | TPS p50 | 시간 p50 | Batch 호출 | Batch 1 대비 TPS | 총 저장 크기 |
|---:|---:|---:|---:|---:|---:|---:|
| 1,000 | 1 | 414.81 | 2.389초 | 1,000 | 기준 | 13.23 MiB |
| 1,000 | 100 | **472.13** | **2.116초** | 10 | **+13.82%** | 13.23 MiB |
| 1,000 | 500 | 467.97 | 2.119초 | 2 | +12.82% | 13.23 MiB |
| 1,000 | 1,000 | 464.08 | 2.133초 | 1 | +11.88% | 13.23 MiB |
| 10,000 | 1 | 1,143.18 | 8.708초 | 10,000 | 기준 | 61.38 MiB |
| 10,000 | 100 | 1,524.13 | 6.467초 | 100 | +33.32% | 61.38 MiB |
| 10,000 | 500 | 1,397.54 | 6.922초 | 20 | +22.25% | 61.40 MiB |
| 10,000 | 1,000 | **1,533.76** | **6.495초** | 10 | **+34.17%** | 61.38 MiB |
| 100,000 | 1 | 1,130.06 | 83.220초 | 100,000 | 기준 | 732.80 MiB |
| 100,000 | 100 | 1,559.21 | 61.827초 | 1,000 | +37.98% | 733.44 MiB |
| 100,000 | 500 | 1,580.80 | 62.624초 | 200 | +39.89% | 732.71 MiB |
| 100,000 | 1,000 | **1,631.31** | **60.590초** | 100 | **+44.36%** | 734.47 MiB |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

p50 TPS와 p50 시간을 동일 Round 값처럼 해석하지 마십시오.

가정: TimingSummary.from(...)는 Duration과 rowsPerSecond 표본을 각각 정렬해 p50을 계산합니다. 이 가정은 JSON 값과 일치합니다. 예를 들어 100,000건, Batch Size 1에서 TPS p50 1,130.0688.491초 Round의 값이고, 시간 p50 83.220초1,201.63 rows/s Round의 값입니다.

두 해석 중 하나를 명시하십시오. 두 열이 독립 분포 요약이면 동일 Round가 아님을 표에 명시하십시오. 대표 Round를 의미하면 같은 Round에서 Duration과 TPS를 함께 선택하십시오. 현재 표의 두 수를 함께 사용하면 설계 문서의 TPS = rowCount / durationSeconds를 재계산할 수 없습니다.

코딩 가이드라인의 “State assumptions explicitly, surface uncertainty, and present multiple interpretations rather than choosing silently.” 규칙을 적용했습니다.

🤖 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/gimin-vector-storage-batch-benchmark.md` around lines 40 -
56, 문서의 벤치마크 표에서 p50 TPS와 p50 시간을 동일한 Round의 값처럼 제시하지 않도록 정리하십시오.
TimingSummary.from(...)이 두 표본을 독립적으로 요약한다는 전제를 명시하고 두 열이 서로 다른 Round일 수 있음을
설명하거나, 대표 Round를 사용하려면 같은 Round의 Duration과 TPS를 함께 기록하도록 표와 해설을 수정하십시오. 선택한 해석에
맞게 TPS = rowCount / durationSeconds 재계산이 일관되도록 모든 관련 수치를 갱신하십시오.

Source: Coding guidelines

@Gimini-3
Gimini-3 merged commit af14d2e into develop Aug 12, 2026
1 check passed
@Gimini-3 Gimini-3 self-assigned this Aug 13, 2026
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.

Vector 저장 TPS 및 JDBC Batch Size 비교 지원 추가 구현

1 participant