2009-11-05 11 views
8

Tengo una aplicación de escritorio que se ejecuta en una red y cada instancia se conecta a la misma base de datos.Cómo hacer Mutex en una red?

Entonces, en esta situación, ¿cómo puedo implementar un mutex que funcione en todas las instancias en ejecución que están conectadas a la misma base de datos?

En otras palabras, no quiero que las dos instancias + ejecuten la misma función al mismo tiempo. Si uno ya está ejecutando la función, las otras instancias no deberían tener acceso a ella.

PD: La transacción de la base de datos no se resolverá, porque la función que quiero excluir no utiliza la base de datos. Mencioné la base de datos solo porque se puede usar para intercambiar información entre las instancias en ejecución.

PS2: La función tarda aproximadamente ~ 30 minutos en completarse, por lo que si una segunda instancia intenta ejecutar la misma función, me gustaría mostrar un buen mensaje de que no se puede realizar ahora porque la computadora 'X' es ya está ejecutando esa función.

PS3: La función debe procesarse en la máquina cliente, por lo que no puedo usar los procedimientos almacenados.

+2

¿Cuál es su base de datos y admite transacciones? – ephemient

Respuesta

2

Creo que está buscando una transacción de base de datos. Una transacción aislará sus cambios de todos los otros clientes.

Actualización: Mencionó que la función no se escribe actualmente en la base de datos. Si desea silenciar esta función, tendrá que haber una ubicación central para almacenar el soporte de mutex actual. La base de datos puede funcionar para esto: simplemente agregue una nueva tabla que incluya el nombre de la computadora del titular actual. Verifique esa tabla antes de comenzar su función.

Creo que su pregunta puede ser una confusión sin embargo. Los mensajes mutex deberían tratar de proteger los recursos. Si su función no está accediendo a la base de datos, ¿qué recurso compartido está protegiendo?

+0

En realidad, no estoy buscando una transacción de base de datos. Por favor, mira las actualizaciones en la pregunta. –

+4

Para hacer el mutex, necesitará * alguna * forma de comunicación entre sus clientes y un punto central. Si ya tiene una conexión de base de datos, que incluye una ** ** muy buena implementación de mutex (las bases de datos están pensadas para esto), ¿por qué no usarlo? – Wim

0

coloque el código dentro de una transacción, ya sea en la aplicación, o mejor, dentro de un procedimiento almacenado, y llame al procedimiento almacenado. el mecanismo de transacción aislará el código entre las personas que llaman.

0

Considere, por el contrario, una cola de mensajes. Como se mencionó, la base de datos debe gestionar todo esto por usted, ya sea en transacciones o acceso en serie a tablas (ala MyISAM).

0

En el pasado he hecho lo siguiente:

  1. crear una tabla que básicamente tiene dos campos, y function_name is_running
  2. No sé lo que RDBMS que está utilizando, pero la mayoría tienen una forma de bloquear registros individuales para la actualización.Aquí hay alguna pseduocode basado en Oracle:

    COMENZAR TRANS

    SELECT FOR UPDATE is_running DE DONDE tabla_de_funciones function_name = 'foo';

    - Marque aquí para ver si se está ejecutando, si no, se puede establecer corriendo a 'verdadero'

    actualización del conjunto tabla_de_funciones is_running = 'Y', donde function_name = 'foo';

    COMMIT TRANS

Ahora no tienen la documentación de Oracle PSQL conmigo, pero usted consigue la idea. La cláusula 'FOR UPDATE' bloquea allí el registro después de la lectura hasta la confirmación, por lo que otros procesos se bloquearán en esa instrucción SELECT hasta que se comprometa el proceso actual.

+1

¿Qué ocurre si la instancia que se ejecuta falla en el medio de la función y nunca tiene la oportunidad de ser ser is_running = false? –

+0

En casos como este, generalmente pongo una marca de tiempo en la tabla también, de modo que si una fila tiene una marca de tiempo demasiado antigua, supongo que la siguiente instancia puede iniciar la función desde cero. –

0

Puede usar Terracotta para implementar dicha funcionalidad, si tiene una pila de Java.

+0

¿Qué es Terracotta? –

+0

Parece demasiado. Además, Java no está disponible. –

0

Incluso si su función no utiliza actualmente la base de datos, aún puede resolver el problema con una tabla específica con el fin de sincronizar esta función. Los detalles dependerán de su base de datos y de cómo maneja los niveles de aislamiento y bloqueo. Por ejemplo, con SQL Server, establecería el aislamiento de transacción en lectura repetible, leería un valor de su fila de bloqueo y lo actualizaría dentro de una transacción. No comprometa la transacción hasta que su función esté lista. También puede usar bloqueos de tabla explícitos en una transacción en la mayoría de las bases de datos, que podrían ser más simples. Esta es probablemente la solución más simple dado que ya está usando una base de datos.

Si no quiere confiar en la base de datos por el motivo que sea, podría escribir un servicio simple que acepte las conexiones TCP de su cliente. Cada cliente solicitaría permiso para ejecutar y devolvería una respuesta cuando haya terminado. El servidor podría garantizar que solo un cliente obtenga permiso para ejecutarse a la vez. Los clientes muertos eventualmente soltarían la conexión TCP y se detectarían siempre que tenga la configuración correcta de mantener activo.

La solución de cola de mensajes sugerida por Xepoch también funcionaría. Podría usar algo como MSMQ o Java Message Queue y tener un solo mensaje que actúe como un token de ejecución. Todos sus clientes solicitarían el mensaje y luego volverían a publicarlo cuando terminen. Se arriesga a un punto muerto si un cliente muere antes de reposicionar, por lo que tendría que idear alguna lógica para detectar esto y podría complicarse.

+0

La solución DB es como lo que mjmarsh publicó, pero no depende del valor de una bandera que deba restablecerse. Su función se ejecuta mientras la transacción está abierta y el valor en la tabla no importa.Para modificar su ejemplo: BEGIN TRANS SELECCIONAR bloqueos de ACTUALIZACIÓN FROM tabla_función DONDE nombre_función = 'foo'; UPDATE function_table set bloqueos = bloqueos + 1 donde nombre_funcion = 'foo'; - Haga el procesamiento de su función aquí COMMIT TRANS –