import http from 'k6/http'; import { check, sleep } from 'k6'; import { Trend } from 'k6/metrics'; import { BASE_URL, SLA_THRESHOLDS } from '../lib/config.js'; const textSearchDuration = new Trend('text_search_duration', true); const geoSearchDuration = new Trend('geo_search_duration', true); export const options = { scenarios: { search_load: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '30s', target: 50 }, { duration: '2m', target: 200 }, // peak: 200 concurrent { duration: '30s', target: 0 }, ], gracefulRampDown: '10s', }, }, thresholds: { ...SLA_THRESHOLDS, text_search_duration: ['p(95)<500'], geo_search_duration: ['p(95)<500'], }, }; const TEXT_QUERIES = [ 'căn hộ', 'nhà phố quận 1', 'đất nền', 'chung cư', 'biệt thự', 'apartment Ho Chi Minh', 'house Hanoi', ]; const GEO_SEARCHES = [ { lat: 10.7769, lng: 106.7009, radius: 5000 }, // HCM center { lat: 21.0285, lng: 105.8542, radius: 5000 }, // Hanoi center { lat: 16.0544, lng: 108.2022, radius: 10000 }, // Da Nang { lat: 10.8231, lng: 106.6297, radius: 3000 }, // HCM Tan Binh { lat: 21.0067, lng: 105.8400, radius: 2000 }, // Hanoi Ba Dinh ]; export default function () { const iter = __ITER; if (iter % 2 === 0) { // Full-text search const query = TEXT_QUERIES[iter % TEXT_QUERIES.length]; const url = `${BASE_URL}/search?q=${encodeURIComponent(query)}&limit=20&offset=0`; const res = http.get(url, { tags: { name: 'GET /search (text)' }, }); textSearchDuration.add(res.timings.duration); check(res, { 'text search: status 200|503': (r) => r.status === 200 || r.status === 503, 'text search: valid response': (r) => { if (r.status === 503) return true; // Typesense unavailable — acceptable try { return JSON.parse(r.body).data !== undefined; } catch { return false; } }, }); } else { // Geo search const geo = GEO_SEARCHES[iter % GEO_SEARCHES.length]; const url = `${BASE_URL}/search/geo?lat=${geo.lat}&lng=${geo.lng}&radius=${geo.radius}&limit=20`; const res = http.get(url, { tags: { name: 'GET /search/geo' }, }); geoSearchDuration.add(res.timings.duration); check(res, { 'geo search: status 200|503': (r) => r.status === 200 || r.status === 503, 'geo search: valid response': (r) => { if (r.status === 503) return true; try { return JSON.parse(r.body).data !== undefined; } catch { return false; } }, }); } sleep(Math.random() * 1 + 0.5); }