[Config]Delete the minimum data pool variable (#11)
* fix at_eof bug * update uv.lock * fix bug and change pool min values
This commit is contained in:
@@ -28,7 +28,6 @@ DORIS_BE_WEBSERVER_PORT=8040
|
|||||||
# Connection Pool Configuration
|
# Connection Pool Configuration
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
DORIS_MIN_CONNECTIONS=5
|
|
||||||
DORIS_MAX_CONNECTIONS=20
|
DORIS_MAX_CONNECTIONS=20
|
||||||
DORIS_CONNECTION_TIMEOUT=30
|
DORIS_CONNECTION_TIMEOUT=30
|
||||||
DORIS_HEALTH_CHECK_INTERVAL=60
|
DORIS_HEALTH_CHECK_INTERVAL=60
|
||||||
@@ -86,5 +85,5 @@ ALERT_WEBHOOK_URL=
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
SERVER_NAME=doris-mcp-server
|
SERVER_NAME=doris-mcp-server
|
||||||
SERVER_VERSION=0.4.1
|
SERVER_VERSION=0.4.2
|
||||||
SERVER_PORT=3000
|
SERVER_PORT=3000
|
||||||
|
|||||||
@@ -53,12 +53,19 @@ class DatabaseConfig:
|
|||||||
be_webserver_port: int = 8040
|
be_webserver_port: int = 8040
|
||||||
|
|
||||||
# Connection pool configuration
|
# Connection pool configuration
|
||||||
min_connections: int = 5
|
# Note: min_connections is fixed at 0 to avoid at_eof connection issues
|
||||||
|
# This prevents pre-creation of connections which can cause state problems
|
||||||
|
_min_connections: int = field(default=0, init=False) # Internal use only, always 0
|
||||||
max_connections: int = 20
|
max_connections: int = 20
|
||||||
connection_timeout: int = 30
|
connection_timeout: int = 30
|
||||||
health_check_interval: int = 60
|
health_check_interval: int = 60
|
||||||
max_connection_age: int = 3600
|
max_connection_age: int = 3600
|
||||||
|
|
||||||
|
@property
|
||||||
|
def min_connections(self) -> int:
|
||||||
|
"""Minimum connections is always 0 to prevent at_eof issues"""
|
||||||
|
return self._min_connections
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class SecurityConfig:
|
class SecurityConfig:
|
||||||
@@ -248,9 +255,6 @@ class DorisConfig:
|
|||||||
config.database.be_webserver_port = int(os.getenv("DORIS_BE_WEBSERVER_PORT", str(config.database.be_webserver_port)))
|
config.database.be_webserver_port = int(os.getenv("DORIS_BE_WEBSERVER_PORT", str(config.database.be_webserver_port)))
|
||||||
|
|
||||||
# Connection pool configuration
|
# Connection pool configuration
|
||||||
config.database.min_connections = int(
|
|
||||||
os.getenv("DORIS_MIN_CONNECTIONS", str(config.database.min_connections))
|
|
||||||
)
|
|
||||||
config.database.max_connections = int(
|
config.database.max_connections = int(
|
||||||
os.getenv("DORIS_MAX_CONNECTIONS", str(config.database.max_connections))
|
os.getenv("DORIS_MAX_CONNECTIONS", str(config.database.max_connections))
|
||||||
)
|
)
|
||||||
@@ -414,7 +418,7 @@ class DorisConfig:
|
|||||||
"fe_http_port": self.database.fe_http_port,
|
"fe_http_port": self.database.fe_http_port,
|
||||||
"be_hosts": self.database.be_hosts,
|
"be_hosts": self.database.be_hosts,
|
||||||
"be_webserver_port": self.database.be_webserver_port,
|
"be_webserver_port": self.database.be_webserver_port,
|
||||||
"min_connections": self.database.min_connections,
|
"min_connections": self.database.min_connections, # Always 0, shown for reference
|
||||||
"max_connections": self.database.max_connections,
|
"max_connections": self.database.max_connections,
|
||||||
"connection_timeout": self.database.connection_timeout,
|
"connection_timeout": self.database.connection_timeout,
|
||||||
"health_check_interval": self.database.health_check_interval,
|
"health_check_interval": self.database.health_check_interval,
|
||||||
@@ -492,11 +496,8 @@ class DorisConfig:
|
|||||||
if not self.database.user:
|
if not self.database.user:
|
||||||
errors.append("Database username cannot be empty")
|
errors.append("Database username cannot be empty")
|
||||||
|
|
||||||
if self.database.min_connections <= 0:
|
if self.database.max_connections <= 0:
|
||||||
errors.append("Minimum connections must be greater than 0")
|
errors.append("Maximum connections must be greater than 0")
|
||||||
|
|
||||||
if self.database.max_connections <= self.database.min_connections:
|
|
||||||
errors.append("Maximum connections must be greater than minimum connections")
|
|
||||||
|
|
||||||
# Validate security configuration
|
# Validate security configuration
|
||||||
if self.security.auth_type not in ["token", "basic", "oauth"]:
|
if self.security.auth_type not in ["token", "basic", "oauth"]:
|
||||||
@@ -549,7 +550,7 @@ class DorisConfig:
|
|||||||
return {
|
return {
|
||||||
"server": f"{self.server_name} v{self.server_version}",
|
"server": f"{self.server_name} v{self.server_version}",
|
||||||
"database": f"{self.database.host}:{self.database.port}/{self.database.database}",
|
"database": f"{self.database.host}:{self.database.port}/{self.database.database}",
|
||||||
"connection_pool": f"{self.database.min_connections}-{self.database.max_connections}",
|
"connection_pool": f"0-{self.database.max_connections} (min fixed at 0 for stability)",
|
||||||
"security": {
|
"security": {
|
||||||
"auth_type": self.security.auth_type,
|
"auth_type": self.security.auth_type,
|
||||||
"masking_enabled": self.security.enable_masking,
|
"masking_enabled": self.security.enable_masking,
|
||||||
|
|||||||
@@ -219,7 +219,8 @@ class DorisConnectionManager:
|
|||||||
password=self.config.database.password,
|
password=self.config.database.password,
|
||||||
db=self.config.database.database,
|
db=self.config.database.database,
|
||||||
charset="utf8",
|
charset="utf8",
|
||||||
minsize=0, # Avoid pre-creation issues - create connections on demand
|
minsize=self.config.database.min_connections, # Always 0 per configuration to avoid at_eof issues
|
||||||
|
|
||||||
maxsize=self.config.database.max_connections or 20,
|
maxsize=self.config.database.max_connections or 20,
|
||||||
autocommit=True,
|
autocommit=True,
|
||||||
connect_timeout=self.connection_timeout,
|
connect_timeout=self.connection_timeout,
|
||||||
@@ -234,6 +235,8 @@ class DorisConnectionManager:
|
|||||||
|
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"Connection pool initialized successfully with on-demand connection creation, "
|
f"Connection pool initialized successfully with on-demand connection creation, "
|
||||||
|
f"min connections: {self.config.database.min_connections}, "
|
||||||
|
|
||||||
f"max connections: {self.config.database.max_connections or 20}"
|
f"max connections: {self.config.database.max_connections or 20}"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -472,7 +475,7 @@ class DorisConnectionManager:
|
|||||||
if conn.connection and not conn.connection.closed:
|
if conn.connection and not conn.connection.closed:
|
||||||
await conn.connection.ensure_closed()
|
await conn.connection.ensure_closed()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass # Ignore errors during forced close
|
||||||
|
|
||||||
# Close connection wrapper
|
# Close connection wrapper
|
||||||
await conn.close()
|
await conn.close()
|
||||||
|
|||||||
@@ -587,57 +587,51 @@ class DorisQueryExecutor:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Execute query with retry logic
|
# Execute query with retry logic
|
||||||
try:
|
result = await self.execute_query(query_request, auth_context)
|
||||||
result = await self.execute_query(query_request, auth_context)
|
|
||||||
|
|
||||||
# Serialize data for JSON response
|
# Serialize data for JSON response
|
||||||
serialized_data = []
|
serialized_data = []
|
||||||
for row in result.data:
|
for row in result.data:
|
||||||
serialized_data.append(self._serialize_row_data(row))
|
serialized_data.append(self._serialize_row_data(row))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"data": serialized_data,
|
"data": serialized_data,
|
||||||
"row_count": result.row_count,
|
"row_count": result.row_count,
|
||||||
"execution_time": result.execution_time,
|
"execution_time": result.execution_time,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"columns": result.metadata.get("columns", []),
|
"columns": result.metadata.get("columns", []),
|
||||||
"query": sql
|
"query": sql
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
except Exception as query_error:
|
|
||||||
# Check if it's a connection-related error that we should retry
|
|
||||||
error_str = str(query_error).lower()
|
|
||||||
connection_errors = [
|
|
||||||
"at_eof", "connection", "closed", "nonetype",
|
|
||||||
"transport", "reader", "broken pipe", "connection reset"
|
|
||||||
]
|
|
||||||
|
|
||||||
is_connection_error = any(err in error_str for err in connection_errors)
|
|
||||||
|
|
||||||
if is_connection_error and retry_count < max_retries:
|
|
||||||
retry_count += 1
|
|
||||||
self.logger.warning(f"Connection error detected, retrying ({retry_count}/{max_retries}): {query_error}")
|
|
||||||
|
|
||||||
# Release the problematic connection
|
|
||||||
try:
|
|
||||||
await self.connection_manager.release_connection(session_id)
|
|
||||||
except Exception:
|
|
||||||
pass # Ignore cleanup errors
|
|
||||||
|
|
||||||
# Wait a bit before retry
|
|
||||||
await asyncio.sleep(0.5 * retry_count)
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
# Re-raise if not a connection error or max retries exceeded
|
|
||||||
raise query_error
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_msg = str(e)
|
error_msg = str(e)
|
||||||
|
error_str = error_msg.lower()
|
||||||
|
|
||||||
# If we've exhausted retries or it's not a connection error, return error
|
# Check if it's a connection-related error that we should retry
|
||||||
if retry_count >= max_retries or "at_eof" not in error_msg.lower():
|
connection_errors = [
|
||||||
|
"at_eof", "connection", "closed", "nonetype",
|
||||||
|
"transport", "reader", "broken pipe", "connection reset"
|
||||||
|
]
|
||||||
|
|
||||||
|
is_connection_error = any(err in error_str for err in connection_errors)
|
||||||
|
|
||||||
|
if is_connection_error and retry_count < max_retries:
|
||||||
|
retry_count += 1
|
||||||
|
self.logger.warning(f"Connection error detected, retrying ({retry_count}/{max_retries}): {e}")
|
||||||
|
|
||||||
|
# Release the problematic connection
|
||||||
|
try:
|
||||||
|
await self.connection_manager.release_connection(session_id)
|
||||||
|
except Exception:
|
||||||
|
pass # Ignore cleanup errors
|
||||||
|
|
||||||
|
# Wait a bit before retry
|
||||||
|
await asyncio.sleep(0.5 * retry_count)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
# If we've exhausted retries or it's not a connection error, return error
|
||||||
error_analysis = self._analyze_error(error_msg)
|
error_analysis = self._analyze_error(error_msg)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -651,24 +645,17 @@ class DorisQueryExecutor:
|
|||||||
"retry_count": retry_count
|
"retry_count": retry_count
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else:
|
|
||||||
# Try one more time for connection errors
|
# This should never be reached, but just in case
|
||||||
retry_count += 1
|
return {
|
||||||
if retry_count <= max_retries:
|
"success": False,
|
||||||
self.logger.warning(f"Retrying query due to connection error ({retry_count}/{max_retries}): {e}")
|
"error": "Maximum retries exceeded",
|
||||||
await asyncio.sleep(0.5 * retry_count)
|
"data": None,
|
||||||
continue
|
"metadata": {
|
||||||
else:
|
"query": sql,
|
||||||
return {
|
"retry_count": retry_count
|
||||||
"success": False,
|
}
|
||||||
"error": f"Query failed after {max_retries} retries: {error_msg}",
|
}
|
||||||
"data": None,
|
|
||||||
"metadata": {
|
|
||||||
"query": sql,
|
|
||||||
"error_details": error_msg,
|
|
||||||
"retry_count": retry_count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def _serialize_row_data(self, row_data: Dict[str, Any]) -> Dict[str, Any]:
|
def _serialize_row_data(self, row_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Serialize row data for JSON response"""
|
"""Serialize row data for JSON response"""
|
||||||
|
|||||||
Reference in New Issue
Block a user