Cleaning up BAM Activity Instances
One of the main challenges of Business Activity Monitoring(BAM) solutions is the control the lifecycle of the activities. This is especially critical in scenarios that involve activity continuations between different applications. Far too many times activities don't get completed affecting important BAM components such as aggregations, KPIs, etc. More importantly, keeping incomplete activities stored in the "ActivityName"_Active tables in the BAMPrimaryImport database can tremendously affect the performance of BizTalk BAM solutions. As part of the BizTalk server SDK we can find a SQL Server stored procedure that moves the incomplete activities from the Active tables to a new set of tables, the essence of this stored procedure is to add the incomplete state to the BAM Activity lifecycle creating a new table per BAM activity as well as its continuations and relationships. This stored procedure recycles individual activities and keeps the incomplete tables in the BAMPrimaryImport database. Although this approach works great, for a lot of scenarios makes sense to move the incomplete activities directly to the BAMArchive database keeping the BAMPrimaryImport database for storing live data. We can accomplish that with a few slight modifications to the same stored procedure highlighted in the following code. This stored procedure is intended to run in the BAMArchive database but it will poll the data from the BAMPrimaryImport database (notice the database prefix in some of the queries)
Create procedure CleanupUncompleteActivity @ActivityName nvarchar (128), @ActivityId nvarchar(128)=NULL, @DateThreshold datetime=NULL, @NewTableExtension nvarchar(30)=NULL as --stored procedure body DECLARE @BAMDBPrefix nvarchar(500 ) DECLARE @QueryString nvarchar(4000 ) DECLARE @ActiveTableName sysname DECLARE @ActiveRelationshipsTableName sysname DECLARE @ContinuationsTableName sysname DECLARE @DanglingActiveTableName sysname DECLARE @DanglingActiveRelationshipsTableName sysname DECLARE @DanglingContinuationsTableName sysname SET @BAMDBPrefix= 'BAMPrimaryImport.dbo.' SET @ActiveTableName = 'bam_' + @ActivityName + '_Active' SET @ActiveRelationshipsTableName = 'bam_' + @ActivityName + '_ActiveRelationships' SET @ContinuationsTableName = 'bam_' + @ActivityName + '_Continuations' SET TRANSACTION ISOLATION LEVEL READ COMMITTED BEGIN TRAN DECLARE @LockActivity nvarchar(128 ) SELECT @LockActivity = ActivityName FROM BAMPrimaryImport.dbo.bam_Metadata_Activities WITH (XLOCK) WHERE ActivityName = @ActivityName EXEC sp_tables @table_name = #DanglingActivities IF @@ROWCOUNT > 0 DROP TABLE #DanglingActivities CREATE TABLE #DanglingActivities( ActivityID nvarchar(128) PRIMARY KEY) SET @QueryString = N'INSERT INTO #DanglingActivities (ActivityID) SELECT ActivityID FROM ' + @BAMDBPrefix + 'bam_' + @ActivityName + '_Active' IF (@DateThreshold is not NULL) OR (@ActivityId is not NULL) BEGIN SET @QueryString = @QueryString + ' WHERE' END IF (@DateThreshold is not NULL) BEGIN SET @QueryString = @QueryString + ' LastModified < N''' + CONVERT (nvarchar(50), @DateThreshold , 109) + '''' IF (@ActivityId is not NULL) BEGIN SET @QueryString = @QueryString + ' AND' END END IF (@ActivityId is not NULL) BEGIN SET @QueryString = @QueryString + ' ActivityID = N''' + @ActivityId + '''' END EXEC sp_executesql @QueryString SELECT * FROM #DanglingActivities SET @QueryString = N'' -- If the user gave a table extension, the dangling instances will be inserted -- into that table. IF (isnull(@NewTableExtension , '') <> '') BEGIN SET @DanglingActiveTableName = @ActiveTableName + '_' + @NewTableExtension SET @DanglingActiveRelationshipsTableName = @ActiveRelationshipsTableName + '_' + @NewTableExtension SET @DanglingContinuationsTableName = @ContinuationsTableName + '_' + @NewTableExtension -- If the table for the dangling instances exist then insert into it -- If the table does not exist, then create the dangling instances table -- and then insert into it. SELECT INTO will do that. -- Use [BAMArchive] EXEC sp_tables @table_name = @DanglingActiveTableName --USE [BAMPrimaryImport] IF @@ROWCOUNT > 0 BEGIN SET @QueryString = N'INSERT INTO ' + '[' + @DanglingActiveTableName + '] SELECT active.* FROM ' + @BAMDBPrefix + @ActiveTableName + ' as active INNER JOIN #DanglingActivities dangling ON active.ActivityID = dangling.ActivityID' EXEC sp_executesql @QueryString END ELSE BEGIN SET @QueryString = N'SELECT active.* INTO [' + @DanglingActiveTableName + '] FROM ' + @BAMDBPrefix + @ActiveTableName + ' as active INNER JOIN #DanglingActivities dangling ON active.ActivityID = dangling.ActivityID' select @QueryString EXEC sp_executesql @QueryString END -- Now do what you did for the Active Instances table for the -- ActiveRelationships table EXEC sp_tables @table_name = @DanglingActiveRelationshipsTableName IF @@ROWCOUNT > 0 BEGIN SET @QueryString = N'INSERT INTO ' + '[' + @DanglingActiveRelationshipsTableName + '] SELECT active.* FROM ' + @BAMDBPrefix + @ActiveRelationshipsTableName + ' as active INNER JOIN #DanglingActivities dangling ON active.ActivityID = dangling.ActivityID' EXEC sp_executesql @QueryString END ELSE BEGIN SET @QueryString = N'SELECT active.* INTO [' + @DanglingActiveRelationshipsTableName + '] FROM ' + @BAMDBPrefix+ @ActiveRelationshipsTableName + ' as active INNER JOIN #DanglingActivities dangling ON active.ActivityID = dangling.ActivityID' EXEC sp_executesql @QueryString END -- And finally for the continuations table EXEC sp_tables @table_name = @DanglingContinuationsTableName IF @@ROWCOUNT > 0 BEGIN SET @QueryString = N'INSERT INTO ' + '[' + @DanglingContinuationsTableName + '] SELECT active.* FROM ' + @BAMDBPrefix + @ContinuationsTableName + ' as active INNER JOIN #DanglingActivities dangling ON active.ParentActivityID = dangling.ActivityID' EXEC sp_executesql @QueryString END ELSE BEGIN SET @QueryString = N'SELECT active.* INTO [' + @DanglingContinuationsTableName + '] FROM ' + @BAMDBPrefix + @ContinuationsTableName + ' as active INNER JOIN #DanglingActivities dangling ON active.ParentActivityID = dangling.ActivityID' EXEC sp_executesql @QueryString END END -- Remove the dangling instances from the Active Instances Table SET @QueryString = 'DELETE FROM ' + @BAMDBPrefix + @ActiveTableName + ' FROM ' + @BAMDBPrefix + @ActiveTableName + ' as active INNER JOIN #DanglingActivities dangling ON active.ActivityID = dangling.ActivityID ' EXEC sp_executesql @QueryString SET @QueryString = 'DELETE FROM ' + @BAMDBPrefix + @ActiveRelationshipsTableName + ' FROM ' + @BAMDBPrefix + @ActiveRelationshipsTableName + ' as active INNER JOIN #DanglingActivities dangling ON active.ActivityID = dangling.ActivityID ' EXEC sp_executesql @QueryString SET @QueryString = 'DELETE FROM ' + @BAMDBPrefix + @ContinuationsTableName + ' FROM ' + @BAMDBPrefix + @ContinuationsTableName + ' as active INNER JOIN #DanglingActivities dangling ON active.ParentActivityID = dangling.ActivityID ' EXEC sp_executesql @QueryString DROP TABLE #DanglingActivities COMMIT TRAN GO |
As we mentioned previously, the stored procedure recycle individual activities. In case you need to cleanup multiple activities at the same time you can loop though the list of activities stored in the bam_Metadata_Activities table in the BAMPrimaryImport database. The following code illustrates that technique.
Create procedure CleanupUncompleteActivities @DateThreshold datetime =NULL, @NewTableExtension nvarchar(30)=NULL as declare @activityname nvarchar(250) declare actcursor cursor for select ActivityName from BAMPrimaryImport. dbo.bam_Metadata_Activities open actcursor FETCH NEXT FROM actcursor INTO @activityname WHILE @@FETCH_STATUS = 0 begin execute CleanupUncompleteActivity @ActivityName= @activityname , @NewTableExtension= 'Uncompleted', @DateThreshold= @DateThreshold FETCH NEXT FROM actcursor INTO @activityname end close actcursor deallocate actcursor
|
You can download both stored procedure here but in any case we don't advice using these in production environments.