Skip to content

Множественная вставка кортежей #44

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
affair opened this issue Oct 14, 2018 · 0 comments
Open

Множественная вставка кортежей #44

affair opened this issue Oct 14, 2018 · 0 comments

Comments

@affair
Copy link

affair commented Oct 14, 2018

Всем привет.
Заметил, что не могу вставить большое кличество кортежей на 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 кортежа. В самом драйвере нашел несколько интересных моментов.

  1. Когда мы вызываем 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. Сказанное в пункте 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

Оставшиеся сообщения будут отправлены в обычном режиме.

Резюме:

  1. По первому пункту я в дравере добавил проверку в эту функцию, что запрос на получение id для этого спейса уже отправлен и возвращаю уже существующий промис. Когда запрос будет выполнен, все промисы буду успешно зарезолвлены. Это ускорит множественную вставку, т.к будет только один запрос. Тесты прошли.
  2. Очевидный баг с тем, что offlineQueue не отправляется на сервер при переходе из состояния AWAITING или AWAITING_LENGTH в состояние CONNECTED
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant