mod.rs (3537B)
1 //! Database connection management 2 //! 3 //! This module provides a wrapper around sqlx's PostgreSQL connection pool 4 //! with automatic configuration of the `products` schema search path. 5 //! 6 //! # Schema Search Path 7 //! All connections are configured to use `search_path TO products, public`, 8 //! which means queries don't need to prefix table names with `products.` 9 //! 10 //! # Connection Pool 11 //! The pool is managed by sqlx and handles: 12 //! - Connection pooling and reuse 13 //! - Automatic reconnection on connection failures 14 //! - Connection health checks 15 //! - Maximum connection limits 16 17 use anyhow::Result; 18 use sqlx::{ 19 postgres::{PgConnectOptions, PgPoolOptions}, 20 ConnectOptions, PgPool, 21 }; 22 use std::str::FromStr; 23 24 pub mod ratings; 25 pub mod repository; 26 27 // Re-export all repository functions for easy access 28 pub use ratings::*; 29 pub use repository::*; 30 31 /// Database connection pool wrapper 32 /// 33 /// Wraps sqlx's PgPool with custom configuration for the products service. 34 #[derive(Clone)] 35 pub struct Database { 36 pool: PgPool, 37 } 38 39 impl Database { 40 /// Create a new database connection pool 41 /// 42 /// This function: 43 /// 1. Parses the database URL 44 /// 2. Configures connection options (disables statement logging) 45 /// 3. Sets application name to "products-service" 46 /// 4. Creates a connection pool with the specified max connections 47 /// 5. Configures each connection to use the `products` schema by default 48 /// 49 /// # Arguments 50 /// * `database_url` - PostgreSQL connection string (e.g., "postgres://user:pass@host/db") 51 /// * `max_connections` - Maximum number of connections in the pool 52 /// 53 /// # Example 54 /// ```no_run 55 /// let db = Database::new("postgres://localhost/mydb", 10).await?; 56 /// let pool = db.pool(); 57 /// ``` 58 /// 59 /// # Errors 60 /// Returns an error if: 61 /// - Database URL is invalid 62 /// - Cannot connect to the database 63 /// - Database authentication fails 64 pub async fn new(database_url: &str, max_connections: u32) -> Result<Self> { 65 // Parse the connection string into structured options 66 let mut connect_opts = PgConnectOptions::from_str(database_url)? 67 // Disable statement logging to reduce noise in production 68 .disable_statement_logging(); 69 70 // Set application name for PostgreSQL monitoring and logging 71 connect_opts = connect_opts.application_name("products-service"); 72 73 // Build the connection pool with custom configuration 74 let pool = PgPoolOptions::new() 75 .max_connections(max_connections) 76 // Hook that runs after each connection is established 77 .after_connect(|conn, _meta| { 78 Box::pin(async move { 79 // Set the schema search path so queries can use unqualified table names 80 // Example: SELECT * FROM products instead of SELECT * FROM products.products 81 sqlx::query("SET search_path TO products, public") 82 .execute(conn) 83 .await?; 84 Ok(()) 85 }) 86 }) 87 .connect_with(connect_opts) 88 .await?; 89 90 Ok(Self { pool }) 91 } 92 93 /// Get a reference to the underlying connection pool 94 /// 95 /// Use this to execute queries: 96 /// ```no_run 97 /// let pool = db.pool(); 98 /// let products = sqlx::query!("SELECT * FROM products").fetch_all(pool).await?; 99 /// ``` 100 pub fn pool(&self) -> &PgPool { 101 &self.pool 102 } 103 }