Spec-Zone.ru › RethinkDB javascript

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

Что означает сообщение «received invalid clustering header»?

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