2.4. CouchDB复制协议

版本:3

这个 CouchDB Replication Protocol 是一个协议,用于通过HTTP/1.1在两个对等端之间使用public同步JSON文档 CouchDB REST API 它基于apachecouchdb MVCC 数据模型。

2.4.1. 序言

2.4.1.1. 语言

本文件中的关键词“必须”、“不得”、“必须”、“必须”、“不得”、“应当”、“不应”、“建议”、“可以”和“可选”的解释如所述 RFC 2119 .

2.4.1.2. 目标

本规范的主要目标是描述 CouchDB Replication Protocol 在引擎盖下面。

第二个目标是提供关于协议的足够详细的信息,以便在可以与CouchDB同步数据的任何语言和平台上构建工具。

2.4.1.3. 定义

JSON:
JSON 是用于序列化结构化数据的文本格式。如中所述 ECMA-262RFC 4627 .
URI:
URI由定义 RFC 3986 . 它可以是中定义的URL RFC 1738 .
身份证件:
标识符(可以是UUID),如中所述 RFC 4122 .
修订:
A MVCC 以下模式的令牌值: N-sig 在哪里? N 总是一个正整数,并且 sig 是文档签名(自定义)。不要把它和版本控制系统中的修订混淆在一起!
叶修订:
一系列更改中的最后一次文档修订。由于并发更新,文档可能有多个叶修订(也称为冲突修订)。
文件:
文档是一个JSON对象,其中定义了ID和修订 _id_rev 字段。文档的ID在存储文档的数据库中必须是唯一的。
笪塔巴涩:
具有唯一URI的文档集合。
更改源:
指定数据库的文档更改事件流(创建、更新、删除)。
序列号:
由Changes提要提供的ID。它必须是增量的,但不一定总是整数。
资料来源:
从中复制文档的数据库。
目标:
文档复制到的数据库。
复制:
源和目标端点的单向定向同步过程。
检查点:
用于复制恢复的中间记录序列ID。
复制器:
启动并运行复制的服务或应用程序。
过滤功能:
任何编程语言的一种特殊功能,用于在复制期间过滤文档(参见 过滤器功能
筛选器函数名:
筛选器函数的ID,可以用作符号引用(也称为回调函数),以便将相关的筛选器函数应用于复制。
筛选的复制:
使用筛选函数将文档从源复制到目标。
完全复制:
将所有文档从源复制到目标。
推送复制:
源为本地终结点而目标为远程的复制过程。
拉式复制:
源为远程终结点而目标为本地的复制过程。
连续复制:
“永不停止”的复制:在处理完来自更改提要的所有事件之后,复制器不会关闭连接,而是等待来自源的新更改事件。这种联系是通过周期性的心跳来维持的。
复制日志:
保存源和目标之间的复制历史记录(记录的检查点和一些其他统计信息)的特殊文档。
复制ID:
明确标识复制日志的唯一值。

2.4.2. 复制协议算法

这个 CouchDB Replication Protocol 不是 神奇的 但这是一个关于公众使用的协议 CouchDB HTTP REST API 使文档能够从源复制到目标。

参考实现,写入 Erlang, 由 couch_replicator apachecouchdb中的模块。

建议您遵循此算法规范,使用相同的HTTP端点,并使用相同的参数运行请求,以提供完全兼容的实现。定制的Replicator实现可能使用不同的httpapi端点和请求参数,这取决于它们的本地特性,并且它们可能只实现复制协议的一部分,以便只运行推或拉复制。然而,虽然这些解决方案也可以运行复制过程,但是它们与CouchDB Replicator的兼容性较差。

2.4.2.1. 验证对等项

+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
' 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}}` 请求。

2.4.2.1.1. 检查源是否存在

请求

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)

2.4.2.1.2. 检查目标是否存在

请求

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)

2.4.2.1.3. 创建目标?

在目标不存在的情况下,复制器可以 :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 或A 403 Forbidden 错误。这些错误应该预料到并妥善处理:

HTTP/1.1 500 Internal Server Error
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"
}

2.4.2.1.4. 中止

如果源或目标不存在,则应中止复制,并返回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"
}

2.4.2.2. 获取对等方信息

+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+
' 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 字段:此值将用于定义 暂时的 (因为数据库数据可能会发生更改)更改的上限会提供侦听和统计计算,以显示正确的复制进度。

2.4.2.2.1. 获取源信息

请求

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
}

2.4.2.2.2. 获取目标信息

请求

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"
}

2.4.2.3. 找到共同的祖先

+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
' 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         | '
'                             +-------------------------------------------+ '
'                                                                           '
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +

2.4.2.3.1. 生成复制ID

在开始复制之前,复制程序必须生成一个复制ID。此值用于跟踪复制历史记录,恢复并继续以前中断的复制过程。

复制ID生成算法是特定于实现的。无论使用什么算法,它都必须唯一地标识复制过程。例如,CouchDB的Replicator在生成复制ID时使用以下因素:

  • 持久对等UUID值。为当地人 Server UUID 使用
  • 源和目标URI,以及源或目标是本地还是远程数据库
  • 如果需要创建目标
  • 如果复制是连续的
  • 任何自定义标题
  • Filter function 代码(如果使用)
  • 更改源查询参数(如果有)

注解

couch_replicator_ids.erl 以获取复制ID生成实现的示例。

2.4.2.3.2. 从源和目标检索复制日志

生成复制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
}

复制日志应包含以下字段:

  • 历史数组 属于 对象 ):复制历史记录。 要求的
    • 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"
}

没关系。这意味着没有关于当前复制的信息,因此它以前一定没有运行过,因此复制器必须运行完整复制。

2.4.2.3.3. 比较复制日志

如果从源和目标成功检索到复制日志,则复制器必须按照下一个算法确定它们的共同祖先:

  • 比较 session_id 按时间顺序排列的上一个会话的值-如果它们同时匹配源和目标,则它们具有一个共同的复制历史记录,并且似乎是有效的。使用 source_last_seq 启动检查点的值
  • 如果不匹配,请在 history 集合以搜索最新(按时间顺序)的公共 session_id 对于源和目标。使用价值 recorded_seq 字段作为启动检查点

如果源和目标没有共同的祖先,复制器必须运行完全复制。

2.4.2.4. 查找更改的文档

+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
' 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  |                              '
'            +-------------------------------+                              '
'                                                                           '
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +

2.4.2.4.1. 收听更改源

定义了启动检查点后,复制器应该读取源代码的 Changes Feed 通过使用 :get:`/{{db}}/_changes` 请求。此请求必须使用以下查询参数:

  • feed 参数定义更改源响应样式:对于连续复制 continuous 应使用值,否则- normal .
  • style=all_docs 查询参数告诉源,它必须包括输出中每个文档事件的所有修订页。
  • 对于连续复制 heartbeat 参数定义心跳周期 毫秒 . 默认情况下,建议值为 10000 (10秒)。
  • 如果在复制日志比较期间发现启动检查点,则 since 查询参数必须与此值一起传递。在完全复制的情况下 0 (数字0)或被省略。

另外, filter 可以指定查询参数来启用 filter function 在源端。也可以提供其他自定义参数。

2.4.2.4.2. 读取批更改

一次阅读整个提要可能不是对资源的最佳利用。建议将馈送分成小块。然而,对于块大小没有具体的建议,因为它严重依赖于可用的资源:大的块需要更多的内存,同时减少I/O操作,反之亦然。

请注意,更改提要输出格式的请求与 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"}]}

对于这两种更改,Feed格式都保留了每行记录的样式,以简化迭代获取和解码JSON对象,并减少内存占用。

2.4.2.4.3. 计算修订差异

在从changes提要中读取了一批更改之后,Replicator为Document ID和相关的leaf Revisions形成一个JSON映射对象,并通过 :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对象:

Request

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)

{}

2.4.2.4.4. 复制已完成

当没有更多的更改需要处理,也没有更多的文档需要复制时,复制器完成复制过程。如果复制不是连续的,复制器可能会向客户机返回一个响应,其中包含有关进程的统计信息。

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
}

2.4.2.5. 复制更改

+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
' 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        |                           '
'               +-------------------------------------+                           '
'                                                                                 '
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +

2.4.2.5.1. 获取更改的文档

在这一步中,复制器必须从源获取所有在目标位置丢失的文档叶修订。如果复制将使用先前计算的修订差异(因为它们定义了缺少的文档及其修订),则此操作有效。

为了获取文档,复制器将生成一个 :get:`/{{db}}/{{docid}}` 具有以下查询参数的请求:

  • revs=true :指示源将所有已知修订的列表包括到 _revisions 字段。在源和目标之间同步文档的祖先历史记录时需要此信息
  • 这个 open_revs 查询参数包含一个JSON数组,其中包含需要获取的叶修订列表。如果存在指定的修订版,则必须返回该修订版的文档。否则,Source必须返回带有单个字段的对象 missing 以漏版为值。如果文档包含附件,则Source必须仅返回自指定修订值以来已更改(添加或更新)的附件的信息。如果附件已被删除,则该文档不得包含其存根信息
  • latest=true :确保源将返回最新的文档修订版,而不管在 open_revs 查询参数。此参数解决了一个争用条件问题,即请求的文档可能在此步骤和处理更改提要上的相关事件之间发生更改

在响应源中应返回 multipart/mixed 或者用 application/json 除非 Accept 标头指定不同的mime类型。这个 multipart/mixed content-type允许将响应数据作为流处理,因为可以有多个文档(每个叶修订版一个)加上多个附件。这些附件大多是二进制的,JSON无法处理这些数据,除了base64编码的字符串之外,这些字符串对于传输和处理操作非常无效。

用一个 multipart/mixed 响应Replicator将多个文档叶修订及其附件作为原始数据逐个处理,而不应用任何额外的编码。还有一个协议可以使数据处理更有效:文档总是在其附件之前进行,因此复制器不需要处理所有数据来映射相关文档附件,并且可以将其作为流处理,内存占用更少。

请求

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--

在接收到响应后,Replicator将所有接收到的数据放入本地堆栈中,以便进一步批量上载,从而有效地利用网络带宽。本地堆栈大小可能受文档数或处理的JSON数据字节数的限制。当堆栈已满时,Replicator将以批量模式将所有已处理的文档上载到目标。虽然强烈建议使用批量操作,但在某些情况下,复制器可能会将文档逐个上载到目标。

注解

替代的Replicator实现可以使用其他方法从源文件中检索文档。例如, PouchDB 不使用Multipart API,只获取带有内联附件的最新文档修订版作为单个JSON对象。虽然这仍然是有效的couchdbhttpapi用法,但是对于非CouchDB对等体,这样的解决方案可能需要不同的API实现。

2.4.2.5.2. 批量上传变更文件

要在一次快照中上载多个文档,Replicator将发送一个 :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 价值观。否则它必须包含 errorreason 具有错误类型和人性化原因描述的字段。

文档更新失败不是致命的,因为目标可能会因自身原因拒绝更新。建议使用错误类型 forbidden 对于拒绝,但也可以使用其他错误类型(如无效字段名等)。除非有充分的理由(例如,有特殊的错误类型),否则复制程序不应重试上载被拒绝的文档。

请注意,虽然更新响应中的一个文档可能失败,但Target仍然可以返回 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"
    }
]

2.4.2.5.3. 上载带附件的文档

有一种特殊的优化情况,即Replicator将不使用批量上载更改的文档。这种情况适用于文档包含大量附加文件或文件太大而无法使用Base64进行有效编码的情况。

对于这种情况,复制器发出一个 :put:`/{{db}}/{{docid}}?new_edits=false </{{db}}/{{docid}}>` 请求 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` 端点,则响应可能带有不同的状态代码。例如,在文档被拒绝的情况下,Target应该用 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 Unauthorized403 Forbidden409 Conflict412 Precondition Failed 因为重复请求无法解决用户凭据或上载数据的问题。

2.4.2.5.4. 确保提交

成功地将一批更改上载到目标后,复制器将发出一个 :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
    }
    

2.4.2.5.5. 记录复制检查点

由于已成功上载并提交了批更改,因此复制器将更新源和目标上的复制日志,记录当前复制状态。此操作是必需的,以便在复制失败的情况下,复制可以从最后一个成功点恢复,而不是从一开始就恢复。

Replicator更新源上的复制日志:

请求

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"
}

2.4.2.6. 继续读取更改

一旦一批更改被处理并成功地传输到目标系统,复制器就可以继续监听更改提要以获得新的更改。如果没有新的处理更改,则认为复制已完成。

对于连续复制,复制器必须继续等待来自源的新更改。

2.4.3. 协议稳健性

自从 CouchDB Replication Protocol 在基于TCP/IP的HTTP之上工作,复制器应该能够在一个不稳定的环境中工作,这种环境最终可能会出现延迟、丢失和其他意外情况。复制程序不应将每个HTTP请求失败都计算为 致命错误 . 它应该足够智能来检测超时、重复失败的请求、准备处理不完整或格式错误的数据等等。 数据必须流动 -这是规矩。

2.4.4. 错误响应

如果出现问题,对等方必须使用带有以下必需字段的JSON对象进行响应:

  • 错误一串 ):程序和开发人员的错误类型
  • 原因一串 ):人类的错误描述

2.4.4.1. 错误的请求

如果请求包含格式错误的数据(如无效的JSON),对等方必须用HTTP响应 400 Bad Requestbad_request 作为错误类型:

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

2.4.4.2. 未经授权

如果对等方要求请求中包含凭据,而请求不包含可接受的凭据,则该对等方必须使用HTTP响应 401 Unauthorizedunauthorized 作为错误类型:

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

2.4.4.3. 被禁止的

如果对等方接收到有效的用户凭据,但请求方没有足够的权限执行该操作,则该对等方必须用HTTP响应 403 Forbiddenforbidden 作为错误类型:

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

2.4.4.4. 找不到资源

如果在对等机上找不到请求的资源、数据库或文档,则该对等机必须用HTTP响应 404 Not Foundnot_found 作为错误类型:

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

2.4.4.5. 不允许的方法

如果使用了不受支持的方法,则对等方必须用HTTP响应 405 Method Not Allowedmethod_not_allowed 作为错误类型:

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

2.4.4.6. 资源冲突

当多个客户端同时更新同一资源时,会发生资源冲突错误。在这种情况下,对等方必须用HTTP响应 409 Conflictconflict 作为错误类型:

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

2.4.4.7. 先决条件失败

HTTP协议 412 Precondition Failed 在尝试创建数据库时可能会发送响应(错误类型 db_exists )已经存在或缺少某些附件信息(错误类型 missing_stub ). 没有明确的错误类型限制,但建议使用前面提到的错误类型:

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

2.4.4.8. 服务器错误

发生错误时引发 致命的 复制器不能做任何事情来继续复制。在这种情况下,复制器必须返回一个HTTP 500 Internal Server Error 带有错误描述的响应(不应用错误类型限制):

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

2.4.5. 优化

建议使用以下方法优化复制过程:

  • 将HTTP请求的数量保持在合理的最小值
  • 尝试使用连接池,并尽可能进行并行/多个请求
  • 不要在每次请求后关闭套接字:请遵守keep alive选项
  • 使用连续会话(cookies等)来减少身份验证开销
  • 尝试对文档的每个操作使用批量请求
  • 找出最佳批量大小的变化饲料处理
  • 保留复制日志并尽可能从上一个检查点恢复复制
  • 优化过滤器功能:让它们尽可能快地运行
  • 为意外做好准备:网络是非常不稳定的环境

2.4.6. API引用

2.4.6.1. 常用方法

  • :head:`/{{db}}` --检查数据库是否存在
  • :get:`/{{db}}` --检索数据库信息
  • :get:`/{{db}}/_local/{{docid}}` --读取最后一个检查点
  • :put:`/{{db}}/_local/{{docid}}` --保存新检查点

2.4.6.2. 针对目标

  • :put:`/{{db}}` --创建目标(如果不存在并且提供了选项)
  • :post:`/{{db}}/_revs_diff` --查找目标不知道的修订
  • :post:`/{{db}}/_bulk_docs` --将修订上载到目标
  • :put:`/{{db}}/{{docid}}` --将带有附件的单个文档上载到目标
  • :post:`/{{db}}/_ensure_full_commit` --确保所有更改都存储在磁盘上

2.4.6.3. 对于来源

  • :get:`/{{db}}/_changes` --获取自上次提取源以来的更改
  • :post:`/{{db}}/_changes` --获取自上次提取源以来指定文档ID的更改
  • :get:`/{{db}}/{{docid}}` --从包含附件的源中检索单个文档