Spec-Zone.ru › CouchDB 3.5

Протокол репликации CouchDB

Версия:

3

Протокол репликации CouchDB — это протокол синхронизации документов JSON между двумя узлами по HTTP/1.1 с использованием общедоступного REST API CouchDB, основанный на модели данных MVCC Apache CouchDB.

Предисловие

Язык

Ключевые слова «MUST», «MUST NOT», «REQUIRED», «SHALL», «SHALL NOT», «SHOULD», «SHOULD NOT», «RECOMMENDED», «MAY» и «OPTIONAL» в этом документе следует интерпретировать в соответствии с описанием в RFC 2119.

Цели

Основная цель этой спецификации — описать внутреннее устройство Протокола репликации CouchDB.

Вторая цель — предоставить достаточно подробную информацию о протоколе, чтобы упростить создание инструментов на любом языке и платформе, способных синхронизировать данные с CouchDB.

Определения

JSON:

JSON — это текстовый формат сериализации структурированных данных. Он описан в ECMA-262 и RFC 4627.

URI:

URI определяется документом RFC 3986. Это может быть URL, определённый в RFC 1738.

ID:

Идентификатор (может быть UUID), описанный в RFC 4122.

Редакция:

Значение токена MVCC следующего шаблона: N-sig, где N ВСЕГДА является положительным целым числом, а sig — это подпись документа (пользовательская). Не путайте её с ревизией в системах контроля версий!

Листовая редакция:

Последняя редакция документа в последовательности изменений. Из-за одновременных обновлений у документов может быть несколько листовых редакций (также называемых конфликтующими редакциями).

Документ:

Документ — это объект JSON с ID и редакцией, определёнными соответственно в полях _id и _rev. ID документа ДОЛЖЕН быть уникальным в пределах базы данных, в которой он хранится.

База данных:

Коллекция документов с уникальным URI.

Лента изменений:

Поток событий изменения документов (создание, обновление, удаление) для указанной базы данных.

ID последовательности:

ID, предоставляемый лентой изменений. Он ДОЛЖЕН возрастать, но НЕ ОБЯЗАТЕЛЬНО всегда является целым числом.

Источник:

База данных, из которой реплицируются документы.

Цель:

База данных, в которую реплицируются документы.

Репликация:

Однонаправленный процесс синхронизации конечных точек источника и цели.

Контрольная точка:

Промежуточный записанный ID последовательности, используемый для восстановления репликации.

Репликатор:

Служба или приложение, которое инициирует и выполняет репликацию.

Функция фильтра:

Специальная функция на любом языке программирования, используемая для фильтрации документов во время репликации (см. Функции фильтра)

Имя функции фильтра:

ID функции фильтра, который можно использовать как символическую ссылку (также называемую функцией обратного вызова), чтобы применить соответствующую функцию фильтра к репликации.

Фильтруемая репликация:

Репликация документов из источника в цель с использованием функции фильтра.

Полная репликация:

Репликация всех документов из источника в цель.

Отправляющая репликация:

Процесс репликации, в котором источник является локальной конечной точкой, а цель — удалённой.

Получающая репликация:

Процесс репликации, в котором источник является удалённой конечной точкой, а цель — локальной.

Непрерывная репликация:

Репликация, которая «никогда не прекращается»: после обработки всех событий из ленты изменений репликатор не закрывает соединение, а ожидает новые события изменения от источника. Соединение поддерживается активным с помощью периодических сигналов проверки связи.

Журнал репликации:

Специальный документ, содержащий историю репликации (записанные контрольные точки и некоторую дополнительную статистику) между источником и целью.

ID репликации:

Уникальное значение, однозначно идентифицирующее журнал репликации.

Алгоритм протокола репликации

Протокол репликации CouchDB не является магическим — это соглашение об использовании общедоступного HTTP REST API CouchDB, позволяющее реплицировать документы из источника в цель.

Эталонная реализация, написанная на Erlang, предоставляется модулем couch_replicator в Apache CouchDB.

РЕКОМЕНДУЕТСЯ следовать этой спецификации алгоритма, использовать те же конечные точки HTTP и выполнять запросы с теми же параметрами, чтобы обеспечить полную совместимость реализации. Пользовательские реализации репликатора МОГУТ использовать другие конечные точки HTTP API и параметры запросов в зависимости от своих особенностей и МОГУТ реализовывать только часть протокола репликации, выполняя только отправляющую или получающую репликацию. Однако, хотя такие решения также могут выполнять процесс репликации, они теряют совместимость с репликатором CouchDB.

Проверка узлов

+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
' Verify Peers:                                                             '
'                                                                           '
'                404 Not Found   +--------------------------------+         '
'       +----------------------- |     Check Source Existence     |         '
'       |                        +--------------------------------+         '
'       |                        |          HEAD /source          |         '
'       |                        +--------------------------------+         '
'       |                          |                                        '
'       |                          | 200 OK                                 '
'       |                          v                                        '
'       |                        +--------------------------------+         '
'       |                        |     Check Target Existence     | ----+   '
'       |                        +--------------------------------+     |   '
'       |                        |         HEAD /target           |     |   '
'       |                        +--------------------------------+     |   '
'       |                          |                                    |   '
'       |                          | 404 Not Found                      |   '
'       v                          v                                    |   '
'   +-------+    No              +--------------------------------+     |   '
'   | Abort | <----------------- |         Create Target?         |     |   '
'   +-------+                    +--------------------------------+     |   '
'       ^                          |                                    |   '
'       |                          | Yes                                |   '
'       |                          v                                    |   '
'       |        Failure         +--------------------------------+     |   '
'       +----------------------- |          Create Target         |     |   '
'                                +--------------------------------+     |   '
'                                |           PUT /target          |     |   '
'                                +--------------------------------+     |   '
'                                  |                                    |   '
'                                  | 201 Created                 200 OK |   '
'                                  |                                    |   '
+ - - - - - - - - - - - - - - - -  | - - - - - - - - - - - - - - - - -  | - +
                                   |                                    |
+ - - - - - - - - - - - - - - - -  | - - - - - - - - - - - - - - - - -  | - +
' Get Peers Information:           |                                    |   '
'                                  +------------------------------------+   '
'                                  |                                        '
'                                  v                                        '
'                                +--------------------------------+         '
'                                |     Get Source Information     |         '
'                                +--------------------------------+         '
'                                                                           '
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +

Репликатор ДОЛЖЕН убедиться в существовании источника и цели с помощью запросов HEAD /{db}.

Проверка существования источника

Запрос:

HEAD /source HTTP/1.1
Host: localhost:5984
User-Agent: CouchDB

Ответ:

HTTP/1.1 200 OK
Cache-Control: must-revalidate
Content-Type: application/json
Date: Sat, 05 Oct 2013 08:50:39 GMT
Server: CouchDB (Erlang/OTP)

Проверка существования цели

Запрос:

HEAD /target HTTP/1.1
Host: localhost:5984
User-Agent: CouchDB

Ответ:

HTTP/1.1 200 OK
Cache-Control: must-revalidate
Content-Type: application/json
Date: Sat, 05 Oct 2013 08:51:11 GMT
Server: CouchDB (Erlang/OTP)

Создание цели?

Если цель не существует, репликатор МОЖЕТ отправить запрос PUT /{db}, чтобы создать её:

Запрос:

PUT /target HTTP/1.1
Accept: application/json
Host: localhost:5984
User-Agent: CouchDB

Ответ:

HTTP/1.1 201 Created
Content-Length: 12
Content-Type: application/json
Date: Sat, 05 Oct 2013 08:58:41 GMT
Server: CouchDB (Erlang/OTP)

{
    "ok": true
}

Однако запрос PUT репликатора МОЖЕТ завершиться неудачей из-за недостаточных привилегий (предоставленных указанными учётными данными), в результате чего будет получена ошибка 401 Unauthorized или 403 Forbidden. Следует ожидать таких ошибок и корректно их обрабатывать:

HTTP/1.1 401 Unauthorized
Cache-Control: must-revalidate
Content-Length: 108
Content-Type: application/json
Date: Fri, 09 May 2014 13:50:32 GMT
Server: CouchDB (Erlang OTP)

{
    "error": "unauthorized",
    "reason": "unauthorized to access or create database http://localhost:5984/target"
}

Прерывание

Если источник или цель не существуют, репликацию СЛЕДУЕТ прервать, вернув ответ HTTP с ошибкой:

HTTP/1.1 500 Internal Server Error
Cache-Control: must-revalidate
Content-Length: 56
Content-Type: application/json
Date: Sat, 05 Oct 2013 08:55:29 GMT
Server: CouchDB (Erlang OTP)

{
    "error": "db_not_found",
    "reason": "could not open source"
}

Получение информации об узлах

+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+
' Verify Peers:                                                    '
'                         +------------------------+               '
'                         | Check Target Existence |               '
'                         +------------------------+               '
'                                     |                            '
'                                     | 200 OK                     '
'                                     |                            '
+ - - - - - - - - - - - - - - - - - - | - - - - - - - - - - - - - -+
                                      |
+ - - - - - - - - - - - - - - - - - - | - - - - - - - - - - - - - -+
' Get Peers Information:              |                            '
'                                     v                            '
'                         +------------------------+               '
'                         | Get Source Information |               '
'                         +------------------------+               '
'                         |      GET /source       |               '
'                         +------------------------+               '
'                                     |                            '
'                                     | 200 OK                     '
'                                     v                            '
'                         +------------------------+               '
'                         | Get Target Information |               '
'                         +------------------------+               '
'                         |      GET /target       |               '
'                         +------------------------+               '
'                                     |                            '
'                                     | 200 OK                     '
'                                     |                            '
+ - - - - - - - - - - - - - - - - - - | - - - - - - - - - - - - - -+
                                      |
+ - - - - - - - - - - - - - - - - - - | - - - - - - - - - - - - - -+
' Find Common Ancestry:               |                            '
'                                     |                            '
'                                     v                            '
'                         +-------------------------+              '
'                         | Generate Replication ID |              '
'                         +-------------------------+              '
'                                                                  '
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+

Репликатор получает основные сведения об источнике и цели с помощью запросов GET /{db}. Ответ GET ДОЛЖЕН содержать объекты JSON со следующими обязательными полями:

  • instance_start_time (строка): Всегда "0". (Возвращается для обратной совместимости.)

  • update_seq (число / строка): Текущий ID последовательности базы данных.

Все остальные поля необязательны. Репликатору нужна информация из поля update_seq: это значение будет использоваться для определения временной верхней границы (поскольку данные базы данных могут изменяться) прослушивания ленты изменений и подсчёта статистики, необходимой для отображения правильного хода репликации.

Получение информации об источнике

Запрос:

GET /source HTTP/1.1
Accept: application/json
Host: localhost:5984
User-Agent: CouchDB

Ответ:

HTTP/1.1 200 OK
Cache-Control: must-revalidate
Content-Length: 256
Content-Type: application/json
Date: Tue, 08 Oct 2013 07:53:08 GMT
Server: CouchDB (Erlang OTP)

{
    "committed_update_seq": 61772,
    "compact_running": false,
    "db_name": "source",
    "disk_format_version": 6,
    "doc_count": 41961,
    "doc_del_count": 3807,
    "instance_start_time": "0",
    "purge_seq": 0,
    "sizes": {
      "active": 70781613961,
      "disk": 79132913799,
      "external": 72345632950
    },
    "update_seq": 61772
}

Получение информации о цели

Запрос:

GET /target/ HTTP/1.1
Accept: application/json
Host: localhost:5984
User-Agent: CouchDB

Ответ:

HTTP/1.1 200 OK
Content-Length: 363
Content-Type: application/json
Date: Tue, 08 Oct 2013 12:37:01 GMT
Server: CouchDB (Erlang/OTP)

{
    "compact_running": false,
    "db_name": "target",
    "disk_format_version": 5,
    "doc_count": 1832,
    "doc_del_count": 1,
    "instance_start_time": "0",
    "purge_seq": 0,
    "sizes": {
      "active": 50829452,
      "disk": 77001455,
      "external": 60326450
    },
    "update_seq": "1841-g1AAAADveJzLYWBgYMlgTmGQT0lKzi9KdUhJMtbLSs1LLUst0k"
}

Поиск общего предка

+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
' Get Peers Information:                                                    '
'                                                                           '
'                             +-------------------------------------------+ '
'                             |           Get Target Information          | '
'                             +-------------------------------------------+ '
'                               |                                           '
+ - - - - - - - - - - - - - - - | - - - - - - - - - - - - - - - - - - - - - +
                                |
+ - - - - - - - - - - - - - - - | - - - - - - - - - - - - - - - - - - - - - +
' Find Common Ancestry:         v                                           '
'                             +-------------------------------------------+ '
'                             |          Generate Replication ID          | '
'                             +-------------------------------------------+ '
'                               |                                           '
'                               |                                           '
'                               v                                           '
'                             +-------------------------------------------+ '
'                             |      Get Replication Log from Source      | '
'                             +-------------------------------------------+ '
'                             |     GET /source/_local/replication-id     | '
'                             +-------------------------------------------+ '
'                               |                                           '
'                               | 200 OK                                    '
'                               | 404 Not Found                             '
'                               v                                           '
'                             +-------------------------------------------+ '
'                             |      Get Replication Log from Target      | '
'                             +-------------------------------------------+ '
'                             |     GET /target/_local/replication-id     | '
'                             +-------------------------------------------+ '
'                               |                                           '
'                               | 200 OK                                    '
'                               | 404 Not Found                             '
'                               v                                           '
'                             +-------------------------------------------+ '
'                             |          Compare Replication Logs         | '
'                             +-------------------------------------------+ '
'                               |                                           '
'                               | Use latest common sequence as start point '
'                               |                                           '
+ - - - - - - - - - - - - - - - | - - - - - - - - - - - - - - - - - - - - - +
                                |
                                |
+ - - - - - - - - - - - - - - - | - - - - - - - - - - - - - - - - - - - - - +
' Locate Changed Documents:     |                                           '
'                               |                                           '
'                               v                                           '
'                             +-------------------------------------------+ '
'                             |        Listen Source Changes Feed         | '
'                             +-------------------------------------------+ '
'                                                                           '
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +

Генерация ID репликации

Перед началом репликации репликатор ДОЛЖЕН сгенерировать ID репликации. Это значение используется для отслеживания истории репликации, возобновления и продолжения ранее прерванного процесса репликации.

Алгоритм генерации ID репликации зависит от реализации. Независимо от используемого алгоритма он ДОЛЖЕН однозначно идентифицировать процесс репликации. Например, репликатор CouchDB использует следующие факторы при генерации ID репликации:

  • Постоянное значение UUID узла. В CouchDB используется локальное значение Server UUID

  • URI источника и цели, а также сведения о том, являются ли исходная или целевая базы данных локальными или удалёнными

  • Необходимость создания цели

  • Непрерывность репликации

  • Любые пользовательские заголовки

  • Код функции фильтра, если он используется

  • Параметры запроса ленты изменений, если они есть

Примечание

Пример реализации генерации ID репликации см. в файле couch_replicator_ids.erl.

Получение журналов репликации из источника и цели

После генерации ID репликации репликатору СЛЕДУЕТ получить журналы репликации как из источника, так и из цели с помощью GET /{db}/_local/{docid}:

Запрос:

GET /source/_local/b3e44b920ee2951cb2e123b63044427a HTTP/1.1
Accept: application/json
Host: localhost:5984
User-Agent: CouchDB

Ответ:

HTTP/1.1 200 OK
Cache-Control: must-revalidate
Content-Length: 1019
Content-Type: application/json
Date: Thu, 10 Oct 2013 06:18:56 GMT
ETag: "0-8"
Server: CouchDB (Erlang OTP)

{
    "_id": "_local/b3e44b920ee2951cb2e123b63044427a",
    "_rev": "0-8",
    "history": [
        {
            "doc_write_failures": 0,
            "docs_read": 2,
            "docs_written": 2,
            "end_last_seq": 5,
            "end_time": "Thu, 10 Oct 2013 05:56:38 GMT",
            "missing_checked": 2,
            "missing_found": 2,
            "recorded_seq": 5,
            "session_id": "d5a34cbbdafa70e0db5cb57d02a6b955",
            "start_last_seq": 3,
            "start_time": "Thu, 10 Oct 2013 05:56:38 GMT"
        },
        {
            "doc_write_failures": 0,
            "docs_read": 1,
            "docs_written": 1,
            "end_last_seq": 3,
            "end_time": "Thu, 10 Oct 2013 05:56:12 GMT",
            "missing_checked": 1,
            "missing_found": 1,
            "recorded_seq": 3,
            "session_id": "11a79cdae1719c362e9857cd1ddff09d",
            "start_last_seq": 2,
            "start_time": "Thu, 10 Oct 2013 05:56:12 GMT"
        },
        {
            "doc_write_failures": 0,
            "docs_read": 2,
            "docs_written": 2,
            "end_last_seq": 2,
            "end_time": "Thu, 10 Oct 2013 05:56:04 GMT",
            "missing_checked": 2,
            "missing_found": 2,
            "recorded_seq": 2,
            "session_id": "77cdf93cde05f15fcb710f320c37c155",
            "start_last_seq": 0,
            "start_time": "Thu, 10 Oct 2013 05:56:04 GMT"
        }
    ],
    "replication_id_version": 3,
    "session_id": "d5a34cbbdafa70e0db5cb57d02a6b955",
    "source_last_seq": 5
}

Журнал репликации СЛЕДУЕТ формировать со следующими полями:

  • history (массив объектов): История репликации. Обязательное поле

    • doc_write_failures (число): Число неудачных операций записи

    • docs_read (число): Число прочитанных документов

    • docs_written (число): Число записанных документов

    • end_last_seq (число): ID последней обработанной последовательности обновлений

    • end_time (строка): Временная метка завершения репликации в формате RFC 5322

    • missing_checked (число): Число проверенных редакций в источнике

    • missing_found (число): Число отсутствующих редакций, найденных в цели

    • recorded_seq (число): Записанная промежуточная контрольная точка. Обязательное поле

    • session_id (строка): Уникальный ID сеанса. Обычно используется случайное значение UUID. Обязательное поле

    • start_last_seq (число): Начальный ID последовательности обновлений

    • start_time (строка): Временная метка начала репликации в формате RFC 5322

  • replication_id_version (число): Версия протокола репликации. Определяет алгоритм вычисления ID репликации, вызовы HTTP API и другие процедуры. Обязательное поле

  • session_id (строка): Уникальный ID последнего сеанса. Сокращённая форма поля session_id последнего объекта history. Обязательное поле

  • source_last_seq (число): Последняя обработанная контрольная точка. Сокращённая форма поля recorded_seq последнего объекта history. Обязательное поле

Этот запрос МОЖЕТ завершиться ответом 404 Not Found:

Запрос:

GET /source/_local/b6cef528f67aa1a8a014dd1144b10e09 HTTP/1.1
Accept: application/json
Host: localhost:5984
User-Agent: CouchDB

Ответ:

HTTP/1.1 404 Object Not Found
Cache-Control: must-revalidate
Content-Length: 41
Content-Type: application/json
Date: Tue, 08 Oct 2013 13:31:10 GMT
Server: CouchDB (Erlang OTP)

{
    "error": "not_found",
    "reason": "missing"
}

Это нормально. Это означает, что сведения о текущей репликации отсутствуют, то есть, вероятно, она ещё не выполнялась, поэтому репликатор ДОЛЖЕН выполнить полную репликацию.

Сравнение журналов репликации

Если журналы репликации успешно получены как из источника, так и из цели, репликатор ДОЛЖЕН определить их общего предка, выполнив следующие действия:

  • Сравнить значения session_id для последнего по времени сеанса: если они совпадают, у источника и цели есть общая история репликации, которая, по-видимому, корректна. Использовать значение source_last_seq в качестве начальной контрольной точки

  • При несовпадении выполнить перебор коллекции history, чтобы найти последнюю по времени общую запись session_id источника и цели. Использовать значение поля recorded_seq в качестве начальной контрольной точки

Если у источника и цели нет общего предка, репликатор ДОЛЖЕН выполнить полную репликацию.

Поиск изменённых документов

+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
' Find Common Ancestry:                                                     '
'                                                                           '
'             +------------------------------+                              '
'             |   Compare Replication Logs   |                              '
'             +------------------------------+                              '
'                                          |                                '
'                                          |                                '
+ - - - - - - - - - - - - - - - - - - - -  |  - - - - - - - - - - - - - - - +
                                           |
+ - - - - - - - - - - - - - - - - - - - -  |  - - - - - - - - - - - - - - - +
' Locate Changed Documents:                |                                '
'                                          |                                '
'                                          |                                '
'                                          v                                '
'            +-------------------------------+                              '
'   +------> |     Listen to Changes Feed    | -----+                       '
'   |        +-------------------------------+      |                       '
'   |        |     GET  /source/_changes     |      |                       '
'   |        |     POST /source/_changes     |      |                       '
'   |        +-------------------------------+      |                       '
'   |                                      |        |                       '
'   |                                      |        |                       '
'   |                There are new changes |        | No more changes       '
'   |                                      |        |                       '
'   |                                      v        v                       '
'   |        +-------------------------------+    +-----------------------+ '
'   |        |     Read Batch of Changes     |    | Replication Completed | '
'   |        +-------------------------------+    +-----------------------+ '
'   |                                      |                                '
'   | No                                   |                                '
'   |                                      v                                '
'   |        +-------------------------------+                              '
'   |        |  Compare Documents Revisions  |                              '
'   |        +-------------------------------+                              '
'   |        |    POST /target/_revs_diff    |                              '
'   |        +-------------------------------+                              '
'   |                                      |                                '
'   |                               200 OK |                                '
'   |                                      v                                '
'   |        +-------------------------------+                              '
'   +------- |     Any Differences Found?    |                              '
'            +-------------------------------+                              '
'                                          |                                '
'                                      Yes |                                '
'                                          |                                '
+ - - - - - - - - - - - - - - - - - - - -  |  - - - - - - - - - - - - - - - +
                                           |
+ - - - - - - - - - - - - - - - - - - - -  |  - - - - - - - - - - - - - - - +
' Replicate Changes:                       |                                '
'                                          v                                '
'            +-------------------------------+                              '
'            |  Fetch Next Changed Document  |                              '
'            +-------------------------------+                              '
'                                                                           '
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +

Прослушивание ленты изменений

После определения начальной контрольной точки репликатору СЛЕДУЕТ читать ленту изменений источника с помощью запроса GET /{db}/_changes. Этот запрос ДОЛЖЕН содержать следующие параметры запроса:

  • Параметр feed задаёт стиль ответа ленты изменений: для непрерывной репликации СЛЕДУЕТ использовать значение continuous, в противном случае — normal.

  • Параметр запроса style=all_docs указывает источнику, что в вывод необходимо включить все листовые редакции для события каждого документа.

  • Для непрерывной репликации параметр heartbeat задаёт интервал сигнала проверки связи в миллисекундах. РЕКОМЕНДУЕМОЕ значение по умолчанию — 10000 (10 секунд).

  • Если при сравнении журналов репликации найдена начальная контрольная точка, параметр запроса since ДОЛЖЕН быть передан с этим значением. При полной репликации он МОЖЕТ быть равен 0 (число ноль) или отсутствовать.

Кроме того, для включения функции фильтра на стороне источника МОЖНО указать параметр запроса filter. Также МОЖНО передавать другие пользовательские параметры.

Чтение пакета изменений

Чтение всей ленты за один раз может быть не самым эффективным использованием ресурсов. РЕКОМЕНДУЕТСЯ обрабатывать ленту небольшими фрагментами. Однако конкретных рекомендаций по размеру фрагментов нет, поскольку он в значительной степени зависит от доступных ресурсов: большие фрагменты требуют больше памяти, но сокращают число операций ввода-вывода, и наоборот.

Обратите внимание: формат вывода ленты изменений для запроса с параметром feed=normal отличается от формата для параметра feed=continuous.

Обычная лента:

Запрос:

GET /source/_changes?feed=normal&style=all_docs&heartbeat=10000 HTTP/1.1
Accept: application/json
Host: localhost:5984
User-Agent: CouchDB

Ответ:

HTTP/1.1 200 OK
Cache-Control: must-revalidate
Content-Type: application/json
Date: Fri, 09 May 2014 16:20:41 GMT
Server: CouchDB (Erlang OTP)
Transfer-Encoding: chunked

{"results":[
{"seq":14,"id":"f957f41e","changes":[{"rev":"3-46a3"}],"deleted":true}
{"seq":29,"id":"ddf339dd","changes":[{"rev":"10-304b"}]}
{"seq":37,"id":"d3cc62f5","changes":[{"rev":"2-eec2"}],"deleted":true}
{"seq":39,"id":"f13bd08b","changes":[{"rev":"1-b35d"}]}
{"seq":41,"id":"e0a99867","changes":[{"rev":"2-c1c6"}]}
{"seq":42,"id":"a75bdfc5","changes":[{"rev":"1-967a"}]}
{"seq":43,"id":"a5f467a0","changes":[{"rev":"1-5575"}]}
{"seq":45,"id":"470c3004","changes":[{"rev":"11-c292"}]}
{"seq":46,"id":"b1cb8508","changes":[{"rev":"10-ABC"}]}
{"seq":47,"id":"49ec0489","changes":[{"rev":"157-b01f"},{"rev":"123-6f7c"}]}
{"seq":49,"id":"dad10379","changes":[{"rev":"1-9346"},{"rev":"6-5b8a"}]}
{"seq":50,"id":"73464877","changes":[{"rev":"1-9f08"}]}
{"seq":51,"id":"7ae19302","changes":[{"rev":"1-57bf"}]}
{"seq":63,"id":"6a7a6c86","changes":[{"rev":"5-acf6"}],"deleted":true}
{"seq":64,"id":"dfb9850a","changes":[{"rev":"1-102f"}]}
{"seq":65,"id":"c532afa7","changes":[{"rev":"1-6491"}]}
{"seq":66,"id":"af8a9508","changes":[{"rev":"1-3db2"}]}
{"seq":67,"id":"caa3dded","changes":[{"rev":"1-6491"}]}
{"seq":68,"id":"79f3b4e9","changes":[{"rev":"1-102f"}]}
{"seq":69,"id":"1d89d16f","changes":[{"rev":"1-3db2"}]}
{"seq":71,"id":"abae7348","changes":[{"rev":"2-7051"}]}
{"seq":77,"id":"6c25534f","changes":[{"rev":"9-CDE"},{"rev":"3-00e7"},{"rev":"1-ABC"}]}
{"seq":78,"id":"SpaghettiWithMeatballs","changes":[{"rev":"22-5f95"}]}
],
"last_seq":78}

Непрерывная лента:

Запрос:

GET /source/_changes?feed=continuous&style=all_docs&heartbeat=10000 HTTP/1.1
Accept: application/json
Host: localhost:5984
User-Agent: CouchDB

Ответ:

HTTP/1.1 200 OK
Cache-Control: must-revalidate
Content-Type: application/json
Date: Fri, 09 May 2014 16:22:22 GMT
Server: CouchDB (Erlang OTP)
Transfer-Encoding: chunked

{"seq":14,"id":"f957f41e","changes":[{"rev":"3-46a3"}],"deleted":true}
{"seq":29,"id":"ddf339dd","changes":[{"rev":"10-304b"}]}
{"seq":37,"id":"d3cc62f5","changes":[{"rev":"2-eec2"}],"deleted":true}
{"seq":39,"id":"f13bd08b","changes":[{"rev":"1-b35d"}]}
{"seq":41,"id":"e0a99867","changes":[{"rev":"2-c1c6"}]}
{"seq":42,"id":"a75bdfc5","changes":[{"rev":"1-967a"}]}
{"seq":43,"id":"a5f467a0","changes":[{"rev":"1-5575"}]}
{"seq":45,"id":"470c3004","changes":[{"rev":"11-c292"}]}
{"seq":46,"id":"b1cb8508","changes":[{"rev":"10-ABC"}]}
{"seq":47,"id":"49ec0489","changes":[{"rev":"157-b01f"},{"rev":"123-6f7c"}]}
{"seq":49,"id":"dad10379","changes":[{"rev":"1-9346"},{"rev":"6-5b8a"}]}
{"seq":50,"id":"73464877","changes":[{"rev":"1-9f08"}]}
{"seq":51,"id":"7ae19302","changes":[{"rev":"1-57bf"}]}
{"seq":63,"id":"6a7a6c86","changes":[{"rev":"5-acf6"}],"deleted":true}
{"seq":64,"id":"dfb9850a","changes":[{"rev":"1-102f"}]}
{"seq":65,"id":"c532afa7","changes":[{"rev":"1-6491"}]}
{"seq":66,"id":"af8a9508","changes":[{"rev":"1-3db2"}]}
{"seq":67,"id":"caa3dded","changes":[{"rev":"1-6491"}]}
{"seq":68,"id":"79f3b4e9","changes":[{"rev":"1-102f"}]}
{"seq":69,"id":"1d89d16f","changes":[{"rev":"1-3db2"}]}
{"seq":71,"id":"abae7348","changes":[{"rev":"2-7051"}]}
{"seq":75,"id":"SpaghettiWithMeatballs","changes":[{"rev":"21-5949"}]}
{"seq":77,"id":"6c255","changes":[{"rev":"9-CDE"},{"rev":"3-00e7"},{"rev":"1-ABC"}]}
{"seq":78,"id":"SpaghettiWithMeatballs","changes":[{"rev":"22-5f95"}]}

В обоих форматах ленты изменений сохраняется стиль «одна запись на строку», что упрощает итеративное получение данных и декодирование объектов JSON с меньшим объёмом используемой памяти.

Вычисление различий редакций

Прочитав пакет изменений из ленты изменений, репликатор формирует объект сопоставления JSON для ID документа и соответствующих листовых редакций и отправляет результат цели с помощью запроса POST /{db}/_revs_diff:

Запрос:

POST /target/_revs_diff HTTP/1.1
Accept: application/json
Content-Length: 287
Content-Type: application/json
Host: localhost:5984
User-Agent: CouchDB

{
    "baz": [
        "2-7051cbe5c8faecd085a3fa619e6e6337"
    ],
    "foo": [
        "3-6a540f3d701ac518d3b9733d673c5484"
    ],
    "bar": [
        "1-d4e501ab47de6b2000fc8a02f84a0c77",
        "1-967a00dff5e02add41819138abb3284d"
    ]
}

Ответ:

HTTP/1.1 200 OK
Cache-Control: must-revalidate
Content-Length: 88
Content-Type: application/json
Date: Fri, 25 Oct 2013 14:44:41 GMT
Server: CouchDB (Erlang/OTP)

{
    "baz": {
        "missing": [
            "2-7051cbe5c8faecd085a3fa619e6e6337"
        ]
    },
    "bar": {
        "missing": [
            "1-d4e501ab47de6b2000fc8a02f84a0c77"
        ]
    }
}

В ответе репликатор получает сопоставление ID документов и редакций, но только для редакций, отсутствующих в цели и подлежащих обязательной передаче из источника.

Если все редакции в запросе соответствуют текущему состоянию документов, ответ будет содержать пустой объект JSON:

Запрос

POST /target/_revs_diff HTTP/1.1
Accept: application/json
Content-Length: 160
Content-Type: application/json
Host: localhost:5984
User-Agent: CouchDB

{
    "foo": [
        "3-6a540f3d701ac518d3b9733d673c5484"
    ],
    "bar": [
        "1-967a00dff5e02add41819138abb3284d"
    ]
}

Ответ:

HTTP/1.1 200 OK
Cache-Control: must-revalidate
Content-Length: 2
Content-Type: application/json
Date: Fri, 25 Oct 2013 14:45:00 GMT
Server: CouchDB (Erlang/OTP)

{}

Завершение репликации

Когда больше нет изменений для обработки и документов для репликации, репликатор завершает процесс репликации. Если репликация не была непрерывной, репликатор МОЖЕТ вернуть клиенту ответ со статистикой процесса.

HTTP/1.1 200 OK
Cache-Control: must-revalidate
Content-Length: 414
Content-Type: application/json
Date: Fri, 09 May 2014 15:14:19 GMT
Server: CouchDB (Erlang OTP)

{
    "history": [
        {
            "doc_write_failures": 2,
            "docs_read": 2,
            "docs_written": 0,
            "end_last_seq": 2939,
            "end_time": "Fri, 09 May 2014 15:14:19 GMT",
            "missing_checked": 1835,
            "missing_found": 2,
            "recorded_seq": 2939,
            "session_id": "05918159f64842f1fe73e9e2157b2112",
            "start_last_seq": 0,
            "start_time": "Fri, 09 May 2014 15:14:18 GMT"
        }
    ],
    "ok": true,
    "replication_id_version": 3,
    "session_id": "05918159f64842f1fe73e9e2157b2112",
    "source_last_seq": 2939
}

Репликация изменений

+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
' Locate Changed Documents:                                                       '
'                                                                                 '
'               +-------------------------------------+                           '
'               |      Any Differences Found?         |                           '
'               +-------------------------------------+                           '
'                                                   |                             '
'                                                   |                             '
'                                                   |                             '
+ - - - - - - - - - - - - - - - - - - - - - - - - - | - - - - - - - - - - - - - - +
                                                    |
+ - - - - - - - - - - - - - - - - - - - - - - - - - | - - - - - - - - - - - - - - +
' Replicate Changes:                                |                             '
'                                                   v                             '
'               +-------------------------------------+                           '
'   +---------> |     Fetch Next Changed Document     | <---------------------+   '
'   |           +-------------------------------------+                       |   '
'   |           |          GET /source/docid          |                       |   '
'   |           +-------------------------------------+                       |   '
'   |             |                                                           |   '
'   |             |                                                           |   '
'   |             |                                          201 Created      |   '
'   |             | 200 OK                                   401 Unauthorized |   '
'   |             |                                          403 Forbidden    |   '
'   |             |                                                           |   '
'   |             v                                                           |   '
'   |           +-------------------------------------+                       |   '
'   |   +------ |  Document Has Changed Attachments?  |                       |   '
'   |   |       +-------------------------------------+                       |   '
'   |   |         |                                                           |   '
'   |   |         |                                                           |   '
'   |   |         | Yes                                                       |   '
'   |   |         |                                                           |   '
'   |   |         v                                                           |   '
'   |   |       +------------------------+   Yes    +---------------------------+ '
'   |   | No    |  Are They Big Enough?  | -------> | Update Document on Target | '
'   |   |       +------------------------+          +---------------------------+ '
'   |   |         |                                 |     PUT /target/docid     | '
'   |   |         |                                 +---------------------------+ '
'   |   |         |                                                               '
'   |   |         | No                                                            '
'   |   |         |                                                               '
'   |   |         v                                                               '
'   |   |       +-------------------------------------+                           '
'   |   +-----> |     Put Document Into the Stack     |                           '
'   |           +-------------------------------------+                           '
'   |             |                                                               '
'   |             |                                                               '
'   |             v                                                               '
'   |     No    +-------------------------------------+                           '
'   +---------- |           Stack is Full?            |                           '
'   |           +-------------------------------------+                           '
'   |             |                                                               '
'   |             | Yes                                                           '
'   |             |                                                               '
'   |             v                                                               '
'   |           +-------------------------------------+                           '
'   |           | Upload Stack of Documents to Target |                           '
'   |           +-------------------------------------+                           '
'   |           |       POST /target/_bulk_docs       |                           '
'   |           +-------------------------------------+                           '
'   |             |                                                               '
'   |             | 201 Created                                                   '
'   |             v                                                               '
'   |           +-------------------------------------+                           '
'   |           |          Ensure in Commit           |                           '
'   |           +-------------------------------------+                           '
'   |           |  POST /target/_ensure_full_commit   |                           '
'   |           +-------------------------------------+                           '
'   |             |                                                               '
'   |             | 201 Created                                                   '
'   |             v                                                               '
'   |           +-------------------------------------+                           '
'   |           |    Record Replication Checkpoint    |                           '
'   |           +-------------------------------------+                           '
'   |           |  PUT /source/_local/replication-id  |                           '
'   |           |  PUT /target/_local/replication-id  |                           '
'   |           +-------------------------------------+                           '
'   |             |                                                               '
'   |             | 201 Created                                                   '
'   |             v                                                               '
'   |     No    +-------------------------------------+                           '
'   +---------- | All Documents from Batch Processed? |                           '
'               +-------------------------------------+                           '
'                                                   |                             '
'                                               Yes |                             '
'                                                   |                             '
+ - - - - - - - - - - - - - - - - - - - - - - - - - | - - - - - - - - - - - - - - +
                                                    |
+ - - - - - - - - - - - - - - - - - - - - - - - - - | - - - - - - - - - - - - - - +
' Locate Changed Documents:                         |                             '
'                                                   v                             '
'               +-------------------------------------+                           '
'               |       Listen to Changes Feed        |                           '
'               +-------------------------------------+                           '
'                                                                                 '
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +

Получение изменённых документов

На этом этапе репликатор ДОЛЖЕН получить из источника все листовые редакции документов, отсутствующие в цели. Эта операция эффективна, если при репликации будут использованы ранее вычисленные различия редакций, поскольку они определяют отсутствующие документы и их редакции.

Для получения документа репликатор отправляет запрос GET /{db}/{docid} со следующими параметрами запроса:

  • revs=true: указывает источнику включить список всех известных редакций документа в поле _revisions. Эта информация необходима для синхронизации истории предков документа между источником и целью

  • Параметр запроса open_revs содержит массив JSON со списком листовых редакций, которые необходимо получить. Если указанная редакция существует, документ ДОЛЖЕН быть возвращён для этой редакции. В противном случае источник ДОЛЖЕН вернуть объект с единственным полем missing, содержащим отсутствующую редакцию. Если документ содержит вложения, источник ДОЛЖЕН возвращать сведения только о тех вложениях, которые были изменены (добавлены или обновлены) после указанных значений редакций. Если вложение было удалено, документ НЕ ДОЛЖЕН содержать для него заглушку

  • latest=true: гарантирует, что источник вернёт последнюю редакцию документа независимо от того, какая редакция была указана в параметре запроса open_revs. Этот параметр устраняет проблему гонки, при которой запрошенный документ может измениться между этим этапом и обработкой связанных событий в ленте изменений

В ответе источнику СЛЕДУЕТ возвращать содержимое типа multipart/mixed либо, если заголовок Accept не задаёт другой MIME-тип, ответить с типом application/json. Тип содержимого multipart/mixed позволяет обрабатывать данные ответа как поток, поскольку там может быть несколько документов (по одному для каждой листовой редакции), а также несколько вложений. Эти вложения в основном являются двоичными данными, а JSON не позволяет обрабатывать их иначе, чем в виде строк с кодировкой Base64, что крайне неэффективно при передаче и обработке.

Получив ответ multipart/mixed, репликатор обрабатывает несколько листовых редакций документов и их вложения по одному, как необработанные данные без дополнительного кодирования. Существует также соглашение, повышающее эффективность обработки данных: документ ВСЕГДА предшествует своим вложениям, поэтому репликатору не нужно обрабатывать все данные, чтобы сопоставить документы с вложениями; он может обрабатывать их как поток, используя меньше памяти.

Запрос:

GET /source/SpaghettiWithMeatballs?revs=true&open_revs=[%225-00ecbbc%22,%221-917fa23%22,%223-6bcedf1%22]&latest=true HTTP/1.1
Accept: multipart/mixed
Host: localhost:5984
User-Agent: CouchDB

Ответ:

HTTP/1.1 200 OK
Content-Type: multipart/mixed; boundary="7b1596fc4940bc1be725ad67f11ec1c4"
Date: Thu, 07 Nov 2013 15:10:16 GMT
Server: CouchDB (Erlang OTP)
Transfer-Encoding: chunked

--7b1596fc4940bc1be725ad67f11ec1c4
Content-Type: application/json

{
    "_id": "SpaghettiWithMeatballs",
    "_rev": "1-917fa23",
    "_revisions": {
        "ids": [
            "917fa23"
        ],
        "start": 1
    },
    "description": "An Italian-American delicious dish",
    "ingredients": [
        "spaghetti",
        "tomato sauce",
        "meatballs"
    ],
    "name": "Spaghetti with meatballs"
}
--7b1596fc4940bc1be725ad67f11ec1c4
Content-Type: multipart/related; boundary="a81a77b0ca68389dda3243a43ca946f2"

--a81a77b0ca68389dda3243a43ca946f2
Content-Type: application/json

{
    "_attachments": {
      "recipe.txt": {
          "content_type": "text/plain",
          "digest": "md5-R5CrCb6fX10Y46AqtNn0oQ==",
          "follows": true,
          "length": 87,
          "revpos": 7
      }
    },
    "_id": "SpaghettiWithMeatballs",
    "_rev": "7-474f12e",
    "_revisions": {
        "ids": [
            "474f12e",
            "5949cfc",
            "00ecbbc",
            "fc997b6",
            "3552c87",
            "404838b",
            "5defd9d",
            "dc1e4be"
        ],
        "start": 7
    },
    "description": "An Italian-American delicious dish",
    "ingredients": [
        "spaghetti",
        "tomato sauce",
        "meatballs",
        "love"
    ],
    "name": "Spaghetti with meatballs"
}
--a81a77b0ca68389dda3243a43ca946f2
Content-Disposition: attachment; filename="recipe.txt"
Content-Type: text/plain
Content-Length: 87

1. Cook spaghetti
2. Cook meetballs
3. Mix them
4. Add tomato sauce
5. ...
6. PROFIT!

--a81a77b0ca68389dda3243a43ca946f2--
--7b1596fc4940bc1be725ad67f11ec1c4
Content-Type: application/json; error="true"

{"missing":"3-6bcedf1"}
--7b1596fc4940bc1be725ad67f11ec1c4--

Получив ответ, репликатор помещает все полученные данные в локальный стек для последующей массовой загрузки с эффективным использованием пропускной способности сети. Размер локального стека может ограничиваться количеством документов или объёмом обрабатываемых данных JSON. Когда стек заполнен, репликатор массово загружает все обработанные документы в цель. Хотя настоятельно РЕКОМЕНДУЕТСЯ использовать массовые операции, в некоторых случаях репликатор МОЖЕТ загружать документы в цель по одному.

Примечание

Альтернативные реализации репликатора МОГУТ использовать другие способы получения документов из источника. Например, PouchDB не использует Multipart API и получает только последнюю редакцию документа со встроенными вложениями в виде единого объекта JSON. Хотя это допустимый вариант использования HTTP API CouchDB, для работы таких решений с узлами, отличными от CouchDB, МОЖЕТ потребоваться другая реализация API.

Загрузка пакета изменённых документов

Чтобы загрузить несколько документов за один запрос, репликатор отправляет цели запрос POST /{db}/_bulk_docs с полезной нагрузкой, содержащей объект JSON со следующими обязательными полями:

  • docs (массив объектов): Список объектов документов для обновления в цели. Эти документы ДОЛЖНЫ содержать поле _revisions со списком полной истории редакций, чтобы цель могла создать листовые редакции, правильно сохраняющие родословную

  • new_edits (логическое значение): Специальный флаг, указывающий цели сохранить документы с указанным значением редакции (поле _rev) без изменений, не создавая новую редакцию. Всегда false

Запрос также МОЖЕТ содержать заголовок X-Couch-Full-Commit, использовавшийся для управления поведением CouchDB до версии 3.0 при включённых отложенных фиксациях. Другие узлы МОГУТ игнорировать этот заголовок или использовать его для управления аналогичной локальной функцией.

Запрос:

POST /target/_bulk_docs HTTP/1.1
Accept: application/json
Content-Length: 826
Content-Type:application/json
Host: localhost:5984
User-Agent: CouchDB
X-Couch-Full-Commit: false

{
    "docs": [
        {
            "_id": "SpaghettiWithMeatballs",
            "_rev": "1-917fa2381192822767f010b95b45325b",
            "_revisions": {
                "ids": [
                    "917fa2381192822767f010b95b45325b"
                ],
                "start": 1
            },
            "description": "An Italian-American delicious dish",
            "ingredients": [
                "spaghetti",
                "tomato sauce",
                "meatballs"
            ],
            "name": "Spaghetti with meatballs"
        },
        {
            "_id": "LambStew",
            "_rev": "1-34c318924a8f327223eed702ddfdc66d",
            "_revisions": {
                "ids": [
                    "34c318924a8f327223eed702ddfdc66d"
                ],
                "start": 1
            },
            "servings": 6,
            "subtitle": "Delicious with scone topping",
            "title": "Lamb Stew"
        },
        {
            "_id": "FishStew",
            "_rev": "1-9c65296036141e575d32ba9c034dd3ee",
            "_revisions": {
                "ids": [
                    "9c65296036141e575d32ba9c034dd3ee"
                ],
                "start": 1
            },
            "servings": 4,
            "subtitle": "Delicious with fresh bread",
            "title": "Fish Stew"
        }
    ],
    "new_edits": false
}

В ответе цель ДОЛЖНА вернуть массив JSON со списком результатов обновления документов. Если документ был успешно сохранён, элемент списка ДОЛЖЕН содержать поле ok со значением true. В противном случае он ДОЛЖЕН содержать поля error и reason с типом ошибки и понятным пользователю описанием причины.

Неудачное обновление документа не является фатальной ошибкой, поскольку цель МОЖЕТ отклонить обновление по собственным причинам. Для отклонений РЕКОМЕНДУЕТСЯ использовать тип ошибки forbidden, однако могут использоваться и другие типы ошибок (например, неверное имя поля). Репликатору НЕ СЛЕДУЕТ повторно загружать отклонённые документы, если для этого нет веских оснований (например, специального типа ошибки, указывающего на необходимость повтора).

Обратите внимание: хотя обновление одного документа в ответе может завершиться неудачей, цель всё равно может вернуть ответ 201 Created. То же верно, если не удаётся обновить все загруженные документы.

Ответ:

HTTP/1.1 201 Created
Cache-Control: must-revalidate
Content-Length: 246
Content-Type: application/json
Date: Sun, 10 Nov 2013 19:02:26 GMT
Server: CouchDB (Erlang/OTP)

[
    {
        "ok": true,
        "id": "SpaghettiWithMeatballs",
        "rev":" 1-917fa2381192822767f010b95b45325b"
    },
    {
        "ok": true,
        "id": "FishStew",
        "rev": "1-9c65296036141e575d32ba9c034dd3ee"
    },
    {
        "error": "forbidden",
        "id": "LambStew",
        "reason": "sorry",
        "rev": "1-34c318924a8f327223eed702ddfdc66d"
    }
]

Загрузка документа с вложениями

Существует особый случай оптимизации, когда репликатор НЕ БУДЕТ использовать массовую загрузку изменённых документов. Этот случай возникает, когда документы содержат много вложенных файлов или файлы слишком велики для эффективного кодирования в Base64.

В этом случае репликатор отправляет запрос /{db}/{docid}?new_edits=false с типом содержимого multipart/related. Такой запрос позволяет легко передавать документ и все его вложения по одному в потоковом режиме без накладных расходов на сериализацию.

Запрос:

PUT /target/SpaghettiWithMeatballs?new_edits=false HTTP/1.1
Accept: application/json
Content-Length: 1030
Content-Type: multipart/related; boundary="864d690aeb91f25d469dec6851fb57f2"
Host: localhost:5984
User-Agent: CouchDB

--2fa48cba80d0cdba7829931fe8acce9d
Content-Type: application/json

{
    "_attachments": {
        "recipe.txt": {
            "content_type": "text/plain",
            "digest": "md5-R5CrCb6fX10Y46AqtNn0oQ==",
            "follows": true,
            "length": 87,
            "revpos": 7
        }
    },
    "_id": "SpaghettiWithMeatballs",
    "_rev": "7-474f12eb068c717243487a9505f6123b",
    "_revisions": {
        "ids": [
            "474f12eb068c717243487a9505f6123b",
            "5949cfcd437e3ee22d2d98a26d1a83bf",
            "00ecbbc54e2a171156ec345b77dfdf59",
            "fc997b62794a6268f2636a4a176efcd6",
            "3552c87351aadc1e4bea2461a1e8113a",
            "404838bc2862ce76c6ebed046f9eb542",
            "5defd9d813628cea6e98196eb0ee8594"
        ],
        "start": 7
    },
    "description": "An Italian-American delicious dish",
    "ingredients": [
        "spaghetti",
        "tomato sauce",
        "meatballs",
        "love"
    ],
    "name": "Spaghetti with meatballs"
}
--2fa48cba80d0cdba7829931fe8acce9d
Content-Disposition: attachment; filename="recipe.txt"
Content-Type: text/plain
Content-Length: 87

1. Cook spaghetti
2. Cook meetballs
3. Mix them
4. Add tomato sauce
5. ...
6. PROFIT!

--2fa48cba80d0cdba7829931fe8acce9d--

Ответ:

HTTP/1.1 201 Created
Cache-Control: must-revalidate
Content-Length: 105
Content-Type: application/json
Date: Fri, 08 Nov 2013 16:35:27 GMT
Server: CouchDB (Erlang/OTP)

{
    "ok": true,
    "id": "SpaghettiWithMeatballs",
    "rev": "7-474f12eb068c717243487a9505f6123b"
}

В отличие от массового обновления через конечную точку POST /{db}/_bulk_docs, ответ может иметь другой код состояния. Например, если документ отклонён, цели СЛЕДУЕТ ответить кодом 403 Forbidden:

Ответ:

HTTP/1.1 403 Forbidden
Cache-Control: must-revalidate
Content-Length: 39
Content-Type: application/json
Date: Fri, 08 Nov 2013 16:35:27 GMT
Server: CouchDB (Erlang/OTP)

{
    "error": "forbidden",
    "reason": "sorry"
}

Репликатору НЕ СЛЕДУЕТ повторять запросы при ответах 401 Unauthorized, 403 Forbidden, 409 Conflict или 412 Precondition Failed, поскольку повтор запроса не устранит проблему с учётными данными пользователя или загруженными данными.

Обеспечение фиксации

После успешной загрузки пакета изменений в цель репликатор отправляет запрос POST /{db}/_ensure_full_commit, чтобы гарантировать, что все переданные данные записаны на диск или в другое постоянное хранилище. Цель ДОЛЖНА вернуть ответ 201 Created с объектом JSON, содержащим следующие обязательные поля:

  • instance_start_time (строка): Временная метка открытия базы данных в микросекундах с начала эпохи

  • ok (логическое значение): Статус операции. Постоянно true

    Запрос:

    POST /target/_ensure_full_commit HTTP/1.1
    Accept: application/json
    Content-Type: application/json
    Host: localhost:5984

    Ответ:

    HTTP/1.1 201 Created
    Cache-Control: must-revalidate
    Content-Length: 53
    Content-Type: application/json
    Date: Web, 06 Nov 2013 18:20:43 GMT
    Server: CouchDB (Erlang/OTP)
    
    {
        "instance_start_time": "0",
        "ok": true
    }

Запись контрольной точки репликации

После успешной загрузки и фиксации пакетов изменений репликатор обновляет журнал репликации и в источнике, и в цели, записывая текущее состояние репликации. Эта операция ОБЯЗАТЕЛЬНА, чтобы в случае сбоя репликации её можно было возобновить с последней успешной точки, а не с самого начала.

Репликатор обновляет журнал репликации в источнике:

Запрос:

PUT /source/_local/afa899a9e59589c3d4ce5668e3218aef HTTP/1.1
Accept: application/json
Content-Length: 591
Content-Type: application/json
Host: localhost:5984
User-Agent: CouchDB

{
    "_id": "_local/afa899a9e59589c3d4ce5668e3218aef",
    "_rev": "0-1",
    "_revisions": {
        "ids": [
            "31f36e40158e717fbe9842e227b389df"
        ],
        "start": 1
    },
    "history": [
        {
            "doc_write_failures": 0,
            "docs_read": 6,
            "docs_written": 6,
            "end_last_seq": 26,
            "end_time": "Thu, 07 Nov 2013 09:42:17 GMT",
            "missing_checked": 6,
            "missing_found": 6,
            "recorded_seq": 26,
            "session_id": "04bf15bf1d9fa8ac1abc67d0c3e04f07",
            "start_last_seq": 0,
            "start_time": "Thu, 07 Nov 2013 09:41:43 GMT"
        }
    ],
    "replication_id_version": 3,
    "session_id": "04bf15bf1d9fa8ac1abc67d0c3e04f07",
    "source_last_seq": 26
}

Ответ:

HTTP/1.1 201 Created
Cache-Control: must-revalidate
Content-Length: 75
Content-Type: application/json
Date: Thu, 07 Nov 2013 09:42:17 GMT
Server: CouchDB (Erlang/OTP)

{
    "id": "_local/afa899a9e59589c3d4ce5668e3218aef",
    "ok": true,
    "rev": "0-2"
}

…и в цели:

Запрос:

PUT /target/_local/afa899a9e59589c3d4ce5668e3218aef HTTP/1.1
Accept: application/json
Content-Length: 591
Content-Type: application/json
Host: localhost:5984
User-Agent: CouchDB

{
    "_id": "_local/afa899a9e59589c3d4ce5668e3218aef",
    "_rev": "1-31f36e40158e717fbe9842e227b389df",
    "_revisions": {
        "ids": [
            "31f36e40158e717fbe9842e227b389df"
        ],
        "start": 1
    },
    "history": [
        {
            "doc_write_failures": 0,
            "docs_read": 6,
            "docs_written": 6,
            "end_last_seq": 26,
            "end_time": "Thu, 07 Nov 2013 09:42:17 GMT",
            "missing_checked": 6,
            "missing_found": 6,
            "recorded_seq": 26,
            "session_id": "04bf15bf1d9fa8ac1abc67d0c3e04f07",
            "start_last_seq": 0,
            "start_time": "Thu, 07 Nov 2013 09:41:43 GMT"
        }
    ],
    "replication_id_version": 3,
    "session_id": "04bf15bf1d9fa8ac1abc67d0c3e04f07",
    "source_last_seq": 26
}

Ответ:

HTTP/1.1 201 Created
Cache-Control: must-revalidate
Content-Length: 106
Content-Type: application/json
Date: Thu, 07 Nov 2013 09:42:17 GMT
Server: CouchDB (Erlang/OTP)

{
    "id": "_local/afa899a9e59589c3d4ce5668e3218aef",
    "ok": true,
    "rev": "2-9b5d1e36bed6ae08611466e30af1259a"
}

Продолжение чтения изменений

После успешной обработки и передачи цели пакета изменений репликатор может продолжить прослушивание ленты изменений в ожидании новых изменений. Если новых изменений для обработки нет, репликация считается завершённой.

При непрерывной репликации репликатор ДОЛЖЕН продолжать ожидать новых изменений от источника.

Надёжность протокола

Поскольку Протокол репликации CouchDB работает поверх HTTP, основанного на TCP/IP, репликатору СЛЕДУЕТ рассчитывать на работу в нестабильной среде, где возможны задержки, потери и другие неприятные неожиданности. Репликатор НЕ ДОЛЖЕН считать каждый сбой HTTP-запроса фатальной ошибкой. Он ДОЛЖЕН быть достаточно умным, чтобы обнаруживать тайм-ауты, повторять неудачные запросы, быть готовым обрабатывать неполные или некорректные данные и так далее. Данные должны передаваться — таково правило.

Ответы с ошибками

В случае возникновения ошибки пир ДОЛЖЕН ответить объектом JSON со следующими ОБЯЗАТЕЛЬНЫМИ полями:

  • error (строка): тип ошибки для программ и разработчиков

  • reason (строка): описание ошибки для людей

Некорректный запрос

Если запрос содержит некорректные данные (например, недопустимый JSON), пир ДОЛЖЕН ответить HTTP-статусом 400 Bad Request и указать bad_request в качестве типа ошибки:

{
    "error": "bad_request",
    "reason": "invalid json"
}

Не авторизован

Если пир ТРЕБУЕТ передавать в запросе учётные данные, а запрос не содержит допустимых учётных данных, пир ДОЛЖЕН ответить HTTP-статусом 401 Unauthorized и указать unauthorized в качестве типа ошибки:

{
    "error": "unauthorized",
    "reason": "Name or password is incorrect"
}

Доступ запрещён

Если пир получает действительные учётные данные пользователя, но у запрашивающей стороны недостаточно прав для выполнения операции, пир ДОЛЖЕН ответить HTTP-статусом 403 Forbidden и указать forbidden в качестве типа ошибки:

{
    "error": "forbidden",
    "reason": "You may only update your own user document."
}

Ресурс не найден

Если запрошенный ресурс, база данных или документ не найдены на пире, пир ДОЛЖЕН ответить HTTP-статусом 404 Not Found и указать not_found в качестве типа ошибки:

{
    "error": "not_found",
    "reason": "database \"target\" does not exists"
}

Метод не разрешён

Если был использован неподдерживаемый метод, пир ДОЛЖЕН ответить HTTP-статусом 405 Method Not Allowed и указать method_not_allowed в качестве типа ошибки:

{
    "error": "method_not_allowed",
    "reason": "Only GET, PUT, DELETE allowed"
}

Конфликт ресурсов

Ошибка конфликта ресурсов возникает, когда несколько клиентов одновременно обновляют один и тот же ресурс. В этом случае пир ДОЛЖЕН ответить HTTP-статусом 409 Conflict и указать conflict в качестве типа ошибки:

{
    "error": "conflict",
    "reason": "document update conflict"
}

Предварительное условие не выполнено

Ответ HTTP 412 Precondition Failed может быть отправлен при попытке создать уже существующую базу данных (тип ошибки db_exists) или если отсутствуют некоторые сведения о вложении (тип ошибки missing_stub). Явных ограничений на тип ошибки нет, но РЕКОМЕНДУЕТСЯ использовать упомянутые ранее типы ошибок:

{
    "error": "db_exists",
    "reason": "database \"target\" exists"
}

Ошибка сервера

Возникает в случае, когда ошибка является фатальной и репликатор не может продолжить репликацию. В этом случае репликатор ДОЛЖЕН вернуть ответ HTTP 500 Internal Server Error с описанием ошибки (ограничения на тип ошибки отсутствуют):

{
    "error": "worker_died",
    "reason": "kaboom!"
}

Оптимизация

Для оптимизации процесса репликации РЕКОМЕНДУЕТСЯ придерживаться следующих подходов:

  • Сведите количество HTTP-запросов к разумному минимуму

  • Используйте пул соединений и по возможности отправляйте запросы параллельно или пакетами

  • Не закрывайте сокеты после каждого запроса: соблюдайте параметр keep-alive

  • Используйте постоянные сеансы (cookies и т. п.), чтобы сократить накладные расходы на аутентификацию

  • По возможности используйте пакетные запросы для всех операций с документами

  • Подберите оптимальный размер пакета для обработки ленты изменений

  • Сохраняйте журналы репликации и по возможности возобновляйте репликацию с последней контрольной точки

  • Оптимизируйте функции фильтрации: добивайтесь максимально быстрого выполнения

  • Будьте готовы к неожиданностям: сети — очень нестабильная среда

Справочник API

Общие методы

  • HEAD /{db} – Проверить наличие базы данных

  • GET /{db} – Получить сведения о базе данных

  • GET /{db}/_local/{docid} – Прочитать последнюю контрольную точку

  • PUT /{db}/_local/{docid} – Сохранить новую контрольную точку

Для целевой стороны

  • PUT /{db} – Создать целевую базу данных, если она не существует и передан соответствующий параметр

  • POST /{db}/_revs_diff – Найти ревизии, неизвестные целевой стороне

  • POST /{db}/_bulk_docs – Загрузить ревизии на целевую сторону

  • PUT /{db}/{docid} – Загрузить на целевую сторону один документ с вложениями

  • POST /{db}/_ensure_full_commit – Убедиться, что все изменения сохранены на диске

Для исходной стороны

  • GET /{db}/_changes – Получить изменения, произошедшие с момента последнего извлечения данных из источника

  • POST /{db}/_changes – Получить изменения для указанных идентификаторов документов, произошедшие с момента последнего извлечения данных из источника

  • GET /{db}/{docid} – Получить один документ с вложениями из источника

Ссылки

  • Вики Refuge RCouch

  • Вики CouchBase Lite IOS

Copyright © 2025 The Apache Software Foundation — Licensed under the Apache License 2.0
https://docs.couchdb.org/en/3.5.1/replication/protocol.html

Spec-Zone.ru

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