July 11, 2007 at 1:45 pm
Guys,
We had some issues here - a certain process was blocking other processes, things were hanging and caused problems. I am now thinking to set up some type of monitoring to prevent this in the future.
The goal is to be able to tell which processID is blocking which processID for > 't' amount of time and send an alert if such a situation occurs.
Alerting I know how to do. What I am not clear about is how to identify this issue.
Can someone suggest a way of doing it?
I am currently exploring how I can utilize master..sysprocesses for this purpose, but cannot figure out best way of achieving this goal.
Any suggestions?
Thanks in advance!
July 11, 2007 at 2:02 pm
you will have to take periodical "snapshots" of sysprocess and look the blkby column with the lastbatch datetime. By diffing the lastbatch and current time you will probably achieve what you want.
Hope this helps.
* Noel
July 11, 2007 at 2:54 pm
Noel,
I found something similar to what you have suggested here: http://www.databasejournal.com/features/mssql/article.php/3426431
although, the solution appears a bit more complicated than I need it to be ...
Thank you
July 11, 2007 at 3:05 pm
Correct!
Just leave out all those calls to sp_OA* procedures
You only need pure TSQL
Good Luck!
* Noel
July 11, 2007 at 9:36 pm
There are quite a few versions of Block Monitoring out on the web. Here is one that I found and doctored up a bit. I have the procedure running in a job every n minutes, writing to a table.
CREATE
TABLE [dbo].[Lock_monitor](
[pkid] [int]
IDENTITY(1,1) NOT NULL,
[Occurs] [datetime]
NOT NULL CONSTRAINT [DF_Lock_monitor_Occurs] DEFAULT (getdate()),
[Proc_blocked] [int]
NULL,
[Proc_blocking] [int]
NULL,
[Qry_blocked] [varchar]
(255) NULL,
[Qry_blocking] [varchar]
(255) NULL,
[User_Blocking] [varchar]
(100) NULL,
[WaitResource] [varchar]
(1000) NULL,
[WaitTime] [int]
NULL DEFAULT (0),
PRIMARY
KEY CLUSTERED
(
[pkid]
ASC
)
GO
CREATE
PROCEDURE [dbo].[MONITOR_LOCKS] as
create
table #procs (eventtype varchar(60), Parameters int , EventInfo varchar(600))
declare
@cmd varchar(250), @spid_1 int, @spid_2 int, @qry1 varchar(255), @qry2 varchar(255), @user1 varchar(100), @user2 varchar(100)
declare
@id int, @WaitTime int, @WaitResource varchar(100)
select
spid, blocked, loginame = rtrim(loginame), waittime, waitresource = rtrim(waitresource)
into
#temp_proc
from
master.dbo.sysprocesses where blocked > 0 and waittime > 3000
declare
CUR1 cursor for
select
spid, blocked, rtrim(loginame), WaitTime, rtrim(WaitResource) from #temp_proc
open
CUR1
fetch
next from CUR1 into @spid_1, @spid_2, @user1, @WaitTime, @WaitResource
while
@@fetch_status = 0
BEGIN
truncate table #procs
set @cmd = (select 'dbcc inputbuffer(' + cast(@spid_1 as varchar) + ')')
insert into #procs (eventtype, Parameters, EventInfo)
exec (@cmd)
Insert into dbo.Lock_monitor (Proc_blocked, Proc_blocking, Qry_blocked, [User_Blocking], WaitTime, WaitResource)
select @spid_1, @spid_2, rtrim(EventInfo), rtrim(@user1), @WaitTime, rtrim(@WaitResource)
from #procs
set @id = scope_identity()
truncate table #procs
set @cmd = (select 'dbcc inputbuffer(' + cast(@spid_2 as varchar) + ')')
insert into #procs (eventtype, Parameters, EventInfo)
exec (@cmd)
update dbo.Lock_monitor
set Qry_blocking = IsNull((select rtrim(EventInfo) from #procs), ''),
proc_blocked
= @spid_1,
proc_blocking
= @spid_2,
user_blocking
= rtrim(@user1)
where pkid = @id
fetch next from CUR1 into @spid_1, @spid_2, @user1, @WaitTime, @WaitResource
END
CLOSE
CUR1
DEALLOCATE
CUR1
return
GO
Regards,
Rubes
July 13, 2007 at 3:49 am
here's mine....
I run it from sqlagent outputting to a file. that way I have a history if the block is gone by the time I get there. It gives enough info to investigate the blocking code and who was affected. I choose to trap blocks lasting 30secs + but you can tune that. Set yourself up jobs to stop/start the job once a day and cycle the output log. It works for 7,2000,2005
if exists (select 1 from sysobjects where type = 'P' and name = 'up_blocker_check_loop')
begin
drop proc up_blocker_check_loop
end
go
create procedure up_blocker_check_loop @time_delay char(8) = '00:00:30'
as
declare @spid char(4), @blocker char(4),
@spid2 char(4), @blocker2 char(4),
@head_of_chain char(4), @head_of_chain2 char(4),
@time datetime, @found int, @blocked_users int
DECLARE @Handle binary(20)
create table #inputbuffer( EventType varchar(40) not null,
Parameters varchar(40)not null,
EventInfo varchar(255) null)
/*******************************************************************************************************/
/* read ahead to set up initial blocking information if present */
/********************************************************************************************************/
select @head_of_chain = ' '
set @found = 0
set nocount on
declare block_cursor cursor for
select distinct "Spid"=convert(char(4),spid),
"Block"=convert(char(4),blocked)
from master..sysprocesses
where blocked != 0
order by block, spid
open block_cursor
if @@cursor_rows = 0 -- no block found
goto loopstart
fetch block_cursor into @spid, @blocker
while @@fetch_status = 0
begin
select 'spid ', @spid, 'is blocked by ', @blocker
insert into #inputbuffer
exec ('dbcc inputbuffer (' + @spid + ')')
select 'SQL issued by blocked process ', @spid, ' is'
-- exec ('dbcc inputbuffer (' + @spid + ')')
if (select eventtype from #inputbuffer) = 'rpc event'
begin
SELECT @Handle = sql_handle FROM master.dbo.sysprocesses
WHERE spid = @spid
SELECT * FROM ::fn_get_sql(@Handle)
end
else
select eventinfo from #inputbuffer
truncate table #inputbuffer
fetch block_cursor into @spid, @blocker
end
close block_cursor
deallocate block_cursor
-- find out spid at top of blocking chain
select @head_of_chain =
(select max(spid) from master..sysprocesses where blocked = 0 and
(spid in (select blocked from master..sysprocesses where blocked !=0)))
print 'spid at head of blocking chain = ' +@head_of_chain
loopstart:
/***************************************************************************************************/
/* never ending loop comparing current block to previous block and reporting details if one found */
/***************************************************************************************************/
while 1 != 0
begin
declare block_cursor2 cursor for
select distinct "Spid"=convert(char(4),spid),
"Block"=convert(char(4),blocked)
from master..sysprocesses
where blocked != 0
order by block, spid
open block_cursor2
fetch block_cursor2 into @spid2, @blocker2
while @@fetch_status = 0
begin
if @found = 30
begin
select 'spid ', @spid2, 'is blocked by ', @blocker2
insert into #inputbuffer
exec ('dbcc inputbuffer (' + @spid2 + ')')
select 'SQL issued by blocked process ', @spid2, ' is'
-- exec ('dbcc inputbuffer (' + @spid2 + ')')
if (select eventtype from #inputbuffer) = 'rpc event'
begin
SELECT @Handle = sql_handle FROM master.dbo.sysprocesses
WHERE spid = @spid2
SELECT * FROM ::fn_get_sql(@Handle)
end
else
select eventinfo from #inputbuffer
truncate table #inputbuffer
set @blocked_users = @@cursor_rows
end
fetch block_cursor2 into @spid2, @blocker2
end
close block_cursor2
deallocate block_cursor2
select @head_of_chain2 =
(select max(spid) from master..sysprocesses where blocked = 0 and
(spid in (select blocked from master..sysprocesses where blocked !=0)))
if @head_of_chain != @head_of_chain2
set @found = 0 -- force a 30 sec pause when block changes
if @head_of_chain = @head_of_chain2 and @found = 30 -- same block present as in last check for 30 seconds
begin
set @time = (select getdate())
print '********************************************************************** '
print 'time now is ' +convert(varchar(30),@time) +' and ' +convert(char(2),datepart(ss,@time)) +' seconds'
print '********************************************************************** '
insert into #inputbuffer
exec ('dbcc inputbuffer (' + @head_of_chain2 + ')')
select 'SQL issued by blocking process ', @head_of_chain2, ' is'
-- exec ('dbcc inputbuffer (' + @head_of_chain2 + ')')
if (select eventtype from #inputbuffer) = 'rpc event'
begin
SELECT @Handle = sql_handle FROM master.dbo.sysprocesses
WHERE spid = @head_of_chain2
SELECT * FROM ::fn_get_sql(@Handle)
end
else
select eventinfo from #inputbuffer
-- raiserror ('**** Blocking is occuring by spid %s ****', 16, 1, @head_of_chain2) with log
print ''
select convert(char(10),nt_username) as 'NT User Name', last_batch, db_name(dbid) as 'database'
from master..sysprocesses
where spid = @head_of_chain2
print 'Distinct objects blocks are held on'
select distinct rsc_objid As ObjId,
rsc_indid As IndId,
convert (smallint, req_spid) As spid
from master.dbo.syslockinfo,
master.dbo.spt_values v,
master.dbo.spt_values x,
master.dbo.spt_values u
where master.dbo.syslockinfo.rsc_type = v.number
and v.type = 'LR'
and master.dbo.syslockinfo.req_status = x.number
and x.type = 'LS'
and master.dbo.syslockinfo.req_mode + 1 = u.number
and u.type = 'L'
and req_spid = @head_of_chain2
print '************************************************************************************************* '
truncate table #inputbuffer
waitfor delay @time_delay
goto loopend
end
waitfor delay '00:00:01' -- delay to cut down resource usage of this job
loopend:
set @head_of_chain = @head_of_chain2
if @head_of_chain is null
set @found = 0
else
if @found < 30
set @found = @found + 1
end
go
---------------------------------------------------------------------
Viewing 6 posts - 1 through 5 (of 5 total)
You must be logged in to reply to this topic. Login to reply