Optimise
This commit is contained in:
parent
9179acd4cd
commit
2c613dc0d1
14 changed files with 376 additions and 188 deletions
|
|
@ -2,14 +2,14 @@ use std::sync::Arc;
|
|||
|
||||
use axum::extract::Query;
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::Json;
|
||||
use axum::response::{IntoResponse, Json};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::info;
|
||||
|
||||
use crate::consts::MAX_POIS_PER_REQUEST;
|
||||
use crate::data::POI;
|
||||
use crate::state::{AppState, POICategoryGroup};
|
||||
|
||||
use super::hexagons::write_json_escaped;
|
||||
use super::parse::parse_bounds;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
|
@ -19,15 +19,10 @@ pub struct POIParams {
|
|||
categories: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct POIsResponse {
|
||||
pois: Vec<POI>,
|
||||
}
|
||||
|
||||
pub async fn get_pois(
|
||||
state: Arc<AppState>,
|
||||
Query(params): Query<POIParams>,
|
||||
) -> Result<Json<POIsResponse>, (StatusCode, String)> {
|
||||
) -> Result<impl IntoResponse, (StatusCode, String)> {
|
||||
let bounds_str = params.bounds.ok_or((
|
||||
StatusCode::BAD_REQUEST,
|
||||
"bounds parameter is required".into(),
|
||||
|
|
@ -44,7 +39,7 @@ pub async fn get_pois(
|
|||
|
||||
let num_categories = category_filter.as_ref().map(|cats| cats.len()).unwrap_or(0);
|
||||
|
||||
let result = tokio::task::spawn_blocking(move || {
|
||||
let json_body = tokio::task::spawn_blocking(move || {
|
||||
let t0 = std::time::Instant::now();
|
||||
let row_indices = state.poi_grid.query(south, west, north, east);
|
||||
|
||||
|
|
@ -64,36 +59,46 @@ pub async fn get_pois(
|
|||
.collect();
|
||||
|
||||
if matching_rows.len() > MAX_POIS_PER_REQUEST {
|
||||
// Use a power-of-2 sampling step so each POI's inclusion depends
|
||||
// only on its own priority hash, not on what other POIs are in
|
||||
// the viewport. This prevents visible reshuffling when panning.
|
||||
let ratio = (matching_rows.len() / MAX_POIS_PER_REQUEST) as u32;
|
||||
let step = ratio.next_power_of_two();
|
||||
let mask = step - 1;
|
||||
matching_rows.retain(|&row| state.poi_data.priority[row] & mask == 0);
|
||||
// Statistical noise may leave us slightly over the limit
|
||||
if matching_rows.len() > MAX_POIS_PER_REQUEST {
|
||||
matching_rows.sort_unstable_by_key(|&row| state.poi_data.priority[row]);
|
||||
matching_rows.truncate(MAX_POIS_PER_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
let pois: Vec<POI> = matching_rows
|
||||
.iter()
|
||||
.map(|&row| POI {
|
||||
id: state.poi_data.id[row].clone(),
|
||||
name: state.poi_data.name[row].clone(),
|
||||
category: state.poi_data.category.get(row).to_string(),
|
||||
group: state.poi_data.group.get(row).to_string(),
|
||||
lat: state.poi_data.lat[row],
|
||||
lng: state.poi_data.lng[row],
|
||||
emoji: state.poi_data.emoji.get(row).to_string(),
|
||||
})
|
||||
.collect();
|
||||
// Write JSON directly to string buffer, avoiding intermediate POI allocations
|
||||
let mut buf = String::with_capacity(matching_rows.len() * 128);
|
||||
buf.push_str("{\"pois\":[");
|
||||
|
||||
for (i, &row) in matching_rows.iter().enumerate() {
|
||||
if i > 0 {
|
||||
buf.push(',');
|
||||
}
|
||||
buf.push_str("{\"id\":\"");
|
||||
write_json_escaped(&mut buf, &state.poi_data.id[row]);
|
||||
buf.push_str("\",\"name\":\"");
|
||||
write_json_escaped(&mut buf, &state.poi_data.name[row]);
|
||||
buf.push_str("\",\"category\":\"");
|
||||
write_json_escaped(&mut buf, state.poi_data.category.get(row));
|
||||
buf.push_str("\",\"group\":\"");
|
||||
write_json_escaped(&mut buf, state.poi_data.group.get(row));
|
||||
buf.push_str("\",\"lat\":");
|
||||
buf.push_str(&state.poi_data.lat[row].to_string());
|
||||
buf.push_str(",\"lng\":");
|
||||
buf.push_str(&state.poi_data.lng[row].to_string());
|
||||
buf.push_str(",\"emoji\":\"");
|
||||
write_json_escaped(&mut buf, state.poi_data.emoji.get(row));
|
||||
buf.push_str("\"}");
|
||||
}
|
||||
|
||||
buf.push_str("]}");
|
||||
|
||||
let elapsed = t0.elapsed();
|
||||
info!(
|
||||
results = pois.len(),
|
||||
results = matching_rows.len(),
|
||||
candidates = row_indices.len(),
|
||||
categories = num_categories,
|
||||
categories_raw = categories_str.as_deref().unwrap_or("-"),
|
||||
|
|
@ -101,12 +106,12 @@ pub async fn get_pois(
|
|||
"GET /api/pois"
|
||||
);
|
||||
|
||||
POIsResponse { pois }
|
||||
buf
|
||||
})
|
||||
.await
|
||||
.map_err(|error| (StatusCode::INTERNAL_SERVER_ERROR, error.to_string()))?;
|
||||
|
||||
Ok(Json(result))
|
||||
Ok(([("content-type", "application/json")], json_body))
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue