客观化:处理竞争条件以防止重复的帐户创建 [英] Objectify: Handle race condition to prevent duplicate account creation
问题描述
我正尝试为用户(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创建实体(+您需要额外的工作)
- 提交事务如果提交失败
- 中止并返回重复错误
- 其他
- 一切都很棒!
您可能不希望使用用户名
作为您的主键用户
实体,因此创建一个单独的用户名
实体并混合创建用户名
与 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屋!