简体   繁体   中英

Google App Engine Datastore too slow persistence

I'm developing a GAE/J app using JDO to manage the Datastore. It offers a number of Endpoints to interact with the data and so on. I'm also developing a Javascript client for those endpoints.

Situation:

First I have an endpoint method Token getSessionToken(User usr) , which generates a session token, assigns it to the user and stores both user and session token in the Datastore.

Then I have another endpoint method Info getInfo(Token token) which receives a session token, checks if that session token corresponds to an user and if so, returns some info.

Problem

From my Javascript client, I call the first method and everything is fine. Then I call the second method with the previously retreved session token, and I receive an error saying that the session token doesn't correspond to any user...

I've checked and I realised that this error happens just because the new session token has not been persisted yet!

Question

My current solution is to make the Javascript client call the second method once, and if it receives an error, wait a while and call again... is there a better solution?


EDIT:

This is the code invloved in the first method call to get an idea about why it takes so long...

Endpoint:

@ApiMethod(
    name = "getSessionToken",
    path = "get_session_token",
    httpMethod = HttpMethod.GET
)
public SessionToken getSessionToken (@Named("email") String email, @Named("password") String password) 
        throws InternalServerErrorException {       
    SessionToken sessionToken = new UsersController().getSessionToken(email, password);
    return sessionToken;
}

Control:

public SessionToken getSessionToken(String email, String password) throws InternalServerErrorException {        
    SessionToken sessionToken = null;

    PersistenceManager pm = PMF.get().getPersistenceManager();      
    Transaction txn = DatastoreServiceFactory.getDatastoreService().beginTransaction();
    try {
        DAO dao = new DAO(pm);

        User user = dao.getUserByLogin(email, password);

        dao.deleteSessionToken(user.getSessionToken());
        sessionToken = SessionTokenManager.generateToken(user.getEmail(), user.getPassword());
        user.setSessionToken(sessionToken);

        user = dao.insertUser(user);    

        txn.commit();   

    } catch (Exception e) {
        throw new InternalServerErrorException();
    } finally {
        if (txn.isActive()) {
            txn.rollback();
        }
        pm.close();
    }       
    return sessionToken;
}

DAO:

public User getUserByLogin(String email, String password) throws InternalServerErrorException {
    User user = null;
    Query query = null;
    try {
        query = this.pm.newQuery(User.class);
        query.setFilter("email == emailParam && password == passwordParam");
        query.declareParameters("String emailParam, String passwordParam");     
        List<User> results = (List<User>) query.execute(email, password);
        user = results.get(0);
    } catch (Exception e) {
        e.printStackTrace();
        throw new InternalServerErrorException("Error in DAO.getUserByLogin()");
    } finally {
        query.closeAll();
    }
    return user;
}

/////

public void deleteSessionToken(SessionToken sessionToken) throws InternalServerErrorException {
    try {
        this.pm.deletePersistent(sessionToken);
    } catch (Exception e) {
        e.printStackTrace();
        throw new InternalServerErrorException("Error in DAO.deleteSessionToken()");
    }
}

/////

public User insertUser(User user) throws InternalServerErrorException {     
    try {
        this.pm.makePersistent(user);
    } catch (Exception e) {
        e.printStackTrace();
        throw new InternalServerErrorException("Error in DAO.insertUser()", e);
    }                
    return user;
}

Model:

@PersistenceCapable
public class User { 
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;    
    @Persistent
    private String name;
    @Persistent
    private String email;
    @Persistent
    private String password;    
    @Persistent
    private Folder rootFolder;  
    @Persistent
    private SessionToken sessionToken;  
    @Persistent(defaultFetchGroup = "true")
    private AccountContainer accountContainer;
    //getters & setters
}

/////

@PersistenceCapable
public class SessionToken {         
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;
    @Persistent
    private String token;
    @Persistent
    private Long created;
    @Persistent
    private String sha1;
    //getters & setters
}

Maybe one of the following is possible and suits your needs

  • Can you combine the two calls to a "getSessionTokenAndInfo" call? Where you return the Token and info together. Depending how your code works maybe the token and user objects are already available and dont need to be loaded then?

  • Use some form of caching so that the first endpoint leaves something in say memcache so that the need to try to retreive it from the datastore in the second call is removed (at least in most cases where memcache should be reliable enough for such scenario).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM