Search code examples
androidgoogle-app-enginegoogle-cloud-datastorequota

Google App Engine - Too many Datastore Read Operations


I have implemented an online leaderboard via Google App Engine for my Android app. But after 2 hours I reached 100% of my quotas in "Datastore Read Operations". Can anybody help me to modify my code to reduce the read operations?
Here is my code:

public class The_Big_Bang_Theory_Quiz_HighscoreserverServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    String game = req.getParameter("game");
    String name = req.getParameter("name");
    String pointsStr = req.getParameter("points");
    String behaviorStr = req.getParameter("behavior");
    int behavior = 0; // 0 = upload, 1 = download
    if (behaviorStr != null) {
        try {
            behavior = Integer.parseInt(behaviorStr);
        } catch (NumberFormatException e) {
            behavior = 0;
        }
    }
    if (behavior == 0) {
        int points = 0;
        if (pointsStr != null) {
            try {
                points = Integer.parseInt(pointsStr);
            } catch (NumberFormatException e) {
                points = 0;
            }
        }
        if (points > 0 && name != null) {
            addHighscore(game, name, points);
        }
    } else {
        String maxStr = req.getParameter("max");
        int max = 1000;
        if (maxStr != null) {
            try {
                max = Integer.parseInt(maxStr);
            } catch (NumberFormatException e) {
                max = 1000;
            }
        }
        returnHighscores(resp, game, max);
    }
}

private void returnHighscores(HttpServletResponse resp, String game, int max) {
    DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
    Key gameKey = KeyFactory.createKey("game", game);
    Query query = new Query("highscore", gameKey);
    query.addSort("points", Query.SortDirection.DESCENDING);
    List<Entity> highscores = datastore.prepare(query).asList(FetchOptions.Builder.withLimit(max));
    for(Entity e : highscores) {
        try {
            resp.getWriter().println(e.getProperty("name") + ";" +e.getProperty("points"));
        } catch (IOException exc) {
            exc.printStackTrace();
        }
    }
}

private void addHighscore(String game, String name, int points) {
    DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
    Key gameKey = KeyFactory.createKey("game", game);
    Entity highscore = new Entity("highscore", gameKey);
    highscore.setProperty("name", name);
    highscore.setProperty("points", points);
    datastore.put(highscore);
}
}

I read something about the BlobStore. Is it a better method?


Solution

  • I had the same problem, i used the cache mechanism from GEA to solve the problem. Basicly the Cache is a Distributed HasMap

    some code: create the Map:

    try {
                cache = CacheManager.getInstance().getCacheFactory().createCache(
                        new ConcurrentHashMap<String, Category>());
            } catch (CacheException e) {
                Logger
                        .getLogger(TipsDAO.class.getName())
                        .severe(
                                "unable to cretate cache using an internal ConcurrentHashMap");
                cache = new ConcurrentHashMap<String, Category>();
            }
    

    For every read pop you check the Map first, if you find i there you return, if you don't find it you read from the DB and put it in the Map before you return.

    if (cache.containsKey(cat)) {
                return (Category) cache.get(cat);
            }
            try {
                Query query = entityManager
                        .createQuery("SELECT FROM Category WHERE name = ?1");
                query.setParameter(1, cat);
                Category temp = (Category) query.getSingleResult();
                cache.put(cat, temp);
                return temp;
            } catch (Exception e) {
                LOG.severe(e.getMessage());
                return null;
            }
    

    For every write op to the DB you also write to the Map

    cache.put(cat.getName(), cat);