将数据从 PouchDb 复制到 CouchDb 时失败

Fail while replicating data from PouchDb to CouchDb

我的代码

那是我当前代码的简短版本。:

['tableA', 'tableB', 'tableC'].forEach(name => {
    let local = new PouchDB(name, { auto_compaction: true })
    let server = new PouchDB(serverUrl + name)

    var filtro = {
      include_docs: true,
      filter: 'replication/by_dispositivo',
      query_params: { 'dispositivo_id': obj.deviceId }
    }
    local.replicate.from(server, filtro).on('complete', report => {
      var sync = local.sync(server, {
        live: true,
        retry: true,
        ...filtro
      })
    })
})

我正在尝试进行实时复制,但由于某种原因,它没有将本地数据复制到服务器,奇怪的是,PouchDb 没有抛出任何异常。

检查 Dev Tools 上的 Network 选项卡,我可以看到关注请求。:

URL: ${serverUrl}/{name}/_revs_diff
Response: {
    "4b0ea507-cd88-4998-baf0-01629b50516b": {
        "missing": [
            "2-2133d30de8d44ebd958cee2b68726ffb"
        ],
        "possible_ancestors": [
            "1-39904a7e55b1cb266c840a2acf34fdc2"
        ]
    }
}

好的,PouchDb 检测到服务器中缺少某些内容,必须进行复制。

审计同步

寻找有关正在发生的事情的提示,我修改了我的代码以记录 completeerror 事件。:

['tableA', 'tableB', 'tableC'].forEach(name => {
    let local = new PouchDB(name, { auto_compaction: true })
    let server = new PouchDB(serverUrl + name)

    let filtro = {
      include_docs: true,
      filter: 'replication/by_dispositivo',
      query_params: { 'dispositivo_id': obj.deviceId }
    }
    local.replicate.from(server, filtro).on('complete', report => {
      let sync = local.sync(server, {
        live: true,
        retry: true,
        ...filtro
      })
      sync.on('error', (error) => {
        console.error(error)
        console.error(JSON.stringify(error, null, 2))
      }).on('complete', (result) => {
        console.log(result)
        console.log(JSON.stringify(result, null, 2))
      })
      window.setTimeout(function (evt) {
        state.syncProcess[database].cancel()
      }, 15000)
    })
})

我在 error 事件中没有 catch 任何东西,并且 complete 事件没有显示任何错误,如下所示。

{
  "push": {
    "ok": true,
    "start_time": "2018-04-06T15:00:42.266Z",
    "docs_read": 0,
    "docs_written": 0,
    "doc_write_failures": 0,
    "errors": [],
    "status": "cancelled",
    "end_time": "2018-04-06T15:00:42.266Z",
    "last_seq": 0
  },
  "pull": {
    "ok": true,
    "start_time": "2018-04-06T15:00:26.422Z",
    "docs_read": 0,
    "docs_written": 0,
    "doc_write_failures": 0,
    "errors": [],
    "last_seq": "17-g1AAAAJDeJyd0EsOgjAQBuAqJj52nkCPILGldCU3UaYzBA3CQl3rTfQmehO9CRZKAiaGiJtpMs18mX8SxtgodpBNdXbSMUKQZDpM4uxwTMxXP2Qwy_N8Fzsh25vGMILIA62QjU8pUrRNCVvGYW4qrCphUgoCfMVd_W2mTQoKaV1JjpWWIUcuu0qbQjp_pBKeUESLH1OlA1PZxTwGudb7EC1dQt5xH6vdrHYvtF6pSZK-4Oov7WG1Z53QUy56UnRK-LJK406-TxIAm8ruDdzts44",
    "status": "cancelled",
    "end_time": "2018-04-06T15:00:41.427Z"
  }
}

手动调用一次本地到服务器复制

这是我第二次尝试捕捉有用的东西。我试图 audit local.replicate.to method

['tableA', 'tableB', 'tableC'].forEach(name => {
    let local = new PouchDB(name, { auto_compaction: true })
    let server = new PouchDB(serverUrl + name)

    let filtro = {
      include_docs: true,
      filter: 'replication/by_dispositivo',
      query_params: { 'dispositivo_id': obj.deviceId }
    }
    local.replicate.from(server, filtro).on('complete', report => {
      local.replicate.to(server, filtro).on('complete', report => {
        console.log(report)
        console.log(JSON.stringify(report, null, 2))
        let sync = local.sync(server, {
          live: true,
          retry: true,
          ...filtro
        })
      }).on('error', (error) => {
        console.error(error)
        console.error(JSON.stringify(error, null, 2))
      })
    })
})

那时候 complete event 没有被解雇,我抓住了 error,但这太笼统了,我没有提供任何关于正在发生的事情的线索。

{
  "result": {
    "ok": false,
    "start_time": "2018-04-06T15:07:19.105Z",
    "docs_read": 1,
    "docs_written": 0,
    "doc_write_failures": 0,
    "errors": [],
    "status": "aborting",
    "end_time": "2018-04-06T15:07:19.768Z",
    "last_seq": 3
  }
}

正在将本地数据放到服务器

那是我最后一次尝试了。:

我的垃圾代码

var deviceId = ''
var listLocal = []
var listServer = []

getDeviceId().then(response => {
    deviceId = response
    return local.find({ selector: { dispositivo_id: deviceId } })
}).then(response => {
    listLocal = response.docs
    return server.find({ selector: { dispositivo_id: deviceId } })
}).then(response => {
    listServer = response.docs

    var tlocal = listLocal[0]
    var tServer = listServer[0]
    Object.keys(tServer).forEach(key => {
      if (key.indexOf("_") !== 0) {
        tServer[key] = undefined
      }
    })
    Object.keys(tlocal).forEach(key => {
      if (key.indexOf("_") !== 0) {
        tServer[key] = tlocal[key]
      }
    })

    return server.put(tServer).then(result => {
      console.log(result)
      console.log(JSON.stringify(result, null, 2))
    }).catch(error => {
      console.error(error)
      console.error(JSON.stringify(error, null, 2))
    })
})

垃圾代码按预期工作,我收到了回复。:

{
  "ok": true,
  "id": "4b0ea507-cd88-4998-baf0-01629b50516b",
  "rev": "2-d9363f28e53fdc145610f5ad3f75a043"
}

附加信息

我在 CouchDb 中的设计文档

_设计/复制

{
  "_id": "_design/replication",
  "_rev": "1-42df919aaee8ed3fb309bbda999ba03d",
  "language": "javascript",
  "filters": {
    "by_dispositivo": "function(doc, req) {\r\n  return doc._id === '_design/replication' || (doc.dispositivo_id === req.query.dispositivo_id && !doc._deleted)\r\n}",
    "by_situacao_remote": "function(doc, req) {\r\n  return [2, 3, 4, 5].indexOf(doc.situacao) !== -1 && !doc._deleted\r\n}"
  }
}

_设计/授权

{
  "_id": "_design/authorization",
  "_rev": "9-64c4a22645d783c9089c95d69e9424ad",
  "language": "javascript",
  "validate_doc_update": "..."
}

authorization/validate_doc_update

function(newDoc, oldDoc, userCtx) {
  var isAdmin = userCtx.roles.indexOf('_admin') !== -1 || userCtx.roles.indexOf('admin') !== -1;
  if (!isAdmin) {
    if (newDoc._deleted) {
      if (oldDoc.dispositivo_id !== userCtx.name) {
        throw({forbidden: "..." });
      }
    } 
    else {
      if (!newDoc.dispositivo_id || !newDoc.dispositivo_id.trim())
        throw({forbidden: "..." });
      if (newDoc.dispositivo_id !== userCtx.name) {
        throw({forbidden: "..." });
      }

      if (oldDoc && oldDoc.dispositivo_id !== userCtx.name) {
        throw({forbidden: "..." });
      }

      var isRequired = function (prop, msg) {
        var value = newDoc[prop];
        if (!value)
            throw({forbidden: '...' });
      }

      var isDate = function (prop, msg, allow_null) {
        if (!allow_null) 
            isRequired(prop, msg)
        var value = newDoc[prop];
        if (value) {
            var date = new Date(value);
            var isDate = date !== "Invalid Date" && !isNaN(date);
            if (!isDate) {
              throw({forbidden: msg });
            }
        }
      }

      var isFloat = function (prop, msg, allow_null) {
        if (!allow_null) 
            isRequired(prop, msg)
        var value = newDoc[prop];
        if (value) {
            var numero = new Number(value);
            if (!numero || isNaN(numero) || !isFinite(numero)) {
              throw({forbidden: msg });
            }
        }
      }

      var isInteger = function (prop, msg, allow_null) {
        isFloat(prop, msg, allow_null)
        var value = newDoc[prop];
        if (value) {
            var numero = new Number(value);
            var isInteger = Math.floor(numero) == numero;
            if (!isInteger) {
              throw({forbidden: msg });
            }
        }
      }

      isRequired("talao_id", "...");
      isRequired("equipe_id", "...");

      isInteger("situacao", '...');
      isDate("data_envio", "...");
      isDate("data_recebimento", "...", true);
      isDate("data_decisao", "...", true);

      isRequired("tipo_ocorrencia_codigo", "...");
      isRequired("tipo_ocorrencia_descricao", "...");
      isInteger("talao_codigo", "...");
      isRequired("talao_descricao", "...");
      isRequired("talao_solicitante", "...");
      isRequired("talao_endereco", "...");
    }
  }
  else if (!newDoc._deleted) {
    if (!newDoc.dispositivo_id || !newDoc.dispositivo_id.trim())
      throw({forbidden: "..." });
  }
}

在分析 local.replicate.to 抛出的 exceptionstack trace 时,我注意到 reason: promise.all is not a function.

所以我用谷歌搜索了一会儿,找到了那个主题 Webpack: Promise is not a constructor。我只需要将 workaround 波纹管复制到我的 webpack.config 中,一切都很顺利。:

resolve: {
  alias: {
    'pouchdb-promise$': "pouchdb-promise/lib/index.js"
  }
}