Advertisement

MMO Database

Started by February 11, 2017 05:11 PM
25 comments, last by Unduli 7 years, 10 months ago

In essence whether you need transactions boils down to whether it is important that everyone can be guaranteed to agree on the values in the database at all times. For a global item attribute that is only updated when you release a new version, the answer is most likely no. When two players are actively trading items/gold, the answer is definitely yes (i.e. if one player unplugs their ethernet cable in the middle of the trade, is it important to know who ends up with the items?).

Is that truly so? I'm inclined to believe that the "eventually consistent" model that many NoSQL databases implement is mighty fine. It doesn't need to be strictly consistent at all times.

As long as "take X money from A" and "give X money to B" will happen (in exactly this manner, once, and nothing else) all is fine. Even if the overall view of the database is inconsistent for half a second, who cares? Almost certainly, the state is cached in memory on the server process anyway, it normally only ever reloads from the database when a user logs in, or after a server crash/reboot. In case the server crashes right after starting a "money transfer" type of operation, by the time it has restarted and is looking at the database again, it will be in a consistent state. If the NoSQL database crashes, it reconstructs a consistent state from the transaction log during startup.

A user unplugs his ethernet cable hoping to cheat? Well, so what. The server couldn't care less. After getting "agree on trade" from both sides and having verified that the to-be-traded gold exists at all, the server no longer cares what you do or say, it just does the requested transaction (two transactions, actually), as written down in the "contract" (= transaction log) prior to replying "Yes, OK" to both sides. The transaction log makes sure that eventually both transactions are performed (not just one).

You may or may not get the "Yes, OK" if you pulled the cable, but it's still going to happen. Am I fundamentally misunderstanding something there?

For most other stuff, even solely keeping things in memory and saving at logoff (or periodically, say every 5 mins) can be very acceptable. I've been playing games where a server could crash (as in, really did occasionally, not just in theory), and it would simply reload users from the database and reset all zones to defaults, effectively rolling back everything and anything that happened since the last save (which was... more like 15 minutes, not 5). If you killed a boss, got a nice drop, and in anticipation of the crash logged off and on again, you could consider yourself lucky. If you didn't, well, then you were somewhat less lucky.

That's not a perfect thing, sure... but I still found it quite acceptable from a player's point of view (in fact, given the long spawn times of some rares, a server crash sometimes came as a really nice surprise!).

Most games I've been involved with went down the route of just using the database as a save-game file for each player. If servers stay up and there are no other bugs then it is eventually consistent. The DB becomes sort of a write-back cache. It does mean that 'in-memory' is the authoritative data (rather than the DB, as is the case in more traditional software), you do have to be careful around migrating players between servers (zones, instances, whatever), and for important trades that may have significant repercussions you may want to save all participants at once atomically anyway.

Advertisement

Is that truly so? I'm inclined to believe that the "eventually consistent" model that many NoSQL databases implement is mighty fine. It doesn't need to be strictly consistent at all times.

I mean, it depends how valuable your in-game currency and items are in real-world terms. If there's even a hundred milliseconds of inconsistency, you can likely exploit that to duplicate items by crashing the server in between.

For many games it'll make no difference. But something with as thriving a black market as, say, WoW... you care deeply about the consistency of the economy.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

goes on to say "10,000 concurrent" which is actually VERY large for a single server

I guess I didn't read the OP as "on a single server". That would indeed be a lot.

Assuming the world is divided into zones, and you use a server per zone... the largest WoW zones I'm aware of support somewhere on the order of 250 concurrent players. Some of the capital cities might go into the low thousands.

That's still an *awful lot* of players, and comes with plenty of challenges. If all 250 players try and stand on top of each other, your collision and client update algorithms are going to fall over. You end up needing to introduce some concept of 'phase', where you start putting players out of phase with one another when the local density is too high (i.e. of the 250 people in the zone, a maximum of 50 or so can be in your local phase).

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

This discussion has largely missed the direction I was heading in a number of different ways. I will take responsibility for that because I evidently did a poor job setting a direction. Furthermore, the term MMO incorrectly painted pictures in many of your minds, which has led to a continued departure from the point. Since there seems to be no consensus about what an MMO (or other related acronym) is, lets get back to the basics. Ready for it......we will call this a GAME. Whew that was a rough one. What kind of a game you say? An otherwise single player game with common access to data that each can affect and see. Nothing more, nothing less.

With that being said, I am going to borrow from a real world example that we can all relate to and hopefully we can reel this back in.

Imagine for just a moment we were considering emulating the stock market without the need for split second timing and without the obviously ridiculous scale of transactions etc. We are borrowing the concept for discussion's sake only, while completely disregarding the full gravity of it.

1. Traders cannot see one another. This completely eliminates a whole swath of issues related to classic MMO's with regard to sharing a space.

2. All traders must be able to see what a stock is trading at. And YES lets say there are 10,000 traders. Is it inconceivable that a single server could furnish 10,000 people with what price a stock is trading at within 5-10 minutes? I'll answer that for you. NO, it is not inconceivable! It certainly requires some consideration though. For those of you who still can't swallow this fact, go look up the wikipedia article for nginex and the c10k problem.

3. Traders purchase stocks with many types of currency. This information must be stored in a server somewhere. As it pertains to the database, consider the problem here. There are hundreds of forms of currency. It is possible for someone to have all kinds, but it is highly unlikely. I would think this drives some interesting thought processes with regard to the type and structure of a database.

ok, there it is. Focus on the database and try not to get hung up on the periphery.

You're still going to want some form of consistency guarantee, probably along the lines of ACID. That puts you squarely in established SQL territory, and there are plenty of solutions there that you can pick from yourself. (If you want recommendations, I'd pull Postgres out of thin air personally, but that's just me.)

But the bigger thing you're missing is that the "periphery" is extremely important. Do you even know what nginx does?

10k concurrency is doable on commodity server hardware if you know what you're doing.

Since you don't, I'd recommend building incremental prototypes and being ready to re-engineer the whole solution each time your concurrency scales by 10. That is a real-world pattern, by the way, not just something I made up to be off-topic.

Doing it that way will let you learn the hard way what scales and what doesn't, and give you much-needed context and insight into the "periphery" that you're presently discarding.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Advertisement

Yeah, given the dismissive attitude and the preference for pretending we're the ignorant ones here, I'm just going to say Postgres and leave it at that.

Most games I've been involved with went down the route of just using the database as a save-game file for each player. If servers stay up and there are no other bugs then it is eventually consistent. The DB becomes sort of a write-back cache. It does mean that 'in-memory' is the authoritative data (rather than the DB, as is the case in more traditional software), you do have to be careful around migrating players between servers (zones, instances, whatever), and for important trades that may have significant repercussions you may want to save all participants at once atomically anyway.

+1 , I am with DB being eventually consistent as well. Redis seems to be ACI (missing D) but I think it is fair trade in most cases.

mostates by moson?e | Embrace your burden

(most recent description)

I think this is not that a big challenge for a single server backed by a plain normal SQL database server, possibly with memcached running in addition -- assuming I understand you right and make the right assumptions.

So you have, say, 10,000 traders which are however "mostly idle", that is they're maybe doing something once every 10-20 seconds or even less often, not trying to do something 5 times a second. This is something a single server can tackle connection-wise and bandwidth-wise.

I'm not sure what the "what price a stock is" figure is supposed to be. Normally, there is no such thing as a price, but you have bids and asks, and limits. Someone may put up a bid of 500 A with a limit of 100, and someone else may put up a bid of 1000 A with a limit of 105, and yet someone else might put up a bid of 10 A with no limit given.

If you buy 1500 A, you may get 500 for 100 and 1000 for 105, or you might get something different. You might in theory be able to get those 10 no-limit A for 1 currency (but that isn't going to happen, the broker is going to buy them as cheap as possible and sell to you for the highest limit currently visible). It is even possible that 20 other people want to buy A as well and put up ask offers, and as trade volume and settling price goes up, another 50 jump on the wagon, and surprise, you will pay 120 for your half of your As, and 160 for the other half (even though they were "offered for 105" -- they weren't).

So... there is really no such thing as The Price. Of course, it's just a game, so.... who knows, there might just be a price, to keep it simple.

The nice thing is, no matter whether you make it more realistic or just set up a "price", in any case prices can, and will, only ever change when someone puts up an offer (bid or ask). Also, as long as nothing changes, nobody needs to know. So, N is really about the number of transactions, not the number of users.

Which means only a relatively small number of "subscribers" (those watching that particular stock) needs to be informed when a price changes, say once per minute or so (a stock doesn't go up only because you bought 10 of it, so unless someone buys millions in one transaction, updating on a much slower pace seems alright). Maybe two dozen people out of those 10,000 have an interest in that stock at that time. Managing this kind of mapping is just what a relational database is good at, the result of the pricing calculation is the same for all recipients, so it can be cached, and keeping around ten thousand mostly-idle connections is no longer that big a challenge for a modern server. That should fly well.

Lotsa currencies? Unless you want currencies to fluctuate live, too, this is very easy. Just have 1 unit of currency X equal 100 and 1 unit of currency Y equal 120. Or, whatever. Multiply whatever the trader withdraws from his account to pay for something with that factor, there you have a universal, normalized value. Pay something that is offered in X with Y? Well, multiply X price by X-factor, and divide by Y-factor, round up, and there you go. How to store these currencies? Storing a tuple of values (amount and type) in relation to something else (user ID) is, again, something SQL databases excel at (although I would personally most likely just have an array of currency-amounts per-user for simplicity and speed, even if this is not truly "good style", and even at the risk of burning some memory).

This topic is closed to new replies.

Advertisement