Commit 566abfae authored by dwentzel's avatar dwentzel
Browse files

updated SHELL procedure and README.md

parent 3542bbbf
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -19,3 +19,7 @@ http://www.davewentzel.com/content/service-broker-monitoring-routine. For a noo
	- there is a section to place custom TRACER TOKEN code.  You need to code this.  This would be similar to replication's tracer token feature.  Essentially you code a "test" message that flows through your workflow and you monitor it to see if it runs to completion and if not where it failed.  
	- has an option for STALLED MESSAGE CLEANOUT.  This removes old conversations that you deem as not needed that are not in the CLOSED state.  This is useful in dev envs where you changed some activator code and the queues failed and there are a bunch of useless messages.  Probably not good to run this on a production db.  
	
SSB_SHELL_Configure.sql
------------------------
http://www.davewentzel.com/content/setup-and-configure-service-broker-objects-reliably-and-repeatably
This is a "pattern" I use for each new SSB "module" that I need to deploy.  It helps ensure everything is reliably and consistently configured and easy to debug.  
 No newline at end of file
+8 −295
Original line number Diff line number Diff line
@@ -18,14 +18,12 @@ SET NOCOUNT ON
	SELECT @dbname = DB_NAME()
	DECLARE @exec_str varchar(max);

IF @Option NOT IN ('SETUP','TEARDOWN','TEARDOWN FORCE OVERRIDE','CHECK','BROKER HELP','DISABLE ALL QUEUES','STALLED MESSAGE CLEANOUT')
IF @Option NOT IN ('SETUP','TEARDOWN','TEARDOWN FORCE OVERRIDE','DISABLE ALL QUEUES','STALLED MESSAGE CLEANOUT')
BEGIN
	PRINT 'Valid @Option:  SETUP,TEARDOWN,TEARDOWN FORCE OVERRIDE,CHECK,BROKER HELP,DISABLE ALL QUEUES'
	PRINT '     SETUP						= setup/upgrade and run the reasonability checks. '
	PRINT '     TEARDOWN					= remove all traces of SHELL queueing, but only if the Qs are clear.  Leaves the table triggers. '
	PRINT '     TEARDOWN FORCE OVERRIDE		= remove all traces of SHELL queueing, regardless.'
	PRINT '     CHECK						= run only the reasonability checks. '
	PRINT '     BROKER HELP					= show me the state of Service Broker and its various Qs, Services, etc'
	PRINT '     DISABLE ALL QUEUES			= disables all activated Qs and tickling.'
	PRINT '     STALLED MESSAGE CLEANOUT    = cleans out Service Broker messages that are in-flight but damaged.  This should only be run if you know what you are doing, after you fix a broken Q'
	RAISERROR ('You picked an invalid @Option.',16,1)
@@ -143,7 +141,9 @@ END; --TEARDOWN
IF @Option IN ('SETUP')
BEGIN

BEGIN  --SHELL Setup Section
	--add Message Types here, if needed

	--add contracts here, if needed 

	--is the SHELL Q and service installed and running? 
	IF NOT EXISTS (select * from sys.service_queues WHERE name = 'SHELLQ')
@@ -157,13 +157,8 @@ BEGIN --SHELL Setup Section
	END;
	
	
	IF NOT EXISTS (select * from sys.objects WHERE name = 'SHELL')
	BEGIN
		PRINT 'Creating the shell procedure SHELL'
		--we only build a shell so we don't accidentally break the Queue since the Activator
		--relies on this proc
		EXEC ('CREATE PROCEDURE SHELL AS BEGIN DECLARE @i INT END;');
	END;	
	--create activator here, if desired.  
	--or create a "shell" activator temporarily.  We need the activator to enable activation on the Q
	
	--is the Activator procedure installed?
	IF NOT EXISTS (select * from sys.objects WHERE name = 'SHELLQueueTicklerActivator')
@@ -174,290 +169,23 @@ BEGIN --SHELL Setup Section
		EXEC ('CREATE PROCEDURE SHELLQueueTicklerActivator AS BEGIN DECLARE @i INT END;');
	END;
	
	EXEC ('
ALTER PROCEDURE SHELLQueueTicklerActivator
WITH EXECUTE AS OWNER
AS
BEGIN
	SET NOCOUNT ON
	
	--this procedure is actually installed via SB_SHELL_ConfigureAll.  All changes should be made in ClearCase to THAT procedure.  
	DECLARE 
		@mt sysname, 
		@h uniqueidentifier, 
		@newh uniqueidentifier;
	
	BEGIN TRANSACTION;
	WAITFOR (
	RECEIVE TOP (1)
        @mt = message_type_name,
        @h	= conversation_handle
    FROM SHELLTicklerQ  
	), TIMEOUT 2000

	IF @mt IN (
		N''http://schemas.microsoft.com/SQL/ServiceBroker/Error'', 
		N''http://schemas.microsoft.com/SQL/ServiceBroker/DialogTimer''
		) 
	BEGIN
		--start a new tickler if I RECEIVEd anything but EndDialog
		BEGIN DIALOG CONVERSATION @newh
		FROM SERVICE [SHELLTicklerSvc]
		TO SERVICE N''SHELLTicklerSvc'', N''current database''
		WITH ENCRYPTION = OFF;
			
		BEGIN CONVERSATION TIMER (@newh) TIMEOUT = 60;
	END;

	IF @mt IN (
		N''http://schemas.microsoft.com/SQL/ServiceBroker/Error'', 
		N''http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog'', 
		N''http://schemas.microsoft.com/SQL/ServiceBroker/DialogTimer''
		)
	BEGIN
		--these conversations must be ended
		BEGIN TRY
			END CONVERSATION @h;
		END TRY
		BEGIN CATCH
			IF ERROR_NUMBER () = 8426 
			BEGIN
				--this overcomes the monolog situation
				SELECT @mt = @mt
			END
		END CATCH
	END
	COMMIT TRANSACTION;


    IF @mt = N''http://schemas.microsoft.com/SQL/ServiceBroker/DialogTimer''
    BEGIN

		--whatever you do here will execute whenever a message is enqueued
		--do NOT perform transaction management here
		BEGIN TRY
			EXEC SHELL 
				
		END TRY
		BEGIN CATCH
			-- put it here too, just in case.
			IF @@trancount > 0 
			BEGIN
				ROLLBACK TRAN
			END;
		END CATCH
        
    END;
    
    SET NOCOUNT OFF
end;
'
	);		
	EXEC ('GRANT EXECUTE ON SHELLQueueTicklerActivator TO PUBLIC;');


	--install the Stop and Start tickler procedures
	IF EXISTS (select * from sys.objects WHERE name = 'SHELLTicklerStopTickling')
	BEGIN
		EXEC ('DROP PROC SHELLTicklerStopTickling');
	END;

	EXEC ('
CREATE PROCEDURE SHELLTicklerStopTickling
WITH EXECUTE AS OWNER 
AS
BEGIN
	--we should NEVER have to run this proc, but I wrote it just in case.  
	--tickling, once enabled, should never be stopped.  
	--if the goal is to "pause" the Q for maintenance or upgrades then we should do that (status = OFF for Q activation), not
	--disable the tickler. 
	--to restore tickling again, simply run SHELLTicklerStopTickling
	--this procedure is actually installed via SB_SHELL_ConfigureAll.  All changes should be made in ClearCase to that procedure.  
	
	--SHELLQueueTicklerActivator sets the amount of time to pause between tickles.  
	
	DECLARE @h uniqueidentifier;
	
	--get the list of outstanding tickling conversations to END
	DECLARE Cur CURSOR FOR 
		SELECT
			e.conversation_handle
			--e.*
		FROM sys.conversation_endpoints e
		JOIN sys.services s ON e.service_id = s.service_id 
		WHERE s.name = ''SHELLTicklerSvc''
	OPEN Cur 
	FETCH NEXT FROM Cur INTO @h
	WHILE (@@FETCH_STATUS = 0)
	BEGIN
		END CONVERSATION @h WITH CLEANUP;  --adding CLEANUP, just in case.  
		
		FETCH NEXT FROM Cur INTO @h
	END
	CLOSE Cur
	DEALLOCATE Cur
	
	ALTER QUEUE SHELLTicklerQ WITH ACTIVATION (STATUS = OFF);
	
END;
	');
	
	EXEC ('GRANT EXECUTE ON SHELLTicklerStopTickling TO PUBLIC;');
	
	IF EXISTS (select * from sys.objects WHERE name = 'SHELLTicklerStartTickling')
	BEGIN
		EXEC ('DROP PROC SHELLTicklerStartTickling')
	END;
	
	EXEC ('
CREATE PROCEDURE SHELLTicklerStartTickling
WITH EXECUTE AS OWNER 
AS
BEGIN
	/*
	SHELLQueueTicklerActivator sets the amount of time to pause between tickles.  
	
	This proc should be run once to start the tickling.  It seeds a conversation to the Svc and thats it.  
	
	The Q has an activator procedure (SHELLQueueTicklerActivator) that fires and then takes over control.  The activator handles 
	putting itself into a reliable, infinite loop that ensures scheduled processing does not stop.  
	
	This is called whenever we want to:
		--set up SB for the first time in a db (ServiceBrokerEnable is called)
		--restore a database as a copy to a different server (ENABLE BROKER should run as part of ServiceBrokerEnable...it shouldnt do anything in this case) 
		--when we run SET NEW_BROKER because it is restored as a copy to the same db server (then its critical because the Q is wiped clean, hence the 
			tickler is gone)
		--during a build/deploy because we do things using a repeatable, properties-based deployment, which means we should be enabling SB if it isnt
			already, and start tickling.  	
			
	this procedure is actually installed via SB_SHELL_ConfigureAll.  All changes should be made in ClearCase to that procedure.  		
	*/
	
	--lets see if we have a tickling conversation already, if so, do nothing.  
	IF NOT EXISTS (
		SELECT
				e.conversation_handle
				--e.*
			FROM sys.conversation_endpoints e
			JOIN sys.services s ON e.service_id = s.service_id 
			WHERE s.name = ''SHELLTicklerSvc''
		)
	BEGIN
		--seed a conversation to start activating every 5 secs
		DECLARE @h uniqueidentifier;
		BEGIN DIALOG CONVERSATION @h
			FROM SERVICE [SHELLTicklerSvc]
			TO SERVICE N''SHELLTicklerSvc'', N''current database''
			WITH ENCRYPTION = OFF;
		BEGIN CONVERSATION TIMER (@h) timeout = 1;	
	END;
END;
	'
	);
	
	EXEC ('GRANT EXECUTE ON SHELLTicklerStartTickling TO PUBLIC;');


	--is activation enabled on the Q?
	--for internal activation attach it to the service queue. 
	--now dbo.SHELLQueueTicklerActivator is run whenever a message arrives in [SHELLTicklerQ]
	IF EXISTS (select * from sys.service_queues WHERE name = 'SHELLTicklerQ' AND is_activation_enabled = 0)
	BEGIN 
		ALTER QUEUE [SHELLTicklerQ]
			WITH ACTIVATION 
				(
					procedure_name = dbo.SHELLQueueTicklerActivator
					, max_queue_readers = 5  --ISSUE:  set this to the amount of concurrency desired
					, max_queue_readers = 5  --set this to the amount of "concurrency" desired
					, EXECUTE AS OWNER
					, STATUS = ON
				);
	END
		
------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------
--Startup All Queues and Activators Section
------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------

	BEGIN
		--Start tickling
		EXEC SHELLTicklerStartTickling;
		
		--Set up triggers if needed here 

	END;

------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------
--Data Conversion Section
------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------
--if needed, HERE
	
	------------------------------------------------------------------------------------------------------------------------------------------------------
	------------------------------------------------------------------------------------------------------------------------------------------------------
	--Check for the Microsoft "DROPPED queue monitors bug"
	------------------------------------------------------------------------------------------------------------------------------------------------------
	------------------------------------------------------------------------------------------------------------------------------------------------------
	/*
		there is a bug in Service Broker where some of the DMVs are not correctly reflecting that we have dropped a database.  
		when a new db is created it "recycles" the old db_id and the DMV sees that things are out-of-sync and starts disabling queues
		incorrectly.  To get around this we are going to flag the situation where we have a "net new" db (by saying that if the tickler isn't installed
		then we are net new).  If we see the situation where the Q monitors go immediately into the DROPPED state then we are going to recursively
		call back into this procedure and issue the necessary drop commands, and then try to set everything up again from scratch.  We have this code in place
		simply because CIT fails on NETNEW database testing. 
	*/ 
	--IF @NetNewInstall = 0
	--BEGIN
	--	--we are a net new install so this condition should not happen, but let's check it
	--	IF EXISTS 
	--		(
	--			select t4.* , t2.name
	--			FROM sys.services t1
	--			JOIN sys.service_queues t2 
	--				ON t1.service_queue_id = t2.object_id
	--			JOIN sys.dm_broker_queue_monitors t4 
	--				ON t2.object_id = t4.queue_id
	--				AND t4.database_id = DB_ID()
	--			where t4.state = 'DROPPED' 
	--			and t2.name LIKE 'SHELL%'
	--		)
	--	BEGIN
	--		--when this happens we need to DROP everything and then start the installation over again.  
	--		--the process of dropping everything removes the Q monitors
	--		PRINT 'WARNING:  we have Q monitors in a DROPPED state.  This is likely a Microsoft bug.  We will try to fix this now.'
	--		EXEC SB_SHELL_ConfigureAll 'TEARDOWN'
	--		EXEC SB_SHELL_ConfigureAll 'SETUP'
	--		RETURN;
	--	END;
	--END;	
	
	
END;  --SETUP

IF @Option IN ('SETUP', 'CHECK')
BEGIN
	EXEC dbo.ServiceBrokerCheck @Service = 'SHELL'
END;	


IF @Option IN ('BROKER HELP')
BEGIN
	EXEC dbo.ServiceBrokerCheck @Service = 'BROKER HELP'
END; 
	
IF @Option = 'TRACER TOKEN'
BEGIN
------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------
--Send a series of test transactions to determine end-to-end functionality
------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------
	PRINT 'Beginning End-to-End testing. These are not totally conclusive tests, but are a good indicator things are working.'
	PRINT 'TRACER TOKEN NOT IMPLEMENTED IN SHELL YET!!!'

END;		

IF @Option = 'STALLED MESSAGE CLEANOUT'
BEGIN
------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -470,7 +198,6 @@ BEGIN
		SELECT conversation_handle
		FROM sys.conversation_endpoints
		WHERE state_desc <> 'CLOSED' 
		AND far_service <> 'SHELLTicklerSvc'
	open cur
	fetch next from cur into @conv
	while @@fetch_status = 0
@@ -484,20 +211,6 @@ BEGIN
END;
END;
GO
GRANT EXECUTE ON SB_SHELL_ConfigureAll to SiemensUser;
GRANT EXECUTE ON SB_SHELL_ConfigureAll to Public;
GRANT EXECUTE ON dbo.SSB_SHELL_Configure to Public;
GO
--setup is actually run as part of ServiceBrokerEnable.sql file.  We 
--need to do that because of the dependency order of execution of RDT.  (SB_SHELL_ConfigureAll will run before ServiceBrokerEnable)

--EXEC SB_SHELL_ConfigureAll @Option = 'SETUP';
GO
--EXEC SB_SHELL_ConfigureAll @Option = 'CHECK';
--EXEC SB_SHELL_ConfigureAll @Option = 'BROKER HELP';
--EXEC SB_SHELL_ConfigureAll @Option = 'DISABLE ALL QUEUES';
--EXEC SB_SHELL_ConfigureAll @Option = 'TEARDOWN FORCE OVERRIDE';
--EXEC SB_SHELL_ConfigureAll @Option = 'STALLED MESSAGE CLEANOUT'; 
--EXEC SB_SHELL_ConfigureAll @Option = 'TRACER TOKEN'; 
--EXEC ServiceBrokerEnable
GO