Resource governor classifier functions only run during the Login process. Impersonation via 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;
GO
Here 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;
GO
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.
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';
GO
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 ORIGINAL_LOGIN WorkloadGroup
------------------------------ ------------------------------ ------------------------------ ------------------------------ ------------------------------
ResourceGovernorTestLogin ResourceGovernorTestLogin guest DOMAIN\USER AgentServiceAccountGroup
(1 rows(s) affected). The step succeeded.
Next, we'll create a user for the login, and run a new version of the job using the Run As
option in the advanced properties of the SQL Server Job Step.
CREATE 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;
GO
The output from that run of the job:
Job 'TestResourceGovernorJob' started successfully.
Executed as user: ResourceGovernorTestLogin.
-
(1 rows(s) affected)
UserName SYSTEM_USER SESSION_USER ORIGINAL_LOGIN WorkloadGroup
------------------------------ ------------------------------ ------------------------------ ------------------------------ ------------------------------
ResourceGovernorTestLogin ResourceGovernorTestLogin ResourceGovernorTestUser DOMAIN\USER AgentServiceAccountGroup
(1 rows(s) affected). The step succeeded.
This cleans up the resource governor configuration, and drops the classifier function and the login.
ALTER 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;
GO
In both result sets above, you can see the Resource Governor classifier function put each job into the AgentServiceAccountGroup
group; this is because the SQL Server Agent initially logs into SQL Server using the Service Account used when SQL Server Agent starts up.