Heroku schneidet HTTP-Antworten ab?

78

Ich verwende eine Flask / Gunicorn Python-App auf einem Heroku Cedar-Prüfstand. Die App kehrt JSON responseszu ihren Kunden zurück (es ist API serverwirklich eine).

Hin und wieder erhalten Clients 0-Byte-Antworten. Ich gebe sie jedoch nicht zurück. Hier ist ein Ausschnitt aus dem Protokoll meiner App:

14. März 13:13:31 d.0b1adf0a-0597-4f5c-8901-dfe7cda9bce0 app [web.1] [2013-03-14 13:13:31 UTC] 10.104.41.136 apisrv - api_get_credits_balance (): session_token = [MASKED ]]

In der ersten Zeile oben beginne ich mit der Bearbeitung der Anfrage.

14. März 13:13:31 d.0b1adf0a-0597-4f5c-8901-dfe7cda9bce0 app [web.1] [2013-03-14 13:13:31 UTC] 10.104.41.136 apisrv 1252148511 api_get_credits_balance (): return [{' Credits_Balance ': 0}]

In der zweiten Zeile gebe ich einen Wert zurück (an Flask - es ist ein Flask "Response" -Objekt).

14. März 13:13:31 d.0b1adf0a-0597-4f5c-8901-dfe7cda9bce0 app [web.1] 10.104.41.136 - - [14 / Mar / 2013: 13: 13: 31] POST / get_credits_balance? Session_token = MASKED HTTP / 1.1 "200 22" - "Appcelerator Titanium / 3.0.0.GA (iPhone / 6.1.2; iPhone OS; en_US;)"

Die dritte Zeile ist Gnicorns, in der Sie sehen können, dass das Gunicorn den 200-Status und den 22-Byte-HTTP-Body (" 200 22") erhalten hat.

Der Client hat jedoch 0 Bytes. Hier ist das Heroku-Router-Protokoll:

14. März 13:13:30 d.0b1adf0a-0597-4f5c-8901-dfe7cda9bce0 heroku [router] at = info method = POST path = / get_credits_balance? Session_token = MASKED host = matchspot-apisrv.herokuapp.com fwd = "66.87. 116.128 "dyno = web.1 queue = 0 wait = 0ms connect = 1ms service = 19ms status = 200 bytes = 0

Warum gibt Gunicorn 22 Bytes zurück, aber Heroku sieht 0 und gibt tatsächlich 0 Bytes an den Client zurück? Ist das ein Heroku-Fehler?

Nitzan schüttelte sich
quelle
1
Haben Sie bemerkt, dass der Heroku-Zeitstempel vor Ihrem Prozess-Zeitstempel liegt? Benutzt du gevent? Irgendetwas stimmt nicht mit der Synchronisation, denke ich.
Tigra
2
Und doch gibt der Zeitstempel einen Unterschied von 1 Sekunde an, nicht 1 1 ms ... Ich habe nicht mit Heroku gearbeitet, es handelt sich also nur um Vorschläge. Sowohl 1 ms als auch 1999 ms können einen Zeitstempelunterschied von 1 Sekunde ergeben. Service 19ms ist auch zu niedrig, um beim Cloud-Service wahr zu sein. Mein Punkt ist also, dass es wahrscheinlich eine Art Timeout gibt und bei Timeout anstelle von Fehler Heroku eine leere Seite bedient. Dieser Vorschlag ist weit gefasst, aber vielleicht sollten Sie eine lange Anfrage emulieren und sehen, was passiert
Tigra
9
Wie hilfreich war Heroku, als Sie sie damit kontaktierten (aus Neugier)?
Orokusaki
6
Bisher nicht sehr viel. Ich habe mich vor 10 Tagen an sie gewandt und mir wurde gesagt, die Python-Leute würden es sich zuerst ansehen, und wenn sie mir nicht helfen können, werden die Routing-Leute einen Blick darauf werfen. 5 Tage später wurde mir mitgeteilt, dass die Python-Leute dies an die Routing-Leute weitergegeben haben, und heute erhielt ich eine E-Mail von einem "Routing-Mann", der sagte, er könne nicht neu erstellen und bat um weitere Informationen. Also ja, sie durchlaufen den richtigen Prozess, aber es dauert ewig.
Nitzan Shaked
1
Kleines Update: Dies wurde noch nicht behoben. Ich habe mit Heroku-Unterstützung hin und her korrespondiert, und das Beste, was ich derzeit sammeln kann, ist, dass sie mich nicht mit "Es ist an Ihrem Ende" entlassen haben und versuchen, ein Tool zu schreiben, das den App-Verkehr von tcpdump erfasst , für "solche Debugging-Fälle".
Nitzan Shaked

Antworten:

1

Ich weiß, dass ich hier als etwas abseits der Mauer betrachtet werden kann, aber es gibt noch eine andere Option.

Wir wissen, dass es von Zeit zu Zeit einen Fehler gibt, der während des Transports auftritt. Wir wissen, dass wir derzeit nicht viel tun können, um das Problem zu stoppen. Wenn Sie nur die API bereitstellen, hören Sie auf zu lesen. Wenn Sie jedoch auch den Client schreiben, fahren Sie fort.

Der Fehler ist ein bekannter Fall und eine bekannte Ursache. Das Ergebnis eines leeren Rückgabewerts bedeutet, dass ein Fehler aufgetreten ist. Der Wert ist jedoch verfügbar und wurde abgerufen, berechnet, was auch immer ... Mein Instinkt als Entwickler wäre es, ein leeres Ergebnis als HTTP-Fehler zu behandeln und das erneute Senden der Daten anzufordern. Sie können dann die erneut gesendeten Anforderungen verfolgen und sehen, wie oft dies geschieht.

Ich würde vorschlagen (obwohl Sie mich als die Art von Entwickler betrachten, die auch daran denkt), dass Sie die Anforderungen zählen und einen vernünftigen Wert für die Antwort "Netzwerkfehler" an den Benutzer festlegen. Mein Instinkt wäre, es sofort noch einmal zu versuchen und dann eine Weile zu warten, bevor ich es noch einmal versuche.

Nach dem, was Sie beschreiben, würde der erste Wiederholungsversuch die Daten wahrscheinlich richtig erfassen. Dies kann natürlich bedeuten, dass ältere Anforderungen einige Minuten im Cache bleiben oder die Anforderung ein zweites Mal ausgeführt wird, je nachdem, was am besten geeignet erscheint.

Dies würde auch eine beliebige Anzahl anderer Punkt-zu-Punkt-Netzwerkfehler umgehen und die App selbst bei Verbindungsproblemen weitaus robuster machen.

Ich weiß, dass unser Instinkt als Entwickler darin besteht, den bekannten Fehler zu beheben, aber manchmal ist es besser, auf ein System hinzuarbeiten, das trotz Fehlern funktioniert. Das heißt, es tut nie weh, Fehler und Probleme zu protokollieren und sie trotzdem zu beheben.

Matthew Brown alias Lord Matt
quelle
Eigentlich ist das kein schlechter Kommentar (obwohl er wahrscheinlich in einem Kommentar und nicht in einer Antwort enthalten sein sollte), und ich glaube nicht, dass ich nicht darüber nachgedacht habe ... Das Problem ist, dass der Client die Anfrage nicht erneut ausgeben kann. weil die Anfrage serverseitige Nebenwirkungen haben kann (z. B. ein zweites Mal Geld überweisen). Die Lösung hierfür besteht darin, dass der Client request_id's ausgibt und der Server eine Liste mit "welchen request_id's in den letzten 60 Sekunden zugestellt wurde" führt. Wenn ein Client eine 200-Antwort mit einem 0-Byte-Text erhält, gibt er die Anforderung mit derselben ID erneut aus, und der Server führt keine erneute Ausführung durch (nicht)
Nitzan Shaked,
(nicht) das Ganze nochmal. Das ist jedoch so hässlich, dass ich mich entschieden habe, es nicht zu implementieren.
Nitzan Shaked
Ich bin kaum ein Anfänger im Caching, aber es scheint mir: Senden Sie eine zufällige Zeichenfolge als Teil der Anfrage und zwischenspeichern Sie das Ergebnis. Wenn Sie die Anforderung mit derselben zufälligen Zeichenfolge erneut senden, rufen Sie natürlich das zwischengespeicherte Ergebnis ab (denselben Inhalt, dieselbe Quelle ...). Wenn Sie jedoch eine legitime neue Anforderung senden, haben Sie eine neue zufällige Zeichenfolge und somit keine zwischengespeicherte Ergebnis.
Narfanator