2017-11-12 4 views
1

Ich habe den Code in meinem Express-Server unten (habe es der Kürze wegen gekürzt). Ich habe ein gemeinsames Objekt, das ich in drei verschiedenen ruhigen Endpunkten hinzufüge/modifiziere/lese. Da alle HTTP-Anfragen in Nodejs asynchron sind, konnte ich beide Requests zur gleichen Zeit setzen und bekommen. Obwohl PUT passiert ist, aber sagen wir, dass der Status nicht aktualisiert ist, könnte mein GET eine etwas schale Antwort bekommen?Gibt es unten eine Racebedingung?

Von dem, was ich verstehe, und meine Tests zeigen, dass es hier keine Race Condition gibt. Da das Aktualisieren des Objekts results eine synchrone Operation ist und alle asynchronen Operationen darauf warten sollten. Kann jemand mit einer besseren Erklärung helfen, ob das richtig ist oder nicht?

var obj = {}; 
    const exec = require('child_process').exec; 
    app.post('/foo', (req, res) => { 
     var result = {}; 
     result.id = generateSomeRandomId(); 
     result.failed = 0; 
     result.status = 'running' 
     //execute some command and update result 
     const child = exec('some command'); 
     child.stdout.on('data', (data) => { 
      //some logic 
     }); 
     child.stderr.on('data', (data) => { 
      result.failed = result.failed + 1; 
      }); 
     child.on('close', (code, signal) => { 
       if (signal !== null && signal !== undefined) { 
        result.status = 'cancelled'; 
       } else { 
        result.status = 'completed'; 
        result.runtime = calculateRunTime(); 
       } 
     }); 
     result.pid = child.pid; 
     obj[result.id] = result; 
     res.send(result); 
    } 

    app.put('/foo/:id', (req, res) => { 
     var result = obj[req.params.id]; 
     if (result.status === 'running' && result.pid !== undefined) { 
       kill(result.pid, 'SIGKILL'); 
       result.status = 'cancelled'; 
       result.runtime = calculateRunTime(); 
     } 
     res.send(result); 
    } 
    app.get('/foo/:id', (req, res) => { 
     var result = obj[req.params.id]; 
     res.send(result); 
    } 
+0

sprichst du in 'app.post ('/ foo''? Es gibt kein" race ", aber du wirst" res.post (result); "bevor irgendwelche der asynchronen Teile überhaupt laufen ... so, was auch immer Sie zwischen '// führen Sie einen Befehl und update Ergebnis 'und' result.pid = Kind.pid; 'wird keinen Effekt auf das haben, was Sie in 'res.send (result) senden;' –

+0

auch, 'result.failed = result.failed + 1;' resultiert in 'results.failed',' undefined' oder wenn irgendwelche Fehler, 'result.failed === NaN' –

+0

@JaromandaX ja ich bekomme diesen Teil. Alle asynchronen Vorgänge in * post * warten, bis alle Synchronisierungsvorgänge abgeschlossen sind. Meine Frage war hauptsächlich auf gleichzeitige PUT und GET-Anfragen. Wäre ich dort rennfähig? Und Sie haben Recht mit dem Ergebnis. "NaN" -Teil fehlgeschlagen, ich übersprang die Initialisierung für die Kürze. Wird es wieder hinzufügen. – Manali

Antwort

1

Sie haben nichts, was ich eine "Race Condition" nennen würde; Hier ist ein Element der Unsicherheit im Spiel, aber in der Praxis wird es wahrscheinlich keine Rolle spielen.

Es sieht aus wie Ihre post startet einen Prozess und gibt die ID, Ihre put bricht den Prozess ab, und Ihre get gibt den aktuellen Status des Prozesses zurück. Von diesem versichere ich, dass Sie nie in der Lage sein werden, get bis Ihre post abgeschlossen ist und die ID bereitstellt.

Wenn Sie einen get Anruf tätigen, der empfangen und zurückgegeben wird, bevor Ihre asynchronen exec Listener abgeschlossen sind, erhalten Sie den letzten in Bearbeitung befindlichen Status - ich nehme an, dass das von Entwurf ist. Der einzige mögliche Konflikt hier ist, wenn Sie einen put Aufruf gemacht haben, um Ihren Prozess anzuhalten.

Sowohl Ihre put als auch Ihre get sind synchron, wenn es um die Interaktion mit Ihrem Ergebnisobjekt geht. Daher wird diejenige, die zuerst empfangen wird, diejenige, die zuerst abgeschlossen wird. Sie können die Anforderung zur Prozesslöschung für unsere Zwecke ignorieren, da das Ergebnisobjekt nicht geändert wird. Es gibt keine Garantie, dass sie in der gleichen Reihenfolge empfangen werden, in der sie vom Kunden gesendet wurden, was in Ihrem Szenario möglicherweise von praktischer Bedeutung ist.

ich glaube (obwohl mein Gedächtnis hier fehlerhaft sein kann), dass, wenn Sie cluster verwendet haben Anfragen in verschiedenen Prozessen zu behandeln, würden Sie nicht in der Lage sein, Daten zu übergeben um über ein gemeinsames Objekt trotzdem, so dass jede Komplexität hinzugefügt durch diese Möglichkeit ist bereits ausgeschlossen.

Daher sind Abweichungen in der Netzwerkleistung und Zuverlässigkeit Ihre einzigen echten Wildcard hier. Der Server verarbeitet Anfragen in der Reihenfolge, in der sie eingehen, und gibt Ihnen die erwarteten Ergebnisse. Wenn Sie nur einen einzigen Client haben, können Sie warten, bis Sie die Antwort von Ihrer vorherigen Anfrage bekommen, um die nächste zu senden, was möglicherweise Ihre Leistung inakzeptabel verlangsamen, aber mehr oder weniger kugelsicher machen würde. Ansonsten senden Sie einfach Ihre Anfragen und machen Sie sich keine Sorgen, machen Sie Ihre App robust genug, um & eine zweite Stornierungsanfrage zu bearbeiten, auch wenn Sie den Vorgang bereits abgebrochen haben.

+0

Danke Jason, das macht Sinn. Zwischen POST und GET gibt es keine race-Bedingung, wie Sie gesagt haben, egal was der aktuelle Zustand des Objekts ist, was GET zurückgibt. Jetzt kommt die Frage für POST und PUT zur gleichen Zeit, wenn POST "fast" erledigt ist (gerade in 'child.on ('close')') und PUT kommt zur gleichen Zeit, könnte ich am Ende den Prozess abbrechen obwohl es die Ausführung rechtmäßig beendet hat. Würden Versprechungen besser dazu dienen, dies zu vermeiden? – Manali

+0

Ich kann das nicht beantworten, ohne Ihre Anforderungen zu verstehen - gibt es eine "ungültige" Zeit, um einen Prozess abzubrechen? Wenn dies der Fall ist, muss diese Bedingung in Ihrem Ergebnisobjekt nachverfolgt werden, und Sie müssen es überprüfen und optional "fehlschlagen", um abzubrechen. Sie können dies erreichen, indem Sie 'result.status = 'finishing'' sobald' child.on ('close') 'feuern, was dazu führen würde, dass Ihr' result.status == 'running'-Check fehlschlägt. Wenn es * keine * ungültige Zeit gibt, einen Prozess abzubrechen, dann müssen Sie einfach akzeptieren, dass es möglich ist, abzubrechen, selbst wenn es legitim beendet ist. – Jason

+0

Oh, aber auch, es gibt immer noch keine "race condition", weil dein 'on ('close')' Event und dein 'put' Aufruf immer noch im selben Prozess ausgeführt werden - wenn der' close' Aufruf zuerst passiert, dann ' lle vervollständige zuerst, wenn der 'put' Anruf zuerst geschieht, wird er zuerst abgeschlossen. Also, das macht meinen ersten Kommentar irrelevant. – Jason

1

Es ist nur eine Idee, aber vielleicht ein Versprechen hier hilfreich sein könnte:

var obj = {}; 
const exec = require('child_process').exec; 
app.post('/foo', (req, res) => { 
    var result = {}; 
    result.id = generateSomeRandomId(); 
    result.status = 'running'; 
    const child = exec('some command'); 
    child.stdout.on('data', (data) => { 
     //some logic 
    }); 

    result.promise = new Promise(resolve => { 
     child.stderr.on('data', (data) => { 
      result.failed = result.failed + 1; 
      resolve(false); 
     }); 
     child.on('close', (code, signal) => { 
      // ... 
      resolve(true); 
     }); 
    }); 

    result.pid = child.pid; 
    obj[result.id] = result; 
    res.send(result); 
} 

app.get('/foo/:id', (req, res) => { 
    var result = obj[req.params.id]; 
    if(result.status === 'running') { 
     result.promise.then(() => res.send(result)); 
    } 
    else { 
     res.send(result); 
    } 
} 

In diesem Fall GET nur reagieren würde, wenn die child durch Fehler gemacht wird oder ‚Schließen‘ Ereignis.

+0

+1, danke. Versprechungen zu verwenden ist eine interessante Idee. Meine Anforderung ist, dass das aktuelle Ergebnis auch dann zurückgegeben werden muss, wenn der untergeordnete Prozess ausgeführt wird. Daher möchte ich nicht warten, bis der untergeordnete Prozess abgeschlossen ist. Ich habe auch einen PUT, der auch Ergebnisse modifiziert. – Manali

+0

Auch in dieser Lösung GET, wenn Kind nicht fertig ist, was passiert? Client wartet? – Manali

+0

@Manali Ja, es wird warten. – dhilt

Verwandte Themen