0

SQL Server 2019

I'm trying to start stored procedures from a trigger (so that the trigger stays lean) to process data in some production facility. Working with queues/messages etc is new to me

I've created a queue and service:

CREATE QUEUE [dbo].[StoredProcedureQueue]
CREATE SERVICE [StoredProcedureService] ON QUEUE [dbo].[StoredProcedureQueue];
ALTER QUEUE dbo.StoredProcedureQueue
    WITH ACTIVATION (
        PROCEDURE_NAME = dbo.ActivationProcedure,
        MAX_QUEUE_READERS = 1,
        EXECUTE AS OWNER)

The activation procedure is very simple (just for now, it will trigger different stored procedures with parameters later on)

CREATE or ALTER PROCEDURE dbo.ActivationProcedure
AS
BEGIN
    print  'test'
    INSERT INTO tbl_Log (LogType, LogText) values ('test', 'ActivationProcedure')
END

But it's not started

The table trigger would do something like this (but I'm starting this manually):

--------------------
DECLARE @message_body XML;
DECLARE @dlg UNIQUEIDENTIFIER;
-- Construct the XML message body
SET @message_body = '
    <Message>
        <barcode>PS23060094/9.9</barcode>
        <palletweight>993</palletweight>
        <timestamp>' + CONVERT(NVARCHAR(30), GETDATE(), 120) + '</timestamp>
        <processing_procedure>spProcessStep1</processing_procedure>
    </Message>
';
-- Begin a dialog conversation
BEGIN DIALOG CONVERSATION @dlg
    FROM SERVICE [StoredProcedureService]
    TO SERVICE 'StoredProcedureService'
    ON CONTRACT [DEFAULT]
    WITH ENCRYPTION = OFF;
-- Send the XML message to the Service Broker queue using the conversation handle
SEND ON CONVERSATION @dlg (@message_body);
-- End the dialog conversation
END CONVERSATION @dlg;

but I'm not getting anything in tbl_log, which indicates that the activation procedure is not triggered.

The activation procedure is linked to the queue

SELECT name, activation_procedure
FROM sys.service_queues
WHERE name = 'StoredProcedureQueue';

When I query

SELECT * FROM sys.transmission_queue
SELECT * FROM sys.conversation_endpoints

I'm not seeing anything, except some times some records, sometimes not. I'm not sure this means my messages are getting processed, but I'm still not seeing the result of the activation procedure in tbl_log

At one point these gave me records, the first ... transmission_status: the target service name could not be found ... and the second ... state_desc: DISCONNECTED_OUTBOUND ...

I am db_owner, so permissions should not be a problem

Anyone that knows what to do? Btw the above code is mostly provided by Chat GPT also

2
  • Which dbms are you using? (The above code is very product specific.) Commented Apr 3, 2024 at 8:16
  • Sorry, it's MS SQL server 15.0 (2019) Commented Apr 3, 2024 at 8:20

2 Answers 2

1

The problem is likely to be that you didn't specify the CONTRACT when creating the Service, ie:

CREATE SERVICE [StoredProcedureService] ON QUEUE [dbo].[StoredProcedureQueue]([DEFAULT]);

Otherwise, the messages will be sent with DEFAULT contract, but your service doesn't support it.

You can probably see the error if you do:

select cast(message_body as xml),*
from StoredProcedureQueue

For me, it returns:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Error xmlns="http://schemas.microsoft.com/SQL/ServiceBroker/Error">
  <Code>-8408</Code>
  <Description>Target service 'StoredProcedureService' does not support contract 'DEFAULT'.</Description>
</Error>

Another thing, among others, is that you shouldn't END CONVERSATION in your script: END CONVERSATION @dlg; but rather do it in the procedure.

Finally, you need to actually enable Activation by setting: STATUS = ON.

Script for queues + services:

drop SERVICE [StoredProcedureService] 
go
drop QUEUE [dbo].[StoredProcedureQueue]
go
CREATE QUEUE [dbo].[StoredProcedureQueue]
go
CREATE SERVICE [StoredProcedureService] ON QUEUE [dbo].[StoredProcedureQueue]([DEFAULT]);
go

ALTER QUEUE dbo.StoredProcedureQueue
    WITH ACTIVATION (
    STATUS = ON,
        PROCEDURE_NAME = dbo.ActivationProcedure,
        MAX_QUEUE_READERS = 1,EXECUTE AS OWNER
        )
GO

Logging code:

drop table tbl_Log 
create table tbl_Log (ID INT IDENTITY PRIMARY KEY, logtype nvarchar(100), logtext nvarchar(max), dateCreated datetime not null default getdate())

Procedure code:

CREATE PROCEDURE dbo.ActivationProcedure
AS
BEGIN
    INSERT INTO tbl_Log (LogType, LogText) values ('test', 'ActivationProcedure')
     DECLARE @conversation_handle UNIQUEIDENTIFIER;
  DECLARE @message_body XML;
  DECLARE @message_type_name sysname;
     WHILE (1=1)
  BEGIN
    BEGIN TRANSACTION;

    WAITFOR
    (
      RECEIVE TOP (1)
        @conversation_handle = conversation_handle,
        @message_body = CAST(message_body AS XML),
        @message_type_name = message_type_name
      FROM StoredProcedureQueue
    ), TIMEOUT 5000;

    IF (@@ROWCOUNT = 0)
    BEGIN
      ROLLBACK TRANSACTION;
      BREAK;
    END

    IF @message_type_name = N'DEFAULT'
    BEGIN
      -- If necessary handle the reply message here
      DECLARE @AccountNumber NVARCHAR(MAX) = @message_body.value('(Message/timestamp)[1]', 'nvarchar(max)');
    print @AccountNumber;
     -- Not needed if you have a service without reply
     SEND ON CONVERSATION @conversation_handle
        ('<ok>OK</ok>');
      -- Since this is all the work being done, end the conversation to send the EndDialog message
      END CONVERSATION @conversation_handle;
    END

    -- If end dialog message, end the dialog
    ELSE IF @message_type_name = N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog'
    BEGIN
       END CONVERSATION @conversation_handle;
    END

    -- If error message, log and end conversation
    ELSE IF @message_type_name = N'http://schemas.microsoft.com/SQL/ServiceBroker/Error'
    BEGIN
       END CONVERSATION @conversation_handle;
    END

    COMMIT TRANSACTION;
  END
END

Test sending code:

DECLARE @message_body XML;
DECLARE @dlg UNIQUEIDENTIFIER;
-- Construct the XML message body
SET @message_body = '
    <Message>
        <barcode>PS23060094/9.9</barcode>
        <palletweight>993</palletweight>
        <timestamp>' + CONVERT(NVARCHAR(30), GETDATE(), 120) + '</timestamp>
        <processing_procedure>spProcessStep1</processing_procedure>
    </Message>
';

-- Begin a dialog conversation
BEGIN DIALOG CONVERSATION @dlg
    FROM SERVICE [StoredProcedureService]
    TO SERVICE 'StoredProcedureService'
    ON CONTRACT [DEFAULT] -- Important!
    WITH ENCRYPTION = OFF;
-- Send the XML message to the Service Broker queue using the conversation handle
SEND ON CONVERSATION @dlg (@message_body);
Sign up to request clarification or add additional context in comments.

Comments

1

Tnx, I fixed some issues, now it works:

ALTER QUEUE dbo.StoredProcedureQueue
    WITH ACTIVATION (
    STATUS = ON,
        PROCEDURE_NAME = dbo.ActivationProcedure,
        MAX_QUEUE_READERS = 1,EXECUTE AS OWNER
        )

I did not have the status = on before, so that fixed that my queue did not trigger the procedure. Think that was the main issue.

Also I checked the MS info on the servicebroker some more, and it seems that I need to specifiy a different service/queue both for sending and receiving, so I also did that. I was not sure if that was actually necessary, your examples seems to show it's not that.

I also changed my procedure: no while loop and no check on @@rowcount (because every message should trigger the procedure, these are redundant. It would be usefull if the procedure continuously runs to process incoming messages but that's not the case here.

2 Comments

Yeah, i'm not sure that's needed for simple code, but perhaps if you need to wait for reply in the originating service, you might need a second queue to read the replies from. ServiceBroker is nice, but a bit complicated to be honest!
A sender queue would be necessary if you are going to send replies, but at this moment I'm not doing that. Might do it perhaps to record processing of messages? Anyway, I have a working setup now, tnx again

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.