Maybe you have a tickler where the message is merely asking the target service to "wake up" and do something. In this case no message type is needed. However, for clarity I would still create a message type and call it Tickler with VALIDATION = EMPTY
...meaning I'm expecting it to have no payload.
Perhaps your service is simple enough that you only ever expect one type of message with a little bit of XML to traverse from service to service. You are fine with not validating your XML...you are a trusting soul. Again, no message type is really needed. But if you believe in "sanitizing your sql inputs" then you really do want to validate your XML. In that case, you need a message type that is either WELL_FORMED_XML
or, even better in most cases, VALID_XML with SCHEMA COLLECTION.
So, a message type is a lot like various constraints on a table.
Type of "constraint" | Message Type Equivalent | Notes |
---|---|---|
no constraint on a NVARCHAR(MAX) column | NONE | With NONE you can do whatever you want with the Message Type payload. Just like you can do just about anything with an NVARCHAR(MAX) column |
CHECK and DEFAULT on a bit column | EMPTY | EMPTY means the message payload must not contain anything. This is kinda/sorta like a NOT NULL specification on a bit column with a DEFAULT of 1 and CHECK CONSTRAINT of 1. The column will be automatically set to the only possible value of 1. EMPTY is used to flag that an event occurred. The target service will have defined workflows that will respond to that event. |
a datatype declaration on a column | WELL_FORMED_XML | When you declare a datatype on a column you are specifying that only certain valid data will be allowed. In this case only text that can be converted to the xml datatype will be accepted. |
CHECK constraint to enforce a mask on a SSN col | VALID_XML WITH SCHEMA COLLECTION | This states that the XML must be in a given format, much like a SSN is in the format "123-45-6789". |
A CHECK constraint limits the allowable values in a column to a defined domain. A Message Type does the same thing for your XML message payloads. As with a CHECK constraint, a message type is not needed, but you'll find you have better quality data, and less support issues, when you have them properly implemented.
Whenever I design a new SB implementation I don't concern myself with the message types in the beginning. I want to get a prototype up and running quickly. After I have some stability I then add in these constraints.
What is a Contract?
A contract is a binding agreement between a message type and who can utilize it. If you don't have message types then you don't have contracts. Period. So again, I save contract implementation for a point in the project when I have a stable design.
Service Broker conversations are always dialogs (that's why the syntax is BEGIN DIALOG CONVERSATION
). A dialogue in the English language, according to google, is a conversation between two or more people. In SQL Server it is a little more strict...a dialog is EXACTLY two parties...an Initiator and a Target. You can model monologs in Service Broker just like in other messaging systems, but it requires some extra effort and I'll cover that in a future blog post. (If you can't wait for that post...for a monolog in SB you really need to have an activator on the initiator side that is ENDing CONVERSATION). Again, we'll cover this in a future post since it is confusing and most noobs model their SB implementations as a series of monolog workflows, not dialogs (just my opinion).
We got off subject...a contract merely says who can SEND the given message type. Let's say you have SB services called EmployeeSvc and AccountingSvc. Let's say you have a message type of ExpenseReportSubmission. The goal is to allow the EmployeeSvc to submit an expense report to the AccountingSvc. Let's model this as simply as possible with NO contracts or message types. You can follow along by downloading the repro script.
Here is the most basic setup...there are NO contracts or message types defined. The [DEFAULT] contract is specified and the "contract clause" is necessary whenever you want to create a Service capable of receiving messages. See my last post for some clarification on that bit of confusion. If we don't at least provide the [DEFAULT] contract we'll get this error in the sending queue:
At this point it would be the responsibility of whatever program or activator is processing the AccountingQ to perform any message validation and send any errors back to the sending service. But it's probably better design to implement those constraints as message types and contracts. We do that by creating a MESSAGE TYPE that specifies that we will only accept WLL_FORMED_XML. We can of course be even more precise, but this should be sufficient to illustrate the point. We then create a contract that specifies that our new message type can only be sent by the initiator. Finally we bind the contract to the existing service.
Let's run a little test. This time we are going to send some XML (Line 81) and ensure that our message made it to the target service. Note that the previous two messages, prior to us adding the "constraints" are still available to be processed.
Let's see what happens when we attempt to send some garbage now that we have proper "constraints"
What? That actually did succeed! Why? Because we did not specify the contract clause of the BEGIN DIALOG statement. Look around Line 97. The ON CONTRACT
statement is missing. Note that we did include that on Line 78 in the previous screenshot above.
So, you are probably asking yourself, even with a proper constraint at the target service we can still send garbage? What's the point of contracts and message types? With a database constraint the constraint is enforced regardless of whether you specifically ask it to be enforced (yes, I know you can disable a constraint and force it to go [[untrusted constraints|untrusted]]).
<Error xmlns="http://schemas.microsoft.com/SQL/ServiceBroker/Error"> <Code>-8408</Code> <Description>Target service 'AccountingSvc' does not support contract 'DEFAULT'.</Description> </Error>
|
So now the error is being thrown correctly and we can no longer use the [DEFAULT] contract.
BEGIN DIALOG
syntax is correct you can always send a message without the sender seeing the error. Instead, the error causes the message to be retained in the sender's queue and sys.transmission_queue
.
You have just read "Service Broker Demystified - Contracts and Message Types" on davewentzel.com. If you found this useful please feel free to subscribe to the RSS feed.
Dave Wentzel CONTENT
sql server service broker service broker demystified