-
Notifications
You must be signed in to change notification settings - Fork 157
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
Support conditional create-on-update via If-None-Match #2050
Comments
Note from robin:
However, as noted in the description, this should be much easier than the locking that would be needed to properly support |
should be done as a SELECT FOR UPDATE on the logical resource id. if we want this logic at the REST layer, then we'd need to introduce a PL change so that the rest layer can tell the PL that its doing a read that should get the SELECT FOR UPDATE semantics. from robin: we need to think this through carefully in the case of bundles because you can easilly get into deadlock cases. |
lower priority since we believe the workaround is to use #2265 |
IfNoneMatch should also work for bundles. See #2754 |
By passing this information down to the persistence layer, we can correctly handle the necessary serialization. The acceptance criteria should be updated to include some deleted resource scenarios. As we're already updating our persistence API in the next release, it makes sense to implement this now. It probably makes sense to add this to the persistence context to avoid more changes to the signatures. But it's still an API change because the behavior of the interaction will change. |
I updated the acceptance criteria to reflect the following design decisions:
|
Issue #2050 conditional create-on-update using If-None-Match
I tested various PUT options things were working as expected.
However, then I noticed the Location header on the 304 response was off: Note the extra |
After further investigation, I think its an artifact of our refactored update code...not specific to conditional create-on-update with UPDATE: I'm not even sure its a regression and so I opened #2965 for it. Moving this one back to QA. |
I was able to force a 500 Server Error by submitting a transaction bundle that has 2 create-on-update requests, each with
The line number may not match up exactly because I had some local edits, but I'm pretty confident this is an issue.
We might need to issue another read at this point if we need to construct the proper locationURI (e.g. with the proper versionId)...or maybe we can just assume a versionId of 1 since it didn't even exist when we tried to read it just above? |
Question: should undeleting a deleted resource be handled like an updateCreate? Without this, we could run into cases where the locationURI could list the deleted version of a resource. Basically, handling the undelete like create-on-update means that we can avoid doing the extra read in the normal IfNoneMatch case. Signed-off-by: Lee Surprenant <lmsurpre@us.ibm.com>
Question: should undeleting a deleted resource be handled like an updateCreate? Without this, we could run into cases where the locationURI could list the deleted version of a resource. Basically, handling the undelete like create-on-update means that we can avoid doing the extra read in the normal IfNoneMatch case. Signed-off-by: Lee Surprenant <lmsurpre@us.ibm.com>
We had a healthy team debate about the proper response code for conditional create-on-update when the If-None-Match precondition fails. The current implementation returns an HTTP 304 Not Modified response for this case. The problem with this interpretation is that, if you had a transaction bundle with a set of conditional create-on-update requests (via ifNoneMatch=*), if any one of those fails it will fail the entire bundle. For this reason, we are now leaning toward making this configurable. The default should be to return the "proper" HTTP 412 Preconditioned failed, but operators will be able to opt in to the less compliant / more usable HTTP 304 behavior via a config property like |
Updated results with default config:
With
And within a transaction bundle where the same resource id is updated twice (each with ifNoneMatch set to "*") it returned 200 OK:
Looks good! |
When ifNoneMatchReturnsNotModified is set to false, the failed precondition fails the transaction bundle with a 400 Bad Request (as expected) and the following response body:
The only oddity here is the |
the corresponding batch bundle succeeds with 200 OK (again as expected):
|
Is your feature request related to a problem? Please describe.
In #160 we added support for conditional read via
If-None-Match
.However, this header is also useful on the update/create-on-update side. Specifically, it can be used to avoid creating the same resource multiple times.
Describe the solution you'd like
Clients can pass the
If-None-Match
header to us in their PUT request.If
If-None-Match
is set to*
and a resource exists at the passed location, then no update performed.Describe alternatives you've considered
The spec defines a conditional create via a custom
If-None-Exists
header that is based on FHIR search. We support that today, but it would be extremely hard to get the locking right if we want to guarantee no duplicates (see #2051).Acceptance Criteria
1.
GIVEN a PUT to [base]/Observation/123
WHEN the
If-None-Match
header is set to *AND a resource (any version) already exists at [base]/Observation/123
THEN the response status is HTTP 304 (Not Modified)
AND the response body is set according to the
Prefer: return
preference (representation=the existing resource, minimal=empty, OperationOutcome=a short description of why the resource wasn't updated)GIVEN a PUT to [base]/Observation/123
WHEN the
If-None-Match
header is set toW/"1"
AND a resource with version "1" already exists at [base]/Observation/123
THEN the response status is HTTP 500 (Server Error)
AND the response body is set according to the
Prefer: return
preference (minimal=empty, OperationOutcome=a description of the error explaining that If-None-Match with a specific version is not supported)GIVEN a PUT to [base]/Observation/123
WHEN the
If-None-Match
header is set to *AND no resource exists at [base]/Observation/123
THEN the response status is HTTP 201 (Created)
AND the response body is set according to the
Prefer: return
preference (representation=the newly created resource, minimal=empty, OperationOutcome=any warnings or informational messages from validating the new resource version)GIVEN a PUT to [base]/Observation/123
WHEN the
If-None-Match
header is not passedAND a resource exists (any version) at [base]/Observation/123
THEN the response status is HTTP 200 (OK)
AND the response body is set according to the
Prefer: return
preference (representation=the newly updated resource, minimal=empty, OperationOutcome=any warnings or informational messages from validating the new resource version)GIVEN a PUT to [base]/Observation/123
WHEN the
If-None-Match
header is set to*
AND a deleted resource exists at [base]/Observation/123
THEN the response status is HTTP 201 (Created)
AND the response body is set according to the
Prefer: return
preference (representation=the newly created resource, minimal=empty, OperationOutcome=any warnings or informational messages from validating the new resource version)Additional context
See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match
Specifically, the part about
If-None-Match: *
The text was updated successfully, but these errors were encountered: