Устранение неполадок в RethinkDB
Как получить дамп таблиц системы RethinkDB?
Это может быть полезно для диагностических целей, а также для подачи отчетов об ошибках. Самый простой способ сделать это — с помощью команд администрирования ReQL. Любую отдельную таблицу можно изучить с помощью r.db('rethinkdb').table(<tablename>).
Следующая команда выведет содержимое всех таблиц конфигурации/статуса, а также последние 50 строк таблицы logs.
r.expr(["current_issues", "jobs", "stats", "server_config", "server_status",
"table_config", "table_status", "db_config", "cluster_config"]).map(
[r.row, r.db('rethinkdb').table(r.row).coerceTo('array')]
).coerceTo('object').merge(
{logs: r.db('rethinkdb').table('logs').limit(50).coerceTo('array')}
)
(Эта команда подходит для использования в Data Explorer, но ее можно легко адаптировать для других языков.)
Получение ошибки «ReqlResourceLimitError: Массив превысил лимит размера 100000» при попытке упорядочить таблицу
Упорядочение без индекса требует, чтобы сервер загрузил всю последовательность в массив, размер которого по умолчанию ограничен 100 000 документами. Вы можете использовать опцию arrayLimit для выполнения, чтобы временно увеличить этот предел. Однако более эффективным вариантом является использование индекса. См. документацию для orderBy для получения дополнительной информации.
Мои запросы вставки медленные. Как их ускорить?
RethinkDB использует безопасную конфигурацию по умолчанию для подтверждения записи. Каждая запись сохраняется на диске перед подтверждением сервером клиенту. Если вы работаете с одним потоком, который вставляет документы в RethinkDB в цикле, каждая вставка должна ждать подтверждения сервера перед переходом к следующей. Это может значительно замедлить общую пропускную способность.
Это поведение аналогично любому другому безопасному системному базе данных. Ниже приведен ряд шагов, которые вы можете предпринять для ускорения производительности вставки в RethinkDB. Большинство этих рекомендаций также применимы к другим системам баз данных.
Увеличить параллельность. Вместо использования одного потока для вставки данных в цикле создайте несколько потоков с несколькими подключениями. Это позволит распараллелить запросы вставки без затрат большей части времени на ожидание подтверждения диска.
Вставка группами. Вместо выполнения отдельных записей в цикле объедините записи вместе. Это может привести к значительному увеличению пропускной способности. Вместо выполнения нескольких запросов вот так:
r.db("foo").table("bar").insert(document_1).run()
r.db("foo").table("bar").insert(document_2).run()
r.db("foo").table("bar").insert(document_3).run()
Объедините их в один запрос:
r.db("foo").table("bar").insert([document_1, document_2, document_3]).run()
RethinkDB работает с максимальной производительностью, когда размер пакета составляет около двухсот документов.
Рассмотрите возможность использования режима мягкой долговечности. В режиме мягкой долговечности RethinkDB подтвердит запись сразу после ее получения, но перед сохранением записи на диск. Сервер будет использовать оперативную память для приема записи и в фоновом режиме записывать новые данные на диск.
Этот режим менее безопасен, чем режим жесткой долговечности по умолчанию. Если вы выполняете запись с использованием режима мягкой долговечности, несколько секунд данных могут быть потеряны в случае сбоя питания.
Вы можете вставить данные в режиме мягкой долговечности следующим образом:
r.db("foo").table("bar").insert(document).run(durability="soft")
Примечание: Хотя при использовании режима мягкой долговечности некоторые данные могут быть потеряны в случае сбоя питания, база данных RethinkDB не будет повреждена.
Рассмотрите использование режима noreply. В этом режиме драйвер клиента не будет ждать подтверждения сервером запроса перед переходом к следующему запросу. Этот режим еще менее безопасен, чем режим мягкой долговечности, но может привести к максимальному улучшению производительности. Вы можете запустить команду в режиме noreply следующим образом:
r.db("foo").table("bar").insert(document).run(noreply=True)
Вы также можете объединить мягкую долговечность и noreply для максимальной производительности:
r.db("foo").table("bar").insert(document).run(durability="soft", noreply=True)
Как упорядочить вывод результата group?
Команды, объединенные после group, действуют на каждую группу отдельно. Если вы хотите обработать все группы сразу (например, упорядочить их), вам необходимо вызвать ungroup перед этим.
Что означает «получен неверный заголовок кластеризации»?
RethinkDB использует три порта для работы: порт веб-интерфейса HTTP, порт драйверов клиента и порт межкластерного трафика. Вы можете подключить браузер к порту веб-интерфейса для администрирования кластера прямо из браузера и подключить драйверы клиента к порту драйвера клиента для выполнения запросов из вашего приложения. Если вы работаете с кластером, различные узлы RethinkDB взаимодействуют друг с другом через порт межкластерного трафика.
Сообщение received invalid clustering header означает несовпадение портов и что что-то подключается к неправильному порту. Например, это распространенная ошибка, если вы случайно укажете браузер или подключите драйверы клиента к порту межкластерного трафика.
Поддерживает ли веб-интерфейс мой браузер?
Следующие браузеры поддерживаются и известны своей работой с веб-интерфейсом:
- Chrome 9 и выше
- Firefox 15 и выше
- Safari 6.02 и выше
- Opera 1.62 и выше
Веб-интерфейс требует поддержки
DataViewиUint8Arrayфункций JavaScript в вашем браузере.
Какие версии Node.js поддерживаются?
Драйвер JavaScript в настоящее время работает с версиями Node.js 0.10.0 и выше. Вы можете проверить свою версию Node следующим образом:
node --version
Вы можете обновить свою версию Node.js с помощью npm:
sudo npm install -g n
Если вы пытаетесь запустить драйвер RethinkDB JavaScript в более старой версии Node.js, вы можете получить ошибку, похожую на эту:
/home/user/rethinkdb.js:13727
return buffer.slice(offset, end);
^
TypeError: Object #<ArrayBuffer> has no method 'slice'
at bufferSlice (/home/user/rethinkdb.js:13727:17)
at Socket.TcpConnection.rawSocket.once.handshake_callback (/home/user/rethinkdb.js:13552:26)
Мне возвращается соединение в моем обработчике событий с драйвером Node
Многие люди сообщали, что получают объект подключения при выполнении запроса, объект имеет вид:
{
_conn: {
host: 'localhost',
port: 28015,
db: undefined,
authKey: '',
timeout: 20,
outstandingCallbacks: {},
nextToken: 2,
open: true,
buffer: <Buffer 04 00 00 00 08 02 10 01>,
_events: {},
rawSocket: { ... }
},
_token: 1,
_chunks: [],
_endFlag: true,
_contFlag: true,
_cont: null,
_cbQueue: []
}
Этот объект не является подключением, а курсором. Для получения результатов вы можете вызвать next, each или toArray в этом объекте.
Например, вы можете получить все результаты и поместить их в массив с помощью toArray:
r.table("test").run( conn, function(error, cursor) {
cursor.toArray( function(error, results) {
console.log(results) // results is an array of documents
})
})
RethinkDB испытывает недостаток памяти
Возможно, вам нужно настроить размер кэша страниц RethinkDB, используя аргумент --cache-size или параметр в конфигурационном файле. Подробнее о том, как RethinkDB использует память и как настроить ее производительность, читайте в разделе «Изучение потребностей RethinkDB в памяти».
Если вы запускаете RethinkDB на Linux и видите предупреждение «Данные из процесса на этом сервере помещены в память подкачки» в таблице Проблемы системы, даже если у вашего сервера есть доступная оперативная память, возможно, вам нужно настроить параметр ядра swappiness. Значение swappiness равное 0 предотвращает использование пространства подкачки, если у сервера не хватает физической памяти; значение 100 постоянно использует пространство подкачки. Чтобы проверить значение swappiness ядра:
$ cat /proc/sys/vm/swappiness
60
Значение 60 (значение по умолчанию для Ubuntu) означает, что система начнет использовать пространство подкачки, когда использование оперативной памяти достигнет примерно 40 %. Если вы хотите, чтобы этот порог был ближе к 90 %, установите swappiness в 10. Это можно сделать, отредактировав файл /etc/sysctl.conf (как root) и изменив там значение:
vm.swappiness = 10
Это изменение не вступит в силу до перезагрузки. Вы также можете изменить его, не перезагружая систему:
$ sysctl vm.swappiness=10
$ swapoff -a
$ swapon -a
Получение неверных результатов при передаче функций с операторами if/for в ReQL
При передаче функций в ReQL драйвер вашего языка сериализует эти функции в лямбда-функции ReQL, которые выполняются на сервере, а не на языке клиента. (См. Все о лямбда-функциях в запросах RethinkDB для получения дополнительной информации.) Следствием этого является то, что такие конструкции языка, как if и for, не дадут ожидаемого результата, когда их условия включают команды ReQL. Хотя они могут не вызывать ошибок, они будут выполняться на стороне клиента до компиляции функции для ReQL и, таким образом, дадут неверный результат. Вместо этого вы должны использовать эквивалентные управляющие функции ReQL, такие как branch и forEach. Вот пример на Python из документации Введение в ReQL:
# WRONG: Get all users older than 30 using the `if` statement
r.table('users').filter(lambda user:
True if user['age'] > 30 else False
).run(conn)
# RIGHT: Get all users older than 30 using the `r.branch` command
r.table('users').filter(lambda user:
r.branch(user['age'] > 30, True, False)
).run(conn)
И эквивалентный пример на JavaScript:
// WRONG: Get all users older than 30 using the ternary operator
r.table('users').filter(function(user) {
return (r.row('age').gt(30) ? true : false);
}).run(conn, callback)
// RIGHT: Get all users older than 30 using the `r.branch` command
r.table('users').filter(function(user) {
r.branch(user('age').gt(30), true, false)
}).run(conn, callback)
(Обратите внимание, что мы должны использовать gt вместо оператора > языка JavaScript по той же причине. В Python оператор > перегружен для перевода в команду ReQL gt, трюк, который невозможен в JavaScript.)
Как указать внешний канонический IP-адрес узла RethinkDB?
Когда узел RethinkDB запускается, он транслирует свой «канонический» IP-адрес, который другие узлы должны использовать для подключения к нему. По умолчанию канонический адрес — это основной IP-адрес сервера. Однако если этот адрес — внутренний IP-адрес, недоступный для других узлов (например, узлы находятся в разных сетях), узлы не смогут связаться друг с другом. Вы можете получить сообщение об ошибке, например:
error: received inconsistent routing information (wrong address) from xxx.xxx.xxx.xxx (expected_address = peer_address{ips=[xxx.xxx.xxx.xxx], port=29015}, other_address = peer_address{ips=[xxx.xxx.xxx.xxx], port=29015}), closing connection
Для решения этой проблемы явно укажите канонический адрес, используя аргумент --canonical-address.
rethinkdb --canonical-address <external IP>
Это также может быть указано в файле конфигурации.
Мой вторичный индекс устарел
При обновлении версий RethinkDB вы можете получить сообщение об ошибке о том, что вторичные индексы при запуске «устарели».
warn: Namespace <x> contains these outdated indexes which should be recreated:
<index names>
(Это может произойти, например, между версиями v1.13 и v1.14, когда внутренний формат вторичных индексов изменился.) Устаревшие индексы по-прежнему можно использовать — они не влияют на доступность. Однако вы должны перестроить свой индекс перед обновлением до следующей версии RethinkDB.
Вы можете перестроить индексы с помощью rethinkdb утилиты командной строки:
rethinkdb index-rebuild [-c HOST:PORT] [-r (DB|DB.TABLE)] [-n CONCURRENT_REBUILDS]
Опции -c и -r аналогичны другим опциям rethinkdb, определяя хост и порт кластера (по умолчанию localhost:28015) и либо базу данных, либо таблицу для перестроения. Опция -n указывает количество перестроек, которые будут выполняться одновременно (по умолчанию 1).
Вы также можете перестроить индексы вручную в ReQL:
- Используйте index_status для получения двоичного представления существующего вторичного индекса (является ли он простым, составным, множественным или основанным на выражении)
- Создайте новый индекс с помощью index_create
- Переименуйте новый индекс в имя старого индекса с помощью index_rename.
Простой пример на Python:
old_index = r.table('posts').index_status('old_index').nth(0)['function'].run(conn)
r.table('posts').index_create('new_index', old_index).run(conn)
r.table('posts').index_wait('new_index').run(conn)
r.table('posts').index_rename('new_index', 'old_index', overwrite=True).run(conn)
(Этот же пример можно найти в index_create для Ruby и JavaScript.)
Как сохранить объект Ruby DateTime в RethinkDB?
Короткий ответ: вы не можете. Используйте объекты Time вместо этого.
Несколько более длинный ответ: в RethinkDB существует только один встроенный time тип данных. Когда язык поддерживает более одного типа объектов даты/времени, мы считаем, что лучше явно поддерживать только один из них в драйвере клиента, чтобы избежать путаницы. В противном случае вы можете вставить объект DateTime и получить объект Time обратно.
Вы можете использовать методы Ruby DateTime.to_time и Time.to_datetime для легкого преобразования между ними.
Фильтры с or возвращают некорректные/неожиданные результаты
Возможно, вы захотите использовать filter для возврата документов, имеющих одно из двух (или более) необязательных полей, например, так:
r.table('posts').filter(
r.row('category').eq('article').or(r.row('genre').eq('mystery'))
).run(conn, callback);
Однако, если какой-либо документ в таблице posts выше не имеет поля category, он не будет включён в набор результатов, даже если у него есть поле genre со значением 'mystery'. Проблема не в команде or; проблема в том, что вызов r.row('category') для документа без этого поля возвращает ошибку, и остальная часть предиката фильтра не оценивается.
Решение состоит в добавлении default к команде row, которая всегда вычисляет значение, отличное от того, что вы проверяете, так что она вернёт false, если поле не существует:
r.table('posts').filter(
r.row('category').default('foo').eq('article').
or(r.row('genre').default('foo').eq('mystery'))
).run(conn, callback);
“Превышен лимит глубины вложения” ошибка
Обычно это указывает на то, что JSON-объект с поддокументами слишком глубоко вложен:
{ "level": 1,
"data": {
"level": 2,
"data": {
"level": 3,
"data": {
"level": 4
}
}
}
}
Глубина вложения в ReQL ограничена 20 уровнями. Это можно изменить с помощью необъявленной опции nestingDepth (или nesting_depth) для r.expr(), но прежде чем использовать её, подумайте, можно ли переорганизовать документ, чтобы избежать ошибки.
Также возможно, что эта ошибка вызвана циклической ссылкой, когда документ непреднамеренно содержит себя:
user1 = { id: 1, name: 'Bob' };
user2 = { id: 2, name: 'Agatha' };
user1['friends'] = [ user1, user2 ];
Попытка получить доступ к user1 в ReQL вызовет ошибку превышения глубины вложения.
В зависимости от драйвера эта ошибка может также отображаться как «Превышен максимальный уровень вложенности выражения».
“Объект RqlTzinfo не может быть сериализован в JSON” ошибка
Если вы пытаетесь сериализовать документ, содержащий объект временной зоны ReQL, используя библиотеку Python json, вы можете получить эту ошибку. Решите это, передав опцию time_format="raw" в run:
import json
today = r.expr(datetime.datetime.now(timezone('US/Pacific'))).run(conn,
time_format="raw")
json.dumps(today)
'{"timezone": "-07:00", "$reql_type$": "TIME", "epoch_time": 1433368112.289}'
“Нельзя использовать r.row в вложенных запросах” ошибка
Драйверы JavaScript и Python поддерживают удобную команду row(), которая просто возвращает выбранный в данный момент документ для использования с другими функциями ReQL в запросе. Однако row не будет работать во вложенных запросах. Решение этой ошибки — переписать row предложение как анонимную функцию. Например, следующее:
r.table('users').filter(
r.row['name'] == r.table('prizes').get('winner')
).run(conn)
Можно переписать с помощью этой функции вместо этого:
r.table('users').filter(
lambda doc: doc['name'] == r.table('prizes').get('winner')
).run(conn)
Любой запрос, вложенный или нет, может быть записан с помощью анонимной функции вместо row. (Официальные драйверы Ruby и Java не включают row вообще.)
© RethinkDB contributors
Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License.
https://rethinkdb.com/docs/troubleshooting/