J'ai un MDB (Message bean) qui reçoit des messages avec chaîne qui représentent un mot. J'ai aussi une table dans la base de données. Le MDB devrait stocker dans la table, les mots et le nombre de fois que chaque mot a été reçu (compteur).Java EE concurrence et verrouillage
Le problème est que pour obtenir de meilleures performances du MDB a commencé dans de nombreux cas et lorsque le même nouveau mot est reçu par différentes instances deux créent la même ligne avec le nombre de 1.
Pour résoudre ce que je devrais faire le mot champ unique et ensuite la deuxième instance échouera sur commit, retransmettant le message, ce qui fonctionnera, mais peut être problématique. Est-ce une bonne pratique?
Une autre solution consiste à fusionner ces lignes après addition du compteur. Mais que se passe-t-il si une autre instance augmente le compteur au milieu de la mise à jour.
Que faire si deux instances tentent d'augmenter le compteur? @Version
devrait être suffisant?
Je ne suis pas sûr de savoir quelle est la bonne solution ici. Comment géreriez-vous de tels cas?
Aussi pouvez-vous suggérer quelques livres sur les pratiques de concurrence (pas sur l'utilisation de synchronized
comme j'ai besoin de soutenir Java EE et peut courir un groupe de serveurs d'application)?
Mise à jour: Après avoir lu plus sur EJB et JPA je suppose que je veux quelque chose comme une entité de verrouillage. Par exemple, je peux créer une nouvelle table avec seulement id et les colonnes clés et des données comme ceci:
ID | KEY
1 | WORDS_CREATE_LOCK
Alors que quand je dois gérer un nouveau mot que je vais faire quelque chose comme ça (code ne exact, pas sûr qu'il va même compiler):
// MAIN FUNCTION
public void handleWord(String wordStr) {
Word w = getWord(wordStr);
if (w == null)
w = getNewOrSychronizedWord(wordStr);
em.lock(w);
w.setCounter(w.getCounter() + 1);
em.unlock(w);
}
// Returns Word instance or null if not found
private Word getWord(String wordStr) {
Word w = null;
Query query = em.createQuery("select w from words as w where w.string = :wordStr order by w.id asc");
query.setParameter("wordStr", wordStr);
List<Word> words = query.getResultList();
if (words.getSize() > 0)
w = words.get(0);
return w;
}
// Handles locking to prevent duplicate word creation
private Word getNewOrSynchronizedWord(String wordStr) {
Word w = null;
Locks l = em.find(WORDS_CREATE_LOCK_ID, Locks.class);
em.lock(l);
Word w = getWord(wordStr);
if (w == null) {
w = new Word(wordStr);
em.persist(w);
}
em.unlock(l);
return w;
}
la question est-il efficace de cette façon? Et puis-je faire de même sans maintenir une table DB avec des lignes de verrouillage? Peut-être un mécanisme de verrouillage de conteneur Java EE?
Si ça aide, j'utilise JBoss 4.2.
J'ai une nouvelle idée pour cela. Je peux créer deux BDM:
1er MDB avec de nombreux cas autorisés, qui va gérer tous les messages et si le mot ne se trouve pas envoie le mot à la deuxième MDB
2ème MDB avec une seule instance autorisée, traitera les messages en série et permettra la création de nouveaux mots
La meilleure partie: aucune table entière/méthode/verrouillage de processus, la ligne de verrouillage uniquement sur le comptoir mise à jour
Quelle est ça?
Merci.
Le niveau d'isolation de REPEATABLE READ n'est pas la solution, car lorsque les deux instances s'exécutent, les requêtes select pour le même mot inexistant retournent toutes deux des lignes zéro et rien n'est verrouillé. Après chaque instance fera insérer avec le même mot. Le niveau d'isolation SERIALIZABLE est préférable car il verrouille les lignes inexistantes (incertaines) avec le nouveau mot mais cela a un impact important sur les performances et peut avoir des problèmes avec Oracle DB. –