This is an automated email from the ASF dual-hosted git repository.

freeoneplus pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris-mcp-server.git


The following commit(s) were added to refs/heads/master by this push:
     new 2613912  [Performance]Optimize Stdio and Streamable HTTP startup 
solutions (#60)
2613912 is described below

commit 2613912df3c1531473e23c543aedd5dfadd8eae6
Author: Yijia Su <[email protected]>
AuthorDate: Tue Sep 23 12:21:30 2025 +0800

    [Performance]Optimize Stdio and Streamable HTTP startup solutions (#60)
    
    * 0.5.1 Version
    
    * fix 0.5.1 schema async bug
    
    * fix security bug
    
    * fix security bug
    
    * Add complete Token, JWT, OAuth authentication system
    
    * Add complete Token, JWT, OAuth authentication system
    
    * Add complete Token, JWT, OAuth authentication system
    
    * Add complete Token, JWT, OAuth authentication system
    
    * Add a controllable MCP Server DB Pool permission authentication system, 
connect it with the Doris permission system, and provide it to enterprise-level 
applications concurrently with the multi-Worker mode.
    
    * Add Tokens Management
    
    * change version
    
    * fix stdio start bug
    
    * fix stdio start bug
---
 doris_mcp_server/main.py     |  14 +--
 doris_mcp_server/utils/db.py | 207 +++++++++++++++++++++++++++++++++++++++++++
 pyproject.toml               |   2 +-
 uv.lock                      |   2 +-
 4 files changed, 218 insertions(+), 7 deletions(-)

diff --git a/doris_mcp_server/main.py b/doris_mcp_server/main.py
index 3bb62fd..b28cce7 100644
--- a/doris_mcp_server/main.py
+++ b/doris_mcp_server/main.py
@@ -432,9 +432,9 @@ class DorisServer:
             await self.security_manager.initialize()
             self.logger.info("Security manager initialization completed")
             
-            # Ensure connection manager is initialized
-            await self.connection_manager.initialize()
-            self.logger.info("Connection manager initialization completed")
+            # For stdio mode, we must establish a working database connection
+            # Use the dedicated stdio mode initialization method
+            await self.connection_manager.initialize_for_stdio_mode()
 
             # Start stdio server - using compatible import approach
             try:
@@ -502,8 +502,12 @@ class DorisServer:
             await self.security_manager.initialize()
             self.logger.info("Security manager initialization completed")
             
-            # Ensure connection manager is initialized
-            await self.connection_manager.initialize()
+            # For HTTP mode, try to initialize global connection pool with 
graceful degradation
+            global_pool_created = await 
self.connection_manager.initialize_for_http_mode()
+            if global_pool_created:
+                self.logger.info("Global database connection pool available 
for HTTP mode")
+            else:
+                self.logger.info("HTTP mode running without global database 
pool, will use token-bound configurations")
 
             # Use Starlette and StreamableHTTPSessionManager according to 
official example
             import uvicorn
diff --git a/doris_mcp_server/utils/db.py b/doris_mcp_server/utils/db.py
index 8d5c58a..69c769b 100644
--- a/doris_mcp_server/utils/db.py
+++ b/doris_mcp_server/utils/db.py
@@ -626,6 +626,213 @@ class DorisConnectionManager:
             self.logger.error(f"Failed to initialize connection pool: {e}")
             raise
 
+    async def initialize_for_stdio_mode(self, timeout: float = 30.0) -> None:
+        """
+        Initialize connection pool for stdio mode with strict validation
+        
+        stdio mode requires a working database connection because:
+        - No HTTP authentication mechanism to support token-bound configs
+        - All database operations depend on the global connection pool
+        
+        Args:
+            timeout: Maximum time to wait for connection establishment
+            
+        Raises:
+            RuntimeError: If configuration is invalid or connection fails
+        """
+        try:
+            # Validate that we have valid global configuration
+            if not self._has_valid_global_config():
+                error_msg = (
+                    "stdio mode requires valid global database configuration. "
+                    "Please set DORIS_HOST and DORIS_USER in environment 
variables or .env file. "
+                    f"Current config: host='{self.host}', user='{self.user}'"
+                )
+                self.logger.error(error_msg)
+                raise RuntimeError(error_msg)
+            
+            self.logger.info(f"stdio mode database config validated: 
{self.host}:{self.port}")
+            
+            # Validate configuration format
+            is_valid, error_message = self.validate_database_configuration()
+            if not is_valid:
+                error_msg = f"Database configuration validation failed: 
{error_message}"
+                self.logger.error(error_msg)
+                raise RuntimeError(error_msg)
+            
+            # Test connectivity with timeout
+            self.logger.info("Testing database connectivity for stdio mode...")
+            if not await self._test_connectivity_with_timeout(timeout):
+                error_msg = (
+                    f"Failed to connect to Doris database within {timeout} 
seconds. "
+                    f"Please check if Doris is running at 
{self.host}:{self.port} "
+                    f"and verify network connectivity."
+                )
+                self.logger.error(error_msg)
+                raise RuntimeError(error_msg)
+            
+            # Initialize the connection pool
+            await self._create_connection_pool()
+            
+            # Verify that we have a working connection pool
+            if not self.pool:
+                error_msg = "Database connection pool was not created 
successfully."
+                self.logger.error(error_msg)
+                raise RuntimeError(error_msg)
+            
+            # Start background monitoring tasks
+            self.pool_health_check_task = 
asyncio.create_task(self._pool_health_monitor())
+            self.pool_cleanup_task = 
asyncio.create_task(self._pool_cleanup_monitor())
+            
+            # Perform initial pool warmup
+            await self._warmup_pool()
+            
+            self.logger.info("Database connection established successfully for 
stdio mode")
+            
+        except Exception as e:
+            self.logger.error(f"stdio mode database initialization failed: 
{e}")
+            raise
+    
+    async def initialize_for_http_mode(self) -> bool:
+        """
+        Initialize connection pool for HTTP mode with graceful degradation
+        
+        HTTP mode can work without global database configuration because:
+        - Supports token-bound database configurations
+        - Can handle authentication and use per-request database configs
+        - Has fallback mechanisms for database operations
+        
+        Returns:
+            bool: True if global database pool was created, False if 
gracefully degraded
+        """
+        try:
+            # First validate configuration format if we have one
+            if self._has_valid_global_config():
+                is_valid, error_message = 
self.validate_database_configuration()
+                if not is_valid:
+                    self.logger.warning(f"Global database configuration 
invalid: {error_message}")
+                    self.logger.info("HTTP mode will rely on token-bound 
database configurations")
+                    return False
+                
+                # Try to establish global connection pool
+                self.logger.info(f"Attempting to create global connection 
pool: {self.host}:{self.port}")
+                
+                try:
+                    # Test connectivity with shorter timeout for HTTP mode
+                    if await self._test_connectivity_with_timeout(10.0):
+                        await self._create_connection_pool()
+                        
+                        if self.pool:
+                            # Start background monitoring tasks
+                            self.pool_health_check_task = 
asyncio.create_task(self._pool_health_monitor())
+                            self.pool_cleanup_task = 
asyncio.create_task(self._pool_cleanup_monitor())
+                            
+                            # Perform initial pool warmup
+                            await self._warmup_pool()
+                            
+                            self.logger.info("Global database connection pool 
created successfully for HTTP mode")
+                            return True
+                    else:
+                        self.logger.warning("Global database connection test 
failed, will use token-bound configs")
+                        return False
+                        
+                except Exception as pool_error:
+                    self.logger.warning(f"Failed to create global connection 
pool: {pool_error}")
+                    self.logger.info("HTTP mode will rely on token-bound 
database configurations")
+                    return False
+            else:
+                self.logger.info("No valid global database config found, HTTP 
mode will use token-bound configurations")
+                return False
+                
+        except Exception as e:
+            self.logger.warning(f"HTTP mode database initialization 
encountered error: {e}")
+            self.logger.info("HTTP mode will rely on token-bound database 
configurations")
+            return False
+    
+    async def _test_connectivity_with_timeout(self, timeout: float) -> bool:
+        """
+        Test database connectivity with timeout
+        
+        Args:
+            timeout: Maximum time to wait for connection test
+            
+        Returns:
+            bool: True if connection successful, False otherwise
+        """
+        try:
+            await asyncio.wait_for(self._test_basic_connectivity(), 
timeout=timeout)
+            return True
+        except asyncio.TimeoutError:
+            self.logger.error(f"Database connectivity test timed out after 
{timeout} seconds")
+            return False
+        except Exception as e:
+            self.logger.error(f"Database connectivity test failed: {e}")
+            return False
+    
+    async def _test_basic_connectivity(self) -> None:
+        """
+        Test basic database connectivity without connection pool
+        
+        Raises:
+            Exception: If connection fails
+        """
+        import aiomysql
+        
+        conn = None
+        try:
+            conn = await aiomysql.connect(
+                host=self.host,
+                port=self.port,
+                user=self.user,
+                password=self.password,
+                db=self.database,
+                charset=self.charset,
+                connect_timeout=self.connect_timeout,
+                autocommit=True
+            )
+            
+            async with conn.cursor() as cursor:
+                await cursor.execute("SELECT 1")
+                result = await cursor.fetchone()
+                if not result or result[0] != 1:
+                    raise RuntimeError("Database connectivity test query 
failed")
+                    
+        except Exception as e:
+            raise RuntimeError(f"Database connectivity test failed: {e}")
+        finally:
+            if conn:
+                conn.close()
+    
+    async def _create_connection_pool(self) -> None:
+        """
+        Create the connection pool
+        
+        Raises:
+            Exception: If pool creation fails
+        """
+        self.pool = await aiomysql.create_pool(
+            host=self.host,
+            port=self.port,
+            user=self.user,
+            password=self.password,
+            db=self.database,
+            charset=self.charset,
+            minsize=self.minsize,
+            maxsize=self.maxsize,
+            pool_recycle=self.pool_recycle,
+            connect_timeout=self.connect_timeout,
+            autocommit=True
+        )
+        
+        # Test pool health
+        if not await self._test_pool_health():
+            # Clean up the pool if health test fails
+            if self.pool:
+                self.pool.close()
+                await self.pool.wait_closed()
+                self.pool = None
+            raise RuntimeError("Connection pool health check failed")
+
     async def _test_pool_health(self) -> bool:
         """Test connection pool health"""
         try:
diff --git a/pyproject.toml b/pyproject.toml
index a54a707..a9ae3da 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -20,7 +20,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "doris-mcp-server"
-version = "0.6.0"
+version = "0.6.1"
 description = "Enterprise-grade Model Context Protocol (MCP) server 
implementation for Apache Doris"
 authors = [
     {name = "Yijia Su", email = "[email protected]"}
diff --git a/uv.lock b/uv.lock
index e6d4c34..93b1e12 100644
--- a/uv.lock
+++ b/uv.lock
@@ -562,7 +562,7 @@ wheels = [
 
 [[package]]
 name = "doris-mcp-server"
-version = "0.5.1"
+version = "0.6.1"
 source = { editable = "." }
 dependencies = [
     { name = "adbc-driver-flightsql" },


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to