Showing posts with label reuse. Show all posts
Showing posts with label reuse. Show all posts

Tuesday, March 20, 2012

quiesce then End Conversation

I want to reuse conversations to minimize overhead during bursts of activity. Remus' article on reusing conversations (http://blogs.msdn.com/remusrusanu/archive/2007/05/02/recycling-conversations.aspx) is great. (I know you are reading this Remus, thanks.)

I was wondering if there is a simpler way of ending a cached conversation - Quiesce the conversation (Stop using it), then after some period of time, end it.

I create a conversation, cache it in RLY_Conversations, and use it for 50 seconds. After 1 minute, the dialog timer servicing proc ends the conversation. There will be no messages sent around the time the End Conversation takes place, thus no race conditions.

Do you see any problems with this method?

Select @.DialogHandle = [conversation_handle]

From RLY_Conversations

Where TableName = @.TableName and IsActive = 1 And

CreatedTmstp > dateadd(ss, -50, getdate())

if @.DialogHandle is null

Begin

-- initialize a conversation and record it in our reuse table

BEGIN DIALOG CONVERSATION @.DialogHandle

FROM SERVICE FirstHostRelayService

TO SERVICE 'SecondHostRelayService'

ON CONTRACT RelayContractSentByAny

WITH ENCRYPTION=OFF ;

-- cache the dialog handle to minimize dialog creation overhead.

Insert into RLY_Conversations (

TableName, conversation_handle, conversation_id, is_initiator, service_contract_id,

conversation_group_id, service_id, lifetime, state, state_desc, IsActive, CreatedBy, CreatedTmstp

)

Select @.TableName, conversation_handle, conversation_id, is_initiator, service_contract_id,

conversation_group_id, service_id, lifetime, state, state_desc, 1, 'Setup', getdate()

From sys.conversation_endpoints

Where conversation_handle = @.DialogHandle;

-- initiate housekeeping process

BEGIN CONVERSATION TIMER ( @.DialogHandle )

TIMEOUT = 60;

End

Hi Bill,

Thanks for reading my blog!

I'm not sure I follow the question, but if I understand correctly you begin a dialog, use it for 50 seconds and then end it, based on th timer, 10 seconds later. If that is correct, I would recommend against it.

For once, if the target never sends any message you should not end the dialog from the sender's side first, as per http://blogs.msdn.com/remusrusanu/archive/2006/04/06/570578.aspx. I can't stress the importance of this, I am faced every day with people reporting some problem that could had been avoided by not using this fire-and-forget pattern.

Second, altohugh I'm not familiar with your application, I think you are running on some very tight budgets of time when relying on the application to finish it's work in 50 seconds and allowing only a 10 seconds margin for the timer to pop and quiesce the dialog. The SEND operations could be delayed by a lock, the application logic might be delayed a bit, the database might hit a log growth or a datafile growth, there might be a temporary resource constraint (memory, execution threads etc). Anything that delayes the 50 seconds SEND time might cause a race between a SEND and the timer trying to quiesce operation. Is just my opinion that in the persisted, transacted database operations world anything measured in seconds will eventually run into problems in production, even more so when talking about asynchronous operations like messaging. How about starting the 60 seconds timer after every SEND? Using BEGIN CONVERSATION TIMER again when a timer is active has the effect of resting the timer to the new value (there is only one timer per dialog). This way the timer would be continously updated and if no SEND occurs for 60 seconds, the timer will pop.

HTH,

~ Remus

quiesce then End Conversation

I want to reuse conversations to minimize overhead during bursts of activity. Remus' article on reusing conversations (http://blogs.msdn.com/remusrusanu/archive/2007/05/02/recycling-conversations.aspx) is great. (I know you are reading this Remus, thanks.)

I was wondering if there is a simpler way of ending a cached conversation - Quiesce the conversation (Stop using it), then after some period of time, end it.

I create a conversation, cache it in RLY_Conversations, and use it for 50 seconds. After 1 minute, the dialog timer servicing proc ends the conversation. There will be no messages sent around the time the End Conversation takes place, thus no race conditions.

Do you see any problems with this method?

Select @.DialogHandle = [conversation_handle]

From RLY_Conversations

Where TableName = @.TableName and IsActive = 1 And

CreatedTmstp > dateadd(ss, -50, getdate())

if @.DialogHandle is null

Begin

-- initialize a conversation and record it in our reuse table

BEGIN DIALOG CONVERSATION @.DialogHandle

FROM SERVICE FirstHostRelayService

TO SERVICE 'SecondHostRelayService'

ON CONTRACT RelayContractSentByAny

WITH ENCRYPTION=OFF ;

-- cache the dialog handle to minimize dialog creation overhead.

Insert into RLY_Conversations (

TableName, conversation_handle, conversation_id, is_initiator, service_contract_id,

conversation_group_id, service_id, lifetime, state, state_desc, IsActive, CreatedBy, CreatedTmstp

)

Select @.TableName, conversation_handle, conversation_id, is_initiator, service_contract_id,

conversation_group_id, service_id, lifetime, state, state_desc, 1, 'Setup', getdate()

From sys.conversation_endpoints

Where conversation_handle = @.DialogHandle;

-- initiate housekeeping process

BEGIN CONVERSATION TIMER ( @.DialogHandle )

TIMEOUT = 60;

End

Hi Bill,

Thanks for reading my blog!

I'm not sure I follow the question, but if I understand correctly you begin a dialog, use it for 50 seconds and then end it, based on th timer, 10 seconds later. If that is correct, I would recommend against it.

For once, if the target never sends any message you should not end the dialog from the sender's side first, as per http://blogs.msdn.com/remusrusanu/archive/2006/04/06/570578.aspx. I can't stress the importance of this, I am faced every day with people reporting some problem that could had been avoided by not using this fire-and-forget pattern.

Second, altohugh I'm not familiar with your application, I think you are running on some very tight budgets of time when relying on the application to finish it's work in 50 seconds and allowing only a 10 seconds margin for the timer to pop and quiesce the dialog. The SEND operations could be delayed by a lock, the application logic might be delayed a bit, the database might hit a log growth or a datafile growth, there might be a temporary resource constraint (memory, execution threads etc). Anything that delayes the 50 seconds SEND time might cause a race between a SEND and the timer trying to quiesce operation. Is just my opinion that in the persisted, transacted database operations world anything measured in seconds will eventually run into problems in production, even more so when talking about asynchronous operations like messaging. How about starting the 60 seconds timer after every SEND? Using BEGIN CONVERSATION TIMER again when a timer is active has the effect of resting the timer to the new value (there is only one timer per dialog). This way the timer would be continously updated and if no SEND occurs for 60 seconds, the timer will pop.

HTH,

~ Remus

Quick Transaction Question.

If in .NET I open a connection to my database, then use some sql text to start a transaction, then reuse that same open connection to call several stored procedures (using SqlCommand with CommandType.StoredProcedure), before ending the transaction. Will that run as a single transaction that can be rolled back? or are the stored procedure calls unable to roll-back after each one completes?

the stored procedures are themselves atomic and would need their own rollbacks. your use of transaction would work if all your statements were sql strings executing one after the other. -- jp

|||

Did a bit of research, and it appears there is a BeginTransaction method that can be used as part of the SqlConnection object. Which allowed me to start a transaction on a connection, then call serveral stored procedures using a SqlCommand object set to CommandType.StoredProcedure, and it all works as a single transaction, rolling everything back if any of then calls fail (internally or enternally). Here is a quick code snippet:

1protected void btnSave_Click(object sender, EventArgs e) {2//Create connection string for SQL query3 String strConnect;4 strConnect = WebConfigurationManager.ConnectionStrings["LocalSqlServer"].ConnectionString;56//Generate call to stored procedure7 SqlConnection con =new SqlConnection(strConnect);8 SqlTransaction trans =null;910//Make Calls11string myNull =null;12try {13 con.Open();14 trans = con.BeginTransaction();15int ret1 = spCall(con, trans,"two");16int ret3 = spCall(con, trans, myNull);17int ret2 = spCall(con, trans,"three");18 trans.Commit();19 Master.Message.CssClass ="Text_Message";20 Master.Message.Text = ret1.ToString();21 Master.Message.Visible =true;22 }23catch (Exception sql) {24 Master.Message.CssClass ="Text_Error";25 Master.Message.Text = sql.Message;26 Master.Message.Visible =true;27if (null != trans) {28 trans.Rollback();29 }30 }31finally {32 con.Close();33 con.Dispose();34 }35 }3637protected int spCall(SqlConnection myConn, SqlTransaction myTrans,string myParam) {3839//Generate call to stored procedure40 SqlCommand storedProcCommand =new SqlCommand("spTest", myConn);41 storedProcCommand.CommandType = CommandType.StoredProcedure;4243//Build SQL parameter list44 storedProcCommand.Parameters.AddWithValue("@.value", myParam);4546//Return code47 SqlParameter retParam = storedProcCommand.Parameters.Add("@.ReturnValue", SqlDbType.Int);48 retParam.Direction = ParameterDirection.ReturnValue;4950//Bind to transaction51 storedProcCommand.Transaction = myTrans;5253//Run stored procedure54int retCode = 0;55 SqlDataReader Reader = storedProcCommand.ExecuteReader();56 retCode = (int)storedProcCommand.Parameters["@.ReturnValue"].Value;57 Reader.Close();5859return retCode;60 }
 
1CREATE PROCEDURE [dbo].[spTest]2--Parameters3 @.valuevarchar(50) =null4AS56BEGIN78SET NOCOUNT ON;910INSERT INTO tbTest11 (12 [value]13 )14VALUES15 (16 @.value17 )1819RETURN@.@.Identity2021END
My first call inserts okay, the second call fails because nulls are not accepted by [value] in the table definition., and the third call never happens due to the exception thrown by the second call, which forced a rollback of the entire transaction, of which each stored procedure is a call of. Did a fair amount of testing and everything appears to be in order, if anyone notices anything I overlooked and or that could be problematic, please let me know.