PUT requests and data consistency?
Thought I'd just double check a potential mistake related to
PUT
requests that I think I spotted in Stephen Grider's React course
objectA: {id:1, title:'Harry Potter', pages:50}
- User1
(top left of image) & User2
fetch objectA
- User1
changes objectA.pages
from '50' to '60'
- User2
waits 10 minutes and then changes objectA.title
from 'Harry Potter' to 'Dark Tower' with the data still populating their screen from the 1st fetch
As shown in the image below, Stephen says that the problem here is that User2
will fetch objectA
with a pages
of '60', and that that will leave their local instance of objectA
with a pages
property of 50 - at odds with the db. But this doesn't feel right as he's using a PUT
request which should overwrite the entire object?
I'm not wrong in thinking that the problem with a PUT
request here is not that the server sends back a response with the wrong pages
property, but that old data is included in the PUT
request that erases any of User1
's previous changes, right?6 Replies
it is just that.
In a case of concurrent edition like this one, a PATCH request that only change a subset of the data would be more appropriate. (you can also do it with a put request but that would not respect the description of a PUT request)
Sure, a
PATCH
fixes the issue by only overwriting the property it intended to change, but I can't see how a PUT
request is appropriate at all in scenarios that involve concurrent access, yet it's right there and often is in tutorials? Just found it interesting that it's such a massive bug sitting there in plain sight 🤷♂️
Would it just be a case of changing the application flow, say to have each user check (for exampe) an updatedAt
field in a db and re-fetch or merge the info server side?it's only an issue if you allow concurrent updates, some apps don't and will prompt you to update your data before attempting to make the update (look at git for example)
Probably just a semantic point, but isn't this distinct from the issue of concurrent updates? (I probably muddied the water by talking of concurrent access, sorry!)
The record doesn't have to be updated at the same time for this problem to occur, the 2nd user just has to have stale data - which like you said can be fixed by re-fetching/updating their records, potentially by checking some sort of field (like updatedAt) on the db
It might sound like I'm splitting hairs, but I'm just trying to separate the more traditional concurrency problems (transaction locks) from whatever this is so that I can be clear on how to address it.
So...
- Use
PATCH
or
- Check the db when a request comes in, and update the front end if it's out of date, which seems like a long winded way of doing things.
Sound about right?yeah
thought, PATH could bring the same issue if 2 users edit the same piece of data only partially, something like :
in this case the edit from user 1 would be overridden completely if you simply replace the value of title (basically same issue as before but at the property level rather than the entity level).
and this can be mitigated the same way :
- only update the relevant part (with some modification merging if needed, git style)
- check if the source data is up to date on both sides and bail out if it's not
"in this case the edit from user 1 would be overridden completely if you simply replace the value of title"but at that point isn't that the behaviour I'm looking for? I guess the question is if
User1
makes the edit to i am a title.
do I want User2
to see that change (by checking and updating their data) before they make their own change, adding the capital? I'm not sure if it matters.
Ultimately, if I want to change a property, does your previous change on that field have any impact on my desire to change it? I can't think of why the answer would be 'yes', but that's probably a lack of imagination on my part.
------------
Tbh I guess it's best if I always try to keep the users' data up-to-date. Any chance you've seen any resources on methods to do this, or is there no real best practice? I guess the two that spring to mind are:
1) always provide an accessedAt
field when users GET
data. On further requests compare that to the updatedAt
field in the db. If accessedAt
< updatedAt
, bail on the operation but provide the updated data in the response
2) compare the object for deep equality instead, and do the same