MongoDB / Mongoose如何配对两个数据库条目没有冲突? [英] MongoDB/Mongoose How do I pair two db entries without conflicts?

查看:602
本文介绍了MongoDB / Mongoose如何配对两个数据库条目没有冲突?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有关其他信息,请参阅此问题:

Refer to this question for additional info:

MongoDB配对和删除顺序数据库条目的最佳方式

好吧,所以我做一个盲目的战争的游戏,如果你愿意!
基本上它的工作原理是这样的:

Okay so I'm making a game of "blind war" if you will! Basically it works like this:

一次玩10,000个用户。
没有用户帐户只是他们在访问我的网站时选择的别名。
用户在访问时获得随机数量的部队。
用户可以提交任何数量的部队去盲目战争。

There are 10,000 users playing at one time. There are no user accounts only an alias they choose upon visiting my website. A user is given a random amount of troops on visit. A user can submit any amount of their troops to go to "blind war".

游戏的工作原理如下:
用户A提交x部队数量。 (说100)
用户B提交x数量的部队(说72)
(所有这些部队都在巨大的部队池中)

The game works like this: User A submits x amount of troops. (say 100) User B submits x amount of troops.(say 72) (all these troops are held in the "giant pool of troops")

应用程序将这两个用户配对。
用户A赢了并给了他的部队加用户Bs部队。 (aka 172部队)
用户B丢失,没有给任何东西。

The application pairs these two users. User A wins and given pack his troops plus user Bs troops. (aka 172 troops) User B loses and is given back nothing.

这里是我的问题:
我可能有2000个用户提交部队相对同时!
我需要确保三个或更多的用户在事故后从不匹配。

Here's my problem: I could potentially have 2,000 users submitting troops at relatively the same time! I need to make sure that three or more users are never matched on accident!

我在想这样做:

Duels
.Where('_id').lt('objectId')
.Where('User').ne('A')
.exec(callback)

function callback(err,doc){
  /* Do troops comparison & other stuff here */
  doc.remove

};

但我担心这可能会导致文档在删除之前抓取两个相同的文档。
导致部队池被过度绘制...

but I'm afraid this may end up in docs grabbing two of the same doc before removing. Causing the pool of troops to be overdrawn...

有没有办法阻止这种情况发生?

Is there a way to stop this from happening?

推荐答案

在我的原始回应之后,其中一些不同的思考可以帮助你。因此,这似乎更多关于建筑,而不是说,以某种方式实现你的代码将是最好的方式。

Following on from my original response, this is again something where some different thinking can come to your aid. And as such this seems to be more about architecture than saying that implementing your code "in a certain way" is going to be the best way to go.

从你的评论在这里和你的问题,似乎你需要解决的问题是如何调整军队的数量为其他玩这个动作的用户。让我们再次查看相同的数据开始:

From your comment on that and your question here, it seems that the problem you need to solve is how to adjust the count of troops for the other user playing the move. Let's get started by looking at the same data again:

{ "_id" : ObjectId("531cf5f3ba53b9dd07756bb7"), "user" : "A", "units" : 50 }
{ "_id" : ObjectId("531cf622ba53b9dd07756bb9"), "user" : "B", "units" : 62 }






移动



所以这里的情况是,用户B刚刚移动并在该移动中提交了 62 单位。在前一篇文章中,我解释了如何找回匹配用户A的移动,因此您可以配对他们并确定胜利。


Making the "move"

So the situation here is that user "B" has just made their move and committed 62 units in that move. In the prior post I explained how to get back the move for the matching user "A", and therefore you can "pair" them and determine the win.

进一步,考虑请求中发生了什么。提交用户B,然后插入文档进行移动,然后读回匹配的文档。所以现在你有两个文档在内存中的请求。如果你考虑会话数据,那么你可能有这样的东西(以非常简短的方式):

Taking that further, consider what happened in the request. User "B" submitted, then you insert the document for their move, then you read back the matching one. So right now you have both documents in memory for the request. If you consider the session data, then you might have something like this (in a very brief way):

{
    currentUnits: 100
}

计数。因此,当您提交一个用户移动时,您只是减少他们拥有的军队数量。因此,在执行 62 部队的插入时,计数器将变为:

Let's call that the starting count. So when you submit a move from the user, you just decrement the count of troops they have. So when doing the insert of 62 troops, the counter goes to this:

{
    currentUnits: 38
}

这是一个很好的做法,就像在移动中插入确认一样。但是接下来的内部回调,你要做的发现,我说,和返回一个文档。现在你有你可以比较和做你的数学的信息。用户B赢得了您可以调整会话价值:

That's good practice, as you do that on the insert acknowledgement within the move. But next up inside that callback you are going to do the find as I said, and that only returns one document. Now you have the information you can compare and do your math. User "B" wins so you can adjust your session value:

{
    currentUnits: 150
}

这样应该覆盖用户B移动的所有内容。你拿走单位,当一个移动发挥,你匹配另一个球员,然后你数学,并调整你的结果。完成!哦,你做了保存所有的会话数据在持久存储没有你?点头是的。并且会话数据绑定到用户句柄(或用户实际上是会话id),以获得修改它的访问。

So that should cover everything for the move for user "B". You took away units when a move was played, you matched the other player, then you "did the math" and adjust your results. Done! Oh, and you did save all the session data in a persistent store didn't you? Nod yes. And also that session data is tied to the user handle (or the user is in fact the session id) in order to gain access to modifying it.

剩下的

这部分应该很简单。所以我不是为你编码。您正在为自己的应用程序使用 socket.io ,所有这一切都归结为发送消息。这意味着你发出的数据告诉客户端上的其他用户,他们失去了他们的部队,但你想处理它。但请记住,在提交移动时,您会移除这些单元。

This part should be simple. So I'm not coding it up for you. You are using socket.io for your application, so all this comes down to is sending a message. That means the data you "emit" tells the other user on the client that they "lost their troops", however you want to deal with it. But also remember that you "took away" those units when their move was submitted. In all cases that is making sure no-one can commit more than they have.

在这里谈论的唯一可能的事情是缩放你的应用程序超出一个实例。因此,您可以在节点上的所有工作在一个服务器实例上的事件愉快地谈话,但是扩展您需要在不同实例之间传递消息。

The only possible thing to talk about here is scaling your application beyond one instance. So you can talk happily with events on "node" all working on one server instance, but to "scale" you would need to pass messages in between different instances.

使用MongoDB处理此问题的方法可以是限制集合

One way to handle this using MongoDB can be with a capped collections.

除了保存集合通常以保持文档集合的设置大小的方式进行操作,还有其他一些内容,是可用光标。使用节点驱动程序创建一个非常典型的方法是:

Aside from what capped collections generally do in the manner of keeping a set size for a collection of documents, there is one more thing they offer, and that is a tailable cursor. A fairly atypical way to create one with the node driver would be like:

var options = { tailable: true, awaitdata: true, numberOfRetries: -1 };
var cursor = collection.find(query, options).sort({ $natural: 1 });

完整选项列在 Cursor()部分。你可以通过典型的方式获得这些本地方法在mongoose。

The full options are listed in the Cursor() section of the driver manual page. You can get at these "native" methods in mongoose by the typical way.

一个tailable光标设置要做的是跟随插入文档在集合中,您可以以这种方式进行偶数投票,如下所示:

What a "tailable" cursor is set up to do is to "follow" the "last inserted" document in the collection and you can sit and "follow" in this way with an even poll, much as in:

    (function more() {
        cursor.nextObject(handle(function(doc) {
            if (!doc) return setTimeout(poll, self.wait);

            callback(doc);
            latest = doc._id;
            more();
        }));
    })();

因此,在这样一个结构中,你找到新插入的文档,并传递给你的内部回调

So within such a construct you "find" the newly inserted document and pass to your inner callback the information to be processed, where you "send" messages to clients, update things and whatever else you want to do.

回到您实际的请求,那么您将是您的客户,在你数学到单独的封顶收藏之后发出一个插入。你会想要一些有意义的东西如下:

Back to you actual "request", then you would be issuing an insert after you "did your math" to the separate "capped collection". You would want something meaningful by brief like:

{ "player": "B", "vsplayer": "A", "win": 50, "loss": 62 }

/ strong>插入。因此,您可以设置 TTL索引来处理随时间的删除,

And again these are just inserts. So you would set up a TTL index to handle the deletions over time and being capped, the old entries would naturally drain by being "pushed out" of the entries present in the collection.

在您的客户端端,每个连接的用户应用程序都保持跟踪的最后一个_id价值观。因此,新插入的条目始终的价值大于之前的旧。

On your "client" side, each connected user application keeps track of the "last _id" value reveived. So the newly inserted entries are always greater in value to the "older" previous ones.

因此,有一种方式使用MongoDB创建一个可以顺序处理以便在多个应用程序服务器实例之间共享消息传递的持久队列。

So there is "one way" to use MongoDB to create a persistent queue that you can sequentially process in order to share message passing amongst multiple application server instances.

所有人都说以这种方式实现一个可尾的游标,对我的钱,我会使用 zeromq 或类似的东西。但是你可能会发现MongoDB方法更适合你,如果你不想深入到另一种技术。或者这种可扩展性不是你的应用程序所需要的(至少在这个阶段),只是传递给socket.io方法在请求内就足够了。由你决定。

With all said for implementing a "tail-able" cursor in this way, for my money I would be using zeromq or something much the like. But you might find the MongoDB method to be more suited to you if you don't want to delve into another technology. Or perhaps this sort of "scalability" is not needed by your application (at least at this stage) and simply passing of to "socket.io" methods within the request would be enough. Up to you.

大部分情况下,你似乎仍然对配对和删除的概念进行挂断。这是在最后一个响应中的意图,并且表示删除的文档在处理时不需要确保您从未获得任何请求的同一对

Largely though, you still seem to be "hung-up" on your concepts of "paring" and "deleting". This was the intention to cover in the last response and was to say that deleting of documents when they are processed is not required. The process described ensures that you never get the "same pair" back on any request.

您可以重新阅读该信息,并真正了解该过程。问你是否有问题。从你所讨论的内容来看,你的数据访问模式的类比更像是匹配对。

I would encourage you to "re-read" that information and really understand the process. And ask if you have questions. From what has been discussed there, the analogy of you data access pattern is more like "playing off a stack" than "matching pairs".

响应后,使用此处所述的逻辑全部,您将需要为了设置您的数据访问模式。您的其他组件当然是消息,但这让您访问所需的数据。

So what you were given in response, following on with the logic described here is all you should need in order to set up your data access patterns. Your other component will be of course the messaging, but this gives you access to the data that you need.

这篇关于MongoDB / Mongoose如何配对两个数据库条目没有冲突?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆