Domanda

Sto lavorando su un sistema che deve gestire un certo numero di gara-condizioni quando serve di posti di lavoro a un certo numero di lavoratori-macchine.

I clienti avrebbero interrogare il sistema per i lavori con lo status = '0' (ToDo), poi, in modo atomico, aggiornare l' 'antico' di fila con lo status = '1' (bloccato) e recuperare l'id per quella riga (per l'aggiornamento del lavoro con le informazioni dei lavoratori come quale macchina sta lavorando su di esso, ecc.).

Il problema principale è che ci potrebbe essere un qualsiasi numero di client di aggiornamento allo stesso tempo. Una soluzione sarebbe quella di bloccare circa 20 delle righe con stato = '0', aggiornare il più vecchio e rilasciare tutte le serrature nuovo in seguito. Ho cercato in TransactionMiddleware, ma non vedo come questo impedirebbe il caso della più vecchia in corso di aggiornamento da sotto di me dopo interrogo esso.

Ho guardato nel QuerySet.update () cosa, e sembra promettente, ma nel caso di due clienti ottenere una sospensione dello stesso disco, lo stato sarebbe semplicemente aggiornato, e avremmo due operai a lavorare su lo stesso lavoro .. sono veramente in perdita qui.

Ho anche trovato biglietto # 2705 che sembra gestire il caso bene, ma ho nessuna idea di come ottenere il codice da lì a causa della mia esperienza SVN limitata (gli ultimi aggiornamenti sono semplicemente diff, ma non so come unire che con il tronco del codice).

Codice: Risultato = Job

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)
È stato utile?

Soluzione

Per unire # 2705 nel vostro Django, è necessario scaricare il programma prima:

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

poi REWIND svn alla versione django necessaria:

svn update -r11366

poi applicarlo:

patch -p1 for_update_11366_cdestigter.diff

Si informa l'utente che i file sono stati patchati con successo e quali no. Nel caso improbabile di conflitti è possibile risolverli guardando manualmente in http://code.djangoproject.com/attachment/ticket/2705/for_update_11366_cdestigter.diff

Per annullare l'applicazione della patch, basta scrivere

svn revert --recursive . 

Altri suggerimenti

Se il Django è in esecuzione su una macchina, c'è un modo molto più semplice per farlo ... scusi la pseudo-codice come i dettagli della vostra implementazione non sono chiare.

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

Avete due scelte al largo della parte superiore della mia testa. Uno è quello di bloccare righe immediatamente dopo il recupero e rilasciare solo la serratura volta quella appropriata è stata contrassegnata come in uso. Il problema qui è che nessun altro processo client può anche guardare ai posti di lavoro che non vengono selezionati. Se siete sempre e solo selezionando automaticamente l'ultimo allora può essere una breve abbastanza di una finestra per essere o.k. per voi.

L'altra opzione sarebbe quella di riportare le righe che sono aperti al momento della query, ma per poi controllare di nuovo ogni volta che il client tenta di afferrare un lavoro con cui lavorare. Quando un client tenta di aggiornare un lavoro per lavorare su di esso un controllo dovrebbe prima essere fatto per vedere se è ancora disponibile. Se qualcun altro ha già afferrato allora una notifica sarebbe stata inviata al client. Questo consente a tutti i clienti per vedere tutti i posti di lavoro come istantanee, ma se sono costantemente afferrando quello più recente, allora si potrebbe avere i clienti che riceve costantemente le notifiche che un lavoro è già in uso. Forse questa è la condizione di competizione a cui ti riferisci?

Un modo per aggirare questo sarebbe quello di restituire i posti di lavoro in gruppi specifici ai clienti in modo che essi non sono sempre sempre le stesse liste. Ad esempio, li abbattere per area geografica o anche solo in modo casuale. Ad esempio, ogni cliente potrebbe avere un ID da 0 a 9. Prendere la mod di un ID sui posti di lavoro e inviare di nuovo quei posti di lavoro con la stessa cifra finale al cliente. Non limitarla ad una quei posti di lavoro anche se, come non si vuole che ci sia posti di lavoro che non si può raggiungere. Così, per esempio se si ha clienti di 1, 2, e 3 e un lavoro di 104 allora nessuno sarebbe in grado di arrivare ad essa. Quindi, una volta che non ci sono abbastanza posti di lavoro con i posti di lavoro corrette finale cifra sarebbe iniziare a tornare con altre cifre solo per riempire la lista. Potrebbe essere necessario giocare con l'algoritmo esatto qui, ma spero che questo vi dà un'idea.

Come si bloccano le righe nel database, al fine di aggiornarli e / o inviare di nuovo le notifiche dipenderà in gran parte il vostro RDBMS. In MS SQL Server si potrebbe avvolgere tutto quel lavoro bene in una stored procedure finché l'intervento dell'utente non è necessario nel mezzo di esso.

Spero che questo aiuta.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top