-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Atomic "find or create" Repository API #1956
Comments
Few thoughts: (1) For LB4 and beyond, I am proposing to provide strong guarantees about robustness of this operation. If the connector and/or the database does not provide a solution that's atomic, then the repository method should throw a "Not Implemented" error (instead of falling-back to a non-atomic version). If there is enough user demand, then we can provide a different repository class that implements operations like (2) For example: interface AtomicCrudRepository<T extends Entity, ID>
extends EntityCrudRepository<T, ID> {
// declare atomic operations like findOrCreate
}
class DefaultAtomicCrudRepository<T extends Entity, ID>
extends EntityCrudRepository<T, ID>
implements AtomicCrudRepository<T, ID> {
// implement atomic operations like findOrCreate
} /cc @strongloop/loopback-maintainers thoughts? |
I think the issue is more complex than just implementing the atomic
My understanding is that we only have mongodb and memory connector support atomic Maybe we have to introduce different options for
|
based on Raymond's comment, Cloudant/Couchdb2 have a similar situation as sql databases. |
Good point! This is loosely related to my ideas related to validation, see #1872 (comment)
👍
Makes sense. To make this option easy to use, I would like our implementation to have the following traits:
Makes sense to me. In the current design, it's up to the connector how it implements
Ouch! When I proposed to use
I don't think it's a good idea to ask the users to decide this matter. In my experience, many LB users don't fully understand ramifications of different ways of enforcing has-one constraint and as a result, we can end up with many LB4 applications prone to race conditions. What I would like to see instead, is a single Model/Repository-level API that allows:
An interesting approach for document databases like Cloudant/Couchdb, based on this StackOverflow discussion: When a In that setup, when we attempt a PUT request of an existing document id ( It makes me wonder - would it make sense to merge the primary key with the foreign key into the same property in the target model of HasOne relation and use this as the solution to enforce uniqueness across all databases? @model()
class User {
@hasOne(() => Credentials)
credentials: Credentials;
}
@model
class Credentials {
@belongsTo(() => User)
@property({id: true})
userId: string;
} Or even @model()
class User {
@hasOne(() => Credentials, {keyTo: 'id'})
credentials: Credentials;
}
@model
class Credentials {
@belongsTo(() => User)
@property({id: true})
id: string;
} Thoughts? |
@b-admike and me have discussed this matter yesterday. We concluded there are two ways forward:
|
@b-admike, per our discussion last week, IIUC since this feature is only applicable for a small subset of connectors (or databases), this task is not a blocker for the |
Yes @dhmlau per #1956 (comment), we've decided to take approach 2) with #2005, and decided to close #1974 as rejected. I've removed this issue from the milestone. |
This issue has been marked stale because it has not seen activity within six months. If you believe this to be in error, please contact one of the code owners, listed in the |
This issue has been closed due to continued inactivity. Thank you for your understanding. If you believe this to be in error, please contact one of the code owners, listed in the |
While implementing
hasOne
relation in #1879, we discovered a requirement for an atomic "find or create" operation that's needed to avoid race conditions inhasOne
's implementation ofcreate
operation.Cross-posting #1879 (comment)
It is crucial to leverage an atomic implementation of
findOrCreate
provided by our connectors. The current proposal is prone to race conditions, where to instances of the target "hasOne" model can be created.Consider the following scenario: the LB4 server is handling two incoming HTTP requests to create a hasOne target and the code is executed in the following way by Node.js runtime:
DefaultHasOneRepository#create
is invokedtargetRepository.find
is called. It's an async function so other stuff happens while the query is in progress.DefaultHasOneRepository#create
is invoked.targetRepository.find
is called. It's an async function so other stuff happens while the query is in progress.targetRepository.find
for Request 1 returns, no model was found.targetRepository.create
in called.targetRepository.find
for Request 2 returns, no model was found.targetRepository.create
in called.create
requests eventually finish. We have two models that are a target ofhasOne
relation. 💥 💣 💥Acceptance criteria
Add a new Repository-like interface describing atomic operations.
Add a new repository class implementing this new interface while extending
DefaultCrudRepository
. Either the constructor or thefindOrCreate
method should verify that the connector provides atomic implementation offindOrCreate
and throw an error with a machine-readable code if it does not.Modify the table mapping known error codes to HTTP status codes to map the new error code to
501 Not Implemented
.Nice to have but not required:
AtomicCrudRepository
on top ofCrudRepositoryImpl
.Out of scope
patchOrCreate
,replaceOrCreate
, etc.The text was updated successfully, but these errors were encountered: