Double $save issue
My team ran into a really obscure problem with invoking $save twice on an object. I'll try to explain using a simple model that has an "a" and "b" property on it.
- Call model.$save(['a'])
- From another part of the code, before the call made in step 1 finishes, set model.b to be a new value and call model.$save(['b'])
- Because restmod serializes the server requests for a single instance, the call to save b waits for the call to save a to complete
- In the response to save a, the server returns the whole object, including the b property. The model object is updated with the server value for b.
- Now, the call to save b is made. However, the value for the b property that was set in step 2 was overriden in step 4, so we end up losing that change.
It looks like the root of the issue is that the object is serialized inside the $action call, which occurs after the previous server requests. It seems like it needs to serialize the object before the $action call to make sure no further modifications have occurred to the object before the server call.
try doing something like
model.$extend({b: 123}).$save(['b']);
That looks like a good workaround.
The real kicker in this case was just two disparate pieces of code doing their own thing in a way that would normally "just work", but due to some surprising edge case behavior had a really subtle bug.
I don't know if I'd call this a bug.
Let's say you've queued up your change to record.b, but it's updated by your server response. Some other user has meanwhile updated record.b, and your change would have clobbered it. In this case you might consider it the desired behaviour.
Or what if you have a third property, record.c, that impacts the validity of your pending change to record.b. You might not want to just blindly send this change off to the server -- you might want to validate it first.
Or maybe a change to one property triggers a server-computed change on another that you would clobber with a blind update.
If you don't want to update your resource with a $save response, you could:
- Refrain from returning it from the server. Otherwise, why bother returning the resource from the server again if you have no intention of updating your client with the response before you continue operations?
- Set up a hook to ignore the returned resource. Phrasing it this way makes it sound like a bad idea.
- Use a custom method rather than
$save. Could be good, depending on your use case. - Use promises or UX design to lock other properties to edits during pending edits. Could be good, depending on use case.
I'd guess a common source of this would be allowing inline-editing of properties. If you edit two properties this way, and then save both of them in quick succession, you would see this bug. Better behaviour would be to only allow editing of one property at a time. If you want to update multiple properties 'at once', you may as well do so using a single save request on the whole object.
If you really want to just ignore the server's response, a custom method sounds like the right choice.