Firebase计数器,膨胀更新案例

时间:2016-11-09 13:20:15

标签: javascript firebase firebase-realtime-database nosql

我们知道Firebase没有计数查询,拥有计数器的方法是两个,在客户端获取所有数据或在数据库中存储计数值。我使用第二种方法,因为第一种方法仅在数据很少时才有效。

这个方法的问题是我可以有很多并发编写器,我用我的方法解决了这个问题,如果没有存储数据,它会尝试用递增或递减的计数器重新保存数据。

好的,当我想只更改一个计数器时,此方法运行良好,但在不同的情况下,我想用一个事务更改许多不同的计数器,在这种模式下,存在一种风险,即在不同的尝试后数据无法保存,因为我们可以在许多不同的节点中拥有许多并发编写器。具有原子更新的数据结构示例如下:

如果我保存

"comment": {
   "counter": {},
   "Data": {
      "$Comment":{}
   }
}


comment/counter = val + 1
comment/Data/comment1 = data

如果我无法更新,它会工作,因为其他编写器正在更新计数器时,我可以使用新的计数器值重试,通常是在我第一次成功结束操作之后,但是,例如,如果我想删除一个用户和他的所有动作,我想存储这种类型的数据

"comment":{
  "counter":{},
  "Data": {
    "$Comment":{}
  }
},

"$user": {
  "follower":{
    "counter":{},
    "data":{
      "$Follower": {}
    }
  }
}
comment/counter = val - 1
comment/Data/comment2 = null
user1/follower/counter = val - 1
user1/follower/data/user30 = null
user2/follower/counter = val - 1
user2/follower/data/user30 = null
user3/follower/counter = val - 1
user3/follower/data/user30 = null

可见,在批量更新的情况下,我必须更新许多可以拥有大量并发写入器的计数器,在这种情况下,很容易就能够在不同的时候更新所有计数器重试。

另一种情况是这个

"photo":{
  "$photo":{
    "comment":{
      "counter":{},
      "Data": {
        "$Comment":{}
      }
   }
}

photo/10/comment/counter = val - 1
photo/10/comment/Data/comment1 = null
photo/10/comment/counter = val - 1
photo/10/comment/Data/comment2 = null
photo/10/comment/counter = val - 1
photo/10/comment/Data/comment3 = null

在这种情况下,无法更新计数器,因为我尝试删除具有相同计数器的不同注释,这些注释只能等于值+ 1或值-1。如果我想删除所有用户数据,则它是可能他在同一张照片中写了一些评论,但我不能在一次交易中删除所有这些评论,因为只能通过单笔交易更新计数器。

我发现很多这样的并发症只是因为不存在任何类型的数据库(sql或nosql)中存在的计数查询。我不懂为什么。这涉及很多问题,否则可以解决而不浪费时间

下面是我更新计数器的javascript函数。这是一个递归函数,如果由于其他并发编写器而导致更新失败,则会回忆自己

this.singleUpdate = function (paths, increment, countPath, retryCounter, callback) {
            firebase.database().ref(countPath).once('value', function (counter) {
                var value = counter.val() ? counter.val().value : 0;
                var countValue = increment ? value + 1 : value - 1;
                paths[countPath] = { value: null };                

                if (!increment && countValue == 0) { paths[countPath] = null; }
                else {
                    paths[countPath].value = countValue;                    
                }

                firebase.database().ref().update(paths, function (error) {
                    if (error) {
                        retryCounter++;
                        if (retryCounter < 3) { singleUpdate(paths, increment, countPath, retryCounter, callback); }
                        else { callback(false, error); }
                    } else {
                        return callback(true, countValue);
                    }
                });
            });
        }

1 个答案:

答案 0 :(得分:0)

您正在寻找transactions,不是吗?

  

处理可能被并发损坏的数据时   修改,如增量计数器,您可以使用事务   操作。您可以为此操作提供更新功能和   可选的完成回调。更新功能接受当前   作为参数的数据状态,并返回新的所需状态   想写。如果另一个客户端之前写入该位置   成功编写新值后,将调用更新函数   再次使用新的当前值,并重试写入。

docs中的示例:

function toggleStar(postRef, uid) {
  postRef.transaction(function(post) {
    if (post) {
      if (post.stars && post.stars[uid]) {
        post.starCount--;
        post.stars[uid] = null;
      } else {
        post.starCount++;
        if (!post.stars) {
          post.stars = {};
        }
        post.stars[uid] = true;
      }
    }
    return post;
  });
}

作为补充,您只能删除已删除用户发布的内容,因此计数器将保持相同的值,而不是评论/帖子将有&#34;帖子被删除&#34;