PostgreSQL La base de donnees la plus sophistiquee au monde.

Forums PostgreSQL.fr

Le forum officiel de la communauté francophone de PostgreSQL

Vous n'êtes pas identifié(e).

#1 Re : C et C++ » Fonction postgres problème multithreading avec getaddrinfo » 23/12/2010 12:02:35

Bonjour et merci de votre réponse. Je vais essayer de faire autrement. Je vous souhaite de bonnes fêtes de fin d'année!

#2 C et C++ » Fonction postgres problème multithreading avec getaddrinfo » 22/12/2010 20:35:11

djeauh
Réponses : 2

Bonsoir,

           Voici mon problème: j'ai une fonction postgresql qui créé X thread de travail, chaque thread commençant par résoudre un nom d'hote avant de pouvoir continuer à travailler. (Voir exemple de code à la fin de ce post.). Le soucis est que postmaster plante de manière régulière au niveau de cette fameuse résolution de noms. Le seul moyen de ne plus faire planter l'appel à la fonction est de protéger par un mutex l'appel à la fonction getaddrinfo.
Mais voilà d'après le man pages de getaddrinfo cette fonction est réentrante! De plus si je prends les fonctions "working_thread" (en chageant elog par printf bien sur!) et "debug_func" du code ci dessous, et que je les compile dans une application, tout fonctionne correctement!!

En générant les coredumps et en les chargeant dans gdb, j'ai systématiquement la pile suivante:

(gdb) bt
#0  0x00007f09efe9cb66 in fgets_unlocked () from /lib/libc.so.6
#1  0x00007f09ed97a577 in ?? () from /lib/libnss_files.so.2
#2  0x00007f09ed97ab78 in _nss_files_gethostbyname2_r () from /lib/libnss_files.so.2
#3  0x00007f09efee752b in ?? () from /lib/libc.so.6
#4  0x00007f09efee8ff9 in getaddrinfo () from /lib/libc.so.6
#5  0x00007f09ed74157c in working_thread (param=0x0) at entry_point.cpp:266
#6  0x00007f09ebf6dfc7 in start_thread () from /lib/libpthread.so.0
#7  0x00007f09efefd64d in clone () from /lib/libc.so.6
#8  0x0000000000000000 in ?? ()

et voici ce que j'obtiens sur stderr:
*** glibc detected *** XXXXX: XXXXX XXXXX XXX.XXX.XXX.XXX(40360) SELECT: double free or corruption (!prev): 0x000000000189ad50 ***
======= Backtrace: =========
/lib/libc.so.6[0x7f09efea19a8]
/lib/libc.so.6(cfree+0x76)[0x7f09efea3ab6]
/lib/libc.so.6(fclose+0x151)[0x7f09efe91e01]
/lib/libnss_files.so.2(_nss_files_gethostbyname2_r+0x17e)[0x7f09ed97abfe]
/lib/libc.so.6[0x7f09efee76cb]
/lib/libc.so.6(getaddrinfo+0x1d9)[0x7f09efee8ff9]
/usr/local/pgsql/lib/XXX/XXX.so(_Z14working_threadPv+0x66)[0x7f09ed74157c]
/lib/libpthread.so.0[0x7f09ebf6dfc7]
/lib/libc.so.6(clone+0x6d)[0x7f09efefd64d]

Note: j'ai aussi essayé gethostbyname_r et j'ai les même soucis sad.

Auriez-vous des pistes à me donner SVP? Merci d'avance!

-----------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
#include <pthread.h>
#include <iostream>
#include <list>

#ifdef __cplusplus
extern "C" {
#endif
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC
;
#endif

PG_FUNCTION_INFO_V1(test)
;
Datum test( PG_FUNCTION_ARGS );

#ifdef __cplusplus
}
#endif

void *working_thread( void *param ) {
    struct addrinfo *address_info = NULL;
    int resolving_result = 0; //

    elog(INFO, "Démarrage thread %lu", pthread_self());
    resolving_result = getaddrinfo( "www.example.com", "80", NULL, &address_info );
    freeaddrinfo( address_info );
    elog(INFO, "Résultat dans le thread %lu : %d", pthread_self(), resolving_result);
    sleep( 2 ); //On simule un gros traitement
    elog(INFO, "Fin thread %lu", pthread_self());

    return (NULL);
}

void debug_func() {
    //Principe: on veut fait "max_calls" appels à la fonction working_thread en parallèle,
    // en n'autorisant que "max_threads" threads concurrent
    //Nos définitions:
    const int max_threads = 5;
    const int max_calls = 100;
    //Element en cours de traitement
    int current_idx = 0;
    //Une liste de threads + un itérateur pour la parcourir
    std::list<pthread_t> threads;
    std::list<pthread_t>::iterator it;
    //Identifiant de thread temporaire
    pthread_t tmp;
    //Un booléen qui servira dans notre boucle
    bool found = false;

    do {
        if( threads.size() < max_threads ) {
            pthread_create( &tmp, NULL, working_thread, NULL );
            threads.push_back( tmp );
            ++current_idx;
        } else {

            //Les "max_calls" threads sont en cours d'éxécution, on attends qu'il y en ait un qui se libère
            for( it = threads.begin(); it != threads.end(); ++it ) {
                if( pthread_tryjoin_np( *it, NULL ) == 0 ) {
                    //Le thread est terminé: on l'enlève de notre liste
                    threads.erase( it );
                    found = true;
                    break;
                }
            }

            //Si aucun "slot" n'est libre, on attends 1 seconde
            if( !found ) {
                sleep( 1 );
            }
        }
    } while( current_idx < max_calls ); //On créé les threads tant qu'on n'a pas fini nos traitements

    //On attends les derniers threads
    for( it = threads.begin(); it != threads.end(); ++it ) {
        pthread_join( *it, NULL );
    }
}

Datum test( PG_FUNCTION_ARGS ) {
    debug_func();

    PG_RETURN_NULL();
}

#3 Re : C et C++ » Lecture sur une socket plus lente dans une procédure C » 20/12/2010 12:48:11

J'ai trouvé la réponse à mon problème: comme vous l'avez soulevé il y avait en effet une différence entre les 2 requêtes SOAP. Mea culpa: c'était un bug dans une de mes fonctions sad
Merci encore pour votre aide et désolé pour la perte de temps

#4 Re : C et C++ » Lecture sur une socket plus lente dans une procédure C » 20/12/2010 10:36:53

J'ai suivi vos conseils en utilisant strace. Les logs produits confirment mes doutes.

Pour mes tests, j'ai utilisé le même serveur, avec la même connexion internet et la même version de ma librairie.
J'utilise epoll afin de détecter l'arrivée de données sur la socket.

1) Voici un extrait du log produit par strace avec mon programme de test:

--------------------------------------------------------------------------------------------------------------------------------------------------------
1292601158.915700 socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 5
1292601158.915745 connect(5, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("XXX.XXX.XXX.XXX")}, 16) = 0
1292601158.970697 sendto(5, "POST XXXXXXXXXXXXXXXXXXX"..., 181, 0, NULL, 0) = 181
1292601158.970778 sendto(5, "<SOAP-ENV:Envelope xmlns:SOAP-ENV"..., 45450, 0, NULL, 0) = 45450
1292601159.190492 epoll_create(1)       = 6
1292601159.190548 epoll_ctl(6, EPOLL_CTL_ADD, 5, {EPOLLIN, {u32=5, u64=18276150996369413}}) = 0
1292601159.190638 epoll_wait(6, {{EPOLLIN, {u32=5, u64=18276150996369413}}}, 1, 120000) = 1
1292601170.874392 recvfrom(5, "HTTP/1.1 200 OK\r\nServer: XXXXX"..., 2048, 0, NULL, NULL) = 150
1292601170.874451 epoll_wait(6, {{EPOLLIN, {u32=5, u64=18276150996369413}}}, 1, 120000) = 1
1292601170.875557 recvfrom(5, "<?xml version='1.0' encoding='UTF"..., 2048, 0, NULL, NULL) = 1380
...............
--------------------------------------------------------------------------------------------------------------------------------------------------------

2) Voici un extrait équivalent, avec l'appel à la librairie depuis une fonction POSTGRES:

--------------------------------------------------------------------------------------------------------------------------------------------------------
1292601990.698854 socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 18
1292601990.698897 connect(18, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("XXX.XXX.XXX.XXX")}, 16) = 0
1292601990.754671 sendto(18, "POST XXXXXXXXXXXXXXXXXXX"..., 181, 0, NULL, 0) = 181
1292601990.754775 sendto(18, "<SOAP-ENV:Envelope xmlns:SOAP-ENV"..., 46945, 0, NULL, 0) = 46945
1292601990.963530 epoll_create(1)       = 19
1292601990.963617 epoll_ctl(19, EPOLL_CTL_ADD, 18, {EPOLLIN, {u32=18, u64=7213054800882040850}}) = 0
1292601990.963670 epoll_wait(19, {{EPOLLIN, {u32=18, u64=7213054800882040850}}}, 1, 120000) = 1
1292602044.286214 recvfrom(18, "HTTP/1.1 200 OK\r\nServer: XXXXX"..., 2048, 0, NULL, NULL) = 150
1292602044.286270 epoll_wait(19, {{EPOLLIN, {u32=18, u64=7213054800882040850}}}, 1, 120000) = 1
1292602044.287482 recvfrom(18, "<?xml version='1.0' encoding='UTF"..., 2048, 0, NULL, NULL) = 1380
...................
--------------------------------------------------------------------------------------------------------------------------------------------------------

Comme vous pourrez le constater, le délai entre mon premier epoll_wait et le début de ma réponse du serveur est d'environ 11s dans le premier test. Lors du second test, cette même durée est de 54s!!!!

A noter que j'ai essayé d'enlever les epoll de mon code en ne faisant que des read et que le résultat est le même.

Je vais continuer mes tests et essayer de modifier mon code faisant les appels réseaux afin d'en savoir plus.

Bonne journée.

#5 Re : C et C++ » Lecture sur une socket plus lente dans une procédure C » 17/12/2010 17:32:39

En effet, c'est d'autant plus étrange que j'ai déjà écrit pas mal de fonction C pour POSTGRES utilisant des librairies et que c'est la première fois que j'obtient ce comportement.
Je vais essayer le strace et je vous tiens au courant.

#6 Re : C et C++ » Lecture sur une socket plus lente dans une procédure C » 17/12/2010 17:07:49

Bonjour et merci de votre réactivité.

Il s'agit en fait d'un appel à une procédure, laquelle procédure appel la fameuse librairie que j'ai écrit. Lorsque j'appel la fonction définie dans la librairie à partir de la procédure postgres le temps d'éxecution de cette fonction est de 60s. Lorsque j'appel la même fonction sans passer par la base de données, l'appel dure 20s.

Voici en gros comment ça se passe:

//Fichier my_func.c pour génération myfunc.so
void myFunc() {
  //Mes traitements....
//......
}

//------------------------
//Application externe liée à myfunc.so
int main() {
  myFunc(); //L'appel prends 20 s
}

//-------------------------
//Fichier procedure.c, pour intégration, à POSTGRES et lié à myfunc.so
PG_FUNCTION_INFO_V1( test );
Datum test( PG_FUNCTION_ARGS );

Datum test( PG_FUNCTION_ARGS ) {
   //Appel à ma fonction défini dans mon so:
   myFunction(); //L'appel prends 60 s
   PG_RETURN_NULL();
}

J'espere que je suis plus clair à présent. A noter que le code contenu dans myFunc ne fait principalement que des lectures sur une socket et que d'après mes logs je reste bloqué sur mon epoll_wait en attente de la réponse du serveur distant.

Je vais essayer d'écrire un programme de test afin de reproduire le problème.

Merci encore de votre aide.

#7 C et C++ » Lecture sur une socket plus lente dans une procédure C » 17/12/2010 16:26:05

djeauh
Réponses : 7

Bonjour à tous,
            j'ai un petit soucis avec l'utilisation des procédures C (fonction que je trouve géniale dans POSTGRES soit dit en passant).
J'ai écrit un shared object (je suis sous DEBIAN) me permettant de faire une requete HTTP vers un serveur se trouvant sur internet.
Lorsque j'utilise cette librairie dans une application dédié, la lecture de la réponse prends toujours environ 20 sec.
Lorsque j'utilise cette librairie dans une procédure intégrée à POSTGRES, la même requete prends environ 60 sec!!
J'ai mis des traces dans ma librairie, et il semblerait que je soit toujours bloqué au niveau de la reception des données.
Auriez-vous un indice? Merci d'avance et bonne journée.

djeauh

Pied de page des forums

Propulsé par FluxBB