patternsqlMinor
Using Resource Governor with SQL Agent Jobs
Viewed 0 times
resourcewithsqlgovernoragentjobsusing
Problem
I'm trying to setup Resource Governor for limiting IOPS for processes running using SQL agent jobs. The classifier function is set to identify a particular login (any spid that is running using that login should use the resource group it is assigned to). I then added
The question is - how do I get Resource Governor to use a login other than the Service Account (when used with agent jobs)? I do have some untested ugly ideas like - running the jobs using a CmdExec or PowerShell proxy. If anyone has come across a similar situation or has any better ideas, I would really appreciate it. Thank you.
```
--Example Setup:
USE [master]
GO
CREATE RESOURCE POOL [SqlJobPool] WITH(
min_iops_per_volume=1,
max_iops_per_volume=5000);
GO
CREATE WORKLOAD GROUP [IOGroup]
USING [SqlJobPool];
GO
CREATE FUNCTION [dbo].[fn_LimitedIO]()
RETURNS SYSNAME WITH SCHEMABINDING
AS
BEGIN
DECLARE @grp SYSNAME;
IF SUSER_NAME() = N'ResourceGovernerUser' -- When this is set to the Service Account, it works
BEGIN
SET @grp = N'IOGroup';
END
ELSE
BEGIN
SET @grp = N'default';
END
RETURN @grp;
END
GO
ALTER RESOURCE GOVERNOR with (CLASSIFIER_FUNCTION = dbo.fn_LimitedIO);
ALTER RESOURCE GOVERNOR RECONFIGURE;
EXECUTE AS LOGIN = 'ResourceGovernerUser' within the job, but I cannot get it to work and it falls back to the default pool because the jobs are 'EXECUTED AS 'ServiceAccount''. Although if I look at the active processes using sp_WhoisActive, the login name is displayed as 'ResourceGovernerUser' instead of the Service Account. So I modified the classifier function to use the Service Account and then it just worked. I verified this using the perfmon counters 'Disk Read IO/Sec' and 'Disk Write IO/Sec' under 'Resource Pool Stats' object. The question is - how do I get Resource Governor to use a login other than the Service Account (when used with agent jobs)? I do have some untested ugly ideas like - running the jobs using a CmdExec or PowerShell proxy. If anyone has come across a similar situation or has any better ideas, I would really appreciate it. Thank you.
```
--Example Setup:
USE [master]
GO
CREATE RESOURCE POOL [SqlJobPool] WITH(
min_iops_per_volume=1,
max_iops_per_volume=5000);
GO
CREATE WORKLOAD GROUP [IOGroup]
USING [SqlJobPool];
GO
CREATE FUNCTION [dbo].[fn_LimitedIO]()
RETURNS SYSNAME WITH SCHEMABINDING
AS
BEGIN
DECLARE @grp SYSNAME;
IF SUSER_NAME() = N'ResourceGovernerUser' -- When this is set to the Service Account, it works
BEGIN
SET @grp = N'IOGroup';
END
ELSE
BEGIN
SET @grp = N'default';
END
RETURN @grp;
END
GO
ALTER RESOURCE GOVERNOR with (CLASSIFIER_FUNCTION = dbo.fn_LimitedIO);
ALTER RESOURCE GOVERNOR RECONFIGURE;
Solution
Resource governor classifier functions only run during the Login process. Impersonation via
Specifying the
Since testing is a great way to understand what is really happening, I've created a small test-bed. Don't run this on a production system since it will modify the resource group configuration.
This creates the classifier function, and several resource groups attached to a test resource pool:
Here we'll create a login, and allow it to
This creates a SQL Server Agent job where the "owner" is set to the login we just created. Since the login is not a member of sysadmin, SQL Server agent will run this job in the context of that login.
The results from the msdb history table for the above job:
Job 'TestResourceGovernorJob' started successfully.
Executed as user: ResourceGovernorTestLogin.
-
(1 rows(s) affected)
UserName SYSTEM_USER SESSION_USER ORIGI
EXECUTE AS does not trigger the classifier function. EXECUTE AS is how SQL Server Agent runs jobs in the context of another login or user.Specifying the
Run As ... user or modifying the Job Owner does not change how SQL Server Agent logs into SQL Server. The only way I can get SQL Server agent jobs to run in a specific resource group with a classifier function that looks only at the user name is to put the SQL Server Agent Service Account into it's own resource group. Since testing is a great way to understand what is really happening, I've created a small test-bed. Don't run this on a production system since it will modify the resource group configuration.
This creates the classifier function, and several resource groups attached to a test resource pool:
USE master;
SET NOCOUNT ON;
GO
CREATE FUNCTION dbo.fnDummyClassifier()
RETURNS sysname
WITH SCHEMABINDING
AS
BEGIN
DECLARE @GroupName sysname = NULL;
IF SUSER_SNAME() = 'DOMAIN\USER'
SET @GroupName = N'AgentServiceAccountGroup';
IF SUSER_SNAME() = 'ResourceGovernorTestLogin'
SET @GroupName = N'ResourceGovernorTestLoginGroup';
IF SUSER_SNAME() = 'ResourceGovernorTestUser'
SET @GroupName = N'ResourceGovernorTestUserGroup';
IF @GroupName IS NULL
SET @GroupName = N'default';
RETURN @GroupName;
END
GO
CREATE RESOURCE POOL TestPool
WITH (MAX_CPU_PERCENT = 10);
CREATE WORKLOAD GROUP AgentServiceAccountGroup
WITH (
group_max_requests=0
, importance=Medium
, request_max_cpu_time_sec=0
, request_max_memory_grant_percent=25
, request_memory_grant_timeout_sec=0
, max_dop=0
)
USING TestPool;
CREATE WORKLOAD GROUP ResourceGovernorTestLoginGroup
WITH (
group_max_requests=0
, importance=Medium
, request_max_cpu_time_sec=0
, request_max_memory_grant_percent=25
, request_memory_grant_timeout_sec=0
, max_dop=0
)
USING TestPool;
CREATE WORKLOAD GROUP ResourceGovernorTestUserGroup
WITH (
group_max_requests=0
, importance=Medium
, request_max_cpu_time_sec=0
, request_max_memory_grant_percent=25
, request_memory_grant_timeout_sec=0
, max_dop=0
)
USING TestPool;
ALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION = [dbo].[fnDummyClassifier]);
ALTER RESOURCE GOVERNOR RECONFIGURE;
GOHere we'll create a login, and allow it to
VIEW SERVER STATE so we can tell which resource group the session has been assigned to.CREATE LOGIN ResourceGovernorTestLogin
WITH PASSWORD = '!NnrtiHummusPlenumPoodle2'
, DEFAULT_LANGUAGE = us_english
, CHECK_EXPIRATION = OFF
, CHECK_POLICY = OFF;
GRANT VIEW SERVER STATE TO ResourceGovernorTestLogin;
GOThis creates a SQL Server Agent job where the "owner" is set to the login we just created. Since the login is not a member of sysadmin, SQL Server agent will run this job in the context of that login.
DECLARE @JobID uniqueidentifier;
EXEC msdb.dbo.sp_add_job @job_name = 'TestResourceGovernorJob'
, @enabled = 1
, @description = 'Tests resource governor classification'
, @start_step_id = 1
, @owner_login_name = 'ResourceGovernorTestLogin'
, @job_id = @JobID OUTPUT;
EXEC msdb.dbo.sp_add_jobstep @job_id = @JobID, @step_id = 1, @step_name = 'Step1'
, @subsystem = 'TSQL'
, @command = 'SELECT '''';
SELECT UserName = CONVERT(nvarchar(30), SUSER_SNAME())
, [SYSTEM_USER] = CONVERT(nvarchar(30), SYSTEM_USER)
, [SESSION_USER] = CONVERT(nvarchar(30), SESSION_USER)
, [ORIGINAL_LOGIN] = CONVERT(nvarchar(30), ORIGINAL_LOGIN())
, WorkloadGroup = CONVERT(nvarchar(30), wg.name)
FROM sys.dm_exec_requests der
INNER JOIN sys.dm_resource_governor_workload_groups wg ON der.group_id = wg.group_id
WHERE der.session_id = @@SPID;'
, @flags = 4 --write step output into msdb.dbo.sysjobstephistory
, @on_success_action = 1;
EXEC msdb.dbo.sp_add_jobserver @job_id = @JobID, @server_name = N'(LOCAL)';
/*
Call SQL Server Agent via msdb.dbo.sp_notify_job
which in turn calls msdb.dbo.xp_notify_job.
The SQL Server Agent executable logs into the
SQL Server instance using the service account,
then performs an "EXECUTE AS ..." to run
job step(s).
*/
EXEC msdb.dbo.sp_start_job @job_id = @JobID;
GO
WAITFOR DELAY N'00:00:01';
DECLARE @msg nvarchar(max);
SELECT @msg = sjh.message
FROM msdb.dbo.sysjobhistory sjh
INNER JOIN msdb.dbo.sysjobs sj ON sjh.job_id = sj.job_id
WHERE sj.name = 'TestResourceGovernorJob'
AND sjh.step_id = 1;
PRINT (N'');
PRINT (@msg);
PRINT (N'');
EXEC msdb.dbo.sp_delete_job @job_name = 'TestResourceGovernorJob';
GOThe results from the msdb history table for the above job:
Job 'TestResourceGovernorJob' started successfully.
Executed as user: ResourceGovernorTestLogin.
-
(1 rows(s) affected)
UserName SYSTEM_USER SESSION_USER ORIGI
Code Snippets
USE master;
SET NOCOUNT ON;
GO
CREATE FUNCTION dbo.fnDummyClassifier()
RETURNS sysname
WITH SCHEMABINDING
AS
BEGIN
DECLARE @GroupName sysname = NULL;
IF SUSER_SNAME() = 'DOMAIN\USER'
SET @GroupName = N'AgentServiceAccountGroup';
IF SUSER_SNAME() = 'ResourceGovernorTestLogin'
SET @GroupName = N'ResourceGovernorTestLoginGroup';
IF SUSER_SNAME() = 'ResourceGovernorTestUser'
SET @GroupName = N'ResourceGovernorTestUserGroup';
IF @GroupName IS NULL
SET @GroupName = N'default';
RETURN @GroupName;
END
GO
CREATE RESOURCE POOL TestPool
WITH (MAX_CPU_PERCENT = 10);
CREATE WORKLOAD GROUP AgentServiceAccountGroup
WITH (
group_max_requests=0
, importance=Medium
, request_max_cpu_time_sec=0
, request_max_memory_grant_percent=25
, request_memory_grant_timeout_sec=0
, max_dop=0
)
USING TestPool;
CREATE WORKLOAD GROUP ResourceGovernorTestLoginGroup
WITH (
group_max_requests=0
, importance=Medium
, request_max_cpu_time_sec=0
, request_max_memory_grant_percent=25
, request_memory_grant_timeout_sec=0
, max_dop=0
)
USING TestPool;
CREATE WORKLOAD GROUP ResourceGovernorTestUserGroup
WITH (
group_max_requests=0
, importance=Medium
, request_max_cpu_time_sec=0
, request_max_memory_grant_percent=25
, request_memory_grant_timeout_sec=0
, max_dop=0
)
USING TestPool;
ALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION = [dbo].[fnDummyClassifier]);
ALTER RESOURCE GOVERNOR RECONFIGURE;
GOCREATE LOGIN ResourceGovernorTestLogin
WITH PASSWORD = '!NnrtiHummusPlenumPoodle2'
, DEFAULT_LANGUAGE = us_english
, CHECK_EXPIRATION = OFF
, CHECK_POLICY = OFF;
GRANT VIEW SERVER STATE TO ResourceGovernorTestLogin;
GODECLARE @JobID uniqueidentifier;
EXEC msdb.dbo.sp_add_job @job_name = 'TestResourceGovernorJob'
, @enabled = 1
, @description = 'Tests resource governor classification'
, @start_step_id = 1
, @owner_login_name = 'ResourceGovernorTestLogin'
, @job_id = @JobID OUTPUT;
EXEC msdb.dbo.sp_add_jobstep @job_id = @JobID, @step_id = 1, @step_name = 'Step1'
, @subsystem = 'TSQL'
, @command = 'SELECT '''';
SELECT UserName = CONVERT(nvarchar(30), SUSER_SNAME())
, [SYSTEM_USER] = CONVERT(nvarchar(30), SYSTEM_USER)
, [SESSION_USER] = CONVERT(nvarchar(30), SESSION_USER)
, [ORIGINAL_LOGIN] = CONVERT(nvarchar(30), ORIGINAL_LOGIN())
, WorkloadGroup = CONVERT(nvarchar(30), wg.name)
FROM sys.dm_exec_requests der
INNER JOIN sys.dm_resource_governor_workload_groups wg ON der.group_id = wg.group_id
WHERE der.session_id = @@SPID;'
, @flags = 4 --write step output into msdb.dbo.sysjobstephistory
, @on_success_action = 1;
EXEC msdb.dbo.sp_add_jobserver @job_id = @JobID, @server_name = N'(LOCAL)';
/*
Call SQL Server Agent via msdb.dbo.sp_notify_job
which in turn calls msdb.dbo.xp_notify_job.
The SQL Server Agent executable logs into the
SQL Server instance using the service account,
then performs an "EXECUTE AS ..." to run
job step(s).
*/
EXEC msdb.dbo.sp_start_job @job_id = @JobID;
GO
WAITFOR DELAY N'00:00:01';
DECLARE @msg nvarchar(max);
SELECT @msg = sjh.message
FROM msdb.dbo.sysjobhistory sjh
INNER JOIN msdb.dbo.sysjobs sj ON sjh.job_id = sj.job_id
WHERE sj.name = 'TestResourceGovernorJob'
AND sjh.step_id = 1;
PRINT (N'');
PRINT (@msg);
PRINT (N'');
EXEC msdb.dbo.sp_delete_job @job_name = 'TestResourceGovernorJob';
GOCREATE USER ResourceGovernorTestUser
FOR LOGIN ResourceGovernorTestLogin;
DECLARE @JobID uniqueidentifier;
EXEC msdb.dbo.sp_add_job @job_name = 'TestResourceGovernorJob'
, @enabled = 1
, @description = 'Tests resource governor classification'
, @start_step_id = 1
, @owner_login_name = 'ResourceGovernorTestLogin'
, @job_id = @JobID OUTPUT;
EXEC msdb.dbo.sp_add_jobstep @job_id = @JobID, @step_id = 1, @step_name = 'Step1'
, @subsystem = 'TSQL'
, @database_name = 'master'
, @database_user_name = 'ResourceGovernorTestUser'
, @command = 'SELECT '''';
SELECT UserName = CONVERT(nvarchar(30), SUSER_SNAME())
, [SYSTEM_USER] = CONVERT(nvarchar(30), SYSTEM_USER)
, [SESSION_USER] = CONVERT(nvarchar(30), SESSION_USER)
, [ORIGINAL_LOGIN] = CONVERT(nvarchar(30), ORIGINAL_LOGIN())
, WorkloadGroup = CONVERT(nvarchar(30), wg.name)
FROM sys.dm_exec_requests der
INNER JOIN sys.dm_resource_governor_workload_groups wg ON der.group_id = wg.group_id
WHERE der.session_id = @@SPID;'
, @flags = 4 --write step output into msdb.dbo.sysjobstephistory
, @on_success_action = 1;
EXEC msdb.dbo.sp_add_jobserver @job_id = @JobID, @server_name = N'(LOCAL)';
/*
Call SQL Server Agent via msdb.dbo.sp_notify_job
which in turn calls msdb.dbo.xp_notify_job.
The SQL Server Agent executable logs into the
SQL Server instance using the service account,
then performs an "EXECUTE AS ..." to run
job step(s).
*/
EXEC msdb.dbo.sp_start_job @job_id = @JobID;
GO
WAITFOR DELAY N'00:00:01';
DECLARE @msg nvarchar(max);
SELECT @msg = sjh.message
FROM msdb.dbo.sysjobhistory sjh
INNER JOIN msdb.dbo.sysjobs sj ON sjh.job_id = sj.job_id
WHERE sj.name = 'TestResourceGovernorJob'
AND sjh.step_id = 1;
PRINT (N'');
PRINT (@msg);
PRINT (N'');
EXEC msdb.dbo.sp_delete_job @job_name = 'TestResourceGovernorJob';
DROP USER ResourceGovernorTestUser;
GOALTER RESOURCE GOVERNOR WITH (CLASSIFIER_FUNCTION = NULL);
DROP WORKLOAD GROUP AgentServiceAccountGroup;
DROP WORKLOAD GROUP ResourceGovernorTestUserGroup;
DROP WORKLOAD GROUP ResourceGovernorTestLoginGroup;
DROP RESOURCE POOL TestPool;
ALTER RESOURCE GOVERNOR RECONFIGURE;
DROP FUNCTION dbo.fnDummyClassifier
DROP LOGIN ResourceGovernorTestLogin;
GOContext
StackExchange Database Administrators Q#184625, answer score: 6
Revisions (0)
No revisions yet.