Spec-Zone.ru › RethinkDB java

Устранение неполадок с 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