Skip to main content
Glama
cloudthinker-ai

Postgres MCP Pro Plus

get_blocking_queries

Identify and analyze blocking queries in PostgreSQL databases to resolve performance issues, providing lock details, hierarchy, and actionable recommendations.

Instructions

Get comprehensive blocking queries analysis with lock information, hierarchy, and recommendations

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • MCP tool handler and registration for 'get_blocking_queries'. Decorated with @mcp.tool and implements the tool execution logic by instantiating BlockingQueriesAnalyzer and calling its get_blocking_queries method.
    @mcp.tool(description="Get comprehensive blocking queries analysis with lock information, hierarchy, and recommendations")
    async def get_blocking_queries() -> ResponseType:
        """Get comprehensive information about blocking queries and locks in the database with analysis and recommendations."""
        try:
            sql_driver = await get_sql_driver()
            analyzer = BlockingQueriesAnalyzer(sql_driver)
            result = await analyzer.get_blocking_queries()
            return format_text_response(result)
        except Exception as e:
            logger.error(f"Error getting blocking queries: {e}")
            return format_error_response(str(e))
  • Core helper method in BlockingQueriesAnalyzer class that implements the detailed logic for analyzing blocking queries, including SQL queries for data collection, graph generation, recommendations, and text formatting.
    async def get_blocking_queries(self) -> str:
        """
        Get comprehensive blocking queries analysis using modern PostgreSQL features.
        Enhanced with lock wait graph visualization, deadlock detection, session termination
        recommendations, lock timeout suggestions, and historical analysis.
    
        Returns:
            String containing blocking queries data, summary, and recommendations
        """
        try:
            # Get comprehensive blocking analysis
            blocking_data = await self._get_blocking_data()
    
            if not blocking_data:
                # Get deadlock information even when no current blocking
                deadlock_info = await self._get_deadlock_analysis()
                lock_contention_hotspots = await self._get_lock_contention_hotspots()
    
                result = {
                    "status": "healthy",
                    "message": "No blocking queries found - all queries are running without locks.",
                    "blocking_queries": [],
                    "summary": {"total_blocked": 0, "total_blocking": 0, "max_wait_time": 0, "affected_relations": []},
                    "deadlock_analysis": deadlock_info,
                    "lock_contention_hotspots": lock_contention_hotspots,
                    "recommendations": await self._generate_healthy_recommendations(),
                }
                return self._format_as_text(result)
    
            # Enhanced analysis for blocking queries
            lock_wait_graph = await self._generate_lock_wait_graph(blocking_data)
            deadlock_info = await self._get_deadlock_analysis()
            session_termination_recs = await self._generate_session_termination_recommendations(blocking_data)
            lock_timeout_suggestions = await self._generate_lock_timeout_suggestions(blocking_data)
            historical_analysis = await self._get_historical_blocking_analysis()
            lock_escalation_info = await self._detect_lock_escalation(blocking_data)
            query_pattern_analysis = await self._analyze_query_patterns(blocking_data)
            batch_operation_impact = await self._analyze_batch_operation_impact(blocking_data)
            lock_contention_hotspots = await self._get_lock_contention_hotspots()
    
            # Process blocking queries data with improved structure
            blocking_pids = set()
            blocked_pids = set()
            relations = set()
            max_wait_time = 0
    
            for block in blocking_data:
                duration = block["blocked_process"]["duration_seconds"]
                max_wait_time = max(max_wait_time, duration)
    
                if block["blocking_process"]["pid"]:
                    blocking_pids.add(block["blocking_process"]["pid"])
                blocked_pids.add(block["blocked_process"]["pid"])
    
                if block["lock_info"]["affected_relations"]:
                    relations.update(block["lock_info"]["affected_relations"].split(", "))
    
            # Generate enhanced summary
            summary = {
                "total_blocked": len(blocked_pids),
                "total_blocking": len(blocking_pids),
                "max_wait_time_seconds": max_wait_time,
                "affected_relations": list(relations),
                "analysis_timestamp": datetime.now().isoformat(),
                "lock_wait_threshold_alerts": self._check_lock_wait_thresholds(blocking_data),
            }
    
            recommendations = await self._generate_enhanced_recommendations(
                blocking_data,
                summary,
                session_termination_recs,
                lock_timeout_suggestions,
                historical_analysis,
                lock_escalation_info,
                query_pattern_analysis,
            )
    
            result = {
                "status": "blocking_detected",
                "blocking_queries": blocking_data,
                "summary": summary,
                "lock_wait_graph": lock_wait_graph,
                "deadlock_analysis": deadlock_info,
                "session_termination_recommendations": session_termination_recs,
                "lock_timeout_suggestions": lock_timeout_suggestions,
                "historical_analysis": historical_analysis,
                "lock_escalation_detection": lock_escalation_info,
                "query_pattern_analysis": query_pattern_analysis,
                "batch_operation_impact": batch_operation_impact,
                "lock_contention_hotspots": lock_contention_hotspots,
                "recommendations": recommendations,
            }
            return self._format_as_text(result)
        except Exception as e:
            logger.error(f"Error analyzing blocking queries: {e}")
            raise
  • Key helper method that executes the main SQL query to fetch blocking query data from pg_stat_activity and pg_locks, structures it into a list of blocking events.
    async def _get_blocking_data(self) -> List[Dict[str, Any]]:
        """Get comprehensive blocking queries data using modern PostgreSQL features."""
        blocking_query = """
            WITH blocking_tree AS (
                SELECT
                    activity.pid,
                    activity.usename,
                    activity.application_name,
                    activity.client_addr,
                    activity.state,
                    activity.query,
                    activity.query_start,
                    activity.state_change,
                    activity.wait_event,
                    activity.wait_event_type,
                    pg_blocking_pids(activity.pid) AS blocking_pids,
                    EXTRACT(EPOCH FROM (now() - activity.query_start)) AS duration_seconds,
                    EXTRACT(EPOCH FROM (now() - activity.state_change)) AS state_duration_seconds,
                    CASE WHEN activity.query_start IS NOT NULL
                        THEN EXTRACT(EPOCH FROM (now() - activity.query_start))
                        ELSE NULL END AS wait_duration_seconds,
                    activity.backend_start,
                    activity.xact_start
                FROM pg_stat_activity activity
                WHERE activity.pid <> pg_backend_pid()
                    AND activity.state IS NOT NULL
            ),
            lock_details AS (
                SELECT
                    pid,
                    string_agg(DISTINCT locktype, ', ') AS lock_types,
                    string_agg(DISTINCT mode, ', ') AS lock_modes,
                    COUNT(*) AS lock_count,
                    string_agg(DISTINCT CASE
                        WHEN relation IS NOT NULL THEN
                            (SELECT schemaname||'.'||relname
                             FROM pg_stat_user_tables
                             WHERE relid = relation)
                        ELSE NULL END, ', ') AS affected_relations,
                    -- Enhanced lock information
                    string_agg(DISTINCT CASE
                        WHEN locktype = 'relation' AND mode LIKE '%ExclusiveLock' THEN 'TABLE_LOCK'
                        WHEN locktype = 'tuple' THEN 'ROW_LOCK'
                        WHEN locktype = 'transactionid' THEN 'TXN_LOCK'
                        ELSE locktype END, ', ') AS lock_categories
                FROM pg_locks
                WHERE granted = false
                GROUP BY pid
            )
            SELECT
                bt.pid AS blocked_pid,
                bt.usename AS blocked_user,
                bt.application_name AS blocked_application,
                bt.client_addr AS blocked_client_addr,
                bt.state AS blocked_state,
                bt.query AS blocked_query,
                bt.query_start AS blocked_query_start,
                bt.state_change AS blocked_state_change,
                bt.wait_event AS blocked_wait_event,
                bt.wait_event_type AS blocked_wait_event_type,
                bt.duration_seconds AS blocked_duration_seconds,
                bt.state_duration_seconds AS blocked_state_duration_seconds,
                bt.wait_duration_seconds AS blocked_wait_duration_seconds,
                bt.backend_start AS blocked_backend_start,
                bt.xact_start AS blocked_xact_start,
                bt.blocking_pids,
                blocker.pid AS blocking_pid,
                blocker.usename AS blocking_user,
                blocker.application_name AS blocking_application,
                blocker.client_addr AS blocking_client_addr,
                blocker.state AS blocking_state,
                blocker.query AS blocking_query,
                blocker.query_start AS blocking_query_start,
                blocker.duration_seconds AS blocking_duration_seconds,
                blocker.backend_start AS blocking_backend_start,
                blocker.xact_start AS blocking_xact_start,
                ld.lock_types,
                ld.lock_modes,
                ld.lock_count,
                ld.affected_relations,
                ld.lock_categories
            FROM blocking_tree bt
            LEFT JOIN LATERAL unnest(bt.blocking_pids) AS blocking_pid_unnest(pid) ON true
            LEFT JOIN blocking_tree blocker ON blocker.pid = blocking_pid_unnest.pid
            LEFT JOIN lock_details ld ON ld.pid = bt.pid
            WHERE cardinality(bt.blocking_pids) > 0
            ORDER BY bt.duration_seconds DESC;
        """
    
        rows = await self.sql_driver.execute_query(blocking_query)
        if not rows:
            return []
    
        blocking_data = []
        for row in rows:
            duration = float(row.cells["blocked_duration_seconds"]) if row.cells["blocked_duration_seconds"] else 0
    
            blocking_data.append(
                {
                    "blocked_process": {
                        "pid": row.cells["blocked_pid"],
                        "user": row.cells["blocked_user"],
                        "application": row.cells["blocked_application"],
                        "client_addr": row.cells["blocked_client_addr"],
                        "state": row.cells["blocked_state"],
                        "query_start": row.cells["blocked_query_start"],
                        "state_change": row.cells["blocked_state_change"],
                        "wait_event": row.cells["blocked_wait_event"],
                        "wait_event_type": row.cells["blocked_wait_event_type"],
                        "duration_seconds": duration,
                        "state_duration_seconds": (
                            float(row.cells["blocked_state_duration_seconds"]) if row.cells["blocked_state_duration_seconds"] else 0
                        ),
                        "wait_duration_seconds": (
                            float(row.cells["blocked_wait_duration_seconds"]) if row.cells["blocked_wait_duration_seconds"] else 0
                        ),
                        "query": row.cells["blocked_query"],
                        "backend_start": row.cells["blocked_backend_start"],
                        "xact_start": row.cells["blocked_xact_start"],
                    },
                    "blocking_process": {
                        "pid": row.cells["blocking_pid"],
                        "user": row.cells["blocking_user"],
                        "application": row.cells["blocking_application"],
                        "client_addr": row.cells["blocking_client_addr"],
                        "state": row.cells["blocking_state"],
                        "query_start": row.cells["blocking_query_start"],
                        "duration_seconds": float(row.cells["blocking_duration_seconds"]) if row.cells["blocking_duration_seconds"] else 0,
                        "query": row.cells["blocking_query"],
                        "backend_start": row.cells["blocking_backend_start"],
                        "xact_start": row.cells["blocking_xact_start"],
                    },
                    "lock_info": {
                        "types": row.cells["lock_types"],
                        "modes": row.cells["lock_modes"],
                        "count": row.cells["lock_count"],
                        "affected_relations": row.cells["affected_relations"],
                        "categories": row.cells["lock_categories"],
                    },
                    "blocking_hierarchy": {
                        "all_blocking_pids": row.cells["blocking_pids"],
                        "immediate_blocker": row.cells["blocking_pid"],
                    },
                }
            )
    
        return blocking_data
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. While it mentions 'comprehensive blocking queries analysis,' it doesn't describe what this entails operationally—such as whether it requires specific database permissions, if it's read-only or has side effects, how it handles large datasets, or what the output format looks like. For a tool with zero annotation coverage, this is insufficient.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence: 'Get comprehensive blocking queries analysis with lock information, hierarchy, and recommendations.' It's front-loaded with the core purpose and includes key details without unnecessary elaboration. Every word earns its place, making it highly concise and well-structured.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity implied by 'comprehensive analysis' and the lack of annotations and output schema, the description is incomplete. It doesn't explain what 'blocking queries analysis' means in practice, what the recommendations entail, or how the results should be interpreted. For a tool that likely returns detailed database diagnostics, this leaves too much ambiguity for effective use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has 0 parameters, and the schema description coverage is 100%, so there's no need for parameter documentation in the description. The baseline for this scenario is 4, as the description appropriately doesn't waste space on non-existent parameters, though it doesn't add value beyond the schema (which already fully covers the lack of parameters).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Get comprehensive blocking queries analysis with lock information, hierarchy, and recommendations.' It specifies the verb ('Get') and resource ('blocking queries analysis') with additional details about what the analysis includes. However, it doesn't explicitly differentiate from sibling tools like 'analyze_db_health' or 'get_top_queries,' which prevents a score of 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. With sibling tools like 'analyze_db_health,' 'get_top_queries,' and 'execute_sql,' there's no indication of when this specific blocking queries analysis is appropriate, what prerequisites might exist, or when other tools should be used instead. This lack of contextual guidance is a significant gap.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cloudthinker-ai/postgres-mcp-pro-plus'

If you have feedback or need assistance with the MCP directory API, please join our Discord server