Creating a game on app engine - calculating costs, thoughts?

108 views
Skip to first unread message

Mark

unread,
Aug 23, 2012, 6:37:37 PM8/23/12
to google-a...@googlegroups.com
Hi,

I'm trying to see if it's feasible to write a game using app engine as the backend.

The task I'm most worried about is the discoverability of games. A user can create a new game (which creates a Game object in the datastore). Then other users can hit an endpoint to get a listing of open games, sorted by creation date.

Creating a new game will be a very low frequency operation, so no worries there. Users polling for new open games will be much higher in frequency though. Each time a user requests a listing of games, I need to go fetch a page of Game objects from the datastore. Let's say a page size = 20. And the Game object looks like this:

    class Game {
        String name;
        long timestampCreated;
        long timestampStarted;
        String mapName;
        String hostUsername;
        int numPlayersMax;
        List<String> playerNames; // usernames of other players currently joined
        Text gameState; // game state as serialized json string
    }

the query would be something like:

    select Games where timestampStarted = 0 
        and playerNames.size() < numPlayersMax 
        order by timestampCreated DESC limit 20;
    
First, I'm hoping it's possible to avoid loading the Text field in this listings query since it's not needed for rendering yet. Still there would be a fair number of fields to fetch and deserialize, and I'm afraid my costs would quickly make it impractical.

I could try keeping a list of open games in memcache, but not sure if I could achieve consistency between the memcache state and the datastore state of games. Will be looking into that.

I've got a very similar game to this already published, using a single jetty instance + mysql on one machine. It's starting to get too much traffic  now, and scaling would be a pain. App engine's scalability looks great for something like this, but not sure if the # of writes and inefficient reads I need to perform are a good idea.

Any general thoughts on this would be greatly appreciated. I'm starting to dig into this now and any thoughts on how you'd implement the above would be great. In my already-published game (mentioned above), I ran a dark test where I'd call through to a (quickly implemented) mirror of the game running on app engine. It quickly ate through the daily quota!

Thanks

Rerngvit Yanggratoke

unread,
Aug 24, 2012, 7:10:47 AM8/24/12
to google-a...@googlegroups.com
I'm also looking into a similar problem. You can use Channel API to avoid polling and thus unnecessary datastore operations. However, Channel API also has limited free quota. 



--
You received this message because you are subscribed to the Google Groups "Google App Engine" group.
To view this discussion on the web visit https://groups.google.com/d/msg/google-appengine/-/cvpOxGrMRaoJ.
To post to this group, send email to google-a...@googlegroups.com.
To unsubscribe from this group, send email to google-appengi...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-appengine?hl=en.



--
Best Regards,
Rerngvit Yanggratoke 

Richard Watson

unread,
Aug 24, 2012, 7:27:57 AM8/24/12
to google-a...@googlegroups.com
How large is numPlayersMax, typically?  And how big is the gameState field?

Mark

unread,
Aug 24, 2012, 10:51:43 AM8/24/12
to google-a...@googlegroups.com
numPlayersMax will be capped at 8. I'm pretty sure I have to store the participating usernames as a List so I can find a user's games in a single query:

    select from Games where playerNames = "my-username";

This has me thinking - app engine also offers mysql - this might be better suited to a relational database. But if I outgrow a single mysql instance, there's no "automatic" scaling, like with the datastore?

Thanks

de Witte

unread,
Aug 24, 2012, 10:54:23 AM8/24/12
to google-a...@googlegroups.com
You will be fine.

The client will send a request for getting the 20 most recent pending games.

The server will return this by a response object, we call it a "game list data object".

1- You can easily cache this response object using MemCache.

2- You can also query only for keys which is a lot cheaper.

3- You can also cache Game Entities.

Combine these three techniques to get the most out of GAE.

Note that the game list doesn't have to be perfect synchronized and up teo date because you can always popup "Sorry this game is full, please select another one".









Op vrijdag 24 augustus 2012 13:27:57 UTC+2 schreef Richard Watson het volgende:

Jeff Schnitzer

unread,
Aug 24, 2012, 12:08:41 PM8/24/12
to google-a...@googlegroups.com
When you say "higher in frequency" what do you mean? How many queries
per second? How many games total will be active in your system at
once?

Keep in mind that your query will be eventually consistent. When
someone joins a game, that fact will not become visible to the queries
right away - often for seconds. But your query is by nature
"eventual" since someone could have joined the game immediately after
the query was run, so presumably you already handle this case.

One immediate improvement is to convert to your regular query to a
keys-only query followed by a batch fetch. If you have caching, you
only pay for the keys-only results (1/7th the cost). I see you're
using Java; if you use Objectify4 this is automatically done for you
with the @Cache annotation and "hybrid" queries (the default with
@Cache entities).

The next step is to cache the keys-only query itself in memcache for a
limited amount of time. It would be nice to expire it explicitly when
you know it changes - which is only when a game is created or a game
fills up (not for simple add or leave game). The problem is, there's
no guarantee that the subsequent query (which will populate the cache)
does not contain stale eventual data. I can't think of a way around
this other than just making sure the expiry period is short so it gets
refreshed; you're just trying to reduce the query count, not eliminate
it.

You can't query by playerNames.size() but you can store an indexed
field numPlayers in the entity.

There's no selecting of specific fields out for an entity (with the
exception of Projection queries, which are very limited and probably
not what you are after) so you can't get everything-but-gameState.
This probably won't affect price (a read op is a read op no matter the
size of the entity) but it could affect latency (more RPCs to get the
same bit of data). The usual solution is simply to put the gameState
blob into separate entity with the same id.

Jeff
Reply all
Reply to author
Forward
0 new messages