Are Your YSQL Sessions Connecting to the Preferred Region?

Suppose your YugabyteDB universe spans three regions. You’ve ranked one region as the first choice for tablet leaders and the others as fallbacks. Your applications, however, connect to YSQL nodes through a driver or load balancer. Those connections may land in a different region from the first-choice leaders.

How do you see where the connections landed? And how do those locations compare with the leader preference you configured?

In this first Tip, we’ll read the preference ranks from the universe’s cluster config and compare them with current YSQL sessions. We’ll then use Active Session History (ASH) to look back at sampled activity from queries that may have finished before we ran the live check.

The Three-Region Universe

My RF3 test universe has one node in each of these AWS regions:

Cloud / region / zone Leader preference rank Role
aws.us-east-1.us-east-1a 1 First choice
aws.us-east-2.us-east-2a 2 First fallback
aws.us-west-2.us-west-2a 3 Second fallback

These ranks describe where YugabyteDB prefers to place tablet leaders. Rank 2 and rank 3 are fallback choices; they should not be reported as though they were equivalent to rank 1.

A universe can also have more than one rank 1 location. For example, two regions in an RF3 universe could both contain rank 1 zones. The queries in this Tip handle zones that share a rank.

How Do We Get the Preferred Locations?

There are three reasonable approaches:

Approach What it provides Consideration
Specify locations in a SQL CTE A simple baseline for a one-time check. The SQL must be updated if the preference changes.
Inspect yb_tablet_metadata The locations of user-table leaders currently observed. Observed leadership is not the configured policy. You need eligible user-table tablets in the scope being checked.
Read the cluster config JSON The configured universe-level leader preference and its ranks. Tablespace-specific preferences are stored separately.

A quick hard-coded CTE might look like this:

				
					WITH preferred_regions(cloud, region) AS (
    VALUES
        ('aws'::text, 'us-east-1'::text),
        ('aws'::text, 'us-east-2'::text)
)
				
			

That’s fine for an ad hoc query, but I wanted the Tip to read the actual configuration.

Why I Stopped Using Tablet Leaders as the Baseline

My first attempt tried to infer preferred regions from yb_tablet_metadata. In this test database, the view returned eight tablets, but every row belonged to system and had relname = 'transactions'. It returned no user-table tablets for the database I was checking.

Example:

				
					yugabyte=# SELECT
yugabyte-#     tablet_id,
yugabyte-#     oid,
yugabyte-#     db_name,
yugabyte-#     relname,
yugabyte-#     leader,
yugabyte-#     tablet_state
yugabyte-# FROM yb_tablet_metadata
yugabyte-# LIMIT 20;
            tablet_id             | oid | db_name |   relname    |     leader     | tablet_state
----------------------------------+-----+---------+--------------+----------------+--------------
 0a8c767d3aae46f59322661eece2c074 |     | system  | transactions | 127.0.0.3:5433 | RUNNING
 29b00f56a41c460ea3a110e7f25eb82d |     | system  | transactions | 127.0.0.2:5433 | RUNNING
 380f8938053841ee85f03b494e461b83 |     | system  | transactions | 127.0.0.3:5433 | RUNNING
 4881db2d4acd49cca420b8f03ef1ae5e |     | system  | transactions | 127.0.0.2:5433 | RUNNING
 77c5b30e964a45289b6ed2f904fd8254 |     | system  | transactions | 127.0.0.3:5433 | RUNNING
 905cc81b4a514d71888b6dc5113223df |     | system  | transactions | 127.0.0.2:5433 | RUNNING
 bea553db53a1409ba07aaa0ae28eb323 |     | system  | transactions | 127.0.0.1:5433 | RUNNING
 f5d239d8cca4435fbf12362cf7eb5fb9 |     | system  | transactions | 127.0.0.1:5433 | RUNNING
(8 rows)
				
			

A query restricted to user tables in the default tablespace therefore had no baseline. For that method, you would need at least one eligible user table in the default tablespace. More fundamentally, the locations of currently observed leaders tell you what is happening now, while cluster config tells you what the leader preference is. Transaction status tablet leaders are also balanced across nodes by default rather than following the preferred zones.

Related tip: Transaction tablet leader placement
The system / transactions rows are transaction status tablets. By default, their leaders are balanced across nodes rather than following the universe’s preferred leader zones. The YB-Master flag transaction_tables_use_preferred_zones controls whether they follow those preferences. See Choose Whether Transaction Tablet Leaders Respect Preferred Zones for an example. Their observed leader locations should not be used to infer the configured preference ranks for user-table leaders.

For this Tip, we’ll use cluster config.

Load the Cluster Config Into ysqlsh

I’ve previously shown how to display YugabyteDB cluster config in HTML, JSON, and text formats. Here we’ll use the JSON output from yb-admin get_universe_config.

You can run yb-admin using \! in ysqlsh, but \! only displays the output. To use the JSON in SQL, load it into a temporary table with \copy ... FROM PROGRAM:

				
					CREATE TEMP TABLE yb_universe_config (
    config jsonb
);

\copy yb_universe_config(config) FROM PROGRAM 'yb-admin --init_master_addrs=127.0.0.1 get_universe_config'
				
			

Change the example master address for your environment. The yb-admin executable must be available on the computer where ysqlsh is running. Keep the same ysqlsh session open for the following queries; the table is temporary.

In the JSON, replicationInfo.multiAffinitizedLeaders contains the ranked leader locations. Do not substitute liveReplicas.placementBlocks: those describe where replicas are placed, which is a different setting.

Check Sessions Connected Right Now

Starting with YugabyteDB 2026.1.2, cluster-wide database views let us query session activity from all live YB-TServers in one SQL statement. The feature is currently Tech Preview and must be enabled for the session or role.

				
					SET yb_enable_global_views = true;

WITH preferred_zones AS (
    SELECT DISTINCT
        preference.preference_rank::integer AS preference_rank,
        zone.value->>'placementCloud' AS cloud,
        zone.value->>'placementRegion' AS region,
        zone.value->>'placementZone' AS zone
    FROM yb_universe_config AS cfg
    CROSS JOIN LATERAL jsonb_array_elements(
        COALESCE(
            cfg.config #> '{replicationInfo,multiAffinitizedLeaders}',
            '[]'::jsonb
        )
    ) WITH ORDINALITY AS preference(value, preference_rank)
    CROSS JOIN LATERAL jsonb_array_elements(
        COALESCE(preference.value->'zones', '[]'::jsonb)
    ) AS zone(value)
),
preference_summary AS (
    SELECT
        count(*) AS configured_zone_count,
        string_agg(
            cloud || '/' || region || '/' || zone ||
                ' (rank ' || preference_rank::text || ')',
            ', ' ORDER BY preference_rank, cloud, region, zone
        ) AS configured_preferences
    FROM preferred_zones
),
client_sessions AS (
    SELECT
        s.host AS connected_node,
        s.cloud AS connected_cloud,
        s.region AS connected_region,
        s.zone AS connected_zone,
        g.datname,
        g.pid,
        g.usename,
        g.application_name,
        g.client_addr,
        g.state,
        g.query_id,
        left(g.query, 200) AS query_text,
        matched.preference_rank
    FROM gv$pg_stat_activity AS g
    JOIN yb_servers() AS s
      ON g.server_uuid::uuid = s.uuid::uuid
    LEFT JOIN LATERAL (
        SELECT min(p.preference_rank) AS preference_rank
        FROM preferred_zones AS p
        WHERE p.cloud = s.cloud
          AND (p.region = s.region OR p.region = '*')
          AND (p.zone = s.zone OR p.zone = '*')
    ) AS matched ON true
    WHERE g.backend_type = 'client backend'
      AND g.client_addr IS NOT NULL
      AND g.datname = current_database()
)
SELECT
    cs.connected_node,
    cs.connected_cloud,
    cs.connected_region,
    cs.connected_zone,
    cs.datname,
    cs.pid,
    cs.usename,
    cs.application_name,
    cs.client_addr,
    cs.state,
    cs.query_id,
    cs.query_text,
    cs.preference_rank,
    ps.configured_preferences,
    CASE
        WHEN ps.configured_zone_count = 0
            THEN 'NO BASELINE: no cluster-level preference found'
        WHEN cs.preference_rank = 1
            THEN 'FIRST CHOICE: rank 1'
        WHEN cs.preference_rank > 1
            THEN 'REVIEW: fallback rank ' ||
                 cs.preference_rank::text
        ELSE 'REVIEW: unranked location'
    END AS placement_check
FROM client_sessions AS cs
CROSS JOIN preference_summary AS ps
ORDER BY cs.connected_region, cs.connected_node, cs.pid;
				
			

The query matches the connected node’s cloud, region, and zone against the configured entries. It recognizes * wildcards and uses the lowest matching rank. Matching the zone matters when different zones in one region have different preferences.

It checks sessions in the current database, whether their state is active or idle. Remove AND g.datname = current_database() to include all databases. You may also see the ysqlsh session running this query in the results. For an idle session, query_text contains its last statement, not a statement currently executing.

A rank 2, rank 3, or unranked result says review the routing. It does not, by itself, establish a fault. The application may deliberately connect there, or a fallback may be in use.

What If the Query Already Finished?

gv$pg_stat_activity shows sessions that exist while the view is read. A short-lived connection could be gone by the time we check. Also, the global view reads nodes sequentially, so it does not produce a perfectly simultaneous snapshot.

For a recent window of sampled active YSQL work, we can query the cluster-wide ASH view. The query below uses the same temporary config table created earlier.

				
					SELECT now() - interval '10 minutes' AS cutoff \gset

WITH preferred_zones AS (
    SELECT DISTINCT
        preference.preference_rank::integer AS preference_rank,
        zone.value->>'placementCloud' AS cloud,
        zone.value->>'placementRegion' AS region,
        zone.value->>'placementZone' AS zone
    FROM yb_universe_config AS cfg
    CROSS JOIN LATERAL jsonb_array_elements(
        COALESCE(
            cfg.config #> '{replicationInfo,multiAffinitizedLeaders}',
            '[]'::jsonb
        )
    ) WITH ORDINALITY AS preference(value, preference_rank)
    CROSS JOIN LATERAL jsonb_array_elements(
        COALESCE(preference.value->'zones', '[]'::jsonb)
    ) AS zone(value)
),
preference_summary AS (
    SELECT count(*) AS configured_zone_count
    FROM preferred_zones
),
activity_samples AS (
    SELECT
        s.host AS executing_node,
        s.cloud AS executing_cloud,
        s.region AS executing_region,
        s.zone AS executing_zone,
        ash.top_level_node_id,
        ash.pid,
        ash.query_id,
        ash.client_node_ip,
        ash.sample_time,
        ash.wait_event,
        ash.wait_event_type,
        matched.preference_rank
    FROM gv$yb_active_session_history AS ash
    JOIN yb_servers() AS s
      ON ash.top_level_node_id = s.uuid::uuid
    LEFT JOIN LATERAL (
        SELECT min(p.preference_rank) AS preference_rank
        FROM preferred_zones AS p
        WHERE p.cloud = s.cloud
          AND (p.region = s.region OR p.region = '*')
          AND (p.zone = s.zone OR p.zone = '*')
    ) AS matched ON true
    WHERE ash.sample_time >= :'cutoff'
      AND ash.client_node_ip IS NOT NULL
      AND ash.wait_event_component = 'YSQL'
)
SELECT
    a.executing_node,
    a.executing_cloud,
    a.executing_region,
    a.executing_zone,
    a.top_level_node_id,
    a.pid,
    a.query_id,
    a.client_node_ip,
    a.preference_rank,
    CASE
        WHEN ps.configured_zone_count = 0
            THEN 'NO BASELINE: no cluster-level preference found'
        WHEN a.preference_rank = 1
            THEN 'FIRST CHOICE: rank 1'
        WHEN a.preference_rank > 1
            THEN 'REVIEW: fallback rank ' ||
                 a.preference_rank::text
        ELSE 'REVIEW: unranked location'
    END AS placement_check,
    min(a.sample_time) AS first_sample,
    max(a.sample_time) AS last_sample,
    count(*) AS captured_samples,
    a.wait_event,
    a.wait_event_type
FROM activity_samples AS a
CROSS JOIN preference_summary AS ps
GROUP BY
    a.executing_node,
    a.executing_cloud,
    a.executing_region,
    a.executing_zone,
    a.top_level_node_id,
    a.pid,
    a.query_id,
    a.client_node_ip,
    a.preference_rank,
    ps.configured_zone_count,
    a.wait_event,
    a.wait_event_type
ORDER BY
    a.executing_region,
    a.executing_node,
    captured_samples DESC;
				
			

ASH’s top_level_node_id identifies the YSQL node associated with the sampled query. Joining it to yb_servers() gives us that node’s location. The \gset command captures a single cutoff value, which helps the time filter reach the nodes holding the ASH data.

captured_samples is a sample count, not an execution count. ASH can miss short queries between samples. It supplies a query_id, but this view does not provide the full SQL text, database name, or application name. See the ASH documentation for the view’s columns and sampling behavior.

💡 What to investigate next: Compare the locations returned here with the application’s driver or load-balancer configuration. If the workload uses tablespaces with their own leader preferences, inspect those separately: tablespace preferences do not come from the universe-level multiAffinitizedLeaders list.

The Takeaway

Preferred leader zones influence where YugabyteDB places tablet leaders; they do not determine where YSQL clients connect. The live session query shows where clients are connected now, while Active Session History shows where sampled YSQL activity originated over time. Neither captures every query.

If connections repeatedly land outside the rank 1 leader zones, check the application’s driver and load balancer configuration. Also check whether the workload uses tablespaces with their own leader preferences, which need to be evaluated separately. A connection in another region is a reason to investigate possible cross-region hops, not proof that routing is wrong.

In the next Tip, we’ll look at where user-table leaders actually reside and compare their locations with the configured preferences.

Have Fun!

Our POD is loaded and headed to our new house in Dallas! 🚛🏡 After getting it onto the truck, the driver turned to us and said, “That was heavy!”

Apparently, we packed a lot of memories… and a few too many other things. 😂 Next stop: Texas! 🤠