Sentry-javascript: @ fuites de mémoire sentinelle / nœud sur le nœud 10.13.0

Créé le 22 nov. 2018  ·  50Commentaires  ·  Source: getsentry/sentry-javascript

Package + Version

  • [x] @sentry/browser
  • [x] @sentry/node

Version:

4.3.4

La description

J'ai essayé d'intégrer sentry dans le projet ah next.js. Je l'ai essayé en utilisant ce modèle https://github.com/sheerun/next.js/tree/with-sentry-fix/examples/with-sentry et j'ai découvert ce qui semble être une fuite de mémoire dans la sentinelle. Si vous consultez ce projet et ajoutez memwatch

if (!dev) {
  memwatch.on("leak", info => {
    console.log("memwatch::leak");
    console.error(info);
  });

  memwatch.on("stats", stats => {
    console.log("memwatch::stats");
    console.error(Util.inspect(stats, true, null));
  });
}

et bombarder le serveur de requêtes, j'ai demandé les ressources suivantes:

    "/",
    "/_next/static/r1zovjaZ1TujaA0hJEp91/pages/_app.js",
    "/_next/static/r1zovjaZ1TujaA0hJEp91/pages/_error.js",
    "/_next/static/r1zovjaZ1TujaA0hJEp91/pages/index.js",
    "/_next/static/runtime/main-1eaa6d1d0c8e7d048efd.js",
    "/_next/static/chunks/commons.b34d260fee0c4a698139.js",
    "/_next/static/runtime/webpack-42652fa8b82c329c0559.js"

Avec cela, l'utilisation de la mémoire augmente sans cesse pour moi. Dès que je supprime la requête et errorHandler de server.js, la fuite de mémoire s'arrête. Donc, il semble être lié à ces 2

In Progress Bug

Commentaire le plus utile

@michalkvasnicak J'ai enquêté sur cela et ce n'est pas directement causé par Sentry.

Notre transport @sentry/node a une dépendance sur https-proxy-agent , qui a une dépendance sur agent-base qui nécessite son fichier patch-core.js _et_ c'est ce qui crée une fuite: shrug:

https://github.com/TooTallNate/node-agent-base/issues/22

Vous pouvez facilement le vérifier en copiant son contenu dans votre test et en l'exécutant avec des statistiques de tas:

const url = require("url");
const https = require("https");

https.request = (function(request) {
  return function(_options, cb) {
    let options;
    if (typeof _options === "string") {
      options = url.parse(_options);
    } else {
      options = Object.assign({}, _options);
    }
    if (null == options.port) {
      options.port = 443;
    }
    options.secureEndpoint = true;
    return request.call(https, options, cb);
  };
})(https.request);

https.get = function(options, cb) {
  const req = https.request(options, cb);
  req.end();
  return req;
};

it("works", () => {
  expect(true).toBe(true);
});

Nous devrons probablement le bifurquer ou écrire une solution de contournement.

Tous les 50 commentaires

@abraxxas pouvez-vous m'aider à reproduire ce problème? Comment déclenchez-vous exactement ce memleak?
J'ai essayé votre description sans succès, elle produit juste des événements de statistiques, jamais une fuite.

@kamilogorek Merci pour votre réponse. Ce que j'ai fait était le suivant. J'ai vérifié cet exemple https://github.com/sheerun/next.js/tree/with-sentry-fix/examples/with-sentry et ajouté memwatch au server.js

if (!dev) {
  memwatch.on("leak", info => {
    console.log("memwatch::leak");
    console.error(info);
  });

  memwatch.on("stats", stats => {
    console.log("memwatch::stats");
    console.error(Util.inspect(stats, true, null));
  });
}

puis j'ai exécuté l'exemple en utilisant le nœud 10.x (avec 8.xi observé aucun problème de mémoire) et demandé les ressources suivantes en utilisant notre suite de tests gatling:

    "/",
    "/_next/static/r1zovjaZ1TujaA0hJEp91/pages/_app.js",
    "/_next/static/r1zovjaZ1TujaA0hJEp91/pages/_error.js",
    "/_next/static/r1zovjaZ1TujaA0hJEp91/pages/index.js",
    "/_next/static/runtime/main-1eaa6d1d0c8e7d048efd.js",
    "/_next/static/chunks/commons.b34d260fee0c4a698139.js",
    "/_next/static/runtime/webpack-42652fa8b82c329c0559.js"

(notez que les hachages peuvent changer pour vous)

mais vous devriez pouvoir obtenir les mêmes résultats avec cette approche très simple https://www.simonholywell.com/post/2015/06/parallel-benchmark-many-urls-with-apachebench/

après quelques milliers de requêtes, notre utilisation de la mémoire était de près de 1 Go et jamais réduite même au ralenti. Dès que je supprime la requête et errorHandler de server.js, la fuite de mémoire s'arrête. Il semble donc être connecté à ces 2. Peut-être avez-vous eu trop peu de requêtes ou utilisé le nœud 8.x?

@abraxxas confirmé. Le nœud 10 + ~ 300req pour chaque ressource fait le travail. Va enquêter plus loin. Merci!

@kamilogorek intersting, mais si vous regardez la taille de votre
reproduction vous verrez que ça reste autour de 20mb sans les handlers
mais augmente rapidement avec eux.

Je pense que l'avertissement de fuite de mémoire sans les gestionnaires se produit simplement parce que
en raison des demandes, il y a une augmentation constante de la mémoire.

Il y a encore une grande différence d'utilisation de la mémoire entre la version avec
sentinelle et sans.

Le jeu.29 novembre 2018, 12:45 Kamil Ogórek < [email protected] a écrit:

@abraxxas https://github.com/abraxxas Je l'ai reproduit avec succès,
cependant, il semble que le serveur fuit toujours des objets de requête tout seul,
même sans les gestionnaires Sentry.

https://streamable.com/bad9j

Le taux de croissance est un peu plus élevé, car nous attachons un domaine et notre propre portée
s'opposer à la demande, mais elle serait utilisée parallèlement à la demande par GC.

-
Vous recevez cela parce que vous avez été mentionné.
Répondez directement à cet e-mail, affichez-le sur GitHub
https://github.com/getsentry/sentry-javascript/issues/1762#issuecomment-442804709 ,
ou couper le fil
https://github.com/notifications/unsubscribe-auth/AIbrNlgPjPd5Jra1aahR-Dthf7XvbCexks5uz8jjgaJpZM4YvOA2
.

@abraxxas ignore mon commentaire précédent (celui que j'ai supprimé), c'est totalement de notre côté :)

Cela semble être le problème dans le noyau de Node lui-même. Veuillez vous référer à ce commentaire https://github.com/getsentry/sentry-javascript/issues/1762#issuecomment -444126990

@kamilogorek Avez-vous déjà eu l'occasion d'examiner cela? Cela nous cause une énorme fuite de mémoire. Après avoir examiné le tas, cette ligne semble être à l'origine du problème:

https://github.com/getsentry/sentry-javascript/blob/c27e1e32d88cc03c8474fcb1e12d5c9a2055a150/packages/node/src/handlers.ts#L233

L'inspecteur a montré des milliers d'entrées dans la liste eventProcessors
image

Je n'ai pas de contexte sur la façon dont les choses sont architecturées, mais nous avons remarqué que les demandes ne sont pas correctement étendues et donnent les mauvaises métadonnées (voir # 1773) donc il semble que tout est géré dans l'état global et pas nettoyé quand un la demande se termine

@abraxxas @tpbowden il y a un problème avec le module de domaine qui fuit dans le noyau de Node lui-même. Nous continuerons à le surveiller et essayerons de trouver une solution temporaire avant qu'elle ne soit corrigée dans le noyau. Problème lié: https://github.com/nodejs/node/issues/23862

@kamilogorek avez-vous des idées pour une solution de contournement ou une solution temporaire pour cela? Les progrès sur le problème du nœud semblent assez lents

Nous utilisons actuellement PM2 pour redémarrer les processus Node.js lorsque la mémoire atteint un certain seuil. https://pm2.io/doc/en/runtime/features/memory-limit/#max -memory-threshold-auto-reload

Utilisation du laboratoire pour les tests unitaires. Les fuites sont toujours présentes. Je sais que les fuites peuvent être pénibles à déboguer. Y a-t-il un ETA pour un correctif?

1 tests terminés
Durée du test: 1832 ms
Les fuites suivantes ont été détectées: __ extend, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __exportStar, __values, __read, __spread, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeDelegator, __asyncValues, __makeimportStarbault

npm ERR! code ELIFECYCLE
npm ERR! erreur no 1
npm ERR! test [email protected] : lab build/test
npm ERR! Statut de sortie 1
npm ERR!
npm ERR! Échec du script de test
npm ERR! Ce n'est probablement pas un problème avec npm. Il y a probablement une sortie de journalisation supplémentaire ci-dessus.

npm ERR! Un journal complet de cette exécution peut être trouvé dans:
npm ERR! /Users/sunknudsen/.npm/_logs/2019-02-13T14_41_28_595Z-debug.log

@sunknudsen Le problème est en fait résolu dans NodeJS, voir https://github.com/nodejs/node/issues/23862 et https://github.com/nodejs/node/pull/25993. Nous devons probablement attendre une sortie.

@garthenweb Cela @sentry/node raison de la façon dont le package a été développé? L'un des projets que je développe sur hapi (et repose sur de nombreuses autres dépendances) ne produit pas de fuites (au moins, ils ne sont pas détectés par le laboratoire ).

@sunknudsen Plus ou moins. C'est la combinaison du package de domaine (obsolète) et des promesses pour autant que j'ai compris. Voir https://github.com/getsentry/sentry-javascript/blob/master/packages/node/src/handlers.ts#L233

Dans mon cas, je viens de supprimer les middlewares sentinelles de mon serveur (express) pour le réparer.

Ce n'est pas un problème dans le nœud mais dans Sentry car la désactivation des gestionnaires Sentry résout le problème;

image

@MartijnHols Nous travaillons actuellement sur une version majeure qui devrait réduire considérablement l'empreinte mémoire de notre SDK. Si vous vous sentez aventureux, vous pouvez l'essayer https://github.com/getsentry/sentry-javascript/pull/1919

@HazAT Merci, je l'ai installé en production hier soir (à 23h10 dans le graphique) et

image

@MartijnHols Merci d'avoir essayé!

Deux choses à garder à l'esprit, le correctif de fuite de mémoire pour les domaines dans le nœud n'a atterri dans le nœud que récemment dans 11.10 .
De plus, nous avons dû annuler la publication de 5.0.0-beta1 car il était étiqueté par erreur comme latest , 5.0.0-rc.1 est maintenant la dernière version de next .
Veuillez essayer 5.0.0-rc.1 nous avons fait un petit changement dans la mise en file d'attente des événements qui devrait améliorer la charge / mémoire de beaucoup.

La mise à jour vers le nœud 11.12 semble avoir stabilisé l'utilisation de la mémoire et du processeur. Il semble qu'il n'y ait pas de différence perceptible maintenant dans l'utilisation des ressources lorsque l'on compare cela à la désactivation des gestionnaires, peut-être même légèrement mieux. Il semble également attraper des erreurs avec toutes les informations dont j'ai besoin (il pourrait avoir plus de "fil d'Ariane" de la console, ce qui est bien). Je ne sais pas quoi d'autre je peux vérifier pour 5.0.0. Je vous ferai savoir si je rencontre des problèmes, mais probablement pas.

LGTM. Merci!

Je serais ravi d'essayer cela aussi. @HazAT savez-vous si le correctif dans 11.10 est déjà rétroporté vers la version LTS active 10.x ?

@adriaanmeuris J'ai lu que quelqu'un avait demandé s'il serait rétroporté vers 10.x , sans savoir s'il le ferait.
réf: https://github.com/nodejs/node/pull/25993#issuecomment -463957701

J'ai résolu le problème de création manuelle du client et de l'étendue dans un middleware express

J'ai créé un fichier services/sentry qui exporte une fonction

import {
  NodeClient as SentryClient, Hub, Integrations, Scope,
} from '@sentry/node';
import config from 'config';

const sentryClient = new SentryClient({
  ...config.sentry,
  frameContextLines: 0,
  integrations: [new Integrations.RewriteFrames()],
});

export default () => {
  const scope = new Scope();
  const client = new Hub(sentryClient, scope);
  return Object.freeze({ client, scope });
};

et dans un middleware, je sauvegarde le client / portée sentinelle sur l'objet de requête comme ceci

app.use((req, res, next) => {
  req.sentry = getSentry();
  req.sentry.scope.setTag('requestId', req.requestId);
  req.sentry.scope.setExtra('More info', 'XXXXXX');
  next();
});
....
// and use the express error handler
app.use((err, req, res, next) => {
  const code = err.code || 500;
  res.status(code).json({
    code,
    error: err.message,
  });
  if (code >= 500) {
    req.sentry.client.captureException(err);
  }
  next();
});

Avec cela semble que la mémoire fuit son fixe

image

@couds Cette implémentation est vraiment sympa, il y a une chose que vous devez considérer.

Pour chaque demande, vous obtiendrez un nouveau client et nous ne détectons aucune erreur globale / fil d'Ariane automatique (ou quoi que ce soit que font les intégrations par défaut).

@HazAT Merci, je le sais, mais c'est un petit prix à payer. pour résoudre cette énorme fuite de mémoire, mais pour minimiser cette perte, j'ai fait quelques choses.

  1. J'essaie de gérer toutes les exceptions que je préfère ne pas me fier aux événements unCoughException / promise.
  2. Pour les fils d'Ariane, je les ajoute manuellement tant que j'ai accès à l'objet req.
  3. Téléchargez les sourcemaps sur Sentry avec @sentry/webpack-plugin
  4. Ajoutez des balises / extra sur chaque demande qui aideront à identifier le problème

Une dernière chose, j'ai oublié de coller une autre balise que j'ajoute qui est la transaction (pour simuler le gestionnaire par défaut)

req.sentry.scope.setTag('transaction', `${req.method}|${req.route ? req.route.path : req.path}`);

J'espère que cela aidera quelqu'un, Sentry c'est un très bon outil et j'ai fait ce que j'ai pu pour éviter de le retirer de ma pile.

@HazAT @kamilogorek, nous voyons toujours l'énorme croissance de la mémoire sur [email protected] et [email protected] - êtes-vous certain que ce patch nodejs l'a corrigé?

@tpbowden Pouvez-vous s'il vous plaît fournir une repro OU au moins dire à quoi ressemble votre code?
De plus, utilisez-vous d'autres plugins liés à Sentry?

@HazAT J'ai essayé de reproduire mais cela est causé par un serveur assez complexe avec beaucoup de middleware (rendu côté serveur React). Avec le middleware Sentry installé, nous enregistrons une énorme croissance de la mémoire (sous une charge importante, elle peut augmenter d'environ 10 Mo / seconde). Lorsque nous supprimons le middleware Sentry.Handlers.requestHandler() de notre serveur, la mémoire est constante à ~ 200 Mo

Nous n'utilisons aucun plug-in, seulement @sentry/node . Pouvez-vous penser à quelque chose que je pourrais essayer pour aider à reproduire cela?

@HazAT On dirait que cela est lié au regroupement de Sentry à l'aide de webpack sur le serveur - ne se produit que lorsque l'application a été créée par Webpack. L'ajout de @sentry/node à externals a résolu notre problème de mémoire. Une idée de pourquoi cela se produit?

La fuite est toujours reproductible sur Node 11.14.0 en utilisant @ sentry / node 5.1.0

Pour nous, la fuite est causée par l'interaction entre @ sentry / node et i18next-express-middleware. Notre application express ressemble à https://github.com/i18next/react-i18next/blob/master/example/razzle-ssr/src/server.js.

Si nous mettons .use(Sentry.Handlers.requestHandler()); au-dessus de .use(i18nextMiddleware.handle(i18n)) nous obtenons une fuite de mémoire. Si nous mettons une sentinelle en dessous, nous n'obtenons pas de fuite.

Nous avons le meme probleme. Je l'ai essayé avec node 10.15.3 et 11.14.0 . Voici un référentiel minimal pour reproduire le problème https://github.com/michalkvasnicak/sentry-memory-leak-reproduction. Exécutez simplement yarn test ou yarn test:watch qui rapportera l'utilisation du tas. Cela augmente toujours.

yarn test
image

yarn test:watch

image

image

@michalkvasnicak Merci d'avoir pris le temps de créer un exemple, j'ai quelques questions tho.

Vous ne testez vraiment rien concernant le SDK

const Sentry = require('@sentry/node');

it('works', () => {
  expect(true).toBe(true);
});

Vous avez besoin du package mais c'est tout.
Je ne suis pas sûr car je n'ai pas encore d'expérience avec jest cours d'exécution de tests de fuite, mais que peut-il fuir en demandant simplement le package?
Pas sûr, il me manque peut-être quelque chose, mais je m'attendrais à ce que la mémoire augmente lors du chargement d'un nouveau paquet.

En ce qui concerne ce qui peut fuir, nous créons des variables globales mais nous en avons besoin pour suivre l'état.

@HazAT oui c'est étrange mais c'est suffisant pour faire fuir les tests, jest échouera avec une fuite détectée. Nous avons le même problème dans notre base de code où le simple fait de commenter l'importation de @sentry/node résolu le problème de fuite.

@michalkvasnicak J'ai enquêté sur cela et ce n'est pas directement causé par Sentry.

Notre transport @sentry/node a une dépendance sur https-proxy-agent , qui a une dépendance sur agent-base qui nécessite son fichier patch-core.js _et_ c'est ce qui crée une fuite: shrug:

https://github.com/TooTallNate/node-agent-base/issues/22

Vous pouvez facilement le vérifier en copiant son contenu dans votre test et en l'exécutant avec des statistiques de tas:

const url = require("url");
const https = require("https");

https.request = (function(request) {
  return function(_options, cb) {
    let options;
    if (typeof _options === "string") {
      options = url.parse(_options);
    } else {
      options = Object.assign({}, _options);
    }
    if (null == options.port) {
      options.port = 443;
    }
    options.secureEndpoint = true;
    return request.call(https, options, cb);
  };
})(https.request);

https.get = function(options, cb) {
  const req = https.request(options, cb);
  req.end();
  return req;
};

it("works", () => {
  expect(true).toBe(true);
});

Nous devrons probablement le bifurquer ou écrire une solution de contournement.

une solution de contournement pour celui-ci?

https://nodejs.org/en/blog/release/v10.16.0/

corrigé des fuites de mémoire, quelqu'un peut-il le tester?

J'ai le même problème sur le nœud 11.10, mais il semble être corrigé sur le nœud 12.3.1

Il y a un PR ouvert pour le problème agent-base : https://github.com/TooTallNate/node-agent-base/pull/25

Nous pouvons soit bifurquer ceci et remplacer la dépendance dans les résolutions Yarn, soit trouver un moyen de la fusionner.

Cela pourrait être un peu hors sujet, mais pourrait également provoquer des fuites indiquant qu'il n'y a pas de nettoyage de domaine dans Handlers.ts, uniquement domain.create?
L'article moyen ou SO suggère qu'il devrait y avoir removeListeners et domain.exit. Mais je n'ai pas trouvé de réponse définitive à cela.

MISE À JOUR: Maintenant, je vois que les gestionnaires utilisent domain.run qui appelle en interne domain.exit. La suppression des auditeurs / émetteurs peut faire la différence, mais honnêtement, je n'en ai aucune idée.

J'ai mis à niveau vers Node 12.4.0 et je constate toujours un mauvais comportement qui semble être lié à Sentry.

Voici quelques essais de médecins de clinique de nœud, réalisés avec l'option --autocannon pendant 90 secondes.

Cela ne semble vraiment fuit que lorsque les gestionnaires de requêtes sont en place. Si vous regardez le bas des creux GC dans la course sans les gestionnaires, ils sont tous à peu près au même niveau (65-70 Mo), où la course avec les gestionnaires semble grimper d'environ 5 Mo avec chaque cycle GC.

@ tstirrat15 ce correctif n'a pas encore été publié, c'est probablement pourquoi vous rencontrez toujours ce problème. Pouvez-vous essayer le dernier maître si c'est une option?

@ tstirrat15 5.4.2 avec le correctif inclus a été publié, essayez-le :)

Voici une autre exécution avec la v5.4.2 en place. Cela semble encore un peu fuyant ...

GC fonctionne toujours correctement et restaure la mémoire à la ligne de base. L'augmentation de l'utilisation de la mémoire est causée par le fil d'Ariane collecté à partir des événements et de la file d'attente d'événements, mais cela s'arrêtera à 100 fil d'Ariane et n'augmentera pas davantage. Ce serait bien si nous pouvions voir un vidage d'environ 15 à 30 minutes et voir si la mémoire de pointe s'arrête à un moment donné.

Hmm ... ça sonne bien. Je vais faire passer ce PR à la production et voir si le comportement change. Merci!

Il semble que cela soit lié au regroupement de Sentry à l'aide de webpack sur le serveur - ne se produit que lorsque l'application a été créée par Webpack. L'ajout de @ sentry / node aux externes a résolu notre problème de mémoire. Une idée de pourquoi cela se produit?

@tpbowden Vous avez raison à ce sujet, j'ai le même problème. J'exécutais le SDK v5.15.0 et le nœud v12.3.1, tous deux censés inclure tous les correctifs requis mentionnés ici.

Je regroupe toutes les dépendances au sein de mon bundle serveur avec webpack. De cette façon, je peux expédier une image docker sans node_modules, mais quelque chose gâche le SDK sentry et il perd de la mémoire lorsqu'il est regroupé de cette façon.

Cela peut être un bug causé par un processus d'optimisation. Ma supposition sauvage est que c'est probablement terser. Une certaine optimisation gâche probablement l'utilisation du module de domaine, et la fermeture du rappel passé à scope.addEventProcessor n'est plus ramassée, donc chaque demande faite fuit une bonne quantité de mémoire.

J'utilise aussi razzle.js qui est un peu en retard sur les versions webpack / terser, c'est peut-être déjà corrigé.

Cela ne semble plus être un bug du côté des sentinelles. Je vais continuer à enquêter sur cela et ouvrir un problème le cas échéant et maintenir ce fil à jour.

Cela ne semble plus être un bug du côté des sentinelles. Je vais continuer à enquêter sur cela et ouvrir un problème le cas échéant et maintenir ce fil à jour.

Tenez-nous au courant, merci!

@kamilogorek Pourriez-vous m'indiquer où dans le code le rappel des processeurs d'événements ajouté au tableau _eventProcessors dans l'instance Scope est supprimé? Je ne peux pas le trouver. Il semble que toutes les requêtes ajoutent un rappel de processeur d'événements à ce tableau et qu'elles ne sont jamais supprimées. Si je savais comment ils sont censés être supprimés, cela pourrait m'aider à mieux comprendre le bogue.

Screen Shot 2020-03-23 at 15 49 03

Ou peut-être est-ce la portée entière qui est censée être unique et collectée pour chaque demande? Il semble que chaque requête obtient la même instance de portée 🤔

Ha! Je pense avoir trouvé quelque chose.

Nous utilisons dynamicRequire:
https://github.com/getsentry/sentry-javascript/blob/fd26d9fa273002502706b03fc1a9a46864cd8440/packages/hub/src/hub.ts#L465-L468

Mais quand j'entre dans le code dynamicRequire:
https://github.com/getsentry/sentry-javascript/blob/fd26d9fa273002502706b03fc1a9a46864cd8440/packages/utils/src/misc.ts#L28-L31

require n'est pas défini sur mod 🤯

Il entre donc dans le bloc catch de la fonction getHubFromActiveDomain et utilise à la place getHubFromCarrier() !

Puisque dans ma configuration _everyting_ est fourni par webpack, il y a probablement des hypothèses faites sur l'objet mod qui est cassé par webpack. Avez-vous une idée de la façon dont cela pourrait être résolu? 🤔

résumer

Nous utilisons dynamicRequire:
Screen Shot 2020-03-24 at 12 05 04

mod.require n'est pas défini:
Screen Shot 2020-03-24 at 12 20 01

À quoi ressemble l'objet mod:
Screen Shot 2020-03-24 at 12 20 38

Nous finissons par utiliser getHubFromCarrier:
Screen Shot 2020-03-24 at 12 21 22

J'ai patché manuellement le module Hub directement dans mon dossier node_modules. J'ai supprimé la ligne en utilisant le dynamicRequire et j'ai juste ajouté import domain from 'domain'; en haut du fichier et ... cela fonctionne maintenant parfaitement! Plus de fuites! 🎉

Peut-être que le hack dynamicRequire était nécessaire avant, mais n'est plus nécessaire avec les nouvelles versions de webpack? 🤔

J'ai également essayé de remplacer:

const domain = dynamicRequire(module, 'domain');

avec:

const domain = require('domain');

et cela fonctionne aussi très bien. Je ne sais pas laquelle de ces deux solutions vous préférez.

Voulez-vous que j'ouvre un PR avec ce correctif?

Cette page vous a été utile?
0 / 5 - 0 notes