I'm using conversation timers successfully to fire events at a predetermined time in the future, but I'm running into issues when using an interval of considerable size. I set the conversation timeout like so:
set @.Timeout = DATEDIFF(SECOND, GETDATE(), DATEADD(MINUTE, -(@.TimeOffset), @.FollowUpDateTime));
if (@.Timeout < 0)
set @.Timeout = 1;
// begin dialog
begin conversation timer (@.FollowUpConversation)
timeout = @.Timeout;
In this case @.Timeout was 94693494.
In the SQL error log I see the following error: "Invalid subcommand value 94693494. Legal range from 1 to 2147483."
I thought I may check the @.Timeout value and simply set it to 2147483 if it is larger than 2147483, but I was wondering if there was a reason the upper limit was so small.
Thanks,
Chris
The upper limit should be 2147483647, max 32 bit integer value, same as the maximum dialog lifetime. The fact that the max value gets divided by 1000 is a defect.
To work around the problem, you can use dialog lifetime instead of dialog timer. I.e. instead of setting a timer on an existing dialog, begin a new dialog with the desired lifetime. Instead of the Timer message, you'll get a Error message that the dialog lifetime has expired. This will give you an upper limit of around 47 years, which I guess is enough.
BTW, since the dialog is errored when the lifetime expires, you must do this on a different dialog that the one on which the document was received in your workflow.
HTH,
~ Remus
Thanks for the reply Remus. I'm hesitant to handle the Error message for the lifetime expired error because I'm hoping that the upper limit for the conversation timer will be addressed in the future and I'd rather not have to refactor that code. I think for now I'll just recalculate the timeout when the dialogtimer message type is handled and reset the timeout if I need to. This raises another question: can I set a conversation lifetime AND a conversation timer on a dialog? I just want to make sure I don't run into a situation where the dialog lifetime expires BEFORE the conversation timer expires.
Thanks again.
|||Reseting the timer also will work.
All dialogs have a lifetime. If you don't specify one, you'll get the default max lifetime. But the dialog lifetime can only be set at the creation of the dialog (in the BEGIN DIALOG statement) and cannot be changed.
You cannot have the lifetime fire before the timer. Attempting to set the timer beyond lifetime will error the BEGIN DIALOG TIMER statement.
HTH,
~ Remus
Remus,
On a similar note, why wouldn't you specify NO lifetime for all dialogs to ensure that they stay alive long enough to complete? I'm guessing best practice is to specify a lifetime ONLY when you are sure that the process that is spawned will complete within the lifetime you specify, no?
Thanks again,
Chris
|||Welcome to 'reliable error semantics'.
I'd say that the best practice is to DO specify a lifetime that should cover the maximum allowed time for a business transaction to complete. Usually this lifetime is much longer than the time needed to complete the transaction (including any process this might launch). Typically, the transaction should be complete in a matter of minutes/seconds and the lifetime should cover maybe a day or so. This lifetime would allow for any service unavailability or transmission problems. The question usually raised is 'why not simply rely on en error message to undo the transaction?' If the initiator is tired of waiting and decide to eror the conversation, it could simply use an END CONVERSATION ... WITH ERROR and call it a day. But the problem is what if the initiator can no longer connect itself to the target? How long is the target service supposed to keep the state for this unfinished business transaction? Similarly, what if the host of the initiator is simply stolen or destroyed? Again, how long should the target wait? Having an explicit lifetime puts a limit on this, and most importantly, is a limit on which both the initiator and target agree. If a conversation has expired on one endpoint, you are guaranteed that it expired on the other as weel.
HTH,
~ Remus
Remus,
Thanks for the info, it is of great help. I have just one more question regarding conversation lifetimes: I have multiple service broker services that process a business transaction. Service A sends a message to service B, which sends a message to service C, where a long-running process takes place (anywhere from a few seconds to 15-20 minutes). Is service C, which actually spawns the long-running process, the only conversation that requires a considerable lifetime, or do the services that converse with service C also need a lifetime of equal or greater size than service C? Intuition tells me that the conversation need be open only long enough for the process it spawns to finish, and in the case of services A and B (which perform little business logic and forward the message onto service C) the lifetime can be relatively short.
Thanks for your help,
Chris
|||In the case when service A has a conversation with service B (we'll call this conversation 'AB') and service B has a related conversation with service C (say 'BC'), then the most generic case should be that the lifetime of 'BC' should be shorter than the lifetime of 'AB'. Probably B cannot send a response to A on 'AB' until it receives the related response from C on 'BC'. If 'AB' expires before C sends the reponse, then there is no reason for C to even bother to send the response. I'm not advocating for B to actualy start making computation of how much from the lifetime of 'AB' has passed when it initiates 'BC' and set a lifetime accordingly. Not that is hard, but I believe this is overkill most cases. But the application designer should consider choosing reasonable lifetimes.
Other case might be when B can send immediatly a response to A: in the same transaction B receives the request from A on 'AB', innitiates 'BC' and sends its own request to C, then ends 'AB' and commits the transactrion. In this case, the lifetimes of 'AB' and 'BC' are completely unrelated.
Also, there might be cases when 'AB' is long lived (days, months, even years) and receives many requests from A to B, while 'BC' is short lived and created from scratch for each individual request from B to C. This is easy to immagine when A and B are in different enterprises and the conversation semantics might have some legal binding contract (e.g the order of requets from A to B must be preserved, therefore all requests must come on the same conversation, think stock trading orders), while B and C are in the same enterprise and requests can be processed in any order.
One other thing the lifetime has to cover is downtime/service unavailability. Having a longer lifetime allows for the targeted service to be unresponsive for a period, or even for the sending service to be unconnected when it creates the conversation. This is why I recommend in general lifetimes of hours or a day.
Without knowing what A/B/C do, I cannot express an opinion on exactly how long the lifetimes should be. But with the cases described above, I think you should be able to make an rational decision.
BTW, in case you're wondering what happens when a request is sent to a service, enqueued, and the lifetime expires before the application actually has a chance to dequeue the request message (is still in the service's queue). The answer is that Error messages are always moved to the top of the queue (for a conversation), so the application will see the Error message first. It can then simply skip the processing of the request. This happens naturally if the application responds to an Error message with and END CONVERSATION, because ending a conversation has the side effect of removing any pending messages from the service queue.
HTH,
~ Remus
P.S. This is a very interesting conversation, it made me think about a couple of issues myself. I'm thinking of posting it as it is into my blog.
|||Remus,
I should have mentioned that I am not expecting a response from the target queue(s) in this scenario. All of the communication that takes place in our app is one-way with the exception of the EndDialog messages sent to the Initiators (we couldn't think of an appropriate response and we are also using the ServiceBrokerInterface, which meant we didn't want to tie up code waiting on a response when one might not come for quite awhile).
Our scenario is as follows: a trigger or nt service app inserts a message into service A, where it is determined by the message type what to do with it. According to the message type (and more business logic), we send a new message to service B (on a new conversation) and end the conversation started by the initiator. Service B then performs additional processing (based on the message contents) and sends a new message (on a new conversation) to Service C. Service B then ends the conversation started by Service A. Service C runs a stored proc (which can be a long-running process) and ends the conversation initiated by Service B. I've put activated stored procedures in place to handle the EndDialog messages sent to the initiators after reading your "Fire and Forget" article.
Should I use the same conversation throughout the process or is beginning a new conversation each time I send a new message to another service (to process a single entity) OK? I'm beginning to wonder if I should use the converstion handle I create originally throughout the process so that I can requeue the process if any portion of it fails.
This conversation is also making me think as well.
Thanks again,
Chris
|||If each message sent by A (by the trigger/NT service) is independent, I'd recommend having one dialog per message.
When several messages sent by A are related, then you must send all related messages on one dialog. You would probably need a table on the sender side to locate the appropiate dialog handle.
Since you don't expect replies, the rules are mostly set by the target processing. Because RECEIVE locks the conversation on which it dequeues messages (actually, the conversation group), it means that only one target thread (SPID) can process messages on a given conversation at any moment. The more distinct conversations you have, the higher the degree of paralelism that can be obtained on the target. Also, using separate conversations may be simpler for the sender, as it does not have to look up and find a conversation handle, it can simply create a new one each time.
One think to keep in mind though is that one conversation per message requires more resources for each message sent. The set-up and tear-down of a conversation is not free, it involves:
- creation of initiator endpoint
- creation of target endpoint
- one EndDialog message
- one acknoledgement message for the EndDialog message
- deletion of initiator endpoint
- deletion of target endpoint
All these operations are causing log writes and database updates and/or network traffic. When more messages are sent per one conversation, this cost is ammortized over all the messages sent. If only one message is is sent per conversation, all this cost goes into the one message sent, so sending a message is more expensive (from a resouce point of view). All these speel that reusing conversations might give some performance gains.
But I must stress that you should carefully consider the simplicity of the one message per conversation vs. the performance gains of multiple messages per conversation. If your expected traffic involves say 10 messages per second, then is hardly worth looking into the benefits of reusing conversations. But if you aim for 5000 messages per second, then you should probably seriously consider the cost of beginning/ending a dialog.
And, again, if the messages are related, then most likely you have to send them on the same conversation.
HTH,
~ Remus
No comments:
Post a Comment