Sequoia изнутри #2

Август 27th, 2009 по SadKo Оставить ответ »

Введение

Собственно, после первой неудачи с RAIDb1, изложенной в этом посте, я решил воспользоваться такой фичей, как RecoveryLog. Для этого ещё раз перечитывал туториал по созданию виртуальной БД.

RecoveryLog

Что ж, для того, чтобы запустить RecoveryLog, нужно чётко прописать параметры каждой таблицы, а также заиметь ещё одну базу. Но, если мы заимеем ещё одну базу, надёжность такой системы резко снижается, так как в случае отказа RecoveryLog всё накрывается медным тазом. Как вариант, разработчики Sequoia предлагают создать для RecoveryLog отдельную виртуальную базу, объединённую… в RAIDb1! Это, получается, матрёшка в матрёшке. Поэтому такой бредятины я делать не стал, а просто-напросто создал в первой базе дополнительную схему (ну не было у меня ещё одного сервера БД), а вторую базу оставил под краш-тесты. Согласно заверениям разработчиков:

The C-JDBC Recovery Log stores write queries and transactions between logical checkpoints defined by the user. The log can be only be stored in a database (or cluster of databases) using a JDBCRecoveryLog element.

The JDBCRecoveryLog stores the recovery information in a database. To access this database, you must provide the driver class name to load (driver), an optional jar file or directory where to find the class to load (driverPath), the JDBC url to access the database as well as a valid login/password.

A timeout in seconds can be defined for the sql requests. If no value is given, the default timeout is set to 60 seconds. Warning! 0 means no timeout and wait forever until completion.

Вроде бы, всё понятно. То есть, я теперь должен описать все типы полей для таблиц, чем я кропотливо и занялся.

Грабли как они есть

Для экономии времени я скопировал пример из каталога config и прописал вместо указанных типов соответствующие им типы ORACLE. После этого я запустил систему, контроллер стартовал и, вроде бы, в лог никаких исключений не сыпалось, я решил запустить тест (см. предыдущий пост), который повис на коннекте, потому что контроллер почему-то не поднял ни одного backend. Что за ерунда?
После получаса гугления я-таки выполз на вот эту страничку:
Using Sequoia with GlassFish.
Собственно, оттуда почерпал сакральные знания о том, что нужно воспользоваться консолью, прилагаемой к дистрибутиву.

Sequoia controller console

Консоль запускается shell-скриптом из каталога bin, имеет гламурный вид (используются цветастые надписи). Примерно так:

sadovnikov:/opt/sequoia # bin/console.sh
Launching the Sequoia controller console
Initializing Controller module...
Initializing VirtualDatabase Administration module...
Initializing SQL Console module...
Sequoia driver (Sequoia core v2.10.10) successfully loaded.
localhost:1090 >

При этом, контроллер тоже должен быть запущен.
Далее надо указать, какую виртуальную базу мы хотим админить:

localhost:1090 > admin CJDBC
Virtual database Administrator Login > admin
Virtual database Administrator Password >
Ready to administrate virtual database CJDBC

Для того, чтобы вся эта дребедень заработала, надо проинициализировать каждый backend:

CJDBC(admin) > initialize oracle01 force
Virtual Database CJDBC has been successfully initialized from backend oracle01
CJDBC(admin) > initialize oracle02 force
Virtual Database CJDBC has been successfully initialized from backend oracle02

После этого надо стартануть backends:

CJDBC(admin) > enable oracle01
Enabling backend oracle01 from its last known checkpoint
CJDBC(admin) > enable oracle02
Enabling backend oracle02 from its last known checkpoint
CJDBC(admin) >

Всё бы хорошо, но вот на включение backend система посыпала кучу исключений, потому что в примерах были описаны типы не для всех колонок.

Мытарства с описанием RecoveryLog

Скрипя сердцем я взял документацию и стал построчно сравнивать каждый параметр с целью поиска недостающих описаний колонок таблицы. После добавления недостающих колонок и присваивания им соответствующих типов я снова попытался поднять backend и… облом. Оказывается, даже в доках были описаны не все поля и требуемые для них типы. Вспомнив, что Sequoia использует для ведения логов log4j, полез в конфигу log4j.properties, где заменил строчку:
log4j.logger.org.continuent.sequoia.controller.recoverylog=INFO, Console,Filetrace
На следующую:
log4j.logger.org.continuent.sequoia.controller.recoverylog=ALL, Console,Filetrace
Вычислить, для кого нужно было включить логи, было несложно, так как в Stack Trace выплёвываемого исключения было написано, какой класс какого пакета его вызывал.
Это мне сильно помогло, так как в лог стали писаться SQL Statements, используемые backend’ом для создания таблиц Recovery Log. Вот их пример:

2009-08-26 17:21:50,679 DEBUG sequoia.controller.recoverylog Log table create statement: CREATE TABLE RECOVERY (log_id INTEGER NOT NULL ENABLE,vlogin VARCHAR2(32) NOT NULL,sql VARCHAR2(32) NOT NULL,sql_param VARCHAR,auto_conn_tran NUMBER NOT NULL,transaction_id INTEGER NOT NULL,request_id INTEGER,exec_status NUMBER NOT NULL,exec_time INTEGER,update_count NUMBER, CONSTRAINT RECOVERY_PK PRIMARY KEY (log_id))
2009-08-26 17:21:50,681 DEBUG sequoia.controller.recoverylog Checkpoint table create statement: CREATE TABLE CHECKPOINT (name VARCHAR2(64) NOT NULL,log_id INTEGER, CONSTRAINT CHECKPOINT_PK PRIMARY KEY (name))
2009-08-26 17:21:50,682 DEBUG sequoia.controller.recoverylog Backend table create statement: CREATE TABLE BACKEND (database_name VARCHAR2(32) NOT NULL, backend_name VARCHAR2(32) NOT NULL,backend_state INTEGER, checkpoint_name VARCHAR2(32) NOT NULL )
2009-08-26 17:21:50,684 DEBUG sequoia.controller.recoverylog Dump table create statement: CREATE TABLE DUMP (dump_name VARCHAR2(32) NOT NULL,dump_date TIMESTAMP(6),dump_path VARCHAR2(64) NOT NULL,dump_format VARCHAR2(64) NOT NULL,checkpoint_name VARCHAR2(64) NOT NULL,backend_name VARCHAR2(64) NOT NULL,tables VARCHAR2(64) NOT NULL)

Только благодаря этому логу я смог выяснить, почему SQL statement у меня не прошёл (оказывается, ВНЕЗАПНО вылез тип VARCHAR для поля, которое не было описано в доке). Стыдно, господа-разработчики!

Итоговый конфиг???

После мытарств с конфигом я в конце-концов получил следующий конфиг контроллера:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE SEQUOIA-CONTROLLER PUBLIC "-//Continuent//DTD SEQUOIA-CONTROLLER 2.10.10//EN"  "http://sequoia.continuent.org/dtds/sequoia-controller-2.10.10.dtd">
<SEQUOIA-CONTROLLER>

  <Controller port="25322">
    <Report hideSensitiveData="true" generateOnShutdown="true" generateOnFatal="true" enableFileLogging="true" />

    <JmxSettings>
      <RmiJmxAdaptor/>
    </JmxSettings>

    <VirtualDatabase configFile="/opt/sequoia/config/virtualdatabase/sequoia.xml" virtualDatabaseName="CJDBC" autoEnableBackends="true" checkpointName="cjdbc_checkpoint" />

  </Controller>
</SEQUOIA-CONTROLLER>

И следующий конфиг виртуальной базы:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE SEQUOIA PUBLIC "-//Continuent//DTD SEQUOIA 2.10.10//EN" "http://sequoia.continuent.org/dtds/sequoia-2.10.10.dtd">
<SEQUOIA>

  <VirtualDatabase name="CJDBC" maxNbOfConnections="150" minNbOfThreads="40" maxNbOfThreads="150" sqlDumpLength="80" >

    <AuthenticationManager>
      <Admin>
        <User username="admin" password="sql"/>
      </Admin>

      <VirtualUsers>
        <VirtualLogin vLogin="user" vPassword="sql"/>
      </VirtualUsers>
    </AuthenticationManager>

    <!--
        Here is a list (non-exhaustive) of backends that can be used with C-JDBC.
    -->

    <!-- Database # 1 -->
    <DatabaseBackend name="oracle01" driver="oracle.jdbc.OracleDriver" url="jdbc:oracle:thin:@192.168.0.140:1521:oracle" connectionTestStatement="select * from dual">

        <DatabaseSchema dynamicPrecision="table" gatherSystemTables="false" />

        <ConnectionManager vLogin="user" rLogin="cjdbc" rPassword="cjdbc">

                <VariablePoolConnectionManager initPoolSize="10" minPoolSize="5" maxPoolSize="50" idleTimeout="180" waitTimeout="120"/>

        </ConnectionManager>
    </DatabaseBackend>
 
    <!-- Database # 2 -->
    <DatabaseBackend name="oracle02" driver="oracle.jdbc.OracleDriver" url="jdbc:oracle:thin:@192.168.0.141:1521:oracle" connectionTestStatement="select * from dual">

        <DatabaseSchema dynamicPrecision="table" gatherSystemTables="false" />

        <ConnectionManager vLogin="user" rLogin="cjdbc" rPassword="cjdbc">

                <VariablePoolConnectionManager initPoolSize="10" minPoolSize="5" maxPoolSize="50" idleTimeout="180" waitTimeout="120"/>

        </ConnectionManager>
    </DatabaseBackend>

    <!--
        Request Manager
    -->
    <RequestManager>

        <RequestScheduler>
            <RAIDb-1Scheduler level="passThrough"/>
        </RequestScheduler>

        <RequestCache>

            <MetadataCache maxNbOfMetadata="10000" maxNbOfField="0"/>

            <ParsingCache backgroundParsing="false" maxNbOfEntries="5000"/>

            <ResultCache granularity="table" maxNbOfEntries="100000" pendingTimeout="0" />
        </RequestCache>

        <LoadBalancer>
            <RAIDb-1>
                <WaitForCompletion policy="first"/>

                <RAIDb-1-LeastPendingRequestsFirst/>

            </RAIDb-1>
        </LoadBalancer>

      <RecoveryLog driver="oracle.jdbc.OracleDriver" url="jdbc:oracle:thin:@192.168.0.140:1521:oracle" login="cjdbcrec" password="cjdbcrec">

        <RecoveryLogTable tableName="RECOVERY"
            logIdColumnType="INTEGER NOT NULL ENABLE"
            vloginColumnType="VARCHAR2(64) NOT NULL"
            sqlColumnName="sql"

            sqlColumnType="VARCHAR2(2048) NOT NULL"
            sqlParamColumnType="VARCHAR2(2048)"
            autoConnTranColumnType="VARCHAR2(4) NOT NULL"
            transactionIdColumnType="INTEGER NOT NULL"
            requestIdColumnType="INTEGER"

            execTimeColumnType="INTEGER"
            updateCountColumnType="NUMBER"
            extraStatementDefinition=", CONSTRAINT RECOVERY_PK PRIMARY KEY (log_id)"/>

        <CheckpointTable tableName="CHECKPOINT"

            checkpointNameColumnType="VARCHAR2(128) NOT NULL"
            logIdColumnType="INTEGER"
            extraStatementDefinition=", CONSTRAINT CHECKPOINT_PK PRIMARY KEY (name)"/>

        <BackendTable tableName="BACKEND"

            databaseNameColumnType="VARCHAR2(64) NOT NULL"
            backendNameColumnType="VARCHAR2(128) NOT NULL"
            checkpointNameColumnType="VARCHAR2(128) NOT NULL"/>

        <DumpTable tableName="DUMP"

            dumpNameColumnType="VARCHAR2(64) NOT NULL"
            dumpDateColumnType="TIMESTAMP(6)"
            dumpPathColumnType="VARCHAR2(128) NOT NULL"
            dumpFormatColumnType="CLOB NOT NULL"
            checkpointNameColumnType="VARCHAR(128) NOT NULL"

            backendNameColumnType="VARCHAR2(128) NOT NULL"
            tablesColumnType="VARCHAR2(128) NOT NULL"/>
      </RecoveryLog>
    </RequestManager>
  </VirtualDatabase>

</SEQUOIA>enableFileLogging="true" />
    <JmxSettings>
      <RmiJmxAdaptor/>
    </JmxSettings>

    <VirtualDatabase configFile="/opt/sequoia/config/virtualdatabase/sequoia.xml" virtualDatabaseName="CJDBC" autoEnableBackends="true" checkpointName="cjdbc_checkpoint" />

  </Controller>
</SEQUOIA-CONTROLLER>

Добившись, наконец, того, что система создавала таблицы (важно — искомые типы полей брались по спецификации из доков и примеров) и запускала оба backend, я решился прогнать тест.

Пока ещё не краш-тест

Вставка данных прошла, но вот контроллер снова высыпал кучу исключений. Я искренне удивился, когда увидел, что он пытается вставлять null-значения в not-nullable колонки. Помимо этого, названия таблиц и колонка ‘log’ были такими же, что и функции ORACLE, то есть, пришлось всё это переименовывать, в результате чего я получил-таки финальную версию конфиги для RecoveryLog:

        <RecoveryLogTable tableName="T_RECOVERY"
            logIdColumnType="INTEGER NOT NULL ENABLE"
            vloginColumnType="VARCHAR2(64) NOT NULL"
            sqlColumnName="sql_query"

            sqlColumnType="VARCHAR2(2048) NOT NULL"
            sqlParamColumnType="VARCHAR2(2048)"
            autoConnTranColumnType="VARCHAR2(4) NOT NULL"
            transactionIdColumnType="INTEGER NOT NULL"
            requestIdColumnType="INTEGER"

            execTimeColumnType="INTEGER"
            updateCountColumnType="NUMBER"
            extraStatementDefinition=", CONSTRAINT RECOVERY_PK PRIMARY KEY (log_id)"/>

        <CheckpointTable tableName="T_CHECKPOINT"

            checkpointNameColumnType="VARCHAR2(128) NOT NULL"
            logIdColumnType="INTEGER"
            extraStatementDefinition=", CONSTRAINT CHECKPOINT_PK PRIMARY KEY (name)"/>

        <BackendTable tableName="T_BACKEND"

            databaseNameColumnType="VARCHAR2(64) NOT NULL"
            backendNameColumnType="VARCHAR2(128) NOT NULL"
            checkpointNameColumnType="VARCHAR2(128)"/>

        <DumpTable tableName="T_DUMP"

            dumpNameColumnType="VARCHAR2(64) NOT NULL"
            dumpDateColumnType="TIMESTAMP(6)"
            dumpPathColumnType="VARCHAR2(128) NOT NULL"
            dumpFormatColumnType="CLOB NOT NULL"
            checkpointNameColumnType="VARCHAR(128) NOT NULL"

            backendNameColumnType="VARCHAR2(128) NOT NULL"
            tablesColumnType="VARCHAR2(128) NOT NULL"/>

Думаете, это было последняя битва с Recovery Log? Ошибаетесь! При попытке вставить в таблицу контроллер продолжал стабильно сыпать исключения такого вида:

2009-08-26 17:59:53,394 ERROR sequoia.controller.recoverylog Failed to log begin for transaction 0
java.sql.SQLException: ORA-00911: invalid character

        at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112)
        at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:331)
        at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:288)
        at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:743)
        at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:216)
        at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:955)
        at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1168)
        at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3285)
        at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate(OraclePreparedStatement.java:3368)
        at org.continuent.sequoia.controller.recoverylog.events.LogRequestEvent.execute(LogRequestEvent.java:104)
        at org.continuent.sequoia.controller.recoverylog.LoggerThread.run(LoggerThread.java:678)

Вот вам и опровержение абзаца из мануала:

Users reported successful usage of Sequoia with the following RDBMS: Oracle®, PostgreSQL, MySQL, Apache Derby, IBM DB2®, Sybase®, SAP DB (MySQL MaxDB), HyperSonic SQL, Firebird, MS SQL Server and InstantDB.

В общем, Google помог слабо, нашёл один хиленький баг-тред, из которого вынес только то, что для достижения совместимости с ORACLE придётся патчить исходники.

Резюме

Проковырявшись с настройками RecoveryLog целый рабочий день, я так и не смог до конца заставить его работать. Видать, разработчики очень плохо тестировали свой продукт на СУБД ORACLE. Что самое обидное, в сети по возникающим проблемам с Sequoia найти ответ на свой вопрос очень тяжело.
В следующей части я опишу процесс сборки Sequoia и её краш-тест.

Реклама

Добавить комментарий

Blue Captcha Image
Refresh

*