2009-11-05 8 views
8

J'ai une application de bureau qui fonctionne sur un réseau et chaque instance se connecte à la même base de données.Comment Mutex à travers un réseau?

Donc, dans cette situation, comment puis-je implémenter un mutex qui fonctionne sur toutes les instances en cours d'exécution qui sont connectées à la même base de données? En d'autres termes, je ne veux pas que deux instances + exécutent la même fonction en même temps. Si vous exécutez déjà la fonction, les autres instances ne doivent pas y avoir accès. PS: La transaction de base de données ne résout pas, car la fonction que je ne veux pas mutex n'utilise pas la base de données. J'ai mentionné la base de données juste parce qu'elle peut être utilisée pour échanger des informations entre les instances en cours. PS2: La fonction prend environ ~ 30 minutes pour terminer, donc si une seconde instance essaie d'exécuter la même fonction, je voudrais afficher un message gentil qu'il ne peut pas être effectué en ce moment parce que l'ordinateur «X» est exécute déjà cette fonction. PS3: La fonction doit être traitée sur la machine client, donc je ne peux pas utiliser les procédures stockées.

+2

Quelle est votre base de données et gère-t-elle les transactions? – ephemient

Répondre

2

Je pense que vous recherchez une transaction de base de données. Une transaction isolera vos modifications de tous les autres clients.

Mise à jour: Vous avez mentionné que la fonction n'écrit pas actuellement dans la base de données. Si vous voulez mutex cette fonction, il doit y avoir un emplacement central pour stocker le support de mutex actuel. La base de données peut fonctionner pour cela - il suffit d'ajouter une nouvelle table qui inclut le nom d'ordinateur du détenteur actuel. Vérifiez cette table avant de commencer votre fonction.

Je pense que votre question peut être source de confusion. Les mutex devraient être sur la protection des ressources. Si votre fonction n'accède pas à la base de données, quelle ressource partagée protégez-vous?

+0

En fait, je ne cherche pas une transaction de base de données. S'il vous plaît, voir les mises à jour dans la question. –

+4

Pour faire le mutex, vous aurez besoin d'une certaine forme de communication entre vos clients et un point central. Si vous avez déjà une connexion à une base de données, qui inclut une implémentation ** très ** bonne de mutex (les bases de données sont destinées à ce genre de choses), pourquoi ne pas l'utiliser? – Wim

0

Mettez le code dans une transaction soit dans l'application, soit mieux: insérez une procédure stockée et appelez la procédure stockée. le mécanisme de transaction va isoler le code entre les appelants.

0

Inversement, considérez une file d'attente de messages. Comme mentionné, le DB devrait gérer tout cela pour vous dans les transactions ou l'accès en série aux tables (al MyISAM).

0

Dans le passé, je l'ai fait ce qui suit:

  1. Créer une table qui a essentiellement deux domaines, function_name et is_running
  2. Je ne sais pas ce SGBDR que vous utilisez, mais la plupart ont un façon de verrouiller les enregistrements individuels pour la mise à jour.Voici quelques pseduocode basé sur Oracle:

    BEGIN TRANS

    SELECT FOR UPDATE is_running DE function_table OU nom_fonction = 'foo';

    - Cochez cette case pour voir si elle est en cours d'exécution, sinon, vous pouvez faire courir à « true »

    MISE À JOUR ensemble function_table is_running « Y » = où nom_fonction = « foo »;

    COMMIT TRANS

Maintenant, je n'ai pas la documentation Oracle PSQL avec moi, mais vous voyez l'idée. La clause 'FOR UPDATE' verrouille l'enregistrement après la lecture jusqu'à la validation, de sorte que les autres processus se bloqueront sur cette instruction SELECT jusqu'à ce que le processus en cours soit validé.

+1

Que se passe-t-il si l'instance en cours d'exécution se bloque au milieu de la fonction et qu'elle n'a jamais la possibilité de ser ser_running = false? –

+0

Dans des cas comme celui-ci, je place aussi un horodatage dans la table de sorte que si une ligne a un horodatage qui est trop ancien, je suppose que l'instance suivante peut démarrer la fonction depuis le début. –

0

Vous pouvez utiliser Terracotta pour implémenter une telle fonctionnalité, si vous avez une pile Java.

+0

Qu'est-ce que Terracotta? –

+0

Cela semble trop. En outre, Java n'est pas disponible. –

0

Même si votre fonction n'utilise pas actuellement la base de données, vous pouvez toujours résoudre le problème avec une table spécifique dans le but de synchroniser cette fonction. Les détails dépendront de votre base de données et de la manière dont elle gère les niveaux d'isolation et de verrouillage. Par exemple, avec SQL Server, vous définissez l'isolation de la transaction sur readable read, lisez une valeur de votre ligne de verrouillage et mettez-la à jour dans une transaction. Ne pas valider la transaction tant que votre fonction n'est pas terminée. Vous pouvez également utiliser des verrous de table explicites dans une transaction sur la plupart des bases de données, ce qui peut être plus simple. C'est probablement la solution la plus simple étant donné que vous utilisez déjà une base de données.

Si vous ne voulez pas compter sur la base de données pour une raison quelconque, vous pouvez écrire un service simple qui accepterait les connexions TCP de votre client. Chaque client demanderait l'autorisation de s'exécuter et retournerait une réponse une fois terminé. Le serveur pourrait s'assurer qu'un seul client obtient l'autorisation de s'exécuter à la fois. Les clients morts finiront par abandonner la connexion TCP et seront détectés aussi longtemps que vous avez le bon paramètre keep alive.

La solution de file d'attente de messages proposée par Xepoch fonctionnerait également. Vous pouvez utiliser quelque chose comme MSMQ ou Java Message Queue et avoir un seul message qui agirait comme un jeton d'exécution. Tous vos clients demandent le message et le repostent lorsque vous avez terminé. Vous risquez un blocage si un client meurt avant de le republier. Vous devrez donc concevoir une logique pour le détecter et cela pourrait devenir compliqué.

+0

La solution DB ressemble à ce que mjmarsh a posté, mais ne dépend pas de la valeur d'un drapeau qui doit être réinitialisé. Votre fonction s'exécute pendant que la transaction est ouverte et la valeur dans la table n'a pas d'importance.Pour modifier son exemple: BEGIN TRANS SELECT FOR UPDATE verrouille DE function_table OU nom_fonction = 'foo'; serrures de consigne UPDATE function_table = serrures + 1 où nom_fonction = 'foo'; - Est-ce que le traitement de votre fonction ici COMMIT TRANS –