Распределенные транзакции / Совместное использование одной коннекции

Wireless
Дата: 07.05.2004 08:57:25
Доброго времени суток !

Господа, подскажите как можно решить такую проблему:

Имеется сложная система - интерфейс написан на mod_perl
скриптах, а ядро системы на С++, причем интерфейсные
скрипты часто вызывают функции ядра системы и в этих
функциях ядра требуется работа в той же транзакции, что
была начата в вызывающей программе.
Собственно, для этого необходимо передать одним из
параметров handler коннекции. Но достаточно ли этого?
Совместимы ли возвращаемые значения функции
perl[Pg.pm]::PQconnectdb и C[libpq]::PQsetdbLogin ?
На бинарном уровне или нужно будет писать какую-то
подпрограмму для преобразования полей этих объектов?
А достаточно ли вообще говоря передать объект,
описывающий коннекцию, чтобы в обычном режиме
продолжить выполнять команды в рамках транзакции,
которая начата в другом процессе/программе ?

С уважением.
LeXa NalBat
Дата: 07.05.2004 10:39:42
Внутри c++ ядра или в perl-xs вы деалете fork? Если нет, то наверное проблем не должно быть, иначе не могу сказать, что вас может ожидать. Загляните в исходники DBD::Pg, в файле dbdimp.c подключение делается с помощью функции PQconnectdb().
Wireless
Дата: 07.05.2004 12:04:05
Ок. Хорошо, то есть получается что эти значения абсолютно совместимы,
но не ожидал что проблема может возникнуть на более низком уровне -
сокетов. А в чем здесь собственно грабли? Ведь одновременно запросы
не будут идти от более чем одного процесса?

fork() делается только в Си-ядре, но коннекция у меня
вызывается в уже ответвленном дочернем процессе, не предполагается
работа с ним в родительском процессе. Единственная задача
одит. процесса - ответвлять потомков на каждый поступающий
запрос.

Спасибо за ссылку. Изучаю.

Кстате, по моему разумению нужно передавать объект-хэдлер
коннекции не только от интерфейсных программ, но и наоборот,
возвращать от ядра вместе с результатами, так как в объекте
должны меняться какие-то внутренние состояния? Правильно.

Спасибо за ответ.
LeXa NalBat
Дата: 07.05.2004 13:02:59
не ожидал что проблема может возникнуть на более низком уровне - сокетов. А в чем здесь собственно грабли?


Я не знаю, есть ли здесь проблема, но судя по обсуждениям на сайте постгреса, может оказаться.

в этих функциях ядра требуется работа в той же транзакции, что была начата в вызывающей программе


коннекция у меня вызывается в уже ответвленном дочернем процессе, не предполагается работа с ним в родительском процессе


Или это противоречащие утверждения, или я чего-то недопонимаю. Если в вызывающем процессе (ребенок апача с мод-перлом) был сделан коннект и начата транзакция (первая цитата), то как же "коннекция вызывается в уже ответвленном дочернем процессе"? Вы ведь не думаете, что можно сделать "connect, begin, update, disconnect", затем "connect, update, commit, disconnect" - и это будет одна транзакция? Или вы описали две схемы - существующую и предполагаемую? Разъясните пожалуйста.

нужно передавать объект-хэдлер коннекции не только от интерфейсных программ, но и наоборот, возвращать от ядра вместе с результатами, так как в объекте должны меняться какие-то внутренние состояния? Правильно.


Возможно. Но реально ли это? В общем, мне кажется, что "connect, fork" - неправильный путь. Но может быть я ошибаюсь.

P.S.: Когда-то натолкнулись на грабли. В одном из перловых модулей, загружаемых апачем-отцом при стартапе посредством PerlScript, создавался коннект к базе и загружались данные из базы в память. Затем каждый из форкнутых детей считал, что к базе он подключен (глобальная переменная $dbh в том модуле была определена, инициализирована,..), но на самом деле с базой работать не мог. Устранили проблему (точно не помню как) или disconnect-ом после загрузки данных из базы в память в апаче-отце, или переносом этой части (коннект, загрузка данных в память) в функцию в модуле, которая стала вызываться не в отце, а в ребенке.
Wireless
Дата: 09.05.2004 12:05:49
> > не ожидал что проблема может возникнуть на более низком уровне
> > - сокетов. А в чем здесь собственно грабли?

> Я не знаю, есть ли здесь проблема, но судя по обсуждениям на
> сайте постгреса, может оказаться.

Для тех функций ядра системы, для которых нужна работа в существующей
транзакции думаю проблем не будет, если явно не вызывать dbh->disconnect,
так как именно эта ф-ия неявно вызывает закрытия сокета. Да, в любом
случае нужно пробовать. Я думал, что кто- уже пробовал делать нечто
подобное, и хотел услышать их мнение.

> Или это противоречащие утверждения, или я чего-то недопонимаю.
> Или вы описали две схемы - существующую и предполагаемую?

Sorry for confuse. Да, это были описаны то что есть, и то что предполагается
сделать.

> > нужно передавать объект-хэдлер коннекции не только от
> > интерфейсных программ, но и наоборот, возвращать от ядра вместе
> > с результатами, так как в объекте должны меняться какие-то
> > внутренние состояния?

> Возможно. Но реально ли это? В общем, мне кажется, что "connect, fork" -
> неправильный путь. Но может быть я ошибаюсь.

Да, у меня сейчас в ядре выолняется fork, connect. А для _некоторых_
запросов нужно делать fork, don't_connect(использовать переданный
$dbh, то о чем я говорил в первом посте).
Wireless
Дата: 09.05.2004 14:27:54
struct pg_conn
{
        /* Saved values of connection options */
        char       *pghost;                     /* the machine on which the server is
                                                                 * running */
        char       *pghostaddr;         /* the IPv4 address of the machine on
                                                                 * which the server is running
                                                                 * numbers-and-dots notation.
                                                                 * precedence over above. */
        char       *pgport;                     /* the server's communication port */
        char       *pgunixsocket;       /* the Unix-domain socket that the server
                                                                 * is listening on; if NULL, u
                                                                 * default constructed from pg
        char       *pgtty;                      /* tty on which the backend messages is
                                                                 * displayed (OBSOLETE, NOT US
        char       *connect_timeout;    /* connection timeout (numeric string) */
        char       *pgoptions;          /* options to start the backend with */
        char       *dbName;                     /* database name */
        char       *pguser;                     /* Postgres username and password, if any */
        char       *pgpass;
        char       *sslmode;            /* SSL mode (require,prefer,allow,disable) */

        /* Optional file to write trace info to */
        FILE       *Pfdebug;

        /* Callback procedures for notice message processing */
        PGNoticeHooks noticeHooks;

        /* Status indicators */
        ConnStatusType status;
        PGTransactionStatusType xactStatus;
        /* note: xactStatus never changes to ACTIVE */
        bool            nonblocking;    /* whether this connection is using
                                                                 * nonblock sending semantics
        bool            ext_query;              /* was our last query sent with extended
                                                                 * query protocol? */
        char            copy_is_binary; /* 1 = copy binary, 0 = copy text */
        int                     copy_already_done;              /* # bytes already returned in
                                                                                 * COPY OUT */
        Dllist     *notifyList;         /* Notify msgs not yet handed to
                                                                 * application */

        /* Connection data */
        int                     sock;                   /* Unix FD for socket, -1 if not conne
        SockAddr        laddr;                  /* Local address */
        SockAddr        raddr;                  /* Remote address */
        ProtocolVersion pversion;       /* FE/BE protocol version in use */
        char            sversion[8];    /* The first few bytes of server version */

        /* Transient state needed while establishing connection */
        struct addrinfo *addrlist;      /* list of possible backend addresses */
        struct addrinfo *addr_cur;      /* the one currently being tried */
        int                     addrlist_family;        /* needed to know how to free addrlist
        PGSetenvStatusType setenv_state;        /* for 2.0 protocol only */
        const PQEnvironmentOption *next_eo;

        /* Miscellaneous stuff */
        int                     be_pid;                 /* PID of backend --- needed for cance
        int                     be_key;                 /* key of backend --- needed for cance
        char            md5Salt[4];             /* password salt received from backend */
        char            cryptSalt[2];   /* password salt received from backend */
        pgParameterStatus *pstatus; /* ParameterStatus data */
        int                     client_encoding;        /* encoding id */
        PGVerbosity verbosity;          /* error/notice message verbosity */
        PGlobjfuncs *lobjfuncs;         /* private state for large-object access
                                                                 * fns */

        /* Buffer for data received from backend and not yet processed */
        char       *inBuffer;           /* currently allocated buffer */
        int                     inBufSize;              /* allocated size of buffer */
        int                     inStart;                /* offset to first unconsumed data in
                                                                 * buffer */
        int                     inCursor;               /* next byte to tentatively consume */
        int                     inEnd;                  /* offset to first position after avai
                                                                 * data */

        /* Buffer for data not yet sent to backend */
        char       *outBuffer;          /* currently allocated buffer */
        int                     outBufSize;             /* allocated size of buffer */
        int                     outCount;               /* number of chars waiting in buffer *

        /* State for constructing messages in outBuffer */
        int                     outMsgStart;    /* offset to msg start (length word); if
                                                                 * -1, msg has no length word
        int                     outMsgEnd;              /* offset to msg end (so far) */

        /* Status for asynchronous result construction */
        PGresult   *result;                     /* result being constructed */
        PGresAttValue *curTuple;        /* tuple currently being read */

#ifdef USE_SSL
        bool            allow_ssl_try;  /* Allowed to try SSL negotiation */
        bool            wait_ssl_try;   /* Delay SSL negotiation until after
                                                                 * attempting normal connectio
        SSL                *ssl;                        /* SSL status, if have SSL connection
        X509       *peer;                       /* X509 cert of server */
        char            peer_dn[256 + 1];               /* peer distinguished name */
        char            peer_cn[SM_USER + 1];   /* peer common name */
#endif

        /* Buffer for current error message */
        PQExpBufferData errorMessage;           /* expansible string */

        /* Buffer for receiving various parts of messages */
        PQExpBufferData workBuffer; /* expansible string */
};

Это та самая структура, которую нужно будет как-то в бинарном формате
сохранять в проге, написанной на Perl и передавать в ядро системы,
написанное на Си++.

Кстате, я заметил, есть в этой структуре по крайней мере 2 "опасных"
поля - указателя - inBuffer, outBuffer. Я так понимаю буфер выделяется
в адресном пространстве процесса, который вызвал connect,
а не где-то в АП самого Постгреса, и тогда
все очень плохо(?) - из другого процесса (ядро моей системы) не удастся
обращаться к ним.

Вообще говоря, как проще из перла передать в Си-программу
область памяти в данном конкретном случае. Не проще ли не возится с
этим, а попробовать посмотреть в сторону библиотек, которые
создают пул коннекций к постресу и при обращении отдают dbh
уже подключенный к БД заранее. Это конечно, возможно, если среди
них есть те, которые умеют работать с Перл- и Си-программами
И опционально(должно переключаться неким ключиком при запросе)
уметь отдавать dbh, которая уже распределена (отдана одним из
предыдущих запросов и используется).

Спасибо за ответы.