From 3885ddd9772ac78da53a4d09379874ad6c854969 Mon Sep 17 00:00:00 2001 From: Dongho Kim Date: Mon, 22 Dec 2025 04:33:54 +0900 Subject: [PATCH] udpate sick --- backend/Cargo.toml | 1 + backend/src/api/handlers/tiles.rs | 64 +++-- backend/src/db.rs | 4 + backend/src/main.rs | 13 +- backend/src/repositories/mod.rs | 1 + backend/src/repositories/redis_repository.rs | 43 +++ docker-compose.yml | 26 ++ frontend/Cargo.toml | 1 + frontend/index.html | 12 + frontend/src/labels.rs | 261 +++++++++++++++---- frontend/src/lib.rs | 6 +- frontend/src/pipelines/water.rs | 22 +- frontend/src/services/render_service.rs | 155 ++++++----- frontend/src/types.rs | 3 + importer/src/main.rs | 249 ++++++++++++++---- importer/src/services/mesh_service.rs | 18 ++ 16 files changed, 689 insertions(+), 190 deletions(-) create mode 100644 backend/src/repositories/redis_repository.rs diff --git a/backend/Cargo.toml b/backend/Cargo.toml index d6c00af..0ec4797 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -12,6 +12,7 @@ bincode = "1.3" serde_json = "1.0" tower = "0.4" tower-http = { version = "0.5", features = ["cors", "fs", "compression-full"] } +redis = { version = "0.24", features = ["tokio-comp", "connection-manager"] } tracing = "0.1" tracing-subscriber = "0.3" async-trait = "0.1" diff --git a/backend/src/api/handlers/tiles.rs b/backend/src/api/handlers/tiles.rs index 58a7a4d..67c13d8 100644 --- a/backend/src/api/handlers/tiles.rs +++ b/backend/src/api/handlers/tiles.rs @@ -5,9 +5,11 @@ use axum::{ }; use std::sync::Arc; use crate::services::tile_service::TileService; +use crate::repositories::redis_repository::RedisRepository; pub struct AppState { pub tile_service: Arc, + pub redis_repo: Arc, } pub async fn get_tile( @@ -110,7 +112,24 @@ pub async fn get_tile_all( Path((z, x, y)): Path<(i32, i32, i32)>, State(state): State>, ) -> impl IntoResponse { - // Parallel fetching for performance + // Cache key for this tile + let cache_key = format!("tile:{}:{}:{}", z, x, y); + + // Try to get from cache first + if let Ok(Some(cached_bytes)) = state.redis_repo.get(&cache_key).await { + tracing::debug!("Cache HIT for tile {}/{}/{}", z, x, y); + return ( + [ + (header::CONTENT_TYPE, "application/octet-stream"), + (header::CACHE_CONTROL, "public, max-age=86400"), // 24 hours + ], + cached_bytes + ).into_response(); + } + + tracing::debug!("Cache MISS for tile {}/{}/{}", z, x, y); + + // Cache miss - fetch from database let (nodes, ways, buildings, landuse, water, railways) = tokio::join!( state.tile_service.get_nodes(z, x, y), state.tile_service.get_ways(z, x, y), @@ -120,23 +139,11 @@ pub async fn get_tile_all( state.tile_service.get_railways(z, x, y), ); - // Initial capacity estimaton (removed unused var) + if let (Ok(n), Ok(w), Ok(b), Ok(l), Ok(wt), Ok(r)) = (nodes.as_ref(), ways.as_ref(), buildings.as_ref(), landuse.as_ref(), water.as_ref(), railways.as_ref()) { + tracing::debug!("Tile {}/{}/{}: nodes={}, ways={}, buildings={}, landuse={}, water={}, railways={}", + z, x, y, n.len(), w.len(), b.len(), l.len(), wt.len(), r.len()); + } - // Check errors and separate results? - // For now, the endpoint likely expects a single binary blob of combined types or just simple sequential data. - // The original logic didn't seem to implement get_tile_all in the viewed main.rs snippet. - // Based on standard practices, I'll return a struct or just concatenation if that's what the frontend expects. - // Wait, the original main.rs HAD `get_tile_all` registered but the implementation was truncated in view. - // I will implementation it by combining all into a single structured response or just separate vectors if I define a TileData DTO. - // Checking the plan... "models/tile_response.rs". I haven't created that yet. - // For now, I'll stick to individual endpoints as primary, but `get_tile_all` is useful. - // I'll return a tuple or struct serialized. - - // Let's assume a structure similar to what the frontend expects. - // If I don't know the exact format of `get_tile_all` from previous code, I should look at it or just stub it safely. - // Actually, looking at `frontend/src/lib.rs` might reveal what it expects. - - // For simplicity in this step, I will implement it returning a generic error if fails, or a tuple. if let (Ok(n), Ok(w), Ok(b), Ok(l), Ok(wt), Ok(r)) = (nodes, ways, buildings, landuse, water, railways) { #[derive(serde::Serialize)] struct TileData { @@ -156,12 +163,31 @@ pub async fn get_tile_all( water: wt, railways: r, }; + let bytes = bincode::serialize(&data).unwrap(); - ([(header::CONTENT_TYPE, "application/octet-stream")], bytes).into_response() + + // Store in cache asynchronously (fire and forget) + let redis_clone = state.redis_repo.clone(); + let cache_key_clone = cache_key.clone(); + let bytes_clone = bytes.clone(); + tokio::spawn(async move { + if let Err(e) = redis_clone.set(&cache_key_clone, &bytes_clone, 86400).await { + tracing::warn!("Failed to cache tile {}: {}", cache_key_clone, e); + } + }); + + ( + [ + (header::CONTENT_TYPE, "application/octet-stream"), + (header::CACHE_CONTROL, "public, max-age=86400"), // 24 hours + ], + bytes + ).into_response() } else { - ( + ( axum::http::StatusCode::INTERNAL_SERVER_ERROR, "Failed to fetch tile data".to_string(), ).into_response() } } + diff --git a/backend/src/db.rs b/backend/src/db.rs index 464d9c4..53b404d 100644 --- a/backend/src/db.rs +++ b/backend/src/db.rs @@ -37,6 +37,7 @@ pub async fn initialize_schema(session: &Session) -> Result<(), Box, points blob, + vertex_buffer blob, PRIMARY KEY ((zoom, tile_x, tile_y), id) )", &[], @@ -52,6 +53,7 @@ pub async fn initialize_schema(session: &Session) -> Result<(), Box, points blob, + vertex_buffer blob, PRIMARY KEY ((zoom, tile_x, tile_y), id) )", &[], @@ -67,6 +69,7 @@ pub async fn initialize_schema(session: &Session) -> Result<(), Box, points blob, + vertex_buffer blob, PRIMARY KEY ((zoom, tile_x, tile_y), id) )", &[], @@ -82,6 +85,7 @@ pub async fn initialize_schema(session: &Session) -> Result<(), Box, points blob, + vertex_buffer blob, PRIMARY KEY ((zoom, tile_x, tile_y), id) )", &[], diff --git a/backend/src/main.rs b/backend/src/main.rs index 3a90b21..c69a67c 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -16,6 +16,7 @@ use tower_http::compression::CompressionLayer; use crate::repositories::way_repository::WayRepository; use crate::repositories::node_repository::NodeRepository; +use crate::repositories::redis_repository::RedisRepository; use crate::services::tile_service::TileService; use crate::api::handlers::tiles::{ get_tile, get_tile_ways, get_tile_buildings, @@ -37,20 +38,28 @@ async fn main() -> Result<(), Box> { .build() .await?; - // Initialize schema and seed data (Keep existing db module for now) + // Initialize schema and seed data db::initialize_schema(&session).await?; db::seed_data(&session).await?; let session_arc = Arc::new(session); println!("Connected to ScyllaDB!"); + // Connect to Redis + println!("Connecting to Redis..."); + let redis_uri = std::env::var("REDIS_URI") + .unwrap_or_else(|_| "redis://redis:6379".to_string()); + let redis_repo = Arc::new(RedisRepository::new(&redis_uri).await?); + println!("Connected to Redis!"); + // Dependency Injection let node_repo = Arc::new(NodeRepository::new(session_arc.clone())); let way_repo = Arc::new(WayRepository::new(session_arc.clone())); let tile_service = Arc::new(TileService::new(node_repo, way_repo)); let state = Arc::new(AppState { - tile_service: tile_service, + tile_service, + redis_repo, }); let app = Router::new() diff --git a/backend/src/repositories/mod.rs b/backend/src/repositories/mod.rs index 9138d67..b0fea77 100644 --- a/backend/src/repositories/mod.rs +++ b/backend/src/repositories/mod.rs @@ -1,2 +1,3 @@ pub mod way_repository; pub mod node_repository; +pub mod redis_repository; diff --git a/backend/src/repositories/redis_repository.rs b/backend/src/repositories/redis_repository.rs new file mode 100644 index 0000000..8a8c034 --- /dev/null +++ b/backend/src/repositories/redis_repository.rs @@ -0,0 +1,43 @@ +use redis::{AsyncCommands, Client}; +use std::sync::Arc; + +pub struct RedisRepository { + client: Arc, +} + +impl RedisRepository { + pub async fn new(uri: &str) -> Result { + let client = Client::open(uri)?; + // Test connection + let mut conn = client.get_async_connection().await?; + let _: () = redis::cmd("PING").query_async(&mut conn).await?; + + Ok(Self { + client: Arc::new(client), + }) + } + + /// Get cached data by key + pub async fn get(&self, key: &str) -> Result>, redis::RedisError> { + let mut conn = self.client.get_async_connection().await?; + conn.get(key).await + } + + /// Set data with TTL (time-to-live) in seconds + pub async fn set(&self, key: &str, value: &[u8], ttl_seconds: u64) -> Result<(), redis::RedisError> { + let mut conn = self.client.get_async_connection().await?; + conn.set_ex(key, value, ttl_seconds).await + } + + /// Check if key exists + pub async fn exists(&self, key: &str) -> Result { + let mut conn = self.client.get_async_connection().await?; + conn.exists(key).await + } + + /// Delete a key + pub async fn delete(&self, key: &str) -> Result<(), redis::RedisError> { + let mut conn = self.client.get_async_connection().await?; + conn.del(key).await + } +} diff --git a/docker-compose.yml b/docker-compose.yml index ec56b98..3516d84 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,6 +5,18 @@ services: command: --smp 1 --memory 2G --overprovisioned 1 --api-address 0.0.0.0 --max-memory-for-unlimited-query-soft-limit 1073741824 --tombstone-warn-threshold 10000000 volumes: - scylla_data:/var/lib/scylla + networks: + - maps-net + + redis: + image: redis:7-alpine + container_name: map-redis + command: redis-server --maxmemory 2gb --maxmemory-policy allkeys-lru + volumes: + - redis_data:/data + networks: + - maps-net + restart: always app: build: @@ -15,6 +27,11 @@ services: - "3000:3000" depends_on: - scylla + - redis + environment: + - REDIS_URI=redis://redis:6379 + networks: + - maps-net restart: always importer: @@ -29,10 +46,19 @@ services: - SCYLLA_URI=scylla:9042 - OSM_PBF_PATH=/app/data.osm.pbf - CACHE_DIR=/cache + - DEBUG_WAY_ID=99 + - VERBOSE_DEBUG=1 depends_on: - scylla + networks: + - maps-net profiles: - import +networks: + maps-net: + driver: bridge + volumes: scylla_data: + redis_data: diff --git a/frontend/Cargo.toml b/frontend/Cargo.toml index 4246a2a..002987d 100644 --- a/frontend/Cargo.toml +++ b/frontend/Cargo.toml @@ -38,6 +38,7 @@ web-sys = { version = "0.3", features = [ "DomTokenList", "CssStyleDeclaration", "Performance", + "CanvasRenderingContext2d", ] } wgpu = { version = "0.19", default-features = false, features = ["webgl", "wgsl"] } winit = { version = "0.29", default-features = false, features = ["rwh_06"] } diff --git a/frontend/index.html b/frontend/index.html index cc987b8..4c38ffa 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -520,6 +520,18 @@
+ + +