Spec-Zone.ru › RethinkDB ruby

Устранение неполадок с 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: Array over size limit 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/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API