Showing posts with label BRE. Show all posts
Showing posts with label BRE. Show all posts
Thursday, August 2, 2007
BRE String comparisons
Another short interjection with another annoying lesson learned (after WAY too much time trying to figure out what was going wrong): when the BRE does a string comparison with "is equal to", the comparison is caps sensitive. This can be a hard thing to see when you're trying to pinpoint a problem among pipeline execution, orchestrations, adapters, etc. and the BRE rule execution tracking is a pain to read.
Labels:
BizTalk,
BRE,
Business Rule Engine,
comparison,
string
Tuesday, July 31, 2007
Refreshing the BRE Facts cache
In my first post I talked a little bit about the FactRetriever object and how it works in concert with a BRE policy. Use of a FactRetriever can help you manage an in-memory cache of a table so that the BRE doesn't have to constantly hit a database for information.
After much debugging, stress, and some research on the part of a coworker, I discovered a crucial bit about how the BRE operates: If a fact (such as a DataTable within a DataSet, or an XML document) is asserted into memory and you wish to refresh that cached version, you must explicitly tell the BRE to retract the fact from memory before asserting it again. Failure to do this will lead to some really strange behavior that will have you debugging in circles.
Depending on your environment and the type of fact you have asserted, there are a couple of ways of doing this. You can call RuleEngine.Clear(), but this halts all BRE execution, cancels all rule firings, and removes all facts from the cache. I'm sure there's a situation where this is desirable, but the odds are that it's not yours. This will clear everything from the BRE's memory.
What you're probably looking for is RuleEngine.Retract() or RuleEngine.RetractByType(). I believe that in the case of a DataTable or DataConnection, these two methods accomplish the same thing, since a DataTable or DataConnection stored in the BRE memory is unique and there can only be one instance of it. Since our goal was to retract a DataTable, we didn't do a lot of research into retracting other types of facts.
Using RetractByType() took a little while to figure out: there is no documentation out there anywhere that we could find, and no extended Intellisense documentation. It took a little experimentation and some creativity to figure out how to do what we wanted. RetractByType() takes a single argument, a FactType object. FactType is abstract, but has a couple of descendants, one of them being DataRowType. DataRowType's constructor takes two strings: the name of a table, and the name of a dataset. These values are what defines a "table type" within the BRE: If you have a table with the Table property set to "ABC" inside a DataSet with the DataSetName property set to "XYZ", that's the only "ABC" "XYZ" table that can be in the BRE's memory at one time. The BRE keys off of those name values when you make references to the table within rules or vocabularies.
In order to create a type identifier so that the BRE can retract your fact for you, all you have to do is create a new DataRowType with the table name and DataSet name correctly specified, and hand that object to RetractByType. The following is an example - this is the first code that runs once the FactRetriever has determined that the cache does indeed need to be refreshed:
if (null != factsHandleIn)
{
engine.RetractByType(new DataRowType("MyTableName", "MyDataSetName"));
}
The reason that I check to see if factsHandleIn is null is because my FactRetriever returns a value as factsHandleOut that gets handed in again as factsHandleIn on the next call. If factsHandleIn is null, that means that this is the first time the FactRetriever has run since restarting its host instance, so there's nothing in memory to Retract anyway. The next thing that
Mixed in with some logic that determines when the cache should be refreshed, you can use this control over retracting/asserting facts to keep an efficient cache in the BRE's memory. Our FactRetriever uses DateTimes and values in the registry to determine if the cache needs to be refreshed - it can be set to refresh automatically on an interval (run the policy and record the time. If the policy runs again and the time span between the last update and now is X seconds, refresh again) or by a user-override (if a user edited the table, he can set a value in the registry to DateTime.Now, and the next time the policy runs it will see a newer value than the last value stored in that registry node and will refresh).
After much debugging, stress, and some research on the part of a coworker, I discovered a crucial bit about how the BRE operates: If a fact (such as a DataTable within a DataSet, or an XML document) is asserted into memory and you wish to refresh that cached version, you must explicitly tell the BRE to retract the fact from memory before asserting it again. Failure to do this will lead to some really strange behavior that will have you debugging in circles.
Depending on your environment and the type of fact you have asserted, there are a couple of ways of doing this. You can call RuleEngine.Clear(), but this halts all BRE execution, cancels all rule firings, and removes all facts from the cache. I'm sure there's a situation where this is desirable, but the odds are that it's not yours. This will clear everything from the BRE's memory.
What you're probably looking for is RuleEngine.Retract() or RuleEngine.RetractByType(). I believe that in the case of a DataTable or DataConnection, these two methods accomplish the same thing, since a DataTable or DataConnection stored in the BRE memory is unique and there can only be one instance of it. Since our goal was to retract a DataTable, we didn't do a lot of research into retracting other types of facts.
Using RetractByType() took a little while to figure out: there is no documentation out there anywhere that we could find, and no extended Intellisense documentation. It took a little experimentation and some creativity to figure out how to do what we wanted. RetractByType() takes a single argument, a FactType object. FactType is abstract, but has a couple of descendants, one of them being DataRowType. DataRowType's constructor takes two strings: the name of a table, and the name of a dataset. These values are what defines a "table type" within the BRE: If you have a table with the Table property set to "ABC" inside a DataSet with the DataSetName property set to "XYZ", that's the only "ABC" "XYZ" table that can be in the BRE's memory at one time. The BRE keys off of those name values when you make references to the table within rules or vocabularies.
In order to create a type identifier so that the BRE can retract your fact for you, all you have to do is create a new DataRowType with the table name and DataSet name correctly specified, and hand that object to RetractByType. The following is an example - this is the first code that runs once the FactRetriever has determined that the cache does indeed need to be refreshed:
if (null != factsHandleIn)
{
engine.RetractByType(new DataRowType("MyTableName", "MyDataSetName"));
}
The reason that I check to see if factsHandleIn is null is because my FactRetriever returns a value as factsHandleOut that gets handed in again as factsHandleIn on the next call. If factsHandleIn is null, that means that this is the first time the FactRetriever has run since restarting its host instance, so there's nothing in memory to Retract anyway. The next thing that
Mixed in with some logic that determines when the cache should be refreshed, you can use this control over retracting/asserting facts to keep an efficient cache in the BRE's memory. Our FactRetriever uses DateTimes and values in the registry to determine if the cache needs to be refreshed - it can be set to refresh automatically on an interval (run the policy and record the time. If the policy runs again and the time span between the last update and now is X seconds, refresh again) or by a user-override (if a user edited the table, he can set a value in the registry to DateTime.Now, and the next time the policy runs it will see a newer value than the last value stored in that registry node and will refresh).
Labels:
assert,
BizTalk,
BRE,
cache,
FactRetriever,
refresh,
retract,
RetractByType
Monday, July 23, 2007
What are FactsHandleIn and FactsHandleOut?
Execution within the BizTalk 2006 BRE follows an interesting paradigm. It took me a while to sort it all out as I'm not familiar at all with rules-based programming or anything of the sort. One of the things that tripped me up the longest was the notion of a FactRetriever.
A FactRetriever is a class that attaches to a policy and is in charge of making sure that when the policy runs, it has all of the facts that it needs in memory. Use of a FactRetriever allows fine control over when and how things are done - instead of just letting the BRE connect to a database, you can craft DataSets however you'd like (build it in memory, call a stored procedure, etc.) and assert those into memory as facts.
The heart of the FactRetriever is the UpdateFacts method, which is called every time the policy it is attached to is run. One of its parameters is object FactsHandleIn, which has a name that doesn't make it too obvious as to what it does. Additionally, UpdateFacts returns object FactsHandleOut. These two objects give you a lot of flexibility in how you'd like to control fact retrieval.
It's this simple: The first time the policy is called, when UpdateFacts is called, FactsHandleIn is null. Whatever you choose to return as FactsHandleOut is passed in as FactsHandleIn when the policy is called again. If the service/host instance/machine/etc. is ever restarted, a null will be passed in again on first calling.
This doesn't sound all that great when you first hear it, but it's a nice way of letting the BRE persist anything you want in memory to be used as a "hint" to the FactRetriever that you want to do something. For example, a great use of the BRE is as a local, in-memory cache of a DataSet. You can control the refreshing of this cache by passing a DateTime around as FactsHandleIn/Out, and using some custom logic in UpdateFacts to determine whether or not the data should be retrieved and re-asserted into memory.
You can use FactsHandleIn/Out for anything. A DateTime is a common use. I imagine that you could think up a great use for just about any kind of objects as a FactsHandle. This is one of those places where I'm sure someday I'll run across a really ingenious use of some strange object to perform a cool task.
A FactRetriever is a class that attaches to a policy and is in charge of making sure that when the policy runs, it has all of the facts that it needs in memory. Use of a FactRetriever allows fine control over when and how things are done - instead of just letting the BRE connect to a database, you can craft DataSets however you'd like (build it in memory, call a stored procedure, etc.) and assert those into memory as facts.
The heart of the FactRetriever is the UpdateFacts method, which is called every time the policy it is attached to is run. One of its parameters is object FactsHandleIn, which has a name that doesn't make it too obvious as to what it does. Additionally, UpdateFacts returns object FactsHandleOut. These two objects give you a lot of flexibility in how you'd like to control fact retrieval.
It's this simple: The first time the policy is called, when UpdateFacts is called, FactsHandleIn is null. Whatever you choose to return as FactsHandleOut is passed in as FactsHandleIn when the policy is called again. If the service/host instance/machine/etc. is ever restarted, a null will be passed in again on first calling.
This doesn't sound all that great when you first hear it, but it's a nice way of letting the BRE persist anything you want in memory to be used as a "hint" to the FactRetriever that you want to do something. For example, a great use of the BRE is as a local, in-memory cache of a DataSet. You can control the refreshing of this cache by passing a DateTime around as FactsHandleIn/Out, and using some custom logic in UpdateFacts to determine whether or not the data should be retrieved and re-asserted into memory.
You can use FactsHandleIn/Out for anything. A DateTime is a common use. I imagine that you could think up a great use for just about any kind of objects as a FactsHandle. This is one of those places where I'm sure someday I'll run across a really ingenious use of some strange object to perform a cool task.
Labels:
BizTalk,
BRE,
Business Rule Engine,
FactRetriever,
FactsHandleIn,
FactsHandleOut
Subscribe to:
Posts (Atom)