Статьи

Пул подключений и другие советы для пользователей драйвера CUBRID Node.js

В этом уроке мы рассмотрим более сложные темы:

  • Использование пула соединений
  • Запросы с параметрами
  • Некоторые советы по использованию драйверов

Но прежде чем мы начнем, давайте кратко рассмотрим, что произошло с момента выпуска первого драйвера CUBRID Node.js 1.0 в октябре этого года.

Большая новость заключается в том, что мы только что выпустили новый драйвер node-cubrid 1.1, и наиболее важным обновлением для этой версии является то, что он добавляет совместимость с двумя новыми недавними выпусками CUBRID:

  • CUBRID 8.4.3  с поддержкой Sharding базы данных и уровня балансировки нагрузки на уровне API
  • Бета-версия CUBRID 9.0  с 3-кратным увеличением производительности и поддержкой баз данных

В то же время драйвер сохраняет полную обратную совместимость с предыдущей версией ядра 8.4.1 — так что вы можете использовать любой из последних выпусков движка, используя одну версию драйвера!

И если вам интересно, каковы конкретные различия в протоколе TCP / IP между различными версиями и как мы реализовали поддержку различных протоколов, пожалуйста, посмотрите на код драйвера и, в частности, на определения пакетов связи TCP / IP. ,

Использование пула соединений

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

  • Контролировать использование ресурсов сервера
  • Ускорьте процесс (подключение к базе данных обычно является «тяжелой» и длительной операцией)
  • Разрешить выполнение «параллельных» задач

connection_pooling.png

Рисунок 1: Пул соединений с базой данных.

Особо следует отметить, что протокол связи с базой данных TCP / IP является синхронным и не поддерживает / поддерживает параллельное выполнение. Это означает, что если вы хотите выполнять два или более запросов по-настоящему параллельно, вам нужно будет использовать несколько соединений с базой данных, и лучшим решением, безусловно, является реализация пула соединений.

В текущем выпуске драйвера CUBRID Node.js пока нет встроенного пула соединений, но поддерживается лучшее из доступных на сегодняшний день приложение пула соединений Node.js — пул  узлов .

Example Вы можете найти пример кода по использованию пула узлов с драйвером в разделе Общие примеры использования CUBRID Node.js API с примерами  блога. В этом блоге вы можете узнать, как использовать пул узлов  для:

  • Повторно использовать соединение с базой данных
  • Ограничить количество одновременных подключений

В этом блоге вы найдете не только пример использования пула узлов , но и многое другое ! Кроме того, вы найдете много примеров кода, которые помогут вам быстро начать работу с драйвером CUBRID Node.js!

Для этого руководства давайте использовать другую реализацию пула соединений Node.js — пул  узлов . И для этого примера давайте сосредоточимся на разных целях:

  • Ограничено количество одновременных подключений
  • Не держать соединение открытым до бесконечности; открывать соединение только при необходимости.

Итак, как мы используем пул узлов с node-cubrid?

Прежде всего — давайте определим пул с:

  • Максимум 2 «активных» клиента
  • Максимальное время простоя 30 сек.
  • Интервал проверки для незанятых клиентов составляет 1 сек.
  • создавать и уничтожать функции, которые генерируют идентификатор клиента и регистрируют эти события
    var conn_idx = 1;
     
    var pool = pooling.createPool({
        checkInterval: 1 * 1000,
        max: 2,
        maxIdleTime: 30*1000,
        name : 'my pool',
        create : function create(callback) {
            var client = new EventEmitter();
            client.id = conn_idx++;
            Helpers.logInfo('Creating pool client id: ' + client.id);
             
            return callback(null, client);
        },
        destroy : function destroy(client) {
            Helpers.logInfo('Destroyed pool client id: ' + client.id);
            client.was = client.id;
            client.id = -1;
        }
    });

Обратите внимание, что в определении пула ничего не говорится о подключении к базе данных CUBRID. Поскольку наша цель — открывать соединение только при необходимости, мы установим соединение в фактическом коде, как показано ниже.

Теперь мы запросим 3 клиентских соединения с базой данных CUBRID demodb , каждая из которых запрашивает выполнение запроса к другой таблице: код , нация , игра и для каждого из них отображает количество строк, возвращаемых в наборе результатов.

Поскольку мы не хотим держать соединение открытым, код закроет соединение с базой данных, когда выполнение запроса будет завершено. Для простоты мы перечислим здесь код только для одного из этих клиентских запросов — два других абсолютно одинаковы:

pool.acquire(function (err, client) {
    var CUBRIDClient = new CUBRIDConnection('localhost', 33000, 'public', '', 'demodb');
 
    CUBRIDClient.connect(function (err) {
        if (err === null) {
            Helpers.logInfo('Database connection acquired for pool client id: ' + client.id);
            Helpers.logInfo('Executing query: select * from code');
 
            CUBRIDClient.query("select * from code", function (err, result, queryHandle) {
                if (err) {
                    Helpers.logError(err);
                }
                else {
                    Helpers.logInfo('Query results rows count for pool client id: ' + client.id + ' is: ' + Result2Array.TotalRowsCount(result));
 
                    CUBRIDClient.closeQuery(queryHandle, function (err) {
                        if (err) {
                            Helpers.logError(err);
                        }
                        else {
                            Helpers.logInfo('Query closed for pool client id: ' + client.id);
 
                            CUBRIDClient.close(function (err) {
                                if (err) {
                                    Helpers.logError(err);
                                }
                                else {
                                    Helpers.logInfo('Connection closed for pool client id: ' + client.id);
 
                                    Helpers.logInfo('Waiting some time before releasing the pool client id: ' + client.id + '...');
 
                                    setTimeout(function () {
                                        Helpers.logInfo('Releasing pool client id: ' + client.id);
                                        pool.release(client);
                                    }, 10 * 1000);
                                }
                            });
                        }
                    });
                }
            });
        }
    });
});

И это вывод результатов выполнения, который показывает, как изначально были созданы только 2 клиента, и когда первое клиентское задание выполнено, оно «повторно используется» для выполнения третьего запроса:

node_pooling.png

Рисунок 2: Пул соединений с использованием узла пулов.

Как видите, совсем нетрудно объединить узел-сетку с пулом узлов (или узлом-пулом) для достижения реализации пула соединений, работающей с серверной частью приложения базы данных CUBRID.

Еще одна вещь, которую стоит упомянуть — мы рассматриваем возможность использования встроенного пула соединений в будущих выпусках драйверов, чтобы еще больше упростить использование драйвера CUBRID Node.js. Пожалуйста, дайте нам знать ваши отзывы и предложения!

Запросы с параметрами

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

В узле-CUBRID реализует две функции драйвера , что параметры поддержки:

функция Описание
executeWithParams(sql, arrParamsValues, arrDelimiters, callback) Выполните инструкцию SQL, которая не возвращает результаты набора записей.
queryWithParams(sql, arrParamsValues, arrDelimiters, callback) Выполните оператор SQL-запроса, который возвращает результаты набора записей (данные строк).

Обычно, когда мы обсуждаем параметры запроса, мы ссылаемся только на параметры, указанные в условиях query ( WHERE) — например:


1
SELECT
*
FROM
code
WHERE
s_name = ?
OR
f_name
LIKE
?

DriverCUBRID Драйвер Node.js продвигает концепцию параметров запроса на шаг вперед!

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


1
SELECT
*
FROM
?
WHERE
? = ?
OR
?
LIKE
?

«Очевидно, что такая большая гибкость сопровождается ценой, и« цена »заключается в том, что вам нужно будет указать правильные разделители для каждого параметра, поскольку разделители SQL могут различаться в зависимости от типа данных:

Тип данных параметра Стандартный разделитель (и)
строка ' (одинарная кавычка)
целое число никто
имя таблицы ` (Кавычка)
имя столбца ` (обратный удар) или нет

Например, давайте рассмотрим следующий очень общий запрос с параметрами:


1
SELECT
*
FROM
?
WHERE
?
LIKE
?
AND
LENGTH(?) > ?

И давайте предположим, что мы хотим выполнить:

cm_query_editor_example.png

Рисунок 3: Пример SQL и результатов запроса в CUBRID Manager.

, Затем необходимо использовать следующие разделители параметров:

Индекс параметра / положение Параметр «Область» Разделитель
1 Имя таблицы ` (Кавычка)
2 Название колонки никто
3 Условие (тип строки) ' (одинарная кавычка)
4 Название колонки никто
5 Состояние (тип номера) никто

И это означает, что так мы будем вызывать функцию node-cubridqueryWithParams :

var sql = 'SELECT * FROM ? WHERE ? LIKE ? AND LENGTH(?) > ?';
 
var arrValues = ['nation', 'code', 'A%', 'capital', '5'];
 
var arrDelimiters = ['`', '', '\'', '', ''];
 
function errorHandler(err) {
    throw err.message;
}
 
CUBRIDClient.connect(function (err) {
    if (err) {
        errorHandler(err);
    }
    else {
        CUBRIDClient.queryWithParams(sql, arrValues, arrDelimiters, function (err, result, queryHandle) {
            if (err) {
                errorHandler(err);
            }
            else {
                assert(Result2Array.TotalRowsCount(result) === 12);
 
                CUBRIDClient.closeQuery(queryHandle, function (err) {
                    ...

Результат выполнения будет:

node_cubrid_query_with_params_results.png

Figure 4: Results of executing a query with parameters in node-cubrid.

Remember – When using SQL statements with parameters, you need to:

  • Use ? for each parameter placeholder
  • Specify the delimiters for each parameter as a JavaScript array in sequential order.

As a side note, you can also specify delimiters as hard-coded in the SQL query definitions – for example:

1
SELECT
*
FROM
`?`
WHERE
`?`
LIKE
‘?’
AND
LENGTH(?) > ?

If you choose to do this, then the parameters you need to provide will obviously be always empty string values.

Some driver usage tips

Querying database schema

All CUBRID drivers provide, in various degrees, support for querying a database schema, which is giving the client the ability to get information about the CUBRID database objects:

  • Tables
  • Views
  • Stored procedures
  • Indexes
  • Etc.

In node-cubrid, so far we are providing a built-in support for:

  • Tables
  • Views

And this is how you do it:

CUBRIDClient.getSchema(CUBRIDClient.SCHEMA_TABLE, callback);
CUBRIDClient.getSchema(CUBRIDClient.SCHEMA_VIEW, callback);

The results are:

node_cubrid_get_schema.png

Figure 5: SCHEMA_TABLE results.

node_cubrid_get_schema_2.png

Figure 6: SCHEMA_VIEW results.

However, you can easily extend the built-in support by simply querying the schema catalog tables and views! The schema catalog will “tell” you about columns, users etc. All you need to do is to write appropriate SELECT queries, and the CUBRID ADO.NET Driver implementation will show you how to do it!

Tip: There is a nice open-source tool available for CUBRID – CUBRID Database Schema – which will show you, in a very friendly way, a CUBRID database schema content. An online demo is available at http://cubdbsch.cubrid.org/.

Stay tuned for the next driver release – 2.0 – which will feature extended schema support:

  • Tables and Views columns/attributes schema
  • Users and users’ access rights
  • Indexes and foreign keys
  • etc.

Querying without explicit connect

Did you know that you can do this?

var CUBRIDConnection = require('./node_modules/node-cubrid/src/CUBRIDConnection');
 
var CUBRIDClient = new CUBRIDConnection();
 
CUBRIDClient.query('select * from nation', function (err, result, queryHandle) {
....

As you can see, there is no connect() call – just direct query. And it works! Why?

The reason is that the driver automatically opens a connection for you in the background with default parameters, if you have not explicitly done that yourself. This means that by simply tweaking the default connect values (just edit the node-cubrid/src/CUBRIDConnection.js file) you can skip the connect() prerequisite and directly execute queries (and not only queries, but batch execute also works!). With default configurations it will connect to demodb database on localhost with dba user and an empty password.

BTW, if you call connection close() before closing the open queries using closeQuery(), don’t worry! The driver automatically takes care of closing all open query handles.

Getting the LAST INSERT ID

One of the most common patterns in database applications is to get the LAST INSERT ID, after performing database INSERT statement(s).

Depending on the driver used, there are various ways to support this functionality. For example, if you used CUBRID PHP Driver, you would use the function called cubrid_insert_id(). But how do you do this in node-cubrid?

node-cubrid 1.0 version does not provide yet a dedicated support for retrieving the LAST INSERT ID value(s). However, there is a quite simple workaround – use the built-in SQL function — LAST_INSERT_ID(). Let’s see how to do that:

CUBRIDClient.batchExecuteNoQuery([
    'drop table if exists node_test',
    'create table node_test(id INT AUTO_INCREMENT NOT NULL PRIMARY KEY)'
], function (err) {
    CUBRIDClient.batchExecuteNoQuery([
        'insert into node_test values(NULL)'
    ], function (err) {
        CUBRIDClient.query('select LAST_INSERT_ID()', function (err, result, queryHandle) {
            ...

Links & Resources