【问题标题】:node.js data consistency when iterating asynchronously异步迭代时的node.js数据一致性
【发布时间】:2017-11-28 13:31:47
【问题描述】:

我有一个工具,基本思路如下:

//get a bunch of couchdb databases. this is an array
    const jsonFile = require('jsonfile');
    let dbList = getDbList();
    const filePath = 'some/path/to/file';
    const changesObject = {};


     //iterate the db list. do asynchronous stuff on each iteration
     dbList.forEach(function(db){
        let merchantDb = nano.use(db);

        //get some changes from the database. validate inside callback 
        merchantDb.get("_changes", function(err,changes){
          validateChanges(changes);
          changesObject['db'] = changes.someAttribute;
          //write changes to file
          jsonFile.writeFile(filePath, changesObject, function (err) {
            if (err) {
              logger.error("Unable to write to file: ");
            }
          });
    })

const validateChanges = function(changes) {
 if (!validateLogic(changes) sendAlertMail();
}

为了提高性能,迭代不是同步完成的。因此,可以“并行”运行多个迭代。我的问题是这会导致任何数据不一致和/或文件写入过程出现任何问题吗?

编辑: 每次迭代都会写入相同的文件。

编辑:2 更改存储为带有键值对的 JSON 对象。关键是数据库名称。

【问题讨论】:

  • 回答这个问题需要太多的猜测(我在开始回答后才意识到)。 jsonFile 是什么? file 是什么?你真的是指nano.use('db') 而不是nano.use(db)

标签: javascript node.js asynchronous concurrency couchdb


【解决方案1】:

如果你真的在写一个单个文件,你看起来是(虽然很难确定),那么不;您有一个竞争条件,其中多个回调可能同时尝试写入同一个文件(请记住,除非您使用 *Sync 函数,否则 I/O 不会在 Node 中的 JavaScript 线程上完成),这充其量意味着最后一个获胜,最坏的情况是由于重叠导致 I/O 错误。

如果您要为每个db 写入单独的文件,那么只要validateChangesvalidateLogicsendAlertMail 等之间没有串扰(共享状态),那应该没问题。

只是为了细节:它将启动任务(作业)获取更改,然后将它们写出来;对 get 的调用的回调将在稍后所有这些作业都排队时运行。

您正在循环中创建闭包,但是您这样做的方式还可以,因为您是在 forEach 回调中执行此操作,并且因为您没有在 get 回调中使用 db (可以使用forEach 回调,但不能使用其他一些可能循环数组的方式)。如果您有兴趣,请在this question's answers 中了解这方面的详细信息。

不过,这条线是可疑的:

let merchantDb = nano.use('db');

我怀疑你的意思是(没有引号):

let merchantDb = nano.use(db);

对于它的价值,从问题的更新和您的各种 cmets 听起来更好的解决方案是 每次都单独写出文件。相反,您希望收集更改,然后将它们写出来。

您可以使用您正在使用的经典节点回调 API 来做到这一点,如下所示:

let completed = 0;
//iterate the db list. do asynchronous stuff on each iteration
dbList.forEach(function(db) {
    let merchantDb = nano.use(db);

    //get some changes from the database. validate inside callback 
    merchantDb.get("_changes", function(err, changes) {
        if (err) {
            // Deal with the fact there was an error (don't return)
        } else {
            validateChanges(changes);
            changesObject[db] = changes.someAttribute; // <=== NOTE: This line had 'db' rather than db, I assume that was meant to be just db
        }
        if (++completed === dbList.length) {
            // All done, write changes to file
            jsonFile.writeFile(filePath, changesObject, function(err) {
                if (err) {
                    logger.error("Unable to write to file: ");
                }
            });
        }
    })
});

【讨论】:

  • 感谢您的回复。是的,它应该是nano.use(db)。我添加了一些导入语句以提高可读性。而且每次迭代都会写入同一个文件。
  • @fsociety:那么不,这根本不安全。您在单个文件上安排多个 I/O 操作,这些操作可能会重叠(请记住,除非您使用 *Sync 函数,否则 I/O 不会在 Node 中的 JavaScript 线程上完成),即使它们没有重叠,这将是“最后一个获胜”,因为您没有指定附加选项。来自writeFilejsonfile.writeFile 包装):“将数据异步写入文件,如果文件已存在则替换该文件。”
  • 每次迭代都有一个追加。我已经修改了问题以反映这一点。您对如何处理此处的文件写入有任何建议吗?像this 这样的简单排队实现可能吗?
  • @fsociety:不,知道何时完成多个异步操作并不需要使它们同步。是的,它会涉及一个回调函数。我不明白为什么会出现问题,因为当前代码使用回调函数。三,其实。 :-) 如果您有时间限制,我会肯定避免不必要的 I/O。我已经更新了答案,以展示在这种情况下你可以如何做到这一点。
  • @fsociety:JavaScript 这种语言不一定是单线程的。但是 Node 只运行一个 JavaScript 线程,所以我们不必担心上面的并发,不会。
猜你喜欢
  • 1970-01-01
  • 2016-07-10
  • 1970-01-01
  • 1970-01-01
  • 2013-08-04
  • 2015-08-24
  • 2014-07-04
  • 2016-06-17
  • 2018-10-09
相关资源
最近更新 更多