November 15, 2014 at 8:30 pm
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!
November 16, 2014 at 10:32 am
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" 😉
November 16, 2014 at 11:00 am
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
November 16, 2014 at 3:54 pm
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" 😉
November 16, 2014 at 3:58 pm
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?
November 16, 2014 at 4:04 pm
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" 😉
November 16, 2014 at 4:08 pm
Ok cool, I'll check into it when I get home.
Thanks
November 18, 2014 at 5:18 am
how you getting on?
-----------------------------------------------------------------------------------------------------------
"Ya can't make an omelette without breaking just a few eggs" 😉
November 20, 2014 at 8:27 am
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!
November 21, 2014 at 6:50 am
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 RoutingAfter 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" 😉
November 21, 2014 at 7:01 am
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" 😉
November 24, 2014 at 6:41 am
assuming you're sorted now??
-----------------------------------------------------------------------------------------------------------
"Ya can't make an omelette without breaking just a few eggs" 😉
November 24, 2014 at 6:51 am
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.
November 24, 2014 at 7:38 am
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" 😉
November 24, 2014 at 7:47 am
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