1. Do You Really Need an Orchestration?
Like all eager developers, you probably want to know
the answer to this question right away. After all, an orchestration
often seems like the best tool for you to use, as it is simply a
procedural algorithm in a visual form. Before identifying when you
should and should not use an orchestration, however, we need to explain
the rationale behind some of the wrong decisions that some new BizTalk
developers make, as well as the reason they are wrong.
The Orchestration Designer is the tool that new
BizTalk developers are most comfortable with, as it is simply a visual
procedural language. It provides a natural transition from VB .NET or C#
to BizTalk development. Almost every new BizTalk developer or architect
tends to think of a BizTalk solution as a set of orchestrations with
defined external entry and exit points—ports—glued together to perform a
business function. Although this visual development approach is the
natural transition from procedural or object-oriented development, to
leverage BizTalk to its full potential, solutions have to be architected
differently. Unfortunately, designing a BizTalk solution is not as
simple as abiding by object-oriented design guidelines, applying
service-oriented architecture, or defining data structures and schemas.
It is a combination of all of the foregoing, but couched in terms of
message processing patterns. For the unfamiliar, the combination of
these design approaches is a new paradigm shift in solution design.
Fortunately, this combination lends itself easily to
business analysis. The mapping from a defined business process or
collection of processes including cross-platform transactions to a
BizTalk solution is usually a one-to-one mapping. It is a matter of
finding the proper set of BizTalk tools—messaging patterns—to perform the defined function.
Orchestrations are a powerful tool. However, they
come at a high cost. For the orchestration engine to properly manage and
maintain an orchestration instance, the engine has to perform multiple
round-trips to the Messagebox to persist its state. Message subscription
resolution is mostly implemented through stored procedures running on
the Messagebox directly. We therefore highly advise you to resort to
content-based routing (CBR), as illustrated in Figure 7-2,
whenever possible rather than using orchestrations to implement
message-routing logic. Simple message routing can be done using content
filtering on ports. Unless it is unavoidable, message transformations
should be implemented using maps on the ports as well. Use
orchestrations if you must correlate multiple messages to fulfill
business needs.
In short, orchestrations should not be used to
Simply route messages between ports.
Perform simple or conditional transformations on messages.
Simply call remote systems through expressions and managed code.
Define complex business rules and policies.
Orchestrations should be used to
Correlate multiple messages to fulfill the required business needs.
Fire business rules in the Business Rule Engine.
Manage and scope business transactions.
2. What Transactions Mean and Cost
Transactions guarantee that any partial updates are rolled back automatically
in the event of a failure during a transactional update and that the
effects of the transaction are erased. Transactions in BizTalk may be atomic or long running.
2.1. Atomic Transactions
In BizTalk, orchestrations are similar to distributed
transaction coordinator (DTC) transactions in that they are generally
short-lived and have the four ACID
attributes—(atomicity, consistency, isolation, and durability).
Atomicity: A transaction represents an atomic unit of work. Either all modifications within a transaction are performed or none.
Consistency:
When committed, a transaction must preserve the integrity of the data
within the system. If a transaction performs a data modification on a
database that was internally consistent before the transaction started,
the database must still be internally consistent when the transaction is
committed. Ensuring this property is largely the responsibility of the
application developer.
Isolation:
Modifications made by concurrent transactions must be isolated from the
modifications made by other concurrent transactions. Isolated
transactions that run concurrently perform modifications that preserve
internal database consistency exactly as they would if the transactions
were run serially.
Durability:
After a transaction is committed, all modifications are permanently in
place in the system by default. The modifications persist even if a
system failure occurs.
According to the BizTalk Server 2009 documentation
(Microsoft, 2009), "Atomic transactions guarantee that any partial
updates are rolled back automatically in the event of a failure during
the transactional update, and that the effects of the transaction are
erased (except for the effects of any .NET calls that are made in the
transaction)." Atomic transactions dictate to the engine that their
scope should be fully executed before the resources allocated to the
orchestration instance are released and reused by another instance.
Therefore, the XLANG engine does not persist the orchestration instance
state until the transaction is fully committed.
This allows for the isolation and consistency of the transaction. This
also implies that, in the case of a server failure or a manual host
instance recycle while the transaction is executing, the orchestration
instance will resume execution at the beginning of the atomic
transaction scope. An atomic transaction cannot contain any other
transaction within it nor can it contain an exception handler.
Crossing the process boundary within an atomic
transaction scope is highly undesirable. For example, a call to a
receive port within the atomic scope will not allow the engine to
dehydrate if there are no messages to receive.Such a design decision might lead to the quick depletion of processing
threads in the host instance's thread pool and the inability of the host
instance to instantiate new orchestrations. On the other hand, if an
atomic transaction contains a Receive shape, a Send shape, or a Start
Orchestration shape, the corresponding action will not be performed
until the transaction is committed. Therefore, scoping multiple
consecutive sends within an atomic scope is very useful. The XLANG
engine will not persist the orchestration instance's state on every
send, instead batching the round-trips to the Messagebox database to be
performed once at the end of the atomic transaction. Atomic transaction
scopes are also handy for wrapping nonserializable managed variables;
although such wrapping is poor design, sometimes it is inevitable,
especially when leveraging third-party managed assemblies or legacy
components.NOTE
DTC transactions, mentioned early in this section, are atomic transactions using COM+ objects derived from System.Enterprise Services.ServicedComponents and agreeing isolation levels between transaction components.
2.2. Long-Running Transactions
Long-running transactions provide you with great
flexibility in designing robust transaction architecture through custom
scope-based compensation, custom scope-based exception handling, and the
ability to nest transactions. Long-running transactions are the right
candidate if transactions might run for an extended time and full ACID
properties are not required (that is, you do not need to guarantee
isolation of data from other transactions). A long-running transaction
might have long periods of inactivity, often due to waiting for external
messages to arrive. The XLANG engine might dehydrate the running
orchestration instance at this point and release its resources back to
the pool. Long-running transactions impose consistency and durability,
but not atomicity and isolation. The data within a long-running
transaction is not locked; other processes or applications can modify
it. The isolation property for state updates is not maintained because
holding locks for a long duration is impractical.
By declaring variables, messages, and .NET
components, a scope can define its own state. A long-running transaction
has access to the state information of its own scope, any scope that
encloses it, and any state information globally defined within the
orchestration. It does not have access to the state information of any
scopes that do not enclose it.
A scope is a
framework for grouping actions. It is primarily used for transactional
execution and exception handling. A scope contains one or more blocks.
It has a body and can optionally have appended to it any number of
exception handling blocks. It may have an optional compensation block as
well, depending on the nature of the scope. Some scopes will be purely
for exception handling and will not require compensation. Other scopes
will be explicitly transactional and will always have a default
compensation handler, along with an optional compensation handler that
you create for it. A transactional scope will also have a default
exception handler and any number of additional exception handlers that
you create for it.
BizTalk developers can specify that scopes are
synchronized or not synchronized. By synchronizing a scope, developers
will ensure that any shared data that is accessed within it will not be
written to by one or more parallel actions in your orchestration, nor
will it be written to while another action is reading it. Atomic
transaction scopes are always synchronized. All actions within a
synchronized scope are considered synchronized, as are all actions in
any of its exception handlers. Actions in the compensation handler for a
transactional scope are not synchronized.
You can nest Scope shapes inside other Scope shapes. The rules for nesting scopes are as follows:
Transactional and/or synchronized scopes
cannot be nested inside synchronized scopes, including the exception
handlers of synchronized scopes. Atomic transaction scopes cannot have any other transactional scopes nested inside them. Transactional scopes cannot be nested inside nontransactional scopes or orchestrations. You can nest scopes up to 44 levels deep.
Call Orchestration shapes can be included inside
scopes, but the called orchestrations are treated the same as any other
nested transaction, and the same rules apply.
Start Orchestration shapes can be included inside scopes. Nesting limitations do not apply to started orchestrations.
You can declare variables such as messages and
correlation sets at the scope level. You cannot use the same name for a
scope variable as for an orchestration variable, however; name hiding is
not allowed.
|
You can still run into a deadlock condition
if you do not design your processes carefully. Example: two branches of a
parallel action in orchestration A access the same message, one to send
it and one to receive it, so both must have a synchronized scope. A
second orchestration receives the message and sends it back. It is
possible that the sending branch in orchestration A will receive its
locks before the receiving branch, and you will end up with a deadlock. |