Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

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

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant