POST to DLO squashes data without fast-POST

Bug #1487791 reported by John Dickinson
20
This bug affects 2 people
Affects Status Importance Assigned to Milestone
OpenStack Object Storage (swift)
Fix Released
Undecided
Janie Richling

Bug Description

If object_post_as_copy is true (the default), then a POST to a DLO ends up squashing the object into the manifest object. This is normal for COPY operations, but if the user ensures that the proper manifest header is set, it still does it, likely resulting in an error.

With a maximum object size of 400000000:

$ curl -H "X-Auth-Token: AUTH_tk9d66e358d3f44d70a6cfda6ac52e5fe4" http://saio:8080/v1/AUTH_test/c1/o -I
HTTP/1.1 200 OK
Content-Length: 524288008
Etag: "10b92c89c18a806498b21b01006fcfcc"
Accept-Ranges: bytes
Last-Modified: Sat, 22 Aug 2015 22:50:10 GMT
X-Object-Manifest: c2/o
X-Timestamp: 1440283809.36887
Content-Type: application/octet-stream
X-Trans-Id: tx5180cd78ffe44a2a9737b-0055d8fd7a
Date: Sat, 22 Aug 2015 22:53:46 GMT

$ curl -i -H "X-Auth-Token: AUTH_tk9d66e358d3f44d70a6cfda6ac52e5fe4" http://saio:8080/v1/AUTH_test/c1/o -XPOST -H "x-object-manifest: c2/o" -H "x-object-meta-foo: bar"
HTTP/1.1 413 Request Entity Too Large
Content-Length: 108
Content-Type: text/html; charset=UTF-8
X-Trans-Id: txff4972ba425847d084eab-0055d8fda3
Date: Sat, 22 Aug 2015 22:54:27 GMT

<html><h1>Request Entity Too Large</h1><p>The body of your request was too large for this server.</p></html>

Logical objects smaller than the max size "work", but the data is duplicated in the system.

The POST-as-copy code needs to respect the manifest header and *not* copy the data in that case.

Revision history for this message
Janie Richling (jrichli) wrote :

from dfg in channel: if you send a COPY with --multipart-manifest=get on it then it just copies the manifest

Revision history for this message
Alistair Coles (alistair-coles) wrote :

So what *should* happen with a POST? (regardless of object_post_as_copy value - the outcome should be the same either way for a consistent API behaviour)

First, should a POST ever result in the DLO segments being "squashed" into what was the manifest object? IMHO, no - with post-as-copy that may result in errors when size of large object is > 5GB as reported.

Second, how should the presence or not of x-object-manifest header in the POST request be handled? I can see three options for the desired outcomes:

1) existing x-object-manifest value is *always preserved* on POST i.e. any x-object-manifest header sent with a POST is ignored, the original PUT x-object-manifest value can only be changed by another PUT.

2) existing x-object-manifest value is *always updated* with the value supplied with POST, and if POST has no x-object-manifest then the outcome is an object with no x-object-manifest value. i.e. client must provide x-object-manifest value with every POST in order to preserve the manifest property of the object.

3) existing x-object-manifest value is updated IF a new value is provided in the POST, otherwise the existing value is preserved. I strongly dislike this outcome because it is very hard to implement with object_post_as_copy=False (fast-POST). With fast-post it requires the object servers to maintain the timestamp of X-Object-Manifest values independently of the data or metadata timestamps in order to reconcile X-Object-Manifest values between replicas that may have become inconsistent. This is exactly the same problem that [1] is fixing with content-type, and it is non-trivial. So if we choose this option for post-as-copy we are adding significant pain to getting fast-post to provide the same behaviour.

So for me its between 1 and 2. Any other opinions?

[1] https://review.openstack.org/135380

Revision history for this message
John Dickinson (notmyname) wrote :

option 1 sounds hard as soon as you add in multiple concurrent users.

I like option 2. That's the current behavior of metadata in objects. You set the current state of metadata on the POST. So basically, a POST to a *LO operates on the manifest, not the logical object. That's how I'd expect it to behave, and I think that's consistent with the way "normal" objects work.

Changed in swift:
status: New → Confirmed
Revision history for this message
Eran Rom (eranr) wrote :

Few thoughts:
1. Is there a reason to keep POST-as-COPY once FAST-post limitations are resolved? I know this is not really related to the above question, by the original bug description did brought it up.
2. To the question: Putting aside implementation difficulties (sorry) what should be the right semantics for x-object-manifest.
Of I get this right then a DLO is more of a metadata (over multiple objects) which happens to be implemented using a Swift object having some Swift (sys?) metadata over it. As such I think the x-object-manifest 'metadata' is essentially the DLO "data" and as such isn't supposed to be updated 'independently' of the Swift data object.
In other words, IMHO, x-object-manifest should not be up-datable via POST.
With this in mind, I would dare to suggest a 1* option which is: block any object POST request that has the 'x-object-manifest' header.

Revision history for this message
Alistair Coles (alistair-coles) wrote :

@John I'm not making an argument in favour of 1, but I'm not sure in what way 1 is hard with concurrent requests - basically x-object-manifest would get treated like Etag and x-object-sysmeta, can only be set on a PUT and the latest PUT timestamp will eventually win over other "concurrent" PUTs.

@Eran - interesting thought, whether we would just remove post-as-copy at same time as 'fixing' fast-POST. My assumption had been that there would at least be a period when both modes worked, to allow cautious operators to not 'upgrade' to fast-post.

Eran makes a good point about the semantics of x-object-manifest. With SLO, once a manifest has been PUT it can only cease to have the semantic of a manifest by being PUT again - POSTs to an SLO manifest will never change it from being a manifest, and the composition of the large object can only be changed by a PUT (since with SLO the segment references are in the object body).

So option 1 makes the semantic of a DLO manifest closer to that of an SLO (with the possible anomaly of making x-object-manifest the only item of client specified metadata that cannot be changed by a POST*, whereas option 2 is current behaviour with fast-post, but not post-as-copy because of the bug.

(* we'd also have to decide if we returned HttpBadRequest if x-object-manifest is sent with a POST, or simply ignore it :/)

Revision history for this message
Janie Richling (jrichli) wrote :

In order to specify my vote on this, I would need to understand more what type of use cases would ever cause somebody to want to update the x-object-manifest.

Eran has said "x-object-manifest should not be up-datable via POST." I guess I want to make sure that this is correct and that it would never really make sense to update that value after the initial PUT. I mean, right now if somebody supplies the header, is it always with the same value that was there before anyway? I guess it might be to fix it if it was done incorrectly in the first place? If that is the only case, it is a failure case, so we can fall to deleting and re-creating.

Revision history for this message
Janie Richling (jrichli) wrote :

It has been brought to my attention that there are use cases to update the x-object-mainfest. The manifest itself could be a segment. In which case, modifying the large object to be composed of different segments (re-using segments, including the manifest segment, in the storage) would lead to the desire to change this value.

In light of the fact that somebody may have been using this capability, I would vote for simply fixing the "bug" in this case, which is the situation initially described in this report. We can fix this behavior that is clearly wrong, and make the minimum amount of change to the current interface. I think that aligns with #2 at this point, but let me know if I am wrong. Hopefully I will have a patch loaded soon - either to the existing review or a new one. Let me know what you think.

Revision history for this message
Janie Richling (jrichli) wrote :

I uploaded a patch that implements option #2: https://review.openstack.org/#/c/252791

Revision history for this message
clayg (clay-gerrard) wrote :

wow, it's sorta tricky. I think as long as we don't make POST be semantically a COPY it's a win.

Whatever the behavior is just what the API is I suppose.

FWIW, not sure it's entirely fair to compare DLO semantics for update to SLO - since SLO's have a body and are validated - and DLO's can point to anything if you update the prefix.

I think most of the time POSTs to DLO manifests will be to add object/http metadata, it's trivial to change the DLO-prefix with a PUT if that's what you want to do, but OTOH I agree it would be inconsistent with other metadata in the namespace if a client request to change it ignored it.

However, from a API use-case perspective I think it *would* have been better to have it in sys-meta (exposed outside of user-meta namespace) and not updatable by anything except PUT. Not sure if we can fix that now, but that's MHO.

Revision history for this message
Alistair Coles (alistair-coles) wrote :

I don't think it would be too hard to make x-object-manifest behave like sysmeta, i.e. option 1 in #2 above, the header can only be set on a PUT, then is immutable. For fast-post we add it to the DATAFILE_SYSTEM_METADATA and...maybe its that simple, for post-as-copy have the copy-hook nuke the header from sink-req when handling a post-as-copy.

BUT...that feels more like an API change than option 2 when you remember that fast-post on master works and it works in the manner of option 2 i.e. it does not copy the segments into the manifest during POST (!), and it allows the header to be modified with a POST, and it=f the header is not specified on POST then it's lost from the object.

So anyone using fast-post (is there anyone?) could find the API changed for them if we do option #1 and make the header immutable, whereas anyone taking advantage of post-as-copy's wonky behavior to COPY during a POST can reasonably be told that was a bug.

Furthermore, if we change diskfile to make x-object-manifest header immutable then anyone who has previously updated a manifest with a fast-post would find that, after upgrading, their x-object-manifest values reverted back to whatever was PUT with the .data file. OOPS! So not only does the API change, but their effective data changes.

So although its not a clear cut choice, I'm for keeping the header as being mutable and ephemeral w.r.t. POSTs (IDK if ephemeral is the right word, transient perhaps?) i.e. option 2 in #2.

Revision history for this message
clayg (clay-gerrard) wrote :

ok so when I said "Not sure if we can fix that now" I was probably being overly optimistic even entertaining the idea - I think Alister draws a clear argument that it obviously can *not* change it at this point.

To rephrase, I see three API's for POSTing DLO-prefix discussed here

1) fast-post - prefix is transient and updatable - consistent will all other user metadata
2) post-as-copy - totally broken
3) dreamy-eyed-time-machine - prefix is sysmeta

Now is not the time to invent a time machine.

We have a consistent behavior of POSTing DLO-prefix with fast-post and a broken implementation with post-as-copy. We should make the post-as-copy the same as fast-post. It's been said over and over - option #2 FTW.

Janie Richling (jrichli)
Changed in swift:
assignee: nobody → Janie Richling (jrichli)
Revision history for this message
Janie Richling (jrichli) wrote :

Now that copy has been moved to middleware, this issue - as written - no longer exists. But the tests that had been developed to assert the correct behavior from https://review.openstack.org/#/c/252791/20/test/functional/tests.py reveal that now when using post-as-copy and dlo, if you attempt a post of a new x-object-manifest header, it does not change the x-object-manifest. I do not yet know the solution. Maybe this should be written as a new bug that references this one.

Revision history for this message
Janie Richling (jrichli) wrote :

The remaining issue mentioned in my last comment has now also been fixed on master. The only thing left to do is to add the dlo + post functests that were proposed in patch 252791. I will upload a new patch soon.

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Fix proposed to swift (master)

Fix proposed to branch: master
Review: https://review.openstack.org/347545

Changed in swift:
status: Confirmed → In Progress
Janie Richling (jrichli)
summary: - POST to DLO squashes data without fast-POST
+ No tests exist for POST to DLO manifest file
Revision history for this message
Janie Richling (jrichli) wrote : Re: No tests exist for POST to DLO manifest file

Well, technically, the behavior that exists on master currently matches what was described in "option #1" (thus only a test would need to be added, but no actual DLO changes). Perhaps it is worth re-assessing whether or not we need to make changes to behave as described in option #2. Is it important to allow the client to modify the x_o_m on an existing DLO manifest file?

Revision history for this message
Tim Burke (1-tim-z) wrote :

Current state of the world:

 * With post-as-copy, POSTing to a DLO preserves the original X-Object-Manifest, whether the client sent an X-Object-Manifest or not.
 * With fast-post, POSTing an X-Object-Manifest to a DLO updates the manifest.
 * With fast-post, POSTing to a DLO while not providing an X-Object-Manifest turns it into a plain old swift object (!). Thought you were setting this to expire next week? Well, it's (effectively) already gone.
 * With either, POSTing an X-Object-Manifest to a plain old swift object turns it into a DLO (!). This means that for post-as-copy the only way to turn it back into a POSO is to re-upload (!!).
 * With either, POSTing an X-Object-Manifest to an SLO results in ... poorly defined behavior [1]

I'd prefer to have the manifest header be treated like sysmeta. Sure, it's user meta in the sense that the user tells us the value, but it seems like a much cleaner semantic to say (1) you can only use *one* type of large object at a time and (2) whatever type of large object you use, the fact that it is a large object and all of the manifest data is set on PUT. We could enforce this with something like what Clay's trying to do for SLO in https://review.openstack.org/#/c/334719/. This also matches fairly well the behavior we're aiming for with symlinks.

But Alistair's right. I'm less worried about the degree to which the API changes -- I'm comfortable with claiming that the old behavior was a bug that is now fixed. Dealing with on-disk data where a user has modified the manifest via a POST, however, is rather terrible. If we modeled enforcement off of Clay's SLO patch, we change the effective data immediately on upgrade. Maybe we could get away with something like the *current* SLO behavior? I.e., prevent POSTs from including X-Object-Manifest at the proxy, then carry forward any existing X-Object-Manifest entries manually. (Maybe only if orig_metadata came from a .meta instead of a .data?) But we'd still run into consistency issues when we have a PUT over a modified DLO followed by a POST.

[1] My manually testing resulted in 500s, but that seems to have been caused (in part) by my test data: the reconstructed DLO was all integer data, which could be parsed as JSON, resulting in a TypeError around `for seg_dict in segments`. This is pipeline dependent, though; I had slo left of dlo, and by switching the order the object would behave as a DLO. At least, until I broke the SLO, at which point it would 409.

Revision history for this message
Janie Richling (jrichli) wrote :

Thank you for the details, Tim. Given clay's SLO patch and current DLO behavior, it sounds like we should not allow POST to change where a manifest points. Is this also what you are thinking? I am thinking about writing a new bug to handle the undesirable behavior that currently happens when X-Object-Manifest is specified on a POST to a plain object (that patch could be dependent on clay's SLO patch).

Revision history for this message
Alistair Coles (alistair-coles) wrote :
Download full text (3.6 KiB)

So somehow we went from POST-as-copy to a DLO manifest causing it to be replaced with the full object, to POST to a DLO manifest not updating the manifest header :/ That in itself confirms the current topic of this bug.

I am wondering if we should have closed the old bug and started a new one?

Re. changing fast-post behaviour: I cannot (yet) think of how we can "safely" make X-Object-Manifest become an immutable property of an object without risking breaking existing data. More discussion on that below.

So my conclusion is unchanged: however much I like or dislike the API, I think that post-as-copy should be made to behave the same as fast-post, which at this moment means that it is a bug for the post-as-copy to not update the x-object-manifest header. i.e. I still end up voting for option 2 described in comment #2.

*** objects that are both DLO and SLO

As for objects that are both DLO and SLO, maybe we should declare DLO to be preferred i.e. SLO GET/HEAD handling is bypassed when X-Object-Manifest is found in response headers. I *think* that avoids problems whichever way the middlewares are ordered.

If there is a pre-existing object with both headers that was supposed to be GETtable as an SLO then (a) did it ever work? and (b) you can make it an SLO by removing the X-Object-Manifest header with a POST (subject to fixing the post-as-copy to a DLO manifest).

Can a DLO/SLO currently ever work for GETs? Let's say you had a SLO manifest at a/c1/m1, you then added X-Object-Manifest=a/c2 with a fast-post (remember, you cannot yet do that with post-as-copy) and the x-object-manifest resolves to a different valid SLO manifest a/c2/m2. Currently with SLO to left of DLO (which the client has no control over) a GET for the SLO manifest at a/c/m1 would result in DLO first resolving a/c/m2 and passing its body to SLO, which would SLO would then successfully parse that but probably find an ETag mismatch wrt to the headers on a/c/m1 (I forget which headers DLO would return with its response...).

Maybe if the SLO manifest st a/c/m1 were identical to that at a/c/m2 then the GET would work on master?

*** Why it is hard to make x-object-manifest become immutable ***

Consider a pre-existing object:

t1.data{x-object-manifest=bar} + t2.meta{x-object-manifest=foo}

we'd have to make the new algorithm favour foo over bar i.e. preserve the .meta file value, so we could copy it from old metadata to new metadata (maybe what Tim means by "carry forward...manually "). But now consider a pre-existing object:

t1.data{x-object-manifest=bar} + t2.meta{}

there is nothing to carry forward from the .meta file, but we must not carry-forward the data file value (bar).

Now consider a post-changed-semantic POST to an object:

t1.data{x-object-manifest=bar} + t3.meta{}

...now we *do* want to carry-forward the .data file value.

We could try to be clever and have the object server write a marker to inform the future reader that the .meta file was written in the post-changed-semantic future world:

t1.data{x-object-manifest=bar} + t3.meta{x-object-sysmeta-use-the-datafile-object-manifest=True}

but that violates the rpinciple that you should never change the perceive...

Read more...

Revision history for this message
Janie Richling (jrichli) wrote :

Thanks, Alistair.

How about this for a way to proceed:
We have one patch that 'partially' fixes this bug - which simply adds the test that asserts if the x_o_m is provided, then POST to DLO does not squash the contents.

Have a different patch to add documentation and also add the code to allow post-as-copy to change a dlo manifest on a post. We can also resolve what we want to do with SLO referencing DLO or vice-versa in this patch.

I can create a separate bug for the second patch described here, if that is best.

This way, the dlo test gets in place as soon as possible - and I will work on supporting option #2 and make proper docs when the issues are settled.

Revision history for this message
Alistair Coles (alistair-coles) wrote :

@Janie - OK, adding test for current behaviour would be good for as much as the behaviour is common to fast-post and post-as-copy i.e. not squashing the manifest. Thanks.

Maybe revert this bug to original topic and we'll close it (do we know the commit that fixed the original bug - was it copy middleware?). Start a new bug for poast-as-copy not allowing manifest to be updated.

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Change abandoned on swift (master)

Change abandoned by Alistair Coles (<email address hidden>) on branch: master
Review: https://review.openstack.org/252791
Reason: The fix is out of date w.r.t. copy feature being moved to copy middleware [1], at which point I believe the bug was fixed.

Apparently the useful tests here have been included in [2]

Please restore the review if I am incorrect.

[1] https://review.openstack.org/156923
[2] https://review.openstack.org/#/c/347545/

Janie Richling (jrichli)
summary: - No tests exist for POST to DLO manifest file
+ POST to DLO squashes data without fast-POST
Revision history for this message
Janie Richling (jrichli) wrote :

I verified that it was copy as middleware that fixed the original issue. (The segment data was appended to the manifest contents)
FIXED : commit 46d61a4dcd9a5d9157625c06d6fe7d916e80c3d2 Refactor server side copy as middleware
HAS ISSUE: commit 72372c1464d1aae4a9b5de5ef6a3689fddf168cc Merge "decouple versioned writes from COPY"

What is interesting about the old bug is that if the manifest header was supplied on each POST (post-as-copy), then it is clear that the segment data was getting *appended* to the manifest contents (not replacing its contents), so the manifest object would grow by the size of the large object with each post. The appended object data was revealed with every subsequent large object GET after the POST (since the manifest contents are always included first before the segment data).

If the manifest header was NOT supplied on this POST, then the manifest was no longer a manifest, and the contents that had been appended remain the contents (it doesn't grow with subsequent POSTs only because the manifest-ness was lost).

There are still no tests that POST to a DLO manifest, but that is being worked under https://review.openstack.org/#/c/347545/ with no associated bug (Alistair mentioned we could close this as fixed under copy as mw).

Bug https://bugs.launchpad.net/swift/+bug/1612991 was created for the "Post-as-copy does not allow a DLO manifest to be updated".

Revision history for this message
Alistair Coles (alistair-coles) wrote :

IMO this bug is fixed, as Janie confirmed the fix was in this commit 46d61a4. So this bug could be closed.

Changed in swift:
status: In Progress → Fix Released
Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Related fix merged to swift (master)

Reviewed: https://review.openstack.org/347545
Committed: https://git.openstack.org/cgit/openstack/swift/commit/?id=13747021a8743d358961c1c4820fddfe9c955ea2
Submitter: Jenkins
Branch: master

commit 13747021a8743d358961c1c4820fddfe9c955ea2
Author: Janie Richling <email address hidden>
Date: Tue Jul 26 15:20:52 2016 -0500

    Add test for POST to DLO manifest file

    In the past, a POST to a DLO manifest file when object_post_as_copy
    was true resulted in the manifest file contents being replaced
    by the concatenation of the DLO segments. This no longer
    happens, but tests for this case are missing.

    This patch adds a functional test to assert that the manifest
    file is preserved in a POST request.

    Change-Id: I90546014a7dcc7266f0d0e0ff6339688b7954b96
    Related-bug: #1487791
    Related-bug: #1514317

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Related fix proposed to swift (feature/hummingbird)

Related fix proposed to branch: feature/hummingbird
Review: https://review.openstack.org/363111

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Related fix merged to swift (feature/hummingbird)
Download full text (84.1 KiB)

Reviewed: https://review.openstack.org/363111
Committed: https://git.openstack.org/cgit/openstack/swift/commit/?id=1ab2a296f58ae76aeffef9f3f0fb90e15358be27
Submitter: Jenkins
Branch: feature/hummingbird

commit 3b5850836c59c46f2507a7f62aceccf4c37e5d41
Author: gecong1973 <email address hidden>
Date: Tue Aug 30 15:08:49 2016 +0800

    Remove white space between print and ()

    There is a white space between print and ()
    in /tempauth.py, This patch fix it

    Change-Id: Id3493bdef12223aa3a2bffc200db8710f5949101

commit f88e7fc0da2ed6a63e0ea3c3459d80772b3068cd
Author: zheng yin <email address hidden>
Date: Mon Aug 29 20:21:44 2016 +0800

    Clarify test case in common/ring/test_builder

    They use a bare assertRaises(ValueError, ring.RingBuilder, *,*,*), but
    it's not clear which one raises which ValueError(), so I extend them to
    validate the error strings as well.

    Change-Id: I63280a9fc47ff678fe143e635046a0b402fd4506

commit d68b1bd6ddf44c5088e9d02dcb2f1b802c71411b
Author: zhufl <email address hidden>
Date: Mon Aug 29 14:31:27 2016 +0800

    Remove unnecessary tearDown

    This is to remove unnecessary tearDown to keep code clean.

    Change-Id: Ie70e40d6b55f379b0cc9bc372a35705462cade8b

commit d2fc2614575b04fd9cab5ae589880b92eee9b186
Author: Matthew Oliver <email address hidden>
Date: Fri Aug 19 16:17:31 2016 +1000

    Authorise versioned write PUTs before copy

    Currently a versioned write PUT uses a pre-authed request to move
    it into the versioned container before checking whether the
    user is authorised. This can lead to some interesting behaviour
    whereby a user can select a versioned object path that it does not
    have access to, request a put on that versioned object, and this
    request will execute the copy part of the request before it fails
    due to lack of permissions.

    This patch changes the behaviour to be the same as versioned DELETE
    where the request is authorised before anything is moved.

    Change-Id: Ia8b92251718d10b1eb44a456f28d3d2569a30003
    Closes-Bug: #1562175

commit c1ef6539b6ba9d2e4354c9cd2eec8a0195cdb19f
Author: Clay Gerrard <email address hidden>
Date: Thu Aug 25 11:00:49 2016 -0700

    add test for expirer processes == process

    This is a follow up from a change that improved the error message.

    Related-Change: I3d12b79470d122b2114f9ee486b15d381f290f95

    Change-Id: I093801f3516a60b298c13e2aa026c11c68a63792

commit 01477c78c1163822de41484e914a0736e622085b
Author: zheng yin <email address hidden>
Date: Thu Aug 25 15:37:42 2016 +0800

    Fix ValueError information in obj/expirer

    I fix error information in raise ValueError(...)
    For example:
        if a>=b:
            # It should be under below and not 'a must be less than or equal to b'
            raise ValueError('a must be less than b')

    Change-Id: I3d12b79470d122b2114f9ee486b15d381f290f95

commit b81f53b964fdb8f3b50dd369ce2e194ee4dbb0b7
Author: zheng yin <email address hidden>
Date: Tue Aug 23 14:26:47 2016 +0800

    Improve readability in the obj server's unit tests

    This change improves the reada...

tags: added: in-feature-hummingbird
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Duplicates of this bug

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.