Cette ligne envoie un message websocket pour chaque colonne, basé sur l'égalité
Certaines colonnes peuvent être d'énormes vidages de texte ou une configuration JSON, nous devrions donc ajouter des filtres de filtres sensibles ici.
Le nom des canaux a de la valeur dans ETS, il peut être vraiment énorme, même en Go.
Quant à ce bug, je pense qu'il est lié à cette vérification:
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/server/lib/realtime/subscribers_notification.ex#L140 -L142
Dans la mise en œuvre actuelle, les clés ne peuvent être vérifiées que jusqu'à 100 symboles.
Bien merci @ abc3.
Je devrais vérifier mon propre code avant de créer des problèmes 😅
Je vais mettre en place des tests pour faire échouer cet échec - @fracek a confirmé qu'il s'agissait d'un problème, il vaut donc la peine de déterminer où se produit l'échec.
J'ai vérifié avec la longueur de la clé 2500, tout va bien. Je suis heureux d'étudier le bogue si vous montrez comment le reproduire. :rougir:
Je commence avec une nouvelle copie de realtime (commit 894f4bb89230
), récupère les deps pour le serveur ( mix deps.get
) et pour l'exemple node js ( yarn install
). Je démarre la base de données à partir de l'exemple node-js ( docker-compose up db
).
Je lance le serveur:
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
et l'exemple node-js ( yarn run start
).
J'insère quelques données avec le script suivant:
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()
et je peux voir les données diffusées dans l'exemple node-js. Je change la longueur de la chaîne à 4000 ( name = 'name' * 1000
) et cela fonctionne. J'augmente progressivement la taille de la chaîne (10 sur 10) jusqu'à name = 'name' * 1000000
( 1_000_000
). J'ai changé le script pour insérer d'abord une grande chaîne et après cela une petite chaîne, dans ce cas, node-js ne reçoit ni l'un ni l'autre.
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()
Je pense que le problème vient du fait que les canaux ne sont pas capables de gérer de grosses charges utiles.
Une dernière expérience consiste à vérifier le retard de réplication. J'utilise la commande suivante:
select
slot_name, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS replicationSlotLag, active
from pg_replication_slots ;
Normalement, le résultat est:
slot_name | replicationslotlag | active
-----------+--------------------+--------
test_slot | 56 bytes | t
(1 row)
J'exécute mon script python une fois et je le vois grandir (chaque fois que j'exécute le script, il augmente d'environ 40 Ko). @ w3b6x9 m'a montré que sur l'instance client, cette taille est d'environ 20 Mo. Après un certain temps, l'instance en temps réel est tuée par le système d'exploitation (avec SIGKILL). Je redémarre l'instance et elle passe lentement (très lentement) dans le backlog, mais elle est régulièrement tuée car l'utilisation de la mémoire ne cesse d'augmenter.
Pour récapituler, il y a deux problèmes visibles lors de l'insertion de grandes lignes:
Ma supposition éclairée est qu'il y a deux problèmes principaux (peut-être liés, peut-être pas):
Mais name = 'name' * 1000000
ne fait que 40 Mo: confus:
Dans mon cas, cela fonctionne, je vais plonger plus profondément avec l'exemple @fracek .
Btw, mon test montre une très grande latence de ~ 10sec. C'est trop pour livrer quelques dizaines de Mo.
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))
}
merci d'avoir testé @ abc3 - cela pourrait être dû à ceci: https://github.com/supabase/realtime/pull/120
Nous voyions ce comportement dans Prod, nous avons donc dû annuler le PR. Si c'est encore lent après la fusion de ce PR, nous devons absolument corriger la latence
J'ai fait une erreur dans mon calcul: pas 40 mais 4 Mo))
test d'insertion à travers ce INSERT INTO users(name) VALUES(repeat('name', 1000000))
J'ai trouvé ça!
Temps de réponse avec niveau de débogage et sans:
Ces lignes exécutent quelques secondes:
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/server/lib/realtime/replication.ex#L72 -L73
Je peux reproduire le correctif. Les événements ne sont toujours pas propagés via les Websockets, mais au moins l'instance ne cesse de répondre. Bon travail!
Le navigateur a besoin de temps pour décoder et restituer de longues chaînes.
Ce sera plus clair si vous apportez des modifications à l'exemple.
besoin de supprimer ceci:
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/examples/next-js/pages/index.js#L28
remplacer ça
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/examples/next-js/pages/index.js#L44 -L53
avec
messageReceived(channel, msg) {
console.log('got message')
}
et lire les messages dans la console du navigateur
cur = conn.cursor ()
name = 'nom' * 1000000
cur.execute ("INSERT INTO users (name) VALUES (% s)", (name,))
conn.commit ()
@fracek bizarre que cela vous
Je suis allé le vérifier pendant le débogage. J'ai inséré 100 lignes, chaque ligne ayant la valeur 'name' * 1000000
(4 Mo), dans la même transaction.
Au fur et à mesure que la réplication était en cours, j'ai vérifié à la fois le décalage de réplication (est resté cohérent à environ 5,3 Mo):
et le décalage de rinçage:
_Ceci est similaire à ce que j'ai vu lors du débogage de l'instance de base de données du client hier. Leur délai de réplication grimperait puis resterait cohérent avec un décalage (12 Mo, 16 Mo, 20 Mo, etc.) et aurait un décalage entre 3 et 5 minutes.
Après avoir attendu assez longtemps, j'ai reçu les messages. Chacun contenait la 'name' * 1000000
(4 Mo). J'ai également suivi le temps de réplication (~ 30 min.) Par rapport à l'envoi de 100 messages portant chacun la valeur de 4 Mo (~ 90 secondes).
J'ai trouvé quelqu'un avec le problème Déconnecté du canal Phoenix lors de l'envoi de plusieurs messages volumineux dans un court laps de temps . Un utilisateur a mentionné:
- Je pense que Phoenix utilise un délai d'attente d'environ 60 ans pour recevoir un message de battement de cœur ou plus.
- Si cela prend plus de temps pour envoyer un seul message, alors Phoenix pense que la pipe est gelée, débordée ou quelque chose du genre, alors elle la tue.
Et un autre utilisateur, sasajuric
, a proposé la solution suivante qui a fonctionné pour lui:
- compression / décompression de la charge utile en dehors du sérialiseur
- sérialisation / désérialisation des messages à l'aide de: erlang.term_to_binary et: erlang.binary_to_term
Pour l'instant, juste quelque chose à garder à l'esprit, mais si nous découvrons que les messages ne sont pas envoyés en raison d'une fréquence élevée et d'une charge utile énorme, la première chose que nous pouvons faire est d'utiliser le nouveau sérialiseur intégré Phoenix.Socket.V2.JSONSerializer
( nous utilisons actuellement V1
) avant de faire ce que sasajuric
suggéré.
cela pourrait être dû à ceci: # 120
Oui, si le serveur realtime
continuait de redémarrer et de reprendre là où il s'était arrêté avec l'emplacement de réplication permanent, je peux voir que cela prend de plus en plus de temps (si jamais dans le cas où il ne pourrait pas revenir à un fonctionnement état dû à des erreurs :undefined.handle_message/4
) pour que le client reçoive des messages.
Cela pourrait être normal étant donné que le client effectue d'énormes décharges de mises à jour à la fois ( @kiwicopple, je pense que vous l'avez mentionné quelque part). # 120 a été fusionné et sera déployé pour le client prochainement, nous devrons donc continuer à surveiller.
@ abc3 : btw, votre exemple Nextjs avec le graphique du temps de routage # 118 est génial et a été très utile lorsque je déboguais. Merci encore!
Merci! Je suis heureux que ma contribution soit utile: rougissez:
S'il vous plaît, partagez votre résultat de surveillance si quelque chose ne va pas sur la production
L'ancienne solution rapide ajoute également du temps 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
Ainsi, le code ci-dessus et l'implémentation Realtime.SubscribersNotification.notify_subscribers
enverront plusieurs copies des données similaires.
Par exemple, lors de l'insertion d'une ligne dans la table users
le client avec des abonnements à this.addChannel('realtime:*')
et this.addChannel('realtime:public:users')
recevra 4 messages.
Si un message 4 Mo, le serveur enverra 16 Mo
Commentaire le plus utile
J'ai trouvé ça!
Temps de réponse avec niveau de débogage et sans:
Ces lignes exécutent quelques secondes:
https://github.com/supabase/realtime/blob/894f4bb8923017467c78803711d8adbef8c090fe/server/lib/realtime/replication.ex#L72 -L73