-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Save pointer to new object in beforeSave #5186
Comments
This is the expected behavior as the object is not stored in the DB yet and doesn’t have an objectid that is stored. Why can’t you use the afterSave for this intent? |
Because after saving the user other methods try to update the user profile. This can happen immediately after sign-up when the user is created.
In some cases the afterSave hook has not created the user profile yet and the user profile cannot be updated.
I tried to solve this by creating a user profile if it doesn’t exist when trying to update it (via cloud code). But that can also cause race conditions when the update cloud code is called multiple times immediately after each other.
|
ok; I believe I get the problem, however creating a pointer to an unexisting object is not allowed, and at the time of the save, the object is not saved (by definition). Anything could prevent the object from saving actually. What do you suggest? |
@mtrezza, you had the right idea asking this on StackOverflow: https://stackoverflow.com/questions/53321248/how-to-prevent-race-condition-when-creating-a-document-on-parse-server this is a design issue that you can solve. I'm glad to continue helping you on that thread. |
@acinader, thank you, any help appreciated. My understanding is that there is no solution to the issue. There is always a possible race consition. Hence the issue opened here to reflect on the parse server design. What is your suggestion to solve this? |
@acinader I already answered this on SO two days ago. Which information specifically do you need? Again, there seems to be no way to avoid a race condition so I think it is not just a debugging issue. |
While I appreciate all kinds of discussions, this one has nothing to do here @mtrezza =. double posting on multiple forums is never the way to go. Here, I particularly addressed the fact that what you are facing is indeed not a bug. If you wish to open a PR and suggest a 'safe' way to save pointers to unexisting objects feel free to do so. Otherwise, I would appreciate you stick with stack overflow. |
@flovilmart Agreed. As the community confirms that parse server cannot do this by design, I will look into opening a PR. I just wanted to make sure that I did not miss a feature or even an existing PR that actually addresses this. |
In this case, since you're not trying to coordinate multiple clients, I think that there are ways to work around the problem with parse server as is. But, there are definitely times when I could have used a 'blocking after-save hook' where the object is available in its saved state before the client gets a response. Glad to re-open @mtrezza if you want to work through it. |
I’ll be closing again as stalled and against the basic protection that the pointer objects should exist in the DB. |
OK, I will look into a PR when I have more time. If anyone interested, the currently used workaround:
To address concurrency: if no user profile was found and creating a user profile fails with Parse error 137 |
@mtrezza what do you consider as a PR? Because I don't want you to start working on a PR if we know ahead of time that we'll reject it. |
@flovilmart The PR should make it possible to create another document in a collection inside a For example:
However, before working on a PR, it's a good to re-start discussion here. |
Let me challenge that with the following example:
In the following example, there may be an issue after the profile saves the user's reference. So in this case you'll have a dangling reference on the profile which is not a good idea either. |
I think this would be the current implementation of Parse Server. The PR should address exactly the issue you mention, where only when saving the user succeeds, the user profile would be created with a user reference. The PR would add that logic. For example if we introduced a |
No, what I mean is that if there is an issue after you attach the user to the userProfile, (denoted with the throw) then, you profile has a pointer, but it points to nowhere. You still end up creating an object for nothing. If you change your logic to have the user have a userProfile then there is no issue. Because you want a cyclic relation user <-> profile, I would recommend to revise your data structure. You can very well attach the profile to the user and in the afterSave, do the reverse attach the profile to the user |
What is wrong with this approach? In any case, because you associate the profile with the user save, you will need to fetch in from the client. The server is designed to return any mutations that occur in the before/after hooks. Also, with version > 3 you can wait till the afterSave has completed. |
This approach causes the profile to be without user reference before The conclusion would be to not add a user reference to the profile at all, because it is not guaranteed to exist . But without reference, the So the requirement is that the profile needs a user reference from the moment it is created. |
well, just trying to give your the proper way of doing it, without breaking anything and with enough guarantees that the profile saves occurs within the user save cycle. If you manage to query a profile in between this cycle, then I’m not sure what to tell you, but again, consider transactions instead. Whatever you are attempting is also wrong as you would be saving the profile to an unexisting user. |
Going back to my suggestion, what speaks against introducing a Behind the curtain, when the
The
|
This is a quite bad solution, If you want to run something after the object is saved then use afterSave. Did you even try the solution that I recommend, that work and is reliable? After that, what you want / describe, is a transaction. That exactly many operations occur or none. This is an interesting take on transactions, to be able to trigger them from a beforeSave, and add operations to the 'current' transaction, reusing the result of the previous operation (like the objectId saved). |
I tried it with beforeSave and afterSave hook, but it didn't work for me. The client creates a user with Using mongoDB, I am unsure how to use transactions to solve this. |
Which has been solved for a while as if you return a promise in the afterSave hook, this will wait for the promise to complete before responding to the user. |
But the iOS:
Cloud Code:
|
No it won’t if you return a promise in your afterSave hook |
@flovilmart That actually solves the issue and makes the PR unnecessary. Took me a while to understand your example, thanks so much for your patience. Maybe interesting for @acinader who wrote:
|
@mtrezza good! However there is still a slight chance that the _User object save fails, and an dangling profile is created, which is not a big deal, as they are easily identifiable as they lack the back reference to the user (but you can always destroy them in batch as they should not be referenced by anyone). |
Issue Description
It is currently not possible to reference an object in its
beforeSave
hook to create a new object with a pointer to it.Steps to reproduce
Example scenario:
A user in
User
class should have a user profile inUserProfile
class associated with it. A user profile has a pointer to the user.It is currently only possible to create the user profile in the user's
afterSave
hook. But immediately after saving the user, other API calls may look for the user profile which does not exist. It is not a solution to create a user profile if it does not exists, as that can cause race conditions in which multiple user profiles are created. It is also not a solution to set the pointer to the user asunique
in the mongoDB collection which prevents multiple user profile from being created but also causes data loss, because the information to update the user profile with is lost after the failed call.Expected Results
It should be possible to create a pointer to an object in its
beforeSave
hook.Actual Outcome
Trying to save a user profile causes a timeout in path="/parse/batch".
Environment Setup
Server
Database
Logs/Trace
The text was updated successfully, but these errors were encountered: