The following sections provide information on what entity bean locking is and how entity beans are accessed and locked within JBoss. They also describe the problems you may encounter as you use entity beans within your system and how to combat these issues. Deadlocking is formally defined and examined. Finally, you'll learn how to finetune your system in terms of entity bean locking. Why JBoss Needs LockingLocking involves protecting the integrity of your data. Sometimes you need to be sure that only one user can update critical data at a time. Sometimes, access to sensitive objects in a system needs to be serialized so that data is not corrupted by concurrent reads and writes. Databases traditionally provide this sort of functionality with transactional scopes and table- and row-locking facilities. Using entity beans is a great way to provide an object-oriented interface to relational data. Beyond that, entity beans can improve performance by taking the load off the database through caching and delaying updates until absolutely needed in order to maximize database efficiency. But with caching, data integrity is a problem, so some form of application server-level locking is needed for entity beans to provide the transaction isolation properties that are common with traditional databases. The Entity Bean Life CycleWith the default configuration of JBoss there is only one active instance of a given entity bean in memory at one time. This applies for every cache configuration and every type of commit option. The life cycle for this instance is different for every commit option, though:
When a bean is marked for passivation, the bean is placed in a passivation queue. Each entity bean container has a passivation thread that periodically passivates beans that have been placed in the passivation queue. A bean is pulled out of the passivation queue and reused if the application requests access to a bean with the same primary key. On an exception or transaction rollback, the entity bean instance is thrown out of the cache entirely. It is not put into the passivation queue and is not reused by an instance pool. Except for the passivation queue, there is no entity bean instance pooling. Default Locking BehaviorEntity bean locking is totally decoupled from the entity bean instance. The logic for locking is totally isolated and managed in a separate lock object. Because there is only one allowed instance of a given entity bean active at one time, JBoss employs two types of locks to ensure data integrity and to conform to the EJB spec:
Pluggable Interceptors and Locking PolicyYou have seen in this chapter that the basic entity bean life cycle and behavior are defined by the container configuration defined in the standardjboss.xml descriptor. The following is the container-interceptors definition for the Standard CMP 2.x EntityBean configuration:
The interceptors shown here define most of the behaviors of the entity bean. The following are the interceptors that are relevant to this section:
DeadlockingThis section describes what deadlocking means, how you can detect it within an application, and how you can resolve deadlocks. A deadlock can occur when two or more threads have locks on shared resources. Figure 5.8 illustrates a simple deadlock scenario. Here, Thread 1 has the lock for Bean A, and Thread 2 has the lock for Bean B. At a later time, THRead 1 tries to lock Bean B and blocks because Thread 2 has it. Likewise, as Thread 2 TRies to lock Bean A, it also blocks because Thread 1 has the lock. At this point, both threads are deadlocked, waiting for access to the resource already locked by the other thread. Figure 5.8. A deadlock definition example.The default locking policy of JBoss is to lock an entity bean when an invocation occurs in the context of a transaction until the transaction completes. Therefore, it is very easy to encounter deadlock if you have long-running transactions that access many entity beans or if you are not careful about ordering the access to them. You can use various techniques and advanced configurations to avoid deadlocking problems. They are discussed later in this chapter. Deadlock DetectionFortunately, JBoss is able to perform deadlock detection. JBoss holds a global internal graph of waiting transactions and what transactions they are blocking on. Whenever a thread determines that it cannot acquire an entity bean lock, it figures out what transaction currently holds the lock on the bean and adds itself to the blocked transaction graph. Table 5.1 shows an example of what the graph might look like.
Before the thread actually blocks, it tries to detect whether there is a deadlock problem. It does this by traversing the block transaction graph. As it traverses the graph, it keeps track of what transactions are blocked. If it sees a blocked node more than once in the graph, then it knows there is deadlock and throws an ApplicationDeadlockException. This exception causes a transaction rollback, which in turn causes all locks that transaction holds to be released. Catching ApplicationDeadlockExceptionBecause JBoss can detect application deadlock, you should write an application so that it can retry a transaction if the invocation fails because of the ApplicationDeadlockException. Unfortunately, this exception can be deeply embedded within a RemoteException, so you have to search for it in your catch block. Here's an example: try { // ... } catch (RemoteException ex) { Throwable cause = null; RemoteException rex = ex; while (rex.detail != null) { cause = rex.detail; if (cause instanceof ApplicationDeadlockException) { // ... We have deadlock, force a retry of the transaction. break; } if (cause instanceof RemoteException) { rex = (RemoteException)cause; } } } Viewing Lock InformationThe EntityLockMonitor MBean service allows you to view basic locking statistics as well as print out the state of the transaction locking graph. To enable this monitor, you uncomment its configuration in the conf/jboss-service.xml file: <mbean code="org.jboss.monitor.EntityLockMonitor" name="jboss.monitor:name=EntityLockMonitor"/> EntityLockMonitor has no configurable attributes. It does have the following read-only attributes:
EntityLockMonitor also has the following operations:
Advanced Configurations and OptimizationsThe default locking behavior of entity beans can cause deadlock. Because access to an entity bean locks the bean into the transaction, this can also present a huge performance/throughput problem for an application. The following sections walk through various techniques and configurations that you can use to optimize performance and reduce the possibility of deadlock. Short-Lived TransactionsYou should make your transactions as short-lived and fine-grained as possible. The shorter a transaction, the less likelihood that it will have concurrent access collisions and the more the application throughput will go up. Ordered AccessOrdering the access to entity beans can help lessen the likelihood of deadlock. This means making sure that the entity beans in a system are always accessed in exactly the same order. In most cases, user applications are just too complicated to use this approach, and more advanced configurations are needed. Read-only BeansEntity beans can be marked as read-only. When a bean is marked as read-only, it never takes part in a transaction. This means that it is never transactionally locked. Using commit option D with this option is sometimes very useful when a read-only bean's data is sometimes updated by an external source. To mark a bean as read-only, you use the read-only flag in the jboss.xml deployment descriptor, as shown in Listing 5.11. Listing 5.11. Marking an Entity Bean as Read-only by Using jboss.xml<jboss> <enterprise-beans> <entity> <ejb-name>MyEntityBean</ejb-name> <jndi-name>MyEntityHomeRemote</jndi-name> <read-only>True</read-only> </entity> </enterprise-beans> </jboss> Explicitly Defining Read-only MethodsNow that you understand the default locking behavior of entity beans, you're probably wondering, "Why lock the bean if it's not modifying the data?" JBoss allows you to define what methods on an entity bean are read-only so that it will not lock the bean within the transaction if only these types of methods are called. You can define these read-only methods within a jboss.xml deployment descriptor. Wildcards are allowed for method names. Listing 5.12 shows an example of declaring all getter methods and the anotherReadOnlyMethod as read-only. Listing 5.12. Defining Entity Bean Methods as Read-only<jboss> <enterprise-beans> <entity> <ejb-name>nextgen.EnterpriseEntity</ejb-name> <jndi-name>nextgen.EnterpriseEntity</jndi-name> <method-attributes> <method> <method-name>get*</method-name> <read-only>true</read-only> </method> <method> <method-name>anotherReadOnlyMethod</method-name> <read-only>true</read-only> </method> </method-attributes> </entity> </enterprise-beans> </jboss> The Instance per Transaction PolicyThe Instance per Transaction policy is an advanced configuration that can totally wipe away deadlock and throughput problems caused by JBoss's default locking policy. The default entity bean locking policy is to allow only one active instance of a bean. The Instance per Transaction policy breaks this requirement by allocating a new instance of a bean per transaction and dropping that instance at the end of the transaction. Because each transaction has its own copy of the bean, there is no need for transaction-based locking. This option does sound great, but it has some drawbacks right now. First, the transactional isolation behavior of this option is equivalent to READ_COMMITTED. This can create repeatable reads when they are not desired. In other words, a transaction could have a copy of a stale bean. Second, this configuration option currently requires commit option B or C, which can be a performance drain because an ejbLoad must happen at the beginning of the transaction. But, if an application currently requires commit option B or C anyway, then this is the way to go. The JBoss developers are currently exploring ways to allow commit option A as well (which would allow the use of caching for this option). JBoss has container configurations named Instance Per Transaction CMP 2.x EntityBean and Instance Per Transaction BMP EntityBean defined in the standardjboss.xml file that implements this locking policy. To use this configuration, you just have to reference the name of the container configuration to use with your bean in the jboss.xml deployment descriptor, as shown in Listing 5.13. Listing 5.13. An Example of Using the Instance per Transaction Policy<jboss> <enterprise-beans> <entity> <ejb-name>MyCMP2Bean</ejb-name> <jndi-name>MyCMP2</jndi-name> <configuration-name> Instance Per Transaction CMP 2.x EntityBean </configuration-name> </entity> <entity> <ejb-name>MyBMPBean</ejb-name> <jndi-name>MyBMP</jndi-name> <configuration-name> Instance Per Transaction BMP EntityBean </configuration-name> </entity> </enterprise-beans> </jboss> Running Within a ClusterCurrently, there is no distributed locking capability for entity beans within a cluster. This functionality has been delegated to the database and must be supported by the application developer. For clustered entity beans, it is suggested to use commit option B or C in combination with a row-locking mechanism. For CMP, there is a row-locking configuration option. This option uses a SQL select for update when the bean is loaded from the database. With commit option B or C, this implements a transactional lock that can be used across the cluster. For BMP, you must explicitly implement the select for update invocation within the BMP's ejbLoad method. TroubleshootingThe following sections describe some common locking problems and their solutions. Locking Behavior Not WorkingMany JBoss users observe that locking does not seem to be working and see concurrent access to their beansand thus dirty reads. Here are some common solutions for this:
IllegalStateException An IllegalStateException with the message "removing bean lock and it has tx set!" usually means that you have not implemented equals and/or hashCode correctly for your custom/complex primary key class; it may also mean that your primary key class is not implemented correctly for serialization. Hangs and Transaction TimeoutsOne long outstanding bug of JBoss is that on a transaction timeout, that transaction is only marked for a rollback and not actually rolled back. This responsibility is delegated to the invocation thread. This can cause major problems if the invocation thread hangs indefinitely because things like entity bean locks will never be released. The solution to this problem is not a good one: You really just need to avoid doing anything within a transaction that could hang indefinitely. Common mistakes are making connections across the Internet and running a web crawler within a transaction. |