客观化:处理竞争条件以防止重复的帐户创建 [英] Objectify: Handle race condition to prevent duplicate account creation

查看:104
本文介绍了客观化:处理竞争条件以防止重复的帐户创建的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我正尝试为用户(Google用户)创建一个帐户,因此我需要检查用户是否存在,如果不存在,为该用户创建一个帐户,但是我面临的事实是,如果我滥用createAccount API方法,帐户有时会创建两次。

  @ApiMethod(name =account.google.create)
public Account createGoogleAccount(最终用户用户)抛出OAuthRequestException {
if(user == null ){
抛出新的OAuthRequestException(createAccount:OAuthRequestException<用户未被认证>);
}
Account alreadyExisting = RObjectifyService.getObjectify()。load().type(Account.class).filter(accountId.GOOGLE,user.getUserId())。filter(email, user.getEmail())第一()现在()。;
if(alreadyExisting!= null){
抛出新的OAuthRequestException(createAccount:OAuthRequestException<帐户已存在>);
}
return RObjectifyService.getObjectify()。transactNew(new Work< Account>(){
@Override
public Account run(){
Account account = AccountProvider .createAccountFromGoogleProvider(user);
RObjectifyService.save(account);
return account;
}
});
}

我读过我应该使用事务,但我不能,因为如果我做这在交易中:

  RObjectifyService.getObjectify().load().type(Account.class).filter(accountId .GOOGLE,user.getUserId())。filter(email,user.getEmail())。first()。now()

我得到一个错误只有祖先查询被允许在事务内部,但我没有看到另一种方法去做它

这是正确的方法吗?



谢谢 您需要一个事务,并且您需要一个实体,其主键是您尝试创建唯一的值(即用户名)。



该模式有点棘手。 此处有一些讨论。伪代码的基本思想是:

$ ul
$ li $ start
$ b

  • 使用唯一PK加载实体
  • li>
  • 如果有实体

    • 中止并返回重复错误

  • else

    • 使用唯一的PK创建实体(+您需要额外的工作)
    • 提交事务如果提交失败

      • 中止并返回重复错误


    • 其他

      • 一切都很棒!




    您可能不希望使用用户名作为您的主键用户实体,因此创建一个单独的用户名实体并混合创建用户名 User 在同一个事务中。一定要保留用户名实体;这就是保证唯一性的原因。

    这个问题(唯一性)实际上是像GAE数据存储这样的大规模分布式系统中更具技术挑战性的问题之一。只有传统的RDBMS是单主机系统才能在传统的RDBMS中解决问题,这对于可扩展性和容错性产生了影响。 GAE为您提供必要的原语以强化集群范围的独特性;他们只是不容易使用。


    I'm using Google App Engine and Datastore with objectify.

    I'm trying to create an account for a user (Google user), so I need to check if that users exist, and if not, create an account for that user, but I'm facing the fact that sometimes the account is created twice if I spam the createAccount API method

    @ApiMethod(name = "account.google.create")
    public Account createGoogleAccount(final User user) throws OAuthRequestException {
        if (user == null) {
            throw new OAuthRequestException("createAccount: OAuthRequestException<User is not authenticated>");
        }
        Account alreadyExisting = RObjectifyService.getObjectify().load().type(Account.class).filter("accountId.GOOGLE", user.getUserId()).filter("email", user.getEmail()).first().now();
        if (alreadyExisting != null) {
            throw new OAuthRequestException("createAccount: OAuthRequestException<Account already exist>");
        }
        return RObjectifyService.getObjectify().transactNew(new Work<Account>() {
            @Override
            public Account run() {
                Account account = AccountProvider.createAccountFromGoogleProvider(user);
                RObjectifyService.save(account);
                return account;
            }
        });
    }
    

    I read that I should use transactions but I can't because if I do this in the transaction:

    RObjectifyService.getObjectify().load().type(Account.class).filter("accountId.GOOGLE", user.getUserId()).filter("email", user.getEmail()).first().now()
    

    I get an error "Only ancestor queries are allowed inside transactions", but I don't see another way to do it

    Is this the right way to do it?

    Thanks

    解决方案

    You need a transaction and you need an entity whose primary key is the value you are trying to make unique (ie the username).

    The pattern is a little tricky. There is some discussion of it here. The basic idea in pseudocode is:

    • start transaction
    • load entity with the unique PK
    • if there is an entity
      • abort and return duplicate error
    • else
      • create the entity with the unique PK (+ whatever extra work you need)
      • commit the transaction
      • if the commit fails
        • abort and return duplicate error
      • else
        • everything is great!

    You probably don't want to make username the primary key of your User entity, so create a separate Username entity and mix in creation of Username with User in the same transaction. Be sure to leave the Username entity around; that's what guarantees uniqueness.

    This problem (uniqueness) is actually one of the more technically challenging problems in a massively distributed system like the GAE datastore. It's simple to solve in a traditional RDBMS only if the traditional RDBMS is a single-master system, with the resulting impact on scalability and fault tolerance. GAE gives you the necessary primitives to enforce clusterwide uniqueness; they just aren't super easy to use.

    这篇关于客观化:处理竞争条件以防止重复的帐户创建的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆