2017-09-15 7 views
3

Je développe une application qui se connecte à un emplacement de réplication logique, pour consommer les événements WAL. Ces événements WAL sont ensuite transmis à un courtier MQ. Cela fonctionne très bien, mais j'ai remarqué que je manque de mémoire après un certain temps. J'ai réussi à minimiser le problème au code responsable de l'obtention des événements WAL. Il se produit avec le code suivant:Postgres/JDBC/Réplication logique - problèmes de mémoire

final Properties properties = new Properties(); 

PGProperty.USER.set(properties, "user"); 
PGProperty.PASSWORD.set(properties, "password"); 
PGProperty.ASSUME_MIN_SERVER_VERSION.set(properties, "9.4"); 
PGProperty.REPLICATION.set(properties, "database"); 
PGProperty.PREFER_QUERY_MODE.set(properties, "simple"); 

while (true) { 
    Connection   connection = null; 
    PGConnection  PGConnection = null; 
    PGReplicationStream stream  = null; 

    try { 
     connection = DriverManager.getConnection("jdbc:postgresql://localhost:5432/db", properties); 
     PGConnection = connection.unwrap(PGConnection.class); 
     stream = PGConnection.getReplicationAPI().replicationStream().logical().withSlotName("slot").start(); 

     while (true) { 
      final ByteBuffer buffer = stream.read(); 

      // ... logic here ... (disabled during memory test) 

      stream.setAppliedLSN(stream.getLastReceiveLSN()); 
      stream.setFlushedLSN(stream.getLastReceiveLSN()); 
     } 
    } catch (final SQLException e1) { 
     Logger.getLogger(getClass()).error(e1); 

     if (stream != null) { 
      try { 
       stream.close(); 
      } catch (final SQLException e2) { 
       Logger.getLogger(getClass()).error(e2); 
      } 
     } 
     if (connection != null) { 
      try { 
       connection.close(); 
      } catch (final SQLException e2) { 
       Logger.getLogger(getClass()).error(e2); 
      } 
     } 
    } 
} 

Je commentais la logique pour analyser le message et le transmet au courtier MQ, comme de mémoire se produit également sans cela.

J'ai essayé aussi de modifier cet exemple en utilisant la méthode de vote readPending() au lieu de la méthode de blocage read() (comme indiqué à https://jdbc.postgresql.org/documentation/head/replication.html), mais le problème reste.

J'ai également remarqué qu'au bout d'un certain temps, l'application est utilisée à 100% du processeur. Cela doit être dû aux bibliothèques sous-jacentes, car le read() continue de traiter normalement à ce moment (c'est-à-dire, il traite chaque événement WAL de manière séquentielle).

Au cours de ces tests, j'exécute les requêtes INSERT et UPDATE, à un faible débit.

J'utilise la dépendance suivante:

<dependency> 
    <groupId>org.postgresql</groupId> 
    <artifactId>postgresql</artifactId> 
    <version>42.1.4</version> 
</dependency> 

L'application fonctionne comme une guerre dans un récipient Tomcat8.

Une idée de ce qui se passe?

mise à jour 1

j'ai compris ce qui se passe, mais ne peut pas l'expliquer jusqu'à présent. Je vais aller dans les détails.

Toutes les 10 secondes, je fais des requêtes INSERT et UPDATE, comme indiqué. Ces requêtes aboutissent à 645 événements WAL. Donc, toutes les 10 secondes, je dois read() 645 événements. Au début, cela prend 0 (ou parfois 1) millisecondes à read() un événement. Après un certain temps, cela prend 1 millisecondes. Puis, encore quelque temps plus tard, cela prend 2 millisecondes. Et ainsi de suite ...

Donc, après un certain temps, je suis incapable de read() 645 événements en 10 secondes, que le temps requis pour read() ne cesse d'augmenter. Ceci explique l'utilisation du processeur à 100% et le manque de mémoire.

Je ne sais toujours pas comment expliquer et comment résoudre ce problème. Je vais continuer à enquêter.

mise à jour 2

J'ai essayé d'ajouter buffer.clear() à la fin de la boucle, sans succès. Je suis toujours en cours d'exécution dans 100% des problèmes de CPU et de mémoire. C'est comme prévu, car le tampon est une variable locale, donc il est GC'ed après chaque boucle de toute façon. Mais j'ai pensé que ce serait une bonne idée de tester quand même.

Répondre