Pregunta

Estoy trabajando en un sistema que tiene que manejar una serie de carreras condiciones al servir puestos de trabajo a un número de trabajadores-máquinas.

Los clientes sería el sistema en busca trabajos con el estado = '0' (Tareas) y, a continuación, de forma atómica, actualizar la fila 'más antiguo' con el estado = '1' (Bloqueado) y recuperar el identificador de esa fila (para actualizar el trabajo con la información de los trabajadores como los que la máquina está trabajando en él, etc.).

El principal problema aquí es que puede haber cualquier número de clientes actualizar al mismo tiempo. Una solución sería para bloquear alrededor de 20 de las filas con el estado = '0', actualice la más antigua y liberar todos los bloqueos de nuevo más tarde. He estado buscando en la TransactionMiddleware pero yo no veo cómo esto impediría el caso de la más antigua que se actualiza de debajo de mí después de consultarlo.

He mirado en lo QuerySet.update (), y parece prometedor, pero en el caso de dos clientes obtener una bodega del mismo registro, el estado simplemente actualizada, y que tendría dos trabajadores que trabajan en el mismo trabajo .. estoy realmente en una pérdida aquí.

También se encontró billete # 2705 que parece manejar el caso muy bien, pero tengo ni idea de cómo obtener el código de allí debido a mi experiencia limitada SVN (las últimas actualizaciones son simplemente diferenciaciones, pero no saben cómo combinar eso con el tronco del código).

Código: Resultado = Trabajo

class Result(models.Model):
"""
Result: completed- and pending runs

'ToDo': job hasn't been acquired by a client
'Locked': job has been acquired
'Paused'
"""
# relations
run = models.ForeignKey(Run)
input = models.ForeignKey(Input)

PROOF_CHOICES = (
    (1, 'Maybe'),
    (2, 'No'),
    (3, 'Yes'),
    (4, 'Killed'),
    (5, 'Error'),
    (6, 'NA'),
)
proof_status = models.IntegerField(
    choices=PROOF_CHOICES,
    default=6,
    editable=False)

STATUS_CHOICES = (
    (0, 'ToDo'),
    (1, 'Locked'),
    (2, 'Done'),
)
result_status = models.IntegerField(choices=STATUS_CHOICES, editable=False, default=0)

# != 'None' => status = 'Done'
proof_data = models.FileField(upload_to='results/',
    null=True, blank=True)
# part of the proof_data
stderr = models.TextField(editable=False,
    null=True, blank=True)

realtime = models.TimeField(editable=False,
    null=True, blank=True)
usertime = models.TimeField(editable=False,
    null=True, blank=True)
systemtime = models.TimeField(editable=False,
    null=True, blank=True)

# updated when client sets status to locked
start_time = models.DateTimeField(editable=False)

worker = models.ForeignKey('Worker', related_name='solved',
    null=True, blank=True)
¿Fue útil?

Solución

Para combinar # 2705 en su Django, es necesario descargar primero:

cd <django-dir>
wget http://code.djangoproject.com/attachment/ticket/2705/for_update_11366_cdestigter.diff?format=raw

A continuación, SVN de rebobinado para la versión de Django necesaria:

svn update -r11366

A continuación, se aplica:

patch -p1 for_update_11366_cdestigter.diff

Se le informará de los archivos que fueron parcheados con éxito y cuáles no. En el caso poco probable de que los conflictos se puede solucionarlos manualmente en busca http://code.djangoproject.com/attachment/ticket/2705/for_update_11366_cdestigter.diff

Para cancelar la aplicación del parche, simplemente escribir

svn revert --recursive . 

Otros consejos

Si su Django se está ejecutando en una máquina, hay una manera mucho más sencilla de hacerlo ... Con permiso del pseudo-código como los detalles de su aplicación no son claras.

from threading import Lock

workers_lock = Lock()

def get_work(request):
    workers_lock.acquire()
    try:
        # Imagine this method exists for brevity
        work_item = WorkItem.get_oldest()
        work_item.result_status = 1
        work_item.save()
    finally:
        workers_lock.release()

    return work_item

Existen dos opciones de la parte superior de la cabeza. Una de ellas es para bloquear filas inmediatamente después de la recuperación y sólo liberar el bloqueo una vez el apropiado se ha marcado como en uso. El problema aquí es que ningún otro proceso cliente puede incluso mirar a los puestos de trabajo que no se deje seleccionados. Si usted está siempre a seleccionar automáticamente el último, entonces puede ser suficiente una breve de una ventana para ser visto bueno para usted.

La otra opción sería la de traer de vuelta a las filas que están abiertos en el momento de la consulta, pero a continuación, comprobar de nuevo cada vez que los intentos cliente para obtener un puesto de trabajo para trabajar. Cuando un intento de cliente actualizar un trabajo para trabajar en él un cheque en primer lugar puede hacer para ver si todavía está disponible. Si alguien ya ha agarrado a continuación una notificación sería enviado de vuelta al cliente. Esto permite a todos los clientes para ver todos los puestos de trabajo como instantáneas, pero si ellos están agarrando constantemente el más reciente entonces usted podría tener los clientes constantemente recibir notificaciones de que un trabajo que ya está en uso. Quizás esta es la condición de carrera a la que usted se refiere?

Una manera de conseguir alrededor de eso sería para devolver los puestos de trabajo en grupos específicos a los clientes para que no siempre están recibiendo las mismas listas. Por ejemplo, las dividen por área geográfica o incluso simplemente al azar. Por ejemplo, cada cliente puede tener un ID de 0 a 9. Tome el mod de una identificación de los puestos de trabajo y enviar de vuelta los puestos de trabajo con el mismo dígito final para el cliente. No se limite sólo a aquellos puestos de trabajo, aunque, como usted no quiere que haya puestos de trabajo que no se puede alcanzar. Así, por ejemplo, si tiene clientes de 1, 2 y 3 y un puesto de trabajo de 104 entonces nadie sería capaz de llegar a ella. Así, una vez que no hay suficientes puestos de trabajo con los puestos de trabajo que terminan dígitos correctos empezarían volviendo con otros dígitos sólo para llenar la lista. Puede que tenga que jugar con el algoritmo exacto aquí, pero espero que esto le da una idea.

¿Cómo se bloquea las filas de su base de datos con el fin de actualizarlos y / o devolver las notificaciones dependerá en gran medida de su RDBMS. En MS SQL Server que puede envolver todo ese trabajo muy bien en un procedimiento almacenado, siempre y cuando no es necesaria la intervención del usuario en el centro de la misma.

Espero que esto ayude.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top