Chapter 18 · Change Tracking, CDC, Service Broker, and Integration Patterns

Service Broker Concepts, Queues, Services, Dialogs, Activation, and Reliable Messaging

Choose deliberately among AGs, replication, log shipping, CDC/ETL and application replication from the information and failover contract actually required.

Advanced180–220 minutesService Broker transactional messaging labSQL Server 2025 CU7 · 17.0.4065.4Compatibility 170 · Express/DeveloperSingle-instance mandatory path · Last reviewed August 2026

Learning outcomes

Some ServiceHub work should happen reliably after the transaction that accepts a request, but it should not force the foreground session to perform every downstream step synchronously. SQL Server Service Broker provides durable, transactional messaging primitives inside SQL Server: messages, contracts, queues, services and dialogs. Its value comes from coupling SEND and RECEIVE to database transactions. Its boundary is equally important: it is not a drop-in Kafka/RabbitMQ ecosystem, it does not promise global ordering across unrelated dialogs, and external fan-out/consumer-group semantics must be designed explicitly.

01

Define Service Broker message types, contracts, queues, services, dialogs, routes and conversation endpoints before using them.

02

Build a local transactional SEND/RECEIVE lab and observe queue and transmission state.

03

Explain internal activation, MAX_QUEUE_READERS, EXECUTE AS security and why activation is a concurrency mechanism rather than a scheduler.

04

Diagnose disabled queues, transmission backlog and poison-message behavior without deleting evidence blindly.

05

Design conversation cleanup and idempotent message handling within the exact reliability scope Service Broker provides.

Lab baseline. SQL Server 2025 (17.x), CU7 build 17.0.4065.4; database compatibility level 170 unless the lesson says otherwise. Use Enterprise Developer or Standard Developer for free non-production learning when the feature is edition-gated. Change Tracking and Service Broker can be explored on Express; boxed SQL Server CDC requires Standard/Enterprise capability and SQL Server Agent. SSMS 22.8.2, VS Code + the current MSSQL extension, or current sqlcmd are supported paths. Azure Data Studio is retired.

1. Mental model: typed conversations terminate at services backed by queues

A message type names and optionally validates a message body. A contract states which side may send which message types. A queue stores incoming messages. A service is the logical address attached to a queue and contracts. A dialog conversation is a long-lived ordered exchange between two Service Broker services. For remote delivery, routes and a Service Broker endpoint become part of the topology; the mandatory lab keeps both services in one database so no network endpoint is required.

Service Broker is enabled by default for newly created SQL Server databases in current versions, but restored/copied databases and operational changes can produce different states. Always verify sys.databases.is_broker_enabled instead of assuming. Disabling broker delivery leaves messages pending in the transmission path; it does not silently discard them.

sql · inspect broker state before creating objects
SELECT name, is_broker_enabled, service_broker_guidFROM sys.databasesWHERE name = N'ServiceHubLab';GOUSE ServiceHubLab;GOIF SCHEMA_ID(N'lab18') IS NULL  EXEC(N'CREATE SCHEMA lab18 AUTHORIZATION dbo;');GO

2. Build a local dialog and observe transactional delivery

sql · create message, contract, queues and services
USE ServiceHubLab;GOIF EXISTS (SELECT 1 FROM sys.services WHERE name=N'//ServiceHub/Lab18/Initiator')  DROP SERVICE [//ServiceHub/Lab18/Initiator];IF EXISTS (SELECT 1 FROM sys.services WHERE name=N'//ServiceHub/Lab18/Target')  DROP SERVICE [//ServiceHub/Lab18/Target];IF OBJECT_ID(N'lab18.InitiatorQueue', N'SQ') IS NOT NULL DROP QUEUE lab18.InitiatorQueue;IF OBJECT_ID(N'lab18.TargetQueue', N'SQ') IS NOT NULL DROP QUEUE lab18.TargetQueue;IF EXISTS (SELECT 1 FROM sys.service_contracts WHERE name=N'//ServiceHub/Lab18/WorkContract')  DROP CONTRACT [//ServiceHub/Lab18/WorkContract];IF EXISTS (SELECT 1 FROM sys.service_message_types WHERE name=N'//ServiceHub/Lab18/WorkRequested')  DROP MESSAGE TYPE [//ServiceHub/Lab18/WorkRequested];GOCREATE MESSAGE TYPE [//ServiceHub/Lab18/WorkRequested]  VALIDATION = WELL_FORMED_XML;CREATE CONTRACT [//ServiceHub/Lab18/WorkContract]  ([//ServiceHub/Lab18/WorkRequested] SENT BY INITIATOR);CREATE QUEUE lab18.InitiatorQueue;CREATE QUEUE lab18.TargetQueue;CREATE SERVICE [//ServiceHub/Lab18/Initiator]  ON QUEUE lab18.InitiatorQueue ([//ServiceHub/Lab18/WorkContract]);CREATE SERVICE [//ServiceHub/Lab18/Target]  ON QUEUE lab18.TargetQueue ([//ServiceHub/Lab18/WorkContract]);GO
sql · SEND inside a transaction, then receive at the target
DECLARE @dialog uniqueidentifier;DECLARE @body nvarchar(max) = N'<workOrder id="1001" action="dispatch" />';BEGIN TRAN;BEGIN DIALOG CONVERSATION @dialog  FROM SERVICE [//ServiceHub/Lab18/Initiator]  TO SERVICE N'//ServiceHub/Lab18/Target'  ON CONTRACT [//ServiceHub/Lab18/WorkContract]  WITH ENCRYPTION = OFF;SEND ON CONVERSATION @dialog  MESSAGE TYPE [//ServiceHub/Lab18/WorkRequested] (@body);COMMIT;SELECT conversation_handle,       message_type_name,       CAST(message_body AS nvarchar(max)) AS message_bodyFROM lab18.TargetQueue;GO

The message becomes durable as part of the sender’s commit. If the transaction rolls back, the send rolls back with it. On the target, RECEIVE is likewise transactional: a rollback returns received messages to the queue. This creates a strong in-database atomicity boundary that is valuable for reliable work dispatch.

sql · receive one message and close the target conversation endpoint
DECLARE @target_handle uniqueidentifier;DECLARE @message_type sysname;DECLARE @message_body varbinary(max);BEGIN TRAN;WAITFOR(  RECEIVE TOP (1)         @target_handle = conversation_handle,         @message_type = message_type_name,         @message_body = message_body  FROM lab18.TargetQueue), TIMEOUT 5000;SELECT @message_type AS received_type,       CAST(@message_body AS nvarchar(max)) AS received_body;IF @target_handle IS NOT NULL  END CONVERSATION @target_handle;COMMIT;GO

Ending the target endpoint causes an EndDialog system message to flow back to the initiator, which the initiator should receive and use to close its endpoint. Conversation cleanup is part of application correctness; abandoned endpoints can accumulate state.

3. Activation is queue-driven concurrency with a security context

Internal activation associates a stored procedure with a queue. When work arrives and activation is required, SQL Server starts procedure instances up to MAX_QUEUE_READERS. The procedure executes under the configured principal. This is not a general cron scheduler; it is a queue-reader lifecycle optimized for Service Broker work. Set the reader count from measured processing cost, contention, ordering requirements and downstream capacity—not from CPU count folklore.

sql · inspect queue activation and poison-message settings
SELECT  SCHEMA_NAME(q.schema_id) AS schema_name,  q.name,  q.is_receive_enabled,  q.is_activation_enabled,  q.max_readers,  q.activation_procedure,  q.execute_as_principal_id,  q.is_poison_message_handling_enabledFROM sys.service_queues AS qWHERE q.name IN (N'InitiatorQueue', N'TargetQueue');SELECT * FROM sys.dm_broker_activated_tasksWHERE database_id = DB_ID(N'ServiceHubLab');GO

The activation principal needs only the permissions required by the queue-processing module and referenced objects. Using EXECUTE AS OWNER without reviewing ownership chains and module permissions can unintentionally widen the security boundary.

4. Wrong approach: keep retrying a poison message until the whole queue stops

A poison message is not necessarily corrupt. It is a message that repeatedly causes the receive transaction to roll back—for example, a once-valid request whose referenced business object was removed. Automatic poison-message detection can disable a queue after five rollbacks of a transaction that receives from it. This is a circuit breaker, not the normal error-handling strategy.

Failure injection is optional. Do not repeatedly roll back a production queue to “test” poison handling. Use a disposable queue and preserve the message body/conversation/error evidence before remediation.
sql · diagnose queue and transmission state without deleting evidence
SELECT name,       is_receive_enabled,       is_enqueue_enabled,       is_poison_message_handling_enabledFROM sys.service_queuesWHERE name IN (N'InitiatorQueue', N'TargetQueue');SELECT conversation_handle,       to_service_name,       transmission_status,       enqueue_timeFROM sys.transmission_queueORDER BY enqueue_time;SELECT state_desc,       far_service,       lifetime,       dialog_timerFROM sys.conversation_endpointsWHERE far_service LIKE N'//ServiceHub/Lab18/%';GO

If a queue is OFF, identify the failing message and processing rule, repair or quarantine the cause according to the application contract, then use ALTER QUEUE ... WITH STATUS = ON. Blindly clearing a queue destroys the very evidence needed to explain business divergence.

5. Production judgment and bridge

Use Service Broker when transactional, SQL Server-native asynchronous work and dialog semantics are valuable, especially when message handling belongs close to database transactions. Operate queue depth, disabled queues, transmission errors, activation tasks, conversation lifetime and poison handling. For cross-instance designs, add endpoints, routing, transport security and availability-group behavior deliberately. Lesson 4 moves to a very different integration boundary: synchronous distributed queries through linked servers, where remote latency, provider behavior, authentication and distributed transactions can couple two systems at request time.

Check your understanding

  1. What is the atomicity advantage of SEND inside a SQL Server transaction?
  2. Does Service Broker provide global ordering across every queue and dialog?
  3. Why can a queue become disabled after repeated failures?
  4. What is internal activation?
  5. Why is Service Broker not automatically a Kafka replacement?
Review the answers

1. The message send commits or rolls back with the surrounding database transaction, avoiding a database-write/queue-publish split inside that SQL Server boundary.

2. No. Ordering guarantees are conversation/dialog scoped; unrelated dialogs and concurrent readers require explicit application semantics.

3. Automatic poison-message detection can disable a queue after five receive transactions roll back on problematic work.

4. A queue-driven mechanism that starts a stored procedure under a configured principal up to MAX_QUEUE_READERS when work requires processing.

5. Its topology, ecosystem, fan-out/consumer semantics and operational model are SQL Server-native and materially different; choose from the required integration contract.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.