Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions data/migration_v2_postgis_geography.sql
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ WHERE location IS NULL
AND longitude IS NOT NULL;

-- 4. GIST 곡간 인덱슀 생성
-- findNearbyWithGist()의 ST_DWithin + KNN μ •λ ¬μš©. ddl-autoλŠ” geometry/GISTλ₯Ό λͺ» λ§Œλ“€λ―€λ‘œ
-- 이 λ§ˆμ΄κ·Έλ ˆμ΄μ…˜μ΄ μœ μΌν•œ 생성 경둜.
CREATE INDEX IF NOT EXISTS idx_stores_location_gist
ON stores USING GIST (location);

-- 5. nearby λ°”μš΄λ”©λ°•μŠ€ μ„ ν•„ν„°μš© 볡합 B-tree 인덱슀
CREATE INDEX IF NOT EXISTS idx_stores_lat_lng
ON stores (latitude, longitude)
WHERE is_deleted = false;
-- NOTE: (latitude, longitude) B-tree μΈλ±μŠ€λŠ” Store μ—”ν‹°ν‹°μ˜ @Index(idx_store_lat_lng) 둜 이미
-- ddl-auto κ°€ 생성/μœ μ§€ν•˜λ―€λ‘œ μ—¬κΈ°μ„œ 별도 μΆ”κ°€ν•˜μ§€ μ•ŠλŠ”λ‹€. κ³Όκ±° λ²„μ „μ—μ„œ idx_stores_lat_lng
-- λΌλŠ” λ‹€λ₯Έ μ΄λ¦„μœΌλ‘œ 쀑볡 μƒμ„±λ˜λ˜ 문제λ₯Ό λ°©μ§€.

-- 검증
-- SELECT pg_typeof(location) FROM stores LIMIT 1; -- geography 이어야 정상
Expand Down
34 changes: 34 additions & 0 deletions grafana/provisioning/dashboards/catcheat-loadtest-dashboard.json
Original file line number Diff line number Diff line change
Expand Up @@ -2164,6 +2164,40 @@
"legendFormat": "SMEMBERS p95 (k6)"
}
]
},
{
"datasource": { "type": "prometheus", "uid": "DS_PROMETHEUS" },
"fieldConfig": {
"defaults": {
"color": { "mode": "palette-classic" },
"custom": {
"drawStyle": "line",
"fillOpacity": 10,
"lineWidth": 2,
"showPoints": "never",
"lineInterpolation": "smooth"
},
"unit": "ops",
"min": 0
}
},
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 200 },
"id": 999,
"options": {
"legend": { "calcs": ["last", "max", "mean"], "displayMode": "table", "placement": "bottom" },
"tooltip": { "mode": "multi", "sort": "desc" }
},
"title": "D.5 Kafka λ©”μ‹œμ§€ μ²˜λ¦¬λŸ‰ (Topic별)",
"description": "Consumerκ°€ μ²˜λ¦¬ν•œ λ©”μ‹œμ§€ 속도. 빈자리 μ‹œμ—° μ‹œ notification.vacancy.opened 라인 잠깐 μ†Ÿκ΅¬μΉ¨.",
"type": "timeseries",
"targets": [
{
"expr": "sum by (topic) (rate(kafka_consumer_fetch_manager_records_consumed_total[1m]))",
"legendFormat": "{{topic}}",
"refId": "A",
"datasource": { "type": "prometheus", "uid": "DS_PROMETHEUS" }
}
]
}
],
"refresh": "5s",
Expand Down
14 changes: 5 additions & 9 deletions src/main/java/com/catchtable/store/service/StoreService.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,17 +111,12 @@ public String getNearbyPopularStoresForAi(ToolContext toolContext) {
.collect(Collectors.joining("\n"));
}

// λ‚΄ μ£Όλ³€ λ§€μž₯ (λ°”μš΄λ”©λ°•μŠ€ μ„ ν•„ν„° + 거리 μ •λ ¬)
// λ‚΄ μ£Όλ³€ λ§€μž₯ (PostGIS GIST 인덱슀 + ST_DWithin + KNN 거리 μ •λ ¬)
@Transactional(readOnly = true)
public List<StoreListResponse> getNearbyStores(double latitude, double longitude, int page, int size) {
int limitedSize = Math.min(size, 100);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

μš”μ²­λœ sizeκ°€ 1 미만일 경우, PageRequest.of() λ©”μ„œλ“œ 호좜 μ‹œ IllegalArgumentException이 λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ΅œμ†Œ 1 μ΄μƒμ˜ 값을 가지도둝 μ•ˆμ „ν•˜κ²Œ μ œν•œν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

Suggested change
int limitedSize = Math.min(size, 100);
int limitedSize = Math.max(1, Math.min(size, 100));

// 반경 5kmλ₯Ό μœ„κ²½λ„ λΈνƒ€λ‘œ λ³€ν™˜ (1도 β‰ˆ 111km, ν•œκ΅­ μœ„λ„ κΈ°μ€€ 경도 1도 β‰ˆ 88km)
double deltaLat = 5000.0 / 111_000.0;
double deltaLng = 5000.0 / 88_000.0;
return storeRepository.findNearby(
latitude, longitude,
latitude - deltaLat, latitude + deltaLat,
longitude - deltaLng, longitude + deltaLng,
return storeRepository.findNearbyWithGist(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

ν˜„μž¬ Store μ—”ν‹°ν‹°(Store.java 50-51라인)λ₯Ό 보면 location ν•„λ“œκ°€ @Column(columnDefinition = "geography(Point, 4326)", insertable = false, updatable = false)둜 μ„€μ •λ˜μ–΄ μžˆμ–΄, μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λ‹¨μ—μ„œ μ‹ κ·œ λ§€μž₯을 등둝할 λ•Œ location 값이 μ €μž₯λ˜μ§€ μ•Šκ³  NULL둜 μž…λ ₯λ©λ‹ˆλ‹€.\n\nλ§Œμ•½ PostgreSQL DB 상에 latitude와 longitude λ³€κ²½ μ‹œ location을 μžλ™μœΌλ‘œ μ—…λ°μ΄νŠΈν•΄μ£ΌλŠ” DB Triggerκ°€ λ³„λ„λ‘œ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ”λ‹€λ©΄, μ‹ κ·œ λ“±λ‘λœ λ§€μž₯은 location이 NULL이 λ˜μ–΄ findNearbyWithGist 검색 κ²°κ³Όμ—μ„œ λˆ„λ½λ˜λŠ” λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.\n\nν•΄κ²° λ°©μ•ˆ:\n1. DB λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μŠ€ν¬λ¦½νŠΈμ— latitude/longitude μ‚½μž…/μˆ˜μ • μ‹œ location을 μžλ™ κ°±μ‹ ν•˜λŠ” Triggerλ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€.\n2. λ˜λŠ” Store μ—”ν‹°ν‹°μ˜ location ν•„λ“œλ₯Ό insertable = true, updatable = true둜 λ³€κ²½ν•˜κ³ , λ§€μž₯ 생성/μˆ˜μ • λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ—μ„œ GeometryFactoryλ₯Ό μ΄μš©ν•΄ Point 객체λ₯Ό 직접 μƒμ„±ν•˜μ—¬ μ €μž₯ν•˜λ„λ‘ μˆ˜μ •ν•©λ‹ˆλ‹€.

latitude, longitude, 5000.0,
PageRequest.of(page, limitedSize)
).stream().map(StoreListResponse::from).toList();
}
Expand All @@ -135,10 +130,11 @@ public List<StoreListResponse> getNearbyStores(double latitude, double longitude
public List<StoreListResponse> getStoresInBounds(
double minLat, double maxLat, double minLng, double maxLng,
Double centerLat, Double centerLng, int limit) {
int limitedLimit = Math.min(limit, 100);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

μ „λ‹¬λœ limit 값이 1 미만일 경우, PageRequest.of(0, limitedLimit) 호좜 μ‹œ IllegalArgumentException이 λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ΅œμ†Œ 1 μ΄μƒμ˜ 값을 가지도둝 μ•ˆμ „ν•˜κ²Œ μ œν•œν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

Suggested change
int limitedLimit = Math.min(limit, 100);
int limitedLimit = Math.max(1, Math.min(limit, 100));

double cLat = centerLat != null ? centerLat : (minLat + maxLat) / 2.0;
double cLng = centerLng != null ? centerLng : (minLng + maxLng) / 2.0;
return storeRepository
.findInBounds(minLat, maxLat, minLng, maxLng, cLat, cLng, PageRequest.of(0, limit))
.findInBounds(minLat, maxLat, minLng, maxLng, cLat, cLng, PageRequest.of(0, limitedLimit))
.stream()
.map(StoreListResponse::from)
.toList();
Expand Down
Loading