Всем привет.
Заметил, что не могу вставить большое кличество кортежей на nodejs
node v10.12.0
tarantool-driver@3.0.3
MacOS 10.14
for(let i = 1; i <= 1000; i++) {
connection.insert("your_space", [i, i*2, "fft" ])
.then(
() => { log("+OK"); },
err => { error("+ERR"); }
);
}
Очень хорошо воспроизводится на 1000, в итоге стабильно вставляется только 554 кортежа. В самом драйвере нашел несколько интересных моментов.
- Когда мы вызываем connection.insert("your_space", tuple); первым делом драйвер сделает select https://github.com/tarantool/node-tarantool-driver/blob/master/lib/commands.js#L21, чтобы получить id спейса. Если мы делаем 1000 запросов insert в space "your_space", драйвер сделает 1000 одинаковых select'ов на получение его id, хотя хватило бы и одного.
Да, там есть сохранение в spaceId this.namespace для последующего использования
_this.namespace[name] = {
id: spaceId,
name: name,
indexes: {}
};
Но у меня 1000 select'ов выполнялись прежде чем приходил ответ на первый.
- Сказанное в пункте 1 не должно влиять на вставку данных, т.к это не баг, а скорее небольшое упущение.
Ниже приведу пример лога драйвера вставки 1000 кортежей в space "your_space".
Запросы на получение id спейса по имени.
tarantool-driver:commands +OK - _replaceInsert cmd: 2 reqId: 0 spaceId: your_space tuple: [1,2,"fr"] +0ms
tarantool-driver:commands +OK - _getSpaceId name: your_space
tarantool-driver:main +OK - sendCommand. command: [1,1,{}] state: connect +1ms
tarantool-driver:main socket queue -> 1(1) +0ms
...1000 штук....
tarantool-driver:commands +OK - _replaceInsert cmd: 2 reqId: 1998 spaceId: your_space tuple: [1000,2000,"fr"] +0ms
tarantool-driver:commands +OK - _getSpaceId name: your_space +0ms
tarantool-driver:main +OK - sendCommand. command: [1,1999,{}] state: connect +0ms
tarantool-driver:main socket queue -> 1(1999) +1ms
после этого прилетает ответы на запросы. 65536 байт максимум, который мы можем получить за один раз.
tarantool-driver:handler +OK - dataHandler +31ms
tarantool-driver:handler +OK - dataHandler.CONNECTED: data.length 65536 +0ms
Каждый ответ на select имеет длину 147 байт, 142 - payload, и длина data.readUInt32BE(1); 4 байта с оффсетом 1. https://github.com/tarantool/node-tarantool-driver/blob/master/lib/event-handler.js#L63
Итого за один раз мы получили ответ на 445 select'ов и 121(116+5) байт 446-го ответа.
Т.к у нас есть начало следующего сообщения дравер сохраняет его и переводит dataState в состояние AWAITING https://github.com/tarantool/node-tarantool-driver/blob/master/lib/event-handler.js#L89
Следующим шагом драйвер резолвит 445 промисов созданных для получения id спейса вот тут https://github.com/tarantool/node-tarantool-driver/blob/master/lib/commands.js#L418
И отправляется 445 запросов на создание кортежей
tarantool-driver:commands +OK - _replaceInsert metadata: [535,0] +10ms
tarantool-driver:commands +OK - _replaceInsert cmd: 2 reqId: 0 spaceId: 535 tuple: [1,2,"fr"] +0ms
tarantool-driver:main +OK - sendCommand. command: [2,0,{}] state: connect +1ms
tarantool-driver:main socket queue -> 2(0) +1ms
...445 штук....
tarantool-driver:commands +OK - _replaceInsert metadata: [535,0] +0ms
tarantool-driver:commands +OK - _replaceInsert cmd: 2 reqId: 888 spaceId: 535 tuple: [445,890,"fr"] +0ms
tarantool-driver:main +OK - sendCommand. command: [2,888,{}] state: connect +0ms
tarantool-driver:main socket queue -> 2(888) +0ms
Как видно из лога spaceId = 535 полученные из базы.
Далее по логу нам приходит еще 65536 байт, ответ на наши select'ы и 116 было итого 65652
tarantool-driver:handler +OK - dataHandler +1ms
tarantool-driver:handler +OK - dataHandler.AWAITING data.length: 65536 awaitingResponseLength 142 bufferSlide.bufferLength: 116 +0ms
tarantool-driver:handler +OK - dataHandler.AWAITING data.length: 65652 +0ms
Еще на 446 сообщений
И т.к. у нас снова awaitingResponseLength > 0 https://github.com/tarantool/node-tarantool-driver/blob/master/lib/event-handler.js#L129
драйвер оставляет dataState в состоянии AWAITING https://github.com/tarantool/node-tarantool-driver/blob/master/lib/event-handler.js#L130 ,
но переводит еще и state в состояние AWAITING https://github.com/tarantool/node-tarantool-driver/blob/master/lib/event-handler.js#L131
Далее по логике вещей драйвер должен зарезолвить следующие 446 промисов, что должно будет вызвать запись следующих 446 кортежей.
но вот код функции sendCommand
https://github.com/tarantool/node-tarantool-driver/blob/master/lib/connection.js#L135
как видно здесь данные отправляются на сервер, только если state выставлен в CONNECTED, а state был только что переведен в AWAITING
И выполняется default блок https://github.com/tarantool/node-tarantool-driver/blob/master/lib/connection.js#L151
который просто складывает сообщения в offlineQueue.
tarantool-driver:main +OK - sendCommand. command: [2,890,{}] state: awaiting +0ms
tarantool-driver:main push top offlineQueue +0ms
...446 штук....
tarantool-driver:main +OK - sendCommand. command: [2,1780,{}] state: awaiting +1ms
tarantool-driver:main push top offlineQueue +0ms
Далее мы получаем по сети от сервера 35381 байт и 90 байт у нас есть от предыдущего сообщения
в этом чанке содержатся 110 ответов на наши селекты, остальные это ответы на 445 insert'ов, которые мы смогли отправить пока state был равен CONNECTED
tarantool-driver:handler +OK - dataHandler +0ms
tarantool-driver:handler +OK - dataHandler.AWAITING data.length: 35381 awaitingResponseLength 142 bufferSlide.bufferLength: 90 +0ms
tarantool-driver:handler +OK - dataHandler.AWAITING data.length: 35471 +0ms
после переработки этого чанка state снова выставляется в CONNECTED, но offlineQueue при этом не будет отправлена на сервер
https://github.com/tarantool/node-tarantool-driver/blob/master/lib/event-handler.js#L124
Оставшиеся сообщения будут отправлены в обычном режиме.
Резюме:
- По первому пункту я в дравере добавил проверку в эту функцию, что запрос на получение id для этого спейса уже отправлен и возвращаю уже существующий промис. Когда запрос будет выполнен, все промисы буду успешно зарезолвлены. Это ускорит множественную вставку, т.к будет только один запрос. Тесты прошли.
- Очевидный баг с тем, что offlineQueue не отправляется на сервер при переходе из состояния AWAITING или AWAITING_LENGTH в состояние CONNECTED
Всем привет.
Заметил, что не могу вставить большое кличество кортежей на nodejs
node v10.12.0
tarantool-driver@3.0.3
MacOS 10.14
Очень хорошо воспроизводится на 1000, в итоге стабильно вставляется только 554 кортежа. В самом драйвере нашел несколько интересных моментов.
Да, там есть сохранение в spaceId this.namespace для последующего использования
Но у меня 1000 select'ов выполнялись прежде чем приходил ответ на первый.
Ниже приведу пример лога драйвера вставки 1000 кортежей в space "your_space".
Запросы на получение id спейса по имени.
после этого прилетает ответы на запросы. 65536 байт максимум, который мы можем получить за один раз.
Каждый ответ на select имеет длину 147 байт, 142 - payload, и длина data.readUInt32BE(1); 4 байта с оффсетом 1. https://github.com/tarantool/node-tarantool-driver/blob/master/lib/event-handler.js#L63
Итого за один раз мы получили ответ на 445 select'ов и 121(116+5) байт 446-го ответа.
Т.к у нас есть начало следующего сообщения дравер сохраняет его и переводит dataState в состояние AWAITING https://github.com/tarantool/node-tarantool-driver/blob/master/lib/event-handler.js#L89
Следующим шагом драйвер резолвит 445 промисов созданных для получения id спейса вот тут https://github.com/tarantool/node-tarantool-driver/blob/master/lib/commands.js#L418
И отправляется 445 запросов на создание кортежей
Как видно из лога spaceId = 535 полученные из базы.
Далее по логу нам приходит еще 65536 байт, ответ на наши select'ы и 116 было итого 65652
Еще на 446 сообщений
И т.к. у нас снова awaitingResponseLength > 0 https://github.com/tarantool/node-tarantool-driver/blob/master/lib/event-handler.js#L129
драйвер оставляет dataState в состоянии AWAITING https://github.com/tarantool/node-tarantool-driver/blob/master/lib/event-handler.js#L130 ,
но переводит еще и state в состояние AWAITING https://github.com/tarantool/node-tarantool-driver/blob/master/lib/event-handler.js#L131
Далее по логике вещей драйвер должен зарезолвить следующие 446 промисов, что должно будет вызвать запись следующих 446 кортежей.
но вот код функции sendCommand
https://github.com/tarantool/node-tarantool-driver/blob/master/lib/connection.js#L135
как видно здесь данные отправляются на сервер, только если state выставлен в CONNECTED, а state был только что переведен в AWAITING
И выполняется default блок https://github.com/tarantool/node-tarantool-driver/blob/master/lib/connection.js#L151
который просто складывает сообщения в offlineQueue.
Далее мы получаем по сети от сервера 35381 байт и 90 байт у нас есть от предыдущего сообщения
в этом чанке содержатся 110 ответов на наши селекты, остальные это ответы на 445 insert'ов, которые мы смогли отправить пока state был равен CONNECTED
после переработки этого чанка state снова выставляется в CONNECTED, но offlineQueue при этом не будет отправлена на сервер
https://github.com/tarantool/node-tarantool-driver/blob/master/lib/event-handler.js#L124
Оставшиеся сообщения будут отправлены в обычном режиме.
Резюме: