)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"c954bf0eaba25a9bad788dd9b8d0b91f079278c4","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"5fd4c8b9_af4c40ad","updated":"2026-03-31 16:57:16.000000000","message":"I don\u0027t really understand how any of this was working BEFORE we fixed \"invert w/ hexpart\" ... it seemed to me we\u0027d already decided object versioning will keep using inverse timestamps for the object names and once those timestamps have jitter the version ids will expose it as part of the version_id\n\n... so why would we ever use `.normal` - why does the copy-legacy case still use `.normal` !?\n\nIs some or all of this change going to get squashed into the v2/timestamp jitter change (or even merged to master) or does ALL of if depend on tests that are only relevant to optional-s3api-bucket-inventory (!?)","commit_id":"59f4a402f9a015b969dbbf16ee1fee48b58ff37a"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"41afba2079acf82a84553a4ec068ef12f82f422a","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"fd95e411_4fab9e6e","updated":"2026-03-31 17:22:17.000000000","message":"I pulled this patch in front of jitter to reduce the line count o the jitter patch. Maybe that isn\u0027t going to prove useful.\n\nI\u0027ll fix the print","commit_id":"59f4a402f9a015b969dbbf16ee1fee48b58ff37a"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"536d2a8fc10eed92c94990f3c518cd0540c34826","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"9b363515_7f17bd91","updated":"2026-04-08 18:01:09.000000000","message":"\u003e Should this get slated for backporting \n\nYes, it wouldn\u0027t hurt to backport this. It is not as necessary as the other patches because nothing will blow up during upgrades without this change.\n\nIf we did NOT backport:\n\nTL;DR: PUTs in the same deca-microsecond that are concurrent with versioning being disabled could end up being mis-ordered in the versions history.\n\nmore detail...\n\nDuring upgrade to v2 timestamps, an old proxy may copy an existing object and use the \"lossy\" normal format as the version id. That\u0027s ok because it is less that any v2 timestamp in the same deca-microsecond that may have been chosen for a concurrent PUT through a different upgraded proxy. By definition the copied version IS older than the newly PUT version.\n\nIf a \u0027null\u0027 version has been written via a non-upgraded proxy since versioning was disabled, and older versions exist with hex parts, and versioning is then enabled, and a PUT triggers a copy, then it is possible that lossy copied version id might appear older than the latest version-with-hex-part 😞\n\nFor that to happen the sequence:\n\n``PUT (new proxy) object at t1_jitter, disable versioning, PUT (old proxy) object at t1``\n\nwould all need to happen in the same deca-microsecond. And we\u0027d still be better off than a timestamp collision before upgrade.\n\nA similar case might be:\n\n``PUT (new proxy) object at t1_jitter1, disable versioning, PUT (new proxy) object at t1_jitter2``\n\n...then after versioning is enabled, a PUT through a non-upgraded proxy will copy the newer null version with a version-id id based on the lossy t1 which is older than t1_jitter1 😞\n\nSo yeah, backport it!","commit_id":"8cc481de13d9ac1046ca27c637c716d2e8d1711f"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"1a192821111cad07b343fe6c8aabd33b2c2854ff","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"9f599bd7_4de07195","updated":"2026-04-08 03:44:49.000000000","message":"Looks good!","commit_id":"8cc481de13d9ac1046ca27c637c716d2e8d1711f"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"d639e67547c26678482015669cc77de71ba3c581","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"d86cb6ba_85e9d1a1","updated":"2026-04-08 17:26:25.000000000","message":"Should this get slated for backporting alongside https://review.opendev.org/c/openstack/swift/+/981908 and https://review.opendev.org/c/openstack/swift/+/982183 ?","commit_id":"8cc481de13d9ac1046ca27c637c716d2e8d1711f"}],"test/unit/common/middleware/test_object_versioning.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"c954bf0eaba25a9bad788dd9b8d0b91f079278c4","unresolved":true,"context_lines":[{"line_number":1146,"context_line":"            (\u0027PUT\u0027,"},{"line_number":1147,"context_line":"             self.build_versions_path(obj\u003d\u0027o\u0027, version\u003d(~ts_old).normal)),"},{"line_number":1148,"context_line":"            (\u0027PUT\u0027,"},{"line_number":1149,"context_line":"             self.build_versions_path(obj\u003d\u0027o\u0027, version\u003d(~ts_new).internal)),"},{"line_number":1150,"context_line":"            (\u0027PUT\u0027, \u0027/v1/a/c/o\u0027),"},{"line_number":1151,"context_line":"        ])"},{"line_number":1152,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"8abe5354_242b5bcd","side":"PARENT","line":1149,"updated":"2026-03-31 16:57:16.000000000","message":"how weird is it that we already had this normal vs internal dissonance on these PUT requests?","commit_id":"fc2f3a37a2d599d4862d4a08b0902c9f3cb03884"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"c954bf0eaba25a9bad788dd9b8d0b91f079278c4","unresolved":true,"context_lines":[{"line_number":1105,"context_line":""},{"line_number":1106,"context_line":"    def test_PUT_overwrite_object(self):"},{"line_number":1107,"context_line":"        ts_old, ts_new \u003d self.ts(), self.ts()"},{"line_number":1108,"context_line":"        # the copied source version id inverts the response normal x-timestamp"},{"line_number":1109,"context_line":"        exp_old_version \u003d (~(ts_old.normalized())).internal"},{"line_number":1110,"context_line":"        # the new version id inverts the req x-timestamp"},{"line_number":1111,"context_line":"        exp_new_version \u003d (~ts_new).internal"}],"source_content_type":"text/x-python","patch_set":1,"id":"45b81bbd_47b0be0a","line":1108,"updated":"2026-03-31 16:57:16.000000000","message":"WHAT?  why would it normalize the response x-timestamp?\n\nIt may not matter for collisions but if we normally expect:\n\nobject_version_name \u003d ~Timestamp(object-x-timestamp.internal)\n\nwhy would we make a special exception for when we\u0027re doing the copy-null-version?","commit_id":"59f4a402f9a015b969dbbf16ee1fee48b58ff37a"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"41afba2079acf82a84553a4ec068ef12f82f422a","unresolved":true,"context_lines":[{"line_number":1105,"context_line":""},{"line_number":1106,"context_line":"    def test_PUT_overwrite_object(self):"},{"line_number":1107,"context_line":"        ts_old, ts_new \u003d self.ts(), self.ts()"},{"line_number":1108,"context_line":"        # the copied source version id inverts the response normal x-timestamp"},{"line_number":1109,"context_line":"        exp_old_version \u003d (~(ts_old.normalized())).internal"},{"line_number":1110,"context_line":"        # the new version id inverts the req x-timestamp"},{"line_number":1111,"context_line":"        exp_new_version \u003d (~ts_new).internal"}],"source_content_type":"text/x-python","patch_set":1,"id":"7ab25788_519454f9","line":1108,"in_reply_to":"45b81bbd_47b0be0a","updated":"2026-03-31 17:22:17.000000000","message":"The copy from existing uses the GET response x-timestamp which is normal format. I mean, it has to use normal format on master because we can\u0027t invert a timestamp with offset which the source might have.\n\nWhen we have jitter and can invert hex parts then we could change object versioning (I should update the jitter patch). There\u0027s also the question of whether to use x-timestamp, x-backend-timestamp or x-backend-data-timestamp https://review.opendev.org/c/openstack/swift/+/936379/3/swift/common/middleware/versioned_writes/object_versioning.py","commit_id":"59f4a402f9a015b969dbbf16ee1fee48b58ff37a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"c954bf0eaba25a9bad788dd9b8d0b91f079278c4","unresolved":true,"context_lines":[{"line_number":1126,"context_line":"        self.app.register("},{"line_number":1127,"context_line":"            \u0027PUT\u0027, \u0027/v1/a/c/o\u0027, swob.HTTPCreated, {}, \u0027passed\u0027)"},{"line_number":1128,"context_line":"        for r in self.app._responses:"},{"line_number":1129,"context_line":"            print(r)"},{"line_number":1130,"context_line":"        put_body \u003d \u0027stuff\u0027 * 100"},{"line_number":1131,"context_line":"        req \u003d Request.blank("},{"line_number":1132,"context_line":"            \u0027/v1/a/c/o\u0027, method\u003d\u0027PUT\u0027, body\u003dput_body,"}],"source_content_type":"text/x-python","patch_set":1,"id":"3a6e5bf3_933d7bf7","line":1129,"updated":"2026-03-31 16:57:16.000000000","message":"nit: probably helpful for debugging but could be removed","commit_id":"59f4a402f9a015b969dbbf16ee1fee48b58ff37a"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"41afba2079acf82a84553a4ec068ef12f82f422a","unresolved":true,"context_lines":[{"line_number":1126,"context_line":"        self.app.register("},{"line_number":1127,"context_line":"            \u0027PUT\u0027, \u0027/v1/a/c/o\u0027, swob.HTTPCreated, {}, \u0027passed\u0027)"},{"line_number":1128,"context_line":"        for r in self.app._responses:"},{"line_number":1129,"context_line":"            print(r)"},{"line_number":1130,"context_line":"        put_body \u003d \u0027stuff\u0027 * 100"},{"line_number":1131,"context_line":"        req \u003d Request.blank("},{"line_number":1132,"context_line":"            \u0027/v1/a/c/o\u0027, method\u003d\u0027PUT\u0027, body\u003dput_body,"}],"source_content_type":"text/x-python","patch_set":1,"id":"e363d748_3468f6a9","line":1129,"in_reply_to":"3a6e5bf3_933d7bf7","updated":"2026-03-31 17:22:17.000000000","message":"oops","commit_id":"59f4a402f9a015b969dbbf16ee1fee48b58ff37a"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2a0ea7385c6f7202bcf2a6132402b227da7c0eac","unresolved":false,"context_lines":[{"line_number":1126,"context_line":"        self.app.register("},{"line_number":1127,"context_line":"            \u0027PUT\u0027, \u0027/v1/a/c/o\u0027, swob.HTTPCreated, {}, \u0027passed\u0027)"},{"line_number":1128,"context_line":"        for r in self.app._responses:"},{"line_number":1129,"context_line":"            print(r)"},{"line_number":1130,"context_line":"        put_body \u003d \u0027stuff\u0027 * 100"},{"line_number":1131,"context_line":"        req \u003d Request.blank("},{"line_number":1132,"context_line":"            \u0027/v1/a/c/o\u0027, method\u003d\u0027PUT\u0027, body\u003dput_body,"}],"source_content_type":"text/x-python","patch_set":1,"id":"f3f47b0a_0c4f334f","line":1129,"in_reply_to":"e363d748_3468f6a9","updated":"2026-04-08 16:34:28.000000000","message":"Done","commit_id":"59f4a402f9a015b969dbbf16ee1fee48b58ff37a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"c954bf0eaba25a9bad788dd9b8d0b91f079278c4","unresolved":true,"context_lines":[{"line_number":1982,"context_line":"        self._do_test_PUT_version(self.ts().internal)"},{"line_number":1983,"context_line":""},{"line_number":1984,"context_line":"    def test_PUT_version_is_legacy_normal_format(self):"},{"line_number":1985,"context_line":"        self._do_test_PUT_version(self.normal_ts().internal)"},{"line_number":1986,"context_line":""},{"line_number":1987,"context_line":"    def test_PUT_version_with_non_empty_body(self):"},{"line_number":1988,"context_line":"        req \u003d Request.blank("}],"source_content_type":"text/x-python","patch_set":1,"id":"38ac533c_89aa8683","line":1985,"updated":"2026-03-31 16:57:16.000000000","message":"I mean, it seems fine that this would still work - i.e. that object_versioning shouldn\u0027t really care\n\n... but when do we expect a PUT to use a \"legacy normal format\" - is this patch stacked on v2timestamp/jitter?","commit_id":"59f4a402f9a015b969dbbf16ee1fee48b58ff37a"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"41afba2079acf82a84553a4ec068ef12f82f422a","unresolved":true,"context_lines":[{"line_number":1982,"context_line":"        self._do_test_PUT_version(self.ts().internal)"},{"line_number":1983,"context_line":""},{"line_number":1984,"context_line":"    def test_PUT_version_is_legacy_normal_format(self):"},{"line_number":1985,"context_line":"        self._do_test_PUT_version(self.normal_ts().internal)"},{"line_number":1986,"context_line":""},{"line_number":1987,"context_line":"    def test_PUT_version_with_non_empty_body(self):"},{"line_number":1988,"context_line":"        req \u003d Request.blank("}],"source_content_type":"text/x-python","patch_set":1,"id":"6659e85e_318ccf1c","line":1985,"in_reply_to":"38ac533c_89aa8683","updated":"2026-03-31 17:22:17.000000000","message":"IIUC this form of a PUT is used when s3api wants to restore a previous version after the latest is deleted. The version being restored may have a legacy timestamp without jitter.","commit_id":"59f4a402f9a015b969dbbf16ee1fee48b58ff37a"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2a0ea7385c6f7202bcf2a6132402b227da7c0eac","unresolved":true,"context_lines":[{"line_number":1211,"context_line":"        ])"},{"line_number":1212,"context_line":""},{"line_number":1213,"context_line":"    def test_PUT_overwrite_unversioned_object_timestamp_with_hex_part(self):"},{"line_number":1214,"context_line":"        # it\u0027s ok for the source x-backend-timestamp to have a hex part/offset"},{"line_number":1215,"context_line":"        ts_old \u003d Timestamp(self.ts(), offset\u003d1)"},{"line_number":1216,"context_line":"        get_resp_headers \u003d {\u0027x-timestamp\u0027: ts_old.normal,"},{"line_number":1217,"context_line":"                            \u0027x-backend-timestamp\u0027: ts_old.internal,"}],"source_content_type":"text/x-python","patch_set":3,"id":"b2d921db_b9be0c40","line":1214,"updated":"2026-04-08 16:34:28.000000000","message":"note: this test is here because we don\u0027t yet have v2 timestamps with jitter but I want to pre-emptively cover the case when timestamp does have a hex part","commit_id":"8cc481de13d9ac1046ca27c637c716d2e8d1711f"}]}
