[Perf] Vector 저장 TPS 및 JDBC Batch Size 비교 - #156
Conversation
|
Warning Review limit reached
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 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughPostgreSQL pgvector 저장 benchmark를 추가했습니다. 1,000·10,000·100,000행과 JDBC Batch Size 1·100·500·1,000을 측정하고, HNSW 갱신·Commit·저장 공간·검증 결과를 JSON과 문서로 기록합니다. ChangesVector storage benchmark
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
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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 winVector 문자열이 float4 정밀도보다 큰 텍스트를 만들며 Pool 메모리도 커집니다.
values[index] / norm은float / double연산이므로 결과가double입니다.StringBuilder.append(double)는 최대 17자리 유효숫자를 씁니다. pgvectorvector타입은 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); }
norm을float로 바꾸면 나눗셈 결과가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.ymlor 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_size와pg_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
📒 Files selected for processing (6)
build.gradledocs/design/gimin-vector-storage-batch-benchmark.mddocs/test-results/gimin-vector-storage-batch-benchmark-data.jsondocs/test-results/gimin-vector-storage-batch-benchmark.mdsrc/test/java/com/opensource/docgrid/domain/embedding/benchmark/VectorStoragePerformanceBenchmark.javasrc/test/java/com/opensource/docgrid/domain/embedding/benchmark/VectorStoragePerformanceBenchmarkTest.java
| @@ -0,0 +1,124 @@ | |||
| # Vector 저장 TPS·Batch Size 비교 Benchmark 설계 | |||
There was a problem hiding this comment.
📐 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/*.md 및 docs/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
| 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 | |
There was a problem hiding this comment.
🗄️ 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.06은 88.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
🔍️ 작업 내용
vectorStoragePerformanceTest작업을 추가합니다.✨ 상세 설명
비교 조건
주요 결과
작은 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=1DB_SSLMODE=disable JWT_SECRET=<test-only-value> ./gradlew testgit diff --check origin/develop...HEAD🛠️ 추후 리팩토링 및 고도화 계획
💬 리뷰 요구사항
Summary by CodeRabbit
새로운 기능
문서
테스트