Информация о транзакциях Jaybird 3 и Firebird

В предыдущей версии jaybird (2.2) я мог выполнить Services API на сервере Firebird, чтобы получить активные маркеры транзакций: OIT, OAT, Next и т. д.

В версии 3.0 я не могу понять, как это сделать правильно. Есть только ISC-константы (например, isc_info_oldest_snapshot), но нет методов.

Итак, я вижу один из способов сделать это: получить запрос заголовка базы данных с помощью StatisticsManager. Но это не так просто, потому что он вернет текст, который нужно разобрать:

    StatisticsManager SM = new FBStatisticsManager();  //"PURE_JAVA", "NATIVE", "EMBEDDED"

    SM.setHost("localhost");
    SM.setUser("sysdba");
    SM.setPort(3053);
    SM.setPassword("masterkey");
    SM.setDatabase("c:\\Firebird\\3.0.2\\examples\\empbuild\\EMPLOYEE.FDB");

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    SM.setLogger(baos);
    SM.getHeaderPage();
    String outputstr2 = new String( baos.toByteArray(), java.nio.charset.StandardCharsets.UTF_8 );

и теперь мне нужно разобрать текст:

Database "C:\FIREBIRD\3.0.2\EXAMPLES\EMPBUILD\EMPLOYEE.FDB"
Database header page information:
   Flags            0
   Generation        806
   System Change Number    12
   Page size        8192
   ODS version        12.0
   Oldest transaction    520
   Oldest active        521
   Oldest snapshot        521
   Next transaction    521
   Sequence number        0
   Next attachment ID    857
   Implementation        HW=AMD/Intel/x64 little-endian OS=Windows CC=MSVC
   Shadow count        0
   Page buffers        0
   Next header page    0
   Database dialect    3
   Creation date        Apr 15, 2016 17:38:34
   Attributes        

   Variable header data:
   Database backup GUID:    {6F41E937-76D5-4C67-6CAE-F8556AD27BEE}
   Database GUID:    {EE5B2713-7B17-43B0-0CB3-0616B4B8A63D}
   *END*

Может быть, можно получить прямые значения?

upd: Старая версия кода была:

/** [ActiveCount, OAT, OST, OIT, Next] */
public static int[] getTxInfo( final GDS gds,
                               final String host,
                               final int port,
                               final String databasePath,
                               final String user,
                               final String password ) throws Exception {
    final byte[] queryItems = {
            ISCConstants.isc_info_oldest_transaction,
            ISCConstants.isc_info_oldest_active,
            ISCConstants.isc_info_oldest_snapshot,
            ISCConstants.isc_info_next_transaction,
            ISCConstants.isc_info_active_transactions,
            ISCConstants.isc_info_end
    };
    byte[] response = queryDB(
            gds, host, port, databasePath, user, password,
            queryItems, DEFAULT_BUFFER_SIZE
    );
    int i = 0;
    final int[] result = new int[5];
    while ( response[i] != ISCConstants.isc_info_end ) {
        final byte code = response[i++];
        switch ( code ) {
            case ISCConstants.isc_info_active_transactions: {
                //здесь идет столько блоков isc_info_active_transactions, сколько
                //реально активных транзакций в данный момент
                final int valueLen = gds.iscVaxInteger( response, i, LENGTH_LEN );
                i += LENGTH_LEN;
                //final int res = gds.iscVaxInteger( response, i, valueLen );
                i += valueLen;
                result[0]++;
                break;
            }
            case ISCConstants.isc_info_oldest_active: {
                final int valueLen = gds.iscVaxInteger( response, i, LENGTH_LEN );
                i += LENGTH_LEN;
                final int res = gds.iscVaxInteger( response, i, valueLen );
                i += valueLen;
                result[1] = res;
                break;
            }
            case ISCConstants.isc_info_oldest_snapshot: {
                final int valueLen = gds.iscVaxInteger( response, i, LENGTH_LEN );
                i += LENGTH_LEN;
                final int res = gds.iscVaxInteger( response, i, valueLen );
                i += valueLen;
                result[2] = res;
                break;
            }
            case ISCConstants.isc_info_oldest_transaction: {
                final int valueLen = gds.iscVaxInteger( response, i, LENGTH_LEN );
                i += LENGTH_LEN;
                final int res = gds.iscVaxInteger( response, i, valueLen );
                i += valueLen;
                result[3] = res;
                break;
            }
            case ISCConstants.isc_info_next_transaction: {
                final int valueLen = gds.iscVaxInteger( response, i, LENGTH_LEN );
                i += LENGTH_LEN;
                final int res = gds.iscVaxInteger( response, i, valueLen );
                i += valueLen;
                result[4] = res;
                break;
            }
            case ISCConstants.isc_info_truncated: {
                //этот код означает "буфер слишком маленький, дайте больше"
                //обычно это бывает когда слишком много активных транзакций

                //сначала пробуем увеличить буфер
                if ( response.length == DEFAULT_BUFFER_SIZE ) {
                    response = queryDB(
                            gds, host, port, databasePath, user, password,
                            queryItems, 32 * DEFAULT_BUFFER_SIZE
                    );
                    result[0] = 0;//на всякий случай
                    //начинаем разбор заново
                    i = 0;
                } else {
                    //32Кб буфера оказалось тоже недостаточно -- пичалька. Но
                    //делать нечего -- просто обойдемся без числа активных транзакций
                    response = queryDB(
                            gds, host, port, databasePath, user, password,
                            new byte[]{
                                    ISCConstants.isc_info_oldest_transaction,
                                    ISCConstants.isc_info_oldest_active,
                                    ISCConstants.isc_info_oldest_snapshot,
                                    ISCConstants.isc_info_next_transaction,
                                    ISCConstants.isc_info_end
                            }, DEFAULT_BUFFER_SIZE
                    );
                    result[0] = -1;
                    //начинаем разбор заново
                    i = 0;
                }
                break;
            }

            default:
                throw new FBSQLException( "Unrecognized response code: " + code + " (response=" + Arrays.toString( result ) + ")" );
        }
    }
    return result;
}

куда

public static byte[] queryDB( final GDS gds,
                              final String host,
                              final int port,
                              final String databasePath,
                              final String user,    
                              final String password,
                              final byte[] queryItems,
                              final int bufferLength ) throws Exception {
    return doWithDB(
            gds, host, port, databasePath, user, password,
            new DBOperation<byte[]>() {
                public byte[] doWithDB( final GDS gds,
                                        final IscDbHandle db ) throws GDSException {
                    return gds.iscDatabaseInfo(
                            db,
                            queryItems,
                            bufferLength
                    );
                }
            }
    );
}

Это достаточно?


person Сергей Никитин    schedule 22.03.2017    source источник
comment
Вы можете создать подкласс FBServiceManager и получить информацию самостоятельно. Я посмотрю, смогу ли я добавить его в FBStatisticsManager (или я пропустил его, как в прошлый раз).   -  person Mark Rotteveel    schedule 22.03.2017
comment
Это будет хорошо! Мне нужны маркеры транзакций, чтобы проверить некоторые пробелы и зачистить...   -  person Сергей Никитин    schedule 23.03.2017
comment
Для этого я создал JDBC-485. Я обновлю, когда он будет доступен, возможно, через неделю или две, учитывая мой текущий график.   -  person Mark Rotteveel    schedule 23.03.2017
comment
Самая старая интересная, самая старая активная и следующая транзакция и OST, пожалуйста :)   -  person Сергей Никитин    schedule 23.03.2017
comment
Сделал не то, что в билете   -  person Mark Rotteveel    schedule 23.03.2017
comment
Не могли бы вы показать, как вы получили информацию, используя дескриптор службы в Jaybird 2.2, потому что, насколько я могу судить, ее можно получить только с помощью isc_database_info в дескрипторе базы данных.   -  person Mark Rotteveel    schedule 25.03.2017
comment
@MarkRotteveel - я обновляю исходное сообщение своим примером кода...   -  person Сергей Никитин    schedule 26.03.2017
comment
Спасибо, это подтверждает, что вы использовали дескриптор базы данных, а не дескриптор службы.   -  person Mark Rotteveel    schedule 26.03.2017


Ответы (2)


Я добавил функциональность в Jaybird 3.0.0, см. эту фиксацию.

Я добавил два способа получить эту информацию:

  1. Использование StatisticsManager:

    StatisticsManager statsMan = new FBStatisticsManager();
    statsMan.setHost("localhost");
    statsMan.setDatabase("/path/to/your.fdb");
    statsMan.setUser("youruser");
    statsMan.setPassword("yourpassword"); 
    DatabaseTransactionInfo info = statsMan.getDatabaseTransactionInfo();
    
  2. Удобный метод использования существующего Connection для получения этой информации:

    try (Connection connection = dataSource.getConnection()) {
        DatabaseTransactionInfo info = FBStatisticsManager
                .getDatabaseTransactionInfo(connection);
    }
    

    Единственное требование состоит в том, чтобы экземпляр соединения разворачивался на интерфейс FirebirdConnection.

Если вы используете Maven, вы можете попробовать последний снимок Jaybird из репозитория моментальных снимков Sonatype OSS: https://oss.sonatype.org/content/repositories/snapshots (вам нужно добавить этот репозиторий моментальных снимков в конфигурацию maven).

<dependency>
    <groupId>org.firebirdsql.jdbc</groupId>
    <artifactId>jaybird-jdk18</artifactId>
    <version>3.0.0-SNAPSHOT</version>
</dependency>

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

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

person Mark Rotteveel    schedule 26.03.2017

select * from MON$DATABASE запросит "OIT, OAT, Next"

Или вы можете получить более подробную информацию по select * from MON$TRANSACTIONS

Подробнее см. в разделе c:\Program Files\Firebird\Firebird_2_1\doc\README.monitoring_tables.txt

MON$DATABASE (connected database)
  - MON$DATABASE_NAME (database pathname or alias)
  - MON$PAGE_SIZE (page size)
  - MON$ODS_MAJOR (major ODS version)
  - MON$ODS_MINOR (minor ODS version)
  - MON$OLDEST_TRANSACTION (OIT number)
  - MON$OLDEST_ACTIVE (OAT number)
  - MON$OLDEST_SNAPSHOT (OST number)
  - MON$NEXT_TRANSACTION (next transaction number)
  - MON$PAGE_BUFFERS (number of pages allocated in the cache)
  - MON$SQL_DIALECT (SQL dialect of the database)
  - MON$SHUTDOWN_MODE (current shutdown mode)
      0: online
      1: multi-user shutdown
      2: single-user shutdown
      3: full shutdown
  - MON$SWEEP_INTERVAL (sweep interval)
  - MON$READ_ONLY (read-only flag)
  - MON$FORCED_WRITES (sync writes flag)
  - MON$RESERVE_SPACE (reserve space flag)
  - MON$CREATION_DATE (creation date/time)
  - MON$PAGES (number of pages allocated on disk)
  - MON$BACKUP_STATE (current physical backup state)
      0: normal
      1: stalled
      2: merge
  - MON$STAT_ID (statistics ID)

MON$TRANSACTIONS (started transactions)
  - MON$TRANSACTION_ID (transaction ID)
  - MON$ATTACHMENT_ID (attachment ID)
  - MON$STATE (transaction state)
      0: idle
      1: active
  - MON$TIMESTAMP (transaction start date/time)
  - MON$TOP_TRANSACTION (top transaction)
  - MON$OLDEST_TRANSACTION (local OIT number)
  - MON$OLDEST_ACTIVE (local OAT number)
  - MON$ISOLATION_MODE (isolation mode)
      0: consistency
      1: concurrency
      2: read committed record version
      3: read committed no record version
  - MON$LOCK_TIMEOUT (lock timeout)
      -1: infinite wait
      0: no wait
      N: timeout N
  - MON$READ_ONLY (read-only flag)
  - MON$AUTO_COMMIT (auto-commit flag)
  - MON$AUTO_UNDO (auto-undo flag)
  - MON$STAT_ID (statistics ID)

Примечание 1: некоторые данные в этих таблицах будут доступны только при подключении через SYSDBA или RDB$ADMIN или пользователя-владельца базы данных. Пример: в таблице вложений (соединений) соединения других пользователей будут невидимы и пропущены для запросов пользователей, не являющихся администраторами.

Примечание 2: чтение из таблицы мониторинга может быть относительно медленным, особенно из таблиц, содержащих информацию, связанную с соединениями (также называемую вложениями). Медленный и блокирующий при этом. Поэтому не рекомендуется слишком часто заниматься чтением таблиц мониторинга.

person Arioch 'The    schedule 22.03.2017
comment
Нет. Таблицы $MON - худший метод, потому что он замедляет работу... Лучше декодировать структуру заголовка базы данных, чтобы получить эти значения из файла базы данных (я могу получить к нему доступ), но я не уверен - если FB Engine + обновление ОС это правильно на всех системах - person Сергей Никитин; 23.03.2017
comment
они обновляют его должным образом - когда хотят это сделать. Он читает их без предварительной очистки кешей сервера и ОС, что неправильно. В любом случае, чтение одной строки из MON$DATABASSE не должно сильно замедлять работу. FB3 также пытался ускорить таблицы mon и отказаться от поддержки API сервисов, так что у вас все равно должен быть план резервного копирования. Также вы можете обсудить это с самими разработчиками Firebird на sql.ru/forum/interbase - person Arioch 'The; 23.03.2017
comment
Тогда замедление — это очень относительное понятие, если вы делаете эту проверку 1000 раз в секунду — это одно дело, если вы делаете это раз в тысячу секунд — совсем другое. - person Arioch 'The; 23.03.2017