Diese Zeile sendet eine Websocket-Nachricht für jede Spalte, basierend auf der Gleichheit
Einige Spalten können große Text-Dumps oder JSON-Konfigurationen sein, daher sollten wir hier einige sinnvolle Filterfilter hinzufügen.
Der Name der Kanäle ist Wert in ETS, es kann sehr groß sein, sogar GBs.
Was diesen Fehler betrifft, denke ich, dass er mit dieser Überprüfung zusammenhängt:
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/server/lib/realtime/subscribers_notification.ex#L140 -L142
In der aktuellen Implementierung können Schlüssel nur bis zu 100 Symbole überprüft werden.
Schön danke @ abc3.
Ich sollte meinen eigenen Code überprüfen, bevor ich Probleme erstelle 😅
Ich werde einige Tests einrichten, um diesen Fehler zu beheben .
Ich habe mit Schlüssel 2500 Länge überprüft, alles in Ordnung. Ich werde gerne Fehler untersuchen, wenn Sie zeigen, wie man ihn reproduziert. :erröten:
Ich beginne mit einer neuen Kopie von Echtzeit (Commit 894f4bb89230
), erhalte die Deps für den Server ( mix deps.get
) und für das Beispiel des Knotens js ( yarn install
). Ich starte die Datenbank mit dem Beispiel node-js ( docker-compose up db
).
Ich starte den Server:
PORT=4000 \
HOSTNAME=localhost \
DB_USER=postgres \
DB_HOST=localhost \
DB_PASSWORD=postgres \
DB_NAME=postgres \
DB_PORT=5432 \
DB_PORT=5432 \
SLOT_NAME=TEST_SLOT \
mix phx.server
und das Node-js-Beispiel ( yarn run start
).
Ich füge einige Daten mit dem folgenden Skript ein:
import psycopg2
import time
def main():
conn = psycopg2.connect(
host="localhost",
database="postgres",
user="postgres",
password="postgres"
)
cur = conn.cursor()
name = 'name'
cur.execute("INSERT INTO users(name) VALUES(%s)", (name, ))
conn.commit()
main()
und ich kann die Daten sehen, die zum Beispiel des Knotens js gestreamt werden. Ich ändere die Stringlänge auf 4000 ( name = 'name' * 1000
) und es funktioniert. Ich erhöhe die Größe der Zeichenfolge schrittweise (10 zu 10) bis name = 'name' * 1000000
( 1_000_000
). Ich habe das Skript so geändert, dass zuerst eine große Zeichenfolge und danach eine kleine Zeichenfolge eingefügt wird. In diesem Fall empfängt node-js keine von beiden.
import psycopg2
import time
def main():
conn = psycopg2.connect(
host="localhost",
database="postgres",
user="postgres",
password="postgres"
)
cur = conn.cursor()
name = 'name' * 1000000
cur.execute("INSERT INTO users(name) VALUES(%s)", (name, ))
conn.commit()
name = 'name'
cur.execute("INSERT INTO users(name) VALUES(%s)", (name, ))
conn.commit()
main()
Ich glaube, das Problem liegt darin, dass die Kanäle nicht in der Lage sind, große Nutzlasten zu bewältigen.
Ein letztes Experiment besteht darin, die Replikationsverzögerung zu überprüfen. Ich benutze den folgenden Befehl:
select
slot_name, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS replicationSlotLag, active
from pg_replication_slots ;
Normalerweise ist das Ergebnis:
slot_name | replicationslotlag | active
-----------+--------------------+--------
test_slot | 56 bytes | t
(1 row)
Ich führe mein Python-Skript einmal aus und sehe, dass es wächst (jedes Mal, wenn ich das Skript ausführe, wächst es um ca. 40 KB). @ w3b6x9 hat mir gezeigt, dass diese Größe auf der Kundeninstanz etwa 20 MB beträgt. Nach einer Weile wird die Echtzeitinstanz vom Betriebssystem getötet (mit SIGKILL). Ich starte die Instanz neu und sie durchläuft langsam (sehr langsam) den Rückstand, wird jedoch regelmäßig beendet, da die Speichernutzung ständig zunimmt.
Zusammenfassend gibt es zwei sichtbare Probleme beim Einfügen großer Zeilen:
Meine Vermutung ist, dass es zwei Hauptprobleme gibt (vielleicht verwandt, vielleicht nicht):
Aber name = 'name' * 1000000
ist nur 40 MB groß: verwirrt:
In meinem Fall funktioniert es, ich werde mit @fracek Beispiel tiefer
Übrigens zeigt mein Test eine sehr große Latenz von ~ 10 Sekunden. Es ist zu viel für ein paar zehn MB.
export default async (req, res) => {
const values = Array(1000000).fill("name").join('')
const text = `INSERT INTO stress(value) VALUES('${values}') RETURNING id`
const q = await pool.query(text)
res.json(JSON.stringify(q.rows))
}
Vielen Dank für das Testen von https://github.com/supabase/realtime/pull/120
Wir haben dieses Verhalten in Prod gesehen und mussten daher die PR zurücksetzen. Wenn es nach dem Zusammenführen dieser PR immer noch langsam ist, müssen wir die Latenz auf jeden Fall beheben
Ich habe einen Fehler in meiner Berechnung gemacht: nicht 40, sondern 4 MB))
Testeinfügung durch diese INSERT INTO users(name) VALUES(repeat('name', 1000000))
Ich habe es gefunden!
Reaktionszeit mit Debug-Level und ohne:
Diese Zeilen werden einige Sekunden lang ausgeführt:
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/server/lib/realtime/replication.ex#L72 -L73
Ich kann das Update reproduzieren. Ereignisse werden immer noch nicht über Websockets weitergegeben, aber zumindest reagiert die Instanz nicht mehr. Gut gemacht!
Der Browser benötigt einige Zeit, um lange Zeichenfolgen zu dekodieren und zu rendern.
Es wird klarer, wenn Sie einige Änderungen am Beispiel vornehmen.
müssen dies entfernen:
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/examples/next-js/pages/index.js#L28
Ersetzen Sie das
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/examples/next-js/pages/index.js#L44 -L53
mit
messageReceived(channel, msg) {
console.log('got message')
}
und lesen Sie Nachrichten in der Browserkonsole
cur = conn.cursor ()
name = 'name' * 1000000
cur.execute ("INSERT INTO users (name) VALUES (% s)", (name,))
conn.commit ()
@fracek seltsam, dass dies Ihnen Probleme auf Ihrem Computer gab.
Ich habe es mir beim Debuggen angesehen. Ich habe 100 Zeilen in dieselbe Transaktion eingefügt, wobei jede Zeile den Wert 'name' * 1000000
(4 MB) hat.
Während der Replikation überprüfte ich beide Replikationsverzögerungen (blieb konstant bei etwa 5,3 MB):
und die Spülverzögerung:
_Dies ähnelt dem, was ich gestern beim Debuggen der Datenbankinstanz des Kunden gesehen habe. Ihre Replikationsverzögerung würde steigen und dann bei einer Verzögerung (12 MB, 16 MB, 20 MB usw.) konstant bleiben und eine Flush-Verzögerung zwischen 3 und 5 Minuten aufweisen.
Nachdem ich eine Weile gewartet hatte, bekam ich die Nachrichten. Jeder enthielt den 'name' * 1000000
(4 MB). Ich habe auch nachverfolgt, wie lange die Replikation gedauert hat (~ 30 Minuten), verglichen mit dem Senden von 100 Nachrichten mit jeweils 4 MB Wert (~ 90 Sekunden).
Ich habe jemanden mit dem Problem " Vom Phoenix-Kanal getrennt" gefunden, als ich
- Ich denke, Phoenix verwendet eine Zeitüberschreitung von etwa 60 Jahren, um einen Herzschlag oder eine Nachricht zu erhalten.
- Wenn das Senden einer einzelnen Nachricht länger dauert, glaubt Phoenix, dass die Pipe eingefroren oder überfordert ist oder so, und tötet sie.
Und ein anderer Benutzer, sasajuric
, bot die folgende Lösung an, die für ihn funktionierte:
- Komprimieren / Dekomprimieren der Nutzdaten außerhalb des Serializers
- Serialisieren / Deserialisieren von Nachrichten mit: erlang.term_to_binary und: erlang.binary_to_term
Im Moment nur etwas zu beachten, aber wenn wir feststellen, dass Nachrichten aufgrund hoher Frequenz und großer Nutzlast nicht gesendet werden, können wir als Erstes den neueren integrierten Serializer Phoenix.Socket.V2.JSONSerializer
( Wir verwenden derzeit V1
), bevor wir das tun, was sasajuric
vorgeschlagen hat.
es könnte daran liegen: # 120
Ja, wenn der Server realtime
immer wieder neu gestartet und dort fortgesetzt wird, wo er mit dem permanenten Replikationssteckplatz aufgehört hat, kann ich sehen, dass es immer länger dauert (wenn überhaupt, falls er nicht wieder funktioniert Geben Sie aufgrund von :undefined.handle_message/4
-Fehlern an, dass der Kunde Nachrichten erhalten soll.
Dies ist möglicherweise normal, wenn man bedenkt, dass der Kunde große Mengen an Updates gleichzeitig @kiwicopple, ich glaube, Sie haben dies irgendwo erwähnt). # 120 wurde zusammengeführt und wird in Kürze für den Kunden bereitgestellt, sodass wir nur die Überwachung fortsetzen müssen.
@ abc3 : Übrigens, dein Nextjs-Beispiel mit dem Diagramm der Routing-Zeit # 118 ist fantastisch und war beim Debuggen super hilfreich. Danke noch einmal!
Vielen Dank! Ich bin froh, dass mein Beitrag hilfreich ist: erröten:
Bitte teilen Sie Ihr Überwachungsergebnis mit, wenn auf dem Produkt erneut ein Fehler auftritt.
Die alte schnelle Lösung verlängert auch die Zeit https://github.com/supabase/realtime/issues/8#issuecomment -564551365
https://github.com/supabase/realtime/blob/af6344c7746e8a8af6a11a9b498721c1f97e339b/server/lib/realtime_web/channels/realtime_channel.ex#L21 -L24
Der obige Code und die Implementierung Realtime.SubscribersNotification.notify_subscribers
senden also mehrere Kopien der ähnlichen Daten.
Wenn beispielsweise eine Zeile in die Tabelle users
eingefügt wird, erhält der Client mit Abonnements für this.addChannel('realtime:*')
und this.addChannel('realtime:public:users')
4 Nachrichten.
Wenn eine Nachricht 4 MB groß ist, sendet der Server 16 MB
Hilfreichster Kommentar
Ich habe es gefunden!
Reaktionszeit mit Debug-Level und ohne:
Diese Zeilen werden einige Sekunden lang ausgeführt:
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/server/lib/realtime/replication.ex#L72 -L73