BPINirvana

Friday, June 24, 2005

Tips for performance

1- Use promoted props only for routing and subscription evaluation, these ones are slow, if you want to use property inside an orch use distingueshed fields they are faster.

2- Pipelines: don't use XMLDocument object to load the document instead use firehose style cusrors to parse the XML, keep these pipelines as fast as possible as they are the real perforamance bottlenecks

3- Watch out for serialization: BTS serializes your process on various conditions, however you can control that read my "Fast BPI, Faster Than What?" blog entry for options.

4- infact try not to use orchs :), yes if you can replace that with content based routing then do that. the idea is to pay the lowest perfomance cost possible.

5- Watch out for parrlle shapes, they don't look as fast as you think. careful use them as possible.

6- use external code carefuly, BTS in itself is a very fast engine (nice done). but the code you write my not be designed to be as fast so only do that when you need to

7- and if you need to use statless objects for example static methods on classes.

8- Maps: XLTS functiods are faster than external assmblies, need to say why?

7- minimize the state you save: ok everything in an orch is a state, including variables messages correlation sets (ofcource scoped either by orch or by scope shapes) everything gets saved into SQL using serialization, so minimizing this will make BTS consume less memory and ofcource the call to SQL will be smaller.

8- Use transactional carefully, compensating transaction are by nature slow

9- use correlationsets carefully, currently i couldn't see anything slower than that in BTS 2004, the reason is calls to SQL either to put the subscription record or to evaluate it :(

10- oh yes this one is VERY nice, use XMLRecieve pipleine in recieve ports, use passthru in sending, reason: unless you are depromiting properties back to the message then use passthru they are much faster, use XMLRecieve in recive because they promote the propties.

Saturday, June 18, 2005

Did i start that Orch before?

Okay,


So You want to know if you started this particular instance of an orch before or not; here’s the situation: you have a business process, upon reaching a certain condition you spawn off an orch, simple eh? Remind me of the old threading days however here is the challenge your business process is layed over in away that this condition might repeat itself, raises a good question.. have we started this orch before or not. In a server programming model oyou would have kept a handle for the thread or (app domain or process) somewhere in a static variable and always check it before starting it.


However in BTS it is completely stateless, I hear the voice in my head (as in yours) saying dude save the state in the database some where, well looks like the perfect solution (the cost of one straight forward DB call isn’t really much after all). Come to think of it we do have a state DB by default (yes I mean this MsgBox DB), can we just use it and check against it. The answer is yes using

- Direct DB access (DON’T DO THAT AT HOME style, again).
- Using subscription engine! And that is where I am going to leverage on

Anyone familiar with BTS subscription viewer, that small utility that we use to debug our business processes shows us what orchs ports is waiting for what(and their correlations as well). Reflecting that tool showed that it is using Biztalk.RulesEngine assembly.. NIIICE!. We just need to get the same data dump filter it and check against it.

Let us walkthrough the subscription viewer: it is an EXE application.
Single window has a
- Data-grid (displays the subscription record)
- A tree view that shows the details of the subscription (which port, which web method name which correlation and it is value etc…).

Filtering the code showed a single method with the name of LoadSubscriptions that loads all the subscription the code is like:

public void LoadSubscriptions()
{
this.m_Subscriptions.Clear(); // Clears the datagrd
this.PredicateLists.Nodes.Clear(); // clears the tree view
SubscriptionRuleStore store1 = new SubscriptionRuleStore("Subscription Store"); // connect to the store
this.m_rs = store1.GetRuleSet(new RuleSetInfo(this.m_GroupName, 0, 0)); // GroupName is always -> = "BizTalk Group"; (in the CTOR of the form)
foreach (Rule rule1 in this.m_rs.Rules.Values) // enumerate the list – previous call is too slow!
{
// Basiclly what is happening here is creating a data table contains all the subscription and binding the grid to it.
string text1;
DataRow row1 = this.m_Subscriptions.Tables["Subscriptions"].NewRow();
row1["SubscriptionID"] = rule1.Name;
row1["Priority"] = rule1.Priority;
UserFunction function1 = (UserFunction) rule1.Actions[0];
ClassMemberBinding binding1 = (ClassMemberBinding) function1.Binding;
Constant constant1 = (Constant) binding1.Arguments[0];
row1["Name"] = constant1.Value.ToString();
constant1 = (Constant) binding1.Arguments[1];
if ((text1 = constant1.Value.ToString()) == null)
{
goto Label_0186;
}
text1 = string.IsInterned(text1);
if (text1 != "3d7a3f58-4bfb-4593-b99e-c2a5dc35a3b2") // Some stuff are canceled from the view, my guess internal BTS pluming stuff
{
if (text1 == "59f295b0-3123-416e-966b-a2c6d65ff8e6")
{
goto Label_0162;
}
if (text1 == "226fc6b9-0416-47a4-a8e8-4721f1db1a1b")
{
goto Label_0174;
}
goto Label_0186;
}
row1["ServiceType"] = "MSMQT";
goto Label_019D;
Label_0162:
row1["ServiceType"] = "EPM";
goto Label_019D;
Label_0174:
row1["ServiceType"] = "XLANG";
goto Label_019D;
Label_0186:
row1["ServiceType"] = constant1.Value.ToString();
Label_019D:
constant1 = (Constant) binding1.Arguments[2];
row1["ServiceID"] = constant1.Value.ToString();
constant1 = (Constant) binding1.Arguments[3];
row1["InstanceID"] = constant1.Value.ToString();
constant1 = (Constant) binding1.Arguments[4];
row1["PortID"] = constant1.Value.ToString();
constant1 = (Constant) binding1.Arguments[5];
if ((text1 = constant1.Value.ToString()) != null)
{
text1 = string.IsInterned(text1);
if (text1 != "0")
{
if (text1 == "1")
{
goto Label_029A;
}
if (text1 == "2")
{
goto Label_02AC;
}
if (text1 == "3")
{
goto Label_02BE;
}
}
else
{
row1["Enabled"] = "Disabled";
}
}
goto Label_02CE;
Label_029A:
row1["Enabled"] = "Enabled";
goto Label_02CE;
Label_02AC:
row1["Enabled"] = "Deactivated";
goto Label_02CE;
Label_02BE:
row1["Enabled"] = "AdminDeactivated";
Label_02CE:
constant1 = (Constant) binding1.Arguments[6];
row1["ApplicationName"] = constant1.Value.ToString();
this.m_Subscriptions.Tables["Subscriptions"].Rows.Add(row1);
}
if (this.m_rs.Rules.Count > 0)
{
this.SubDataGrid.Select(0);
this.SubDataGrid.CurrentRowIndex = 0;
this.SubDataGrid_CurrentCellChanged(null, null);
}
}





Okay the previous code load s the subscriptions but does nothing else, upon cell change on the grid it displays the detils of the subscription as

this.PredicateLists.Nodes.Clear();
int num1 = 0;
int num2 = 0;
Rule rule1 = (Rule) this.m_rs.Rules[this.SubDataGrid[this.SubDataGrid.CurrentRowIndex, 1]];
if (rule1.Conditions is LogicalAnd)
{
this.PredicateLists.Nodes.Add(new TreeNode("AndGroup0"));
LogicalAnd and1 = (LogicalAnd) rule1.Conditions;
for (num1 = 0; num1 < and1.Arguments.Count; num1++)
{
this.PredicateLists.Nodes[0].Nodes.Add(this.GetPredicateDisplayString(and1.Arguments[num1]));
}
}
else if (rule1.Conditions is LogicalOr)
{
LogicalOr or1 = (LogicalOr) rule1.Conditions;
for (num1 = 0; num1 < or1.Arguments.Count; num1++)
{
this.PredicateLists.Nodes.Add(new TreeNode("AndGroup" + num1));
if (or1.Arguments[num1] is LogicalAnd)
{
LogicalAnd and2 = (LogicalAnd) or1.Arguments[num1];
for (num2 = 0; num2 < and2.Arguments.Count; num2++)
{
this.PredicateLists.Nodes[num1].Nodes.Add(this.GetPredicateDisplayString(and2.Arguments[num2]));
}
}
else
{
this.PredicateLists.Nodes[num1].Nodes.Add(this.GetPredicateDisplayString(or1.Arguments[num1]));
}
}
}
else
{
this.PredicateLists.Nodes.Add(new TreeNode("AndGroup0"));
this.PredicateLists.Nodes[0].Nodes.Add(this.GetPredicateDisplayString(rule1.Conditions));
}
this.PredicateLists.ExpandAll();
}


Cool eh? Well what you need to know is your subscription will always contain data, additions to what you have in mind, stuff that has to do with Port IDs. However if you know the correlation attribute name, value, orchestration name and assembly name it is enough information that you can use to answer the question raised (did I start this one before or not). Be aware you may have multiple subscription for the same correlation if it is being used so many times in your process on different ports so make sure that you filter using the correct information.

Final words:
- The subscription viewer in itself is a slow tool because it dumps all the subscription. I spent sometime trying to find any way to filter the data rather than killing the database but couldn’t find any so far. I am sure there is since BTS is very fast evaluating messages against subscriptions.
- SubscriptionRuleStore is not a supported by BizTalk so whatever you write here is write it at your own risk.