私は、サードパーティ システムへの顧客のプロビジョニングを担当する典型的なエンタープライズ アプリケーションを開発しました。このシステムには、特定の顧客に対して 1 つのスレッドしか動作できないという制限があります。そのため、現在進行中の customerIds@Singleton
を含む単純なロック メカニズムを追加しました。Set
プロビジョニングの新しいリクエストが来るたびに、最初にこれをチェックしますSet
。cusotomerId が存在する場合は待機し、存在しない場合はそれを に追加してSet
処理に入ります。
最近、このアプリケーションをクラスタにデプロイすることが決定されました。これは、このロック アプローチが有効ではなくなったことを意味します。ロックに DB を使用するソリューションを考え出しました。customerIds を含む単一の列を持つテーブルを作成しました (一意の制約もあります)。新しいプロビジョニング要求が来ると、トランザクションを開始し、customerId で行をロックしようとしますSELECT FOR UPDATE
(customerId がまだ存在しない場合は挿入します)。その後、顧客のプロビジョニングを開始し、終了したらトランザクションをコミットします。コンセプトは機能しますが、トランザクションに問題があります。現在、 と からの customerId の追加と削除を処理するとメソッドを含むクラスCustomerLock
があります。このクラスを、Bean 管理のトランザクションを持つステートレス EJB に変換したいと考えました。add()
remove()
Set
add()
メソッドはトランザクションを開始して行をロックし、remove()
メソッドはトランザクションをコミットして行をロック解除します。しかし、トランザクションの開始と終了は同じ方法で行わなければならないようです。私が説明したアプローチを使用する方法はありますか、またはトランザクションが同じ方法で開始および終了するようにロジックを変更する必要がありますか?
カスタマーロック クラス:
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class CustomerLock {
@Resource
private UserTransaction tx;
public void add(String customerId) throws Exception {
try {
tx.begin();
dsApi.lock()
} catch (Exception e) {
throw e;
}
}
public void remove(String customerId) throws Exception {
try {
tx.commit();
} catch (Exception e) {
throw e
}
}
}
CustomerProvisioner クラスの抜粋:
public abstract class CustomerProvisioner {
...
public void execute(String customerId) {
try {
customerLock.add(customerId);
processing....
customerLock.remove(customerId);
} catch (Exception e) {
logger.error("Error", e);
}
}
...
}
StandardCustomerProvisioner クラス:
@Stateless
public class StandardCustomerProvisioner extends CustomerProvisioner {
...
public void provision(String customerId) {
// do some business logic
super.execute(customerId);
}
}