views:

122

answers:

3

I'm working on this game titled "Fortress", located at http://www.joemajewski.com/fortress. Anyways, it's one of those browser-based role-playing games where players build an army and upgrade their stats to get a high ranking on the leaderboard.

Every 30 minutes a cron job is going to execute, which makes some updates. It loops through each player of the game (which will continue to rise naturally over time as more people join), and updates certain stats. It gives everyone 1 extra turn, gives them gold based on their income, food, wood, copper, iron, etc..., plus it updates their ranking.

That's all mumbo-jumbo, I know, but the point is that each player is going to have their own update query. I timed the script with just three members:

execution time with mysql_query() function commented out for the updates: ~ 3.5 ms

execution time with mysql_query() function working properly: ~ 14 ms

It is clearly the update queries that are going to cause my cron job to get very slow once the game has 100+ registered members. I am hoping to have 100 active players within two weeks of the game's launch, but many players create multiple accounts, so I expect at least 200 members after a couple weeks. With 200 update queries, plus the extra time to sort members and calculate rankings, it will likely take over 2 seconds to run the cron job.

My question to you. Is there a faster way to do something like this? I tried adding a "START TRANSACTION" and "COMMIT" queries before and after the loop, respectively, but it's hard to gauge speed with just 3 members. Will transactions make the script better or worse? Any other way around this? Any help would be appreciated.

Thank you very much for taking the time to listen to my plea.

+4  A: 

Based on the limited details in the question, it sounds like you may be able to do all of the player updates in one query or a few queries, rather than looping through all of them individually.

Consider adding turns, and updating their gold based on a formula, all in one query that affects the whole table.

i.e. as a basic oversimplification:

update Players set turns = turns + 1, gold = (gold + (income * 100) + (wood * 25) + (copper * 25))

Does this help?.

As for the ranking, how are you calculating it right now?

Fosco
I really like that idea. Many of the variables in the update are very complicated, and the query would be tremendous in size. I've never thought of doing it that way, but it would make the game virtually limitless in terms of how many members there can be. For now, I may keep it the way that I have it, since it would be a huge investment of time to work a single query to perform all updates, and it's always something that I can implement once the game has already launched (if need be, ya know?).
Joe Majewski
Not to mention that the game is about 75% complete, and I spent all of yesterday optimizing the header includes and stuff, and made it 15% faster, reducing the 5 or so queries into one query that grabbed everything needed.
Joe Majewski
How about letting me know when it's done so I can play? [email protected]
Fosco
I will seriously hold you to that. ;) I couldn't find a "Friend Request" feature on stack overflow, so I'll just have to save this to a notepad file on my desktop like I do with all my notes. :P
Joe Majewski
+1  A: 

My suggestion would be to create a test environment. Then populate that test environment with thousands of rows of user data. For an idea on how much data to put in, I use the rule of thumb of ((What You Expect) + (What You Hope For)) * 2. So if you expcect 100 users, and hope (a realistic hope) to get 5000, you should test for at least 10200 users...

So create the large database, and try to be as realistic as possible, but don't spend too much energy on that. Once you're there, try your queries one at a time. Optimize each. Then build a simple PHP script to execute a number of queries at different delays (query1, sleep 50ms, query2, sleep 100ms, query3, query4, sleep 100ms, etc). Then use a tool like ab (Apache Bench) to concurrently execute that script and look for issues in the db (remember, the task here is to stress the DB, so if it's not stressed out, keep pushing until it is).

ircmaxell
I like that approach. Once I'm more towards completion, I'll take another break from coding and work on optimization again. I'll throw a large amount of junk data into the tables and find out which queries are slowing me down. I spent all of yesterday optimizing, adding indexes, and I got down to a single query to grab all necessary data for the player.A regular page load has 3 queries in total: (1) One to get the meta data for the page, which is run no matter what. (2) Another gets the user data, as said above. Only for logged in users. (3) The last updates the hit counter,logs IP,load time
Joe Majewski
Very good. Remember, `Premature Optimization Is The Root Of All Evil`. Get it working first. Then worry about optimizing everything. After all, what seems like a bottleneck today may be a micro-optimization next week (or may not even wind up in the final codebase due to refactors for other reasons)...
ircmaxell
Haha, words to live by.
Joe Majewski
A: 

You are doing wrong.

Calculate resource amount whenever someone accesses user resource data. You don't need to calculate those data when no one accesses them.

Store last time when data were calculated (lt).

Resource amount now is: r2=r+dr*(now-lt) where
dr - resource change in time now - time now lt - time when last r save to database was made. r - resource amount stored in db r2 - resource amount now

You even dont need to update table every time, just read r, dr, lt from database, calculate r2 now.

Doing this way you will get real time amounts of resources and you no corn jobs.

codez
+1 I like this approach too, but it probably wouldn't work in all cases. Rankings could get pretty volatile based on inactivity.
Fosco
Believe me, I've considered doing it that way, but it wouldn't work out. Before going out and telling someone that they are doing it wrong, make sure you know all the details. Updating all of the resource information on page load every time it becomes necessary would be a huge pain in the ass, as even though someone might have registered to the game months ago, and never played since, it is still necessary to update their data through the cron, as people will be constantly seeing their username in the leaderboards, when finding someone to attack, etc...
Joe Majewski
There are also just far too many variables that need updating, and managing them all in a cron job is the easiest approach, rather than testing timestamps and figuring out how many updates would have gone off, then compounding these updates, as 15 scheduled updates would not be the same as 15 updates at once, given the income that rises with each update. Anyways, with the situation I'm in, your approach just isn't feasible. I'm also trying to make the game load very quickly for the player, as they will be making many page loads, and I don't want to slow them down by updating only when needed.
Joe Majewski