AlwaysOn ReadIntent via SSMS not routing?

  • Hey all,

    I setup an AG with a listener (AO1) on three servers.

    Server1 = Primary, Readable Secondary = No

    Server2 = Secondary, Readable Secondary = Read-intent only

    Server3 = Secondary, Readable Secondary = Read-intent only

    Connecting to AO1 with default settings, I end up on Server1.

    If I attempt to connect to AO1 with specifying ;ApplicationIntent=ReadOnly (with or without specifying the db name) in SSMS, still lands me on Server1.

    What am I missing here?

    Also on topic, in this scenario I would actually want Server1 to be a readable secondary (in the event of a fail-over). So If I did set it as Yes, even If I do get the issue above working, won't I always land on Server1 then?

    Thanks!

  • can you provide details of the scripts you used to define the read only routing configuration (obfuscate any computer names and domain, etc)

    -----------------------------------------------------------------------------------------------------------

    "Ya can't make an omelette without breaking just a few eggs" 😉

  • Just went through a new setup:

    CREATE DATABASE MyDB4

    BACKUP DATABASE MyDB4 TO DISK = 'c:\Backup\MyDB4.bak'

    --- YOU MUST EXECUTE THE FOLLOWING SCRIPT IN SQLCMD MODE.

    :Connect SQL01\AO1

    IF (SELECT state FROM sys.endpoints WHERE name = N'Hadr_endpoint') <> 0

    BEGIN

    ALTER ENDPOINT [Hadr_endpoint] STATE = STARTED

    END

    GO

    use [master]

    GO

    GRANT CONNECT ON ENDPOINT::[Hadr_endpoint] TO [CONTOSO\svcSQLAdm1]

    GO

    :Connect SQL02\AO2

    IF (SELECT state FROM sys.endpoints WHERE name = N'Hadr_endpoint') <> 0

    BEGIN

    ALTER ENDPOINT [Hadr_endpoint] STATE = STARTED

    END

    GO

    use [master]

    GO

    GRANT CONNECT ON ENDPOINT::[Hadr_endpoint] TO [CONTOSO\svcSQLAdm1]

    GO

    :Connect SQL03\AO3

    IF (SELECT state FROM sys.endpoints WHERE name = N'Hadr_endpoint') <> 0

    BEGIN

    ALTER ENDPOINT [Hadr_endpoint] STATE = STARTED

    END

    GO

    use [master]

    GO

    GRANT CONNECT ON ENDPOINT::[Hadr_endpoint] TO [CONTOSO\svcSQLAdm1]

    GO

    :Connect SQL01\AO1

    IF EXISTS(SELECT * FROM sys.server_event_sessions WHERE name='AlwaysOn_health')

    BEGIN

    ALTER EVENT SESSION [AlwaysOn_health] ON SERVER WITH (STARTUP_STATE=ON);

    END

    IF NOT EXISTS(SELECT * FROM sys.dm_xe_sessions WHERE name='AlwaysOn_health')

    BEGIN

    ALTER EVENT SESSION [AlwaysOn_health] ON SERVER STATE=START;

    END

    GO

    :Connect SQL02\AO2

    IF EXISTS(SELECT * FROM sys.server_event_sessions WHERE name='AlwaysOn_health')

    BEGIN

    ALTER EVENT SESSION [AlwaysOn_health] ON SERVER WITH (STARTUP_STATE=ON);

    END

    IF NOT EXISTS(SELECT * FROM sys.dm_xe_sessions WHERE name='AlwaysOn_health')

    BEGIN

    ALTER EVENT SESSION [AlwaysOn_health] ON SERVER STATE=START;

    END

    GO

    :Connect SQL03\AO3

    IF EXISTS(SELECT * FROM sys.server_event_sessions WHERE name='AlwaysOn_health')

    BEGIN

    ALTER EVENT SESSION [AlwaysOn_health] ON SERVER WITH (STARTUP_STATE=ON);

    END

    IF NOT EXISTS(SELECT * FROM sys.dm_xe_sessions WHERE name='AlwaysOn_health')

    BEGIN

    ALTER EVENT SESSION [AlwaysOn_health] ON SERVER STATE=START;

    END

    GO

    :Connect SQL01\AO1

    USE [master]

    GO

    CREATE AVAILABILITY GROUP [AG4]

    WITH (AUTOMATED_BACKUP_PREFERENCE = SECONDARY)

    FOR DATABASE [MyDB4]

    REPLICA ON N'SQL01\AO1' WITH (ENDPOINT_URL = N'TCP://SQL01.contoso.local:5022', FAILOVER_MODE = AUTOMATIC, AVAILABILITY_MODE = SYNCHRONOUS_COMMIT, BACKUP_PRIORITY = 50, SECONDARY_ROLE(ALLOW_CONNECTIONS = ALL)),

    N'SQL02\AO2' WITH (ENDPOINT_URL = N'TCP://SQL02.contoso.local:5022', FAILOVER_MODE = AUTOMATIC, AVAILABILITY_MODE = SYNCHRONOUS_COMMIT, BACKUP_PRIORITY = 50, SECONDARY_ROLE(ALLOW_CONNECTIONS = READ_ONLY)),

    N'SQL03\AO3' WITH (ENDPOINT_URL = N'TCP://SQL03.contoso.local:5022', FAILOVER_MODE = MANUAL, AVAILABILITY_MODE = ASYNCHRONOUS_COMMIT, BACKUP_PRIORITY = 50, SECONDARY_ROLE(ALLOW_CONNECTIONS = READ_ONLY));

    GO

    :Connect SQL01\AO1

    USE [master]

    GO

    ALTER AVAILABILITY GROUP [AG4]

    ADD LISTENER N'DBP_AOTEST4' (

    WITH IP

    ((N'192.168.201.26', N'255.255.255.0')

    )

    , PORT=1433);

    GO

    :Connect SQL02\AO2

    ALTER AVAILABILITY GROUP [AG4] JOIN;

    GO

    :Connect SQL03\AO3

    ALTER AVAILABILITY GROUP [AG4] JOIN;

    GO

    :Connect SQL01\AO1

    BACKUP DATABASE [MyDB4] TO DISK = N'\\SQL01\Backup\MyDB4.bak' WITH COPY_ONLY, FORMAT, INIT, SKIP, REWIND, NOUNLOAD, COMPRESSION, STATS = 5

    GO

    :Connect SQL02\AO2

    RESTORE DATABASE [MyDB4] FROM DISK = N'\\SQL01\Backup\MyDB4.bak' WITH NORECOVERY, NOUNLOAD, STATS = 5

    GO

    :Connect SQL03\AO3

    RESTORE DATABASE [MyDB4] FROM DISK = N'\\SQL01\Backup\MyDB4.bak' WITH NORECOVERY, NOUNLOAD, STATS = 5

    GO

    :Connect SQL01\AO1

    BACKUP LOG [MyDB4] TO DISK = N'\\SQL01\Backup\MyDB4_20141116175437.trn' WITH NOFORMAT, NOINIT, NOSKIP, REWIND, NOUNLOAD, COMPRESSION, STATS = 5

    GO

    :Connect SQL02\AO2

    RESTORE LOG [MyDB4] FROM DISK = N'\\SQL01\Backup\MyDB4_20141116175437.trn' WITH NORECOVERY, NOUNLOAD, STATS = 5

    GO

    :Connect SQL02\AO2

    -- Wait for the replica to start communicating

    begin try

    declare @conn bit

    declare @count int

    declare @replica_id uniqueidentifier

    declare @group_id uniqueidentifier

    set @conn = 0

    set @count = 30 -- wait for 5 minutes

    if (serverproperty('IsHadrEnabled') = 1)

    and (isnull((select member_state from master.sys.dm_hadr_cluster_members where upper(member_name COLLATE Latin1_General_CI_AS) = upper(cast(serverproperty('ComputerNamePhysicalNetBIOS') as nvarchar(256)) COLLATE Latin1_General_CI_AS)), 0) <> 0)

    and (isnull((select state from master.sys.database_mirroring_endpoints), 1) = 0)

    begin

    select @group_id = ags.group_id from master.sys.availability_groups as ags where name = N'AG4'

    select @replica_id = replicas.replica_id from master.sys.availability_replicas as replicas where upper(replicas.replica_server_name COLLATE Latin1_General_CI_AS) = upper(@@SERVERNAME COLLATE Latin1_General_CI_AS) and group_id = @group_id

    while @conn <> 1 and @count > 0

    begin

    set @conn = isnull((select connected_state from master.sys.dm_hadr_availability_replica_states as states where states.replica_id = @replica_id), 1)

    if @conn = 1

    begin

    -- exit loop when the replica is connected, or if the query cannot find the replica status

    break

    end

    waitfor delay '00:00:10'

    set @count = @count - 1

    end

    end

    end try

    begin catch

    -- If the wait loop fails, do not stop execution of the alter database statement

    end catch

    ALTER DATABASE [MyDB4] SET HADR AVAILABILITY GROUP = [AG4];

    GO

    :Connect SQL03\AO3

    RESTORE LOG [MyDB4] FROM DISK = N'\\SQL01\Backup\MyDB4_20141116175437.trn' WITH NORECOVERY, NOUNLOAD, STATS = 5

    GO

    :Connect SQL03\AO3

    -- Wait for the replica to start communicating

    begin try

    declare @conn bit

    declare @count int

    declare @replica_id uniqueidentifier

    declare @group_id uniqueidentifier

    set @conn = 0

    set @count = 30 -- wait for 5 minutes

    if (serverproperty('IsHadrEnabled') = 1)

    and (isnull((select member_state from master.sys.dm_hadr_cluster_members where upper(member_name COLLATE Latin1_General_CI_AS) = upper(cast(serverproperty('ComputerNamePhysicalNetBIOS') as nvarchar(256)) COLLATE Latin1_General_CI_AS)), 0) <> 0)

    and (isnull((select state from master.sys.database_mirroring_endpoints), 1) = 0)

    begin

    select @group_id = ags.group_id from master.sys.availability_groups as ags where name = N'AG4'

    select @replica_id = replicas.replica_id from master.sys.availability_replicas as replicas where upper(replicas.replica_server_name COLLATE Latin1_General_CI_AS) = upper(@@SERVERNAME COLLATE Latin1_General_CI_AS) and group_id = @group_id

    while @conn <> 1 and @count > 0

    begin

    set @conn = isnull((select connected_state from master.sys.dm_hadr_availability_replica_states as states where states.replica_id = @replica_id), 1)

    if @conn = 1

    begin

    -- exit loop when the replica is connected, or if the query cannot find the replica status

    break

    end

    waitfor delay '00:00:10'

    set @count = @count - 1

    end

    end

    end try

    begin catch

    -- If the wait loop fails, do not stop execution of the alter database statement

    end catch

    ALTER DATABASE [MyDB4] SET HADR AVAILABILITY GROUP = [AG4];

    GO

    GO

    I connect to the listener "DBP_AOTEST4" with no changes.

    SELECT @@SERVERNAME

    = SQL01

    Connect to DBP_AOTEST4 with ;ApplicationIntent=ReadOnly

    SELECT @@SERVERNAME

    = SQL01

    Connect to DBP_AOTEST4 with ;ApplicationIntent=ReadOnly + default database = MyDB4

    SELECT @@SERVERNAME

    = SQL01

    What am I doing wrong?

    Thanks

  • you have added a listener but I can't see the scripts to configure your readonly routing?

    See my guide at this link[/url] and scroll down to the section "Configure ReadOnly Routing" for more info

    -----------------------------------------------------------------------------------------------------------

    "Ya can't make an omelette without breaking just a few eggs" 😉

  • Will try later; however I defined the read intent via wizard and it's in the AG creation script above. You have to go back and state read intent a second time to make it work? Seems counter productive?

  • Adam Bean (11/16/2014)


    it's in the AG creation script above.

    mmm, nope, just been through your script and there are no readonly routing urls or secondary\primary configuration scripts. Please read my guide

    Adam Bean (11/16/2014)


    You have to go back and state read intent a second time to make it work? Seems counter productive?

    you just need to run the appropriate routing config scripts.

    Incidentally what user accounts are your AO group replicas running under. In order for the Listener network name SPN to function properly all replicas must use the same service account.

    -----------------------------------------------------------------------------------------------------------

    "Ya can't make an omelette without breaking just a few eggs" 😉

  • Ok cool, I'll check into it when I get home.

    Thanks

  • how you getting on?

    -----------------------------------------------------------------------------------------------------------

    "Ya can't make an omelette without breaking just a few eggs" 😉

  • Hi Perry,

    So first and foremost, why does using the GUI and specifying the Readable Secondary not take into account any of this? Am I missing something in the AG setup, or is manually building the routing and preferences a mandatory step?

    I'm assuming the latter; however ...

    -- Configure ReadOnly Routing

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON

    N'SQL01\AO1' WITH (SECONDARY_ROLE (ALLOW_CONNECTIONS = ALL));

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON

    N'SQL01\AO1' WITH (SECONDARY_ROLE (READ_ONLY_ROUTING_URL = N'TCP://SQL01.contoso.local:5022'));

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON

    N'SQL02\AO2' WITH (SECONDARY_ROLE (ALLOW_CONNECTIONS = ALL));

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON

    N'SQL02\AO2' WITH (SECONDARY_ROLE (READ_ONLY_ROUTING_URL = N'TCP://SQL02.contoso.local:5022'));

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON

    N'SQL03\AO3' WITH (SECONDARY_ROLE (ALLOW_CONNECTIONS = READ_ONLY));

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON

    N'SQL03\AO3' WITH (SECONDARY_ROLE (READ_ONLY_ROUTING_URL = N'TCP://SQL03.contoso.local:5022'));

    That works great, and that is actually handled with the wizard - readable secondary, correct? Changing these values simply updates the availability replicas within the general tab. Looking at my original create script, yes, I believe this part is redundant, can you confirm?

    As for the second part:

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON

    N'SQL01\AO1' WITH (PRIMARY_ROLE (READ_ONLY_ROUTING_LIST = ('SQL03\AO3','SQL02\A02','SQLO1\AO1')));

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON

    N'SQL02\AO2' WITH (PRIMARY_ROLE (READ_ONLY_ROUTING_LIST = ('SQL03\A03','SQLO1\AO1','SQL02\AO2')));

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON

    N'SQL03\AO3' WITH (PRIMARY_ROLE (READ_ONLY_ROUTING_LIST = ('SQL01\A01','SQLO2\AO2','SQL03\AO3')));

    So first a question. The intent of the above is to modify the AG when it's acting as the primary. So for example, that first line above. When AG4 lives on SQL01\A01 and someone specifies a read-only, it will first go to SQL03\AO3, then SQL02\A02, an finally back to SQL01\A01 - Do I have that correct?

    Moving on, running the above results in:

    Msg 19403, Level 16, State 17, Line 21

    The availability replica 'SQL02\A02' specified in the READ_ONLY_ROUTING_LIST for availability replica 'SQL01\AO1' does not exist. Only availability replicas that belong to the specified availability group 'AG4' can be added to this list. To get the names of availability replicas in a given availability group, select replica_server_name from sys.availability_replicas and name from sys.availability_groups. For more information, see SQL Server Books Online.

    Msg 19403, Level 16, State 17, Line 24

    The availability replica 'SQL03\A03' specified in the READ_ONLY_ROUTING_LIST for availability replica 'SQL02\AO2' does not exist. Only availability replicas that belong to the specified availability group 'AG4' can be added to this list. To get the names of availability replicas in a given availability group, select replica_server_name from sys.availability_replicas and name from sys.availability_groups. For more information, see SQL Server Books Online.

    Msg 19403, Level 16, State 17, Line 27

    The availability replica 'SQL01\A01' specified in the READ_ONLY_ROUTING_LIST for availability replica 'SQL03\AO3' does not exist. Only availability replicas that belong to the specified availability group 'AG4' can be added to this list. To get the names of availability replicas in a given availability group, select replica_server_name from sys.availability_replicas and name from sys.availability_groups. For more information, see SQL Server Books Online.

    I then view:

    select * from sys.availability_replicas ar

    join sys.availability_groups ag

    on ar.group_id = ag.group_id

    where ag.name = 'AG4'

    I've attached the output. What am I missing here? Each of the instances is associated for the AG.

    I feel like I'm missing a fundamental concept here about availability groups ...

    Thank you for your help!

  • The ReadOnly routing URL setup is a manul process via PowerShell or TSQL, your readonly routing url and your mirror endpoint urls are the same, this is incorrect! Refer back to my article for the following section

    Perry Whittle - SQL Server 2012 AlwaysOn Groups and FCIs Part 4


    Configure ReadOnly Routing

    After creating your highly available AlwaysOn Availability Group with a Listener, you'll likely want to configure ReadOnly routing to better manage ReadOnly requests. Before we can redirect clients to a readonly intent replica we must first create a listener, then we must configure the read only routing lists for the AlwaysOn replicas. We've already created the listener during the group deployment, so now it's time to configure the read only routing lists required to allow connections to the AlwaysOn group read intent replicas. Below is the T-SQL code used to configure my instances;

    This first code block sets up the secondary role preference for each replica that will be used for Read Only routing. Substitute the items in red, these being the SQL instance ServerName and also the routing URL for the database engine itself (not the mirror endpoint URL or the listener URL). From the Primary replica use the following;

    -----------------------------------------------------------------------------------------------------------

    "Ya can't make an omelette without breaking just a few eggs" 😉

  • This should be the database engine url not the mirror endpoint as you have specified

    READ_ONLY_ROUTING_URL = N'TCP://SQL01.contoso.local:5022'));

    So, as an example, if i set my sql server instance called bob on server SQLAO01 in domain mydomain.com to port 58001, I would use the following to set my routing URL

    READ_ONLY_ROUTING_URL = N'TCP://SQLAO01.mydomain.com:58001'));

    My mirror endpoint would still be

    N'TCP://SQLAO01.mydomain.com:5022'));

    -----------------------------------------------------------------------------------------------------------

    "Ya can't make an omelette without breaking just a few eggs" 😉

  • assuming you're sorted now??

    -----------------------------------------------------------------------------------------------------------

    "Ya can't make an omelette without breaking just a few eggs" 😉

  • Ok, so switched to the engine port #:

    -- Configure ReadOnly Routing

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON N'SQL01\AO1' WITH (SECONDARY_ROLE (ALLOW_CONNECTIONS = ALL));

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON N'SQL01\AO1' WITH (SECONDARY_ROLE (READ_ONLY_ROUTING_URL = N'TCP://SQL01.contoso.local:1433'));

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON N'SQL02\AO2' WITH (SECONDARY_ROLE (ALLOW_CONNECTIONS = ALL));

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON N'SQL02\AO2' WITH (SECONDARY_ROLE (READ_ONLY_ROUTING_URL = N'TCP://SQL02.contoso.local:1433'));

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON N'SQL03\AO3' WITH (SECONDARY_ROLE (ALLOW_CONNECTIONS = READ_ONLY));

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON N'SQL03\AO3' WITH (SECONDARY_ROLE (READ_ONLY_ROUTING_URL = N'TCP://SQL03.contoso.local:1433'));

    Yet when setting up the routing preferences, same error exists:

    -- Routing preferences

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON N'SQL01\AO1' WITH (PRIMARY_ROLE (READ_ONLY_ROUTING_LIST = ('SQL03\AO3','SQL02\A02','SQLO1\AO1')));

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON N'SQL02\AO2' WITH (PRIMARY_ROLE (READ_ONLY_ROUTING_LIST = ('SQL03\A03','SQLO1\AO1','SQL02\AO2')));

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON N'SQL03\AO3' WITH (PRIMARY_ROLE (READ_ONLY_ROUTING_LIST = ('SQL01\A01','SQLO2\AO2','SQL03\AO3')));

    =

    Msg 19403, Level 16, State 17, Line 15

    The availability replica 'SQL02\A02' specified in the READ_ONLY_ROUTING_LIST for availability replica 'SQL01\AO1' does not exist. Only availability replicas that belong to the specified availability group 'AG4' can be added to this list. To get the names of availability replicas in a given availability group, select replica_server_name from sys.availability_replicas and name from sys.availability_groups. For more information, see SQL Server Books Online.

    Msg 19403, Level 16, State 17, Line 18

    The availability replica 'SQL03\A03' specified in the READ_ONLY_ROUTING_LIST for availability replica 'SQL02\AO2' does not exist. Only availability replicas that belong to the specified availability group 'AG4' can be added to this list. To get the names of availability replicas in a given availability group, select replica_server_name from sys.availability_replicas and name from sys.availability_groups. For more information, see SQL Server Books Online.

    Msg 19403, Level 16, State 17, Line 21

    The availability replica 'SQL01\A01' specified in the READ_ONLY_ROUTING_LIST for availability replica 'SQL03\AO3' does not exist. Only availability replicas that belong to the specified availability group 'AG4' can be added to this list. To get the names of availability replicas in a given availability group, select replica_server_name from sys.availability_replicas and name from sys.availability_groups. For more information, see SQL Server Books Online.

  • Would check your replica names if i were you, seems you're using O and not 0 in some of the replica names and that's likely why it fails

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON N'SQL01\AO1' WITH (PRIMARY_ROLE (READ_ONLY_ROUTING_LIST = ('SQL03\AO3','SQL02\A02','SQLO1\AO1')));

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON N'SQL02\AO2' WITH (PRIMARY_ROLE (READ_ONLY_ROUTING_LIST = ('SQL03\A03','SQLO1\AO1','SQL02\AO2')));

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON N'SQL03\AO3' WITH (PRIMARY_ROLE (READ_ONLY_ROUTING_LIST = ('SQL01\A01','SQLO2\AO2','SQL03\AO3')));

    -----------------------------------------------------------------------------------------------------------

    "Ya can't make an omelette without breaking just a few eggs" 😉

  • Wow! hahaha, that's what it was ... oh my, I feel smart. Yeah, using 0's and 0's probably wasn't the best idea in my test lab.

    Thank you!

    Even though that did work, I'm still missing something. When I connect to the listener with ;ApplicationIntent=ReadOnly, it still lands on the current primary (which is SQL01\AO1):

    Shouldn't I have landed on SQL03 first?

    ALTER AVAILABILITY GROUP [AG4] MODIFY REPLICA ON N'SQL01\AO1' WITH (PRIMARY_ROLE (READ_ONLY_ROUTING_LIST = ('SQL03\AO3','SQL02\AO2','SQL01\AO1')));

Viewing 15 posts - 1 through 15 (of 17 total)

You must be logged in to reply to this topic. Login to reply