)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"bc5e40be8ed448f37aeeb24e889aabdc7989a8a1","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"f0239565_0c4cb893","updated":"2026-02-26 06:14:01.000000000","message":"Cool, nice find on the bug! And a fix.","commit_id":"b40c1646cebd41a40fdc16170fcc57781a760b3e"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"a2ab1d6d6be1eb8c5fafb1e4729034c35e2bcc4b","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"9ee9eb3a_296886a5","updated":"2026-02-27 11:59:11.000000000","message":"I\u0027ve squashed the multi_upload parts into the parent patch\nhttps://review.opendev.org/c/openstack/swift/+/968740/","commit_id":"b40c1646cebd41a40fdc16170fcc57781a760b3e"}],"swift/common/middleware/s3api/controllers/multi_upload.py":[{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"bc5e40be8ed448f37aeeb24e889aabdc7989a8a1","unresolved":true,"context_lines":[{"line_number":659,"context_line":"        resp, is_marker \u003d _get_upload_info(req, self.app, upload_id)"},{"line_number":660,"context_line":"        if (is_marker and"},{"line_number":661,"context_line":"                Timestamp(resp.sw_headers.get(\u0027X-Backend-Timestamp\u0027, 0))"},{"line_number":662,"context_line":"                \u003e\u003d NormalTimestamp.now()):"},{"line_number":663,"context_line":"            # Somehow the marker was created in the future w.r.t. this thread\u0027s"},{"line_number":664,"context_line":"            # clock. The manifest PUT may succeed but the subsequent marker"},{"line_number":665,"context_line":"            # DELETE will fail, so don\u0027t attempt either."}],"source_content_type":"text/x-python","patch_set":1,"id":"134c22c4_a408f125","line":662,"updated":"2026-02-26 06:14:01.000000000","message":"Nice fix, so basically can\u0027t compare timestamp.internal (string) to a NormalTimestamp object.. which is a little annoying.\n\nMaybe another option is something like https://review.opendev.org/c/openstack/swift/+/978042? \nWhich changes the _parse method a bit in Base and NormalTimestamp to ignore _ after a float. :shrug:","commit_id":"b40c1646cebd41a40fdc16170fcc57781a760b3e"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"1a8e54ce42e8166af717e339f6ea3de0c37aa284","unresolved":true,"context_lines":[{"line_number":659,"context_line":"        resp, is_marker \u003d _get_upload_info(req, self.app, upload_id)"},{"line_number":660,"context_line":"        if (is_marker and"},{"line_number":661,"context_line":"                Timestamp(resp.sw_headers.get(\u0027X-Backend-Timestamp\u0027, 0))"},{"line_number":662,"context_line":"                \u003e\u003d NormalTimestamp.now()):"},{"line_number":663,"context_line":"            # Somehow the marker was created in the future w.r.t. this thread\u0027s"},{"line_number":664,"context_line":"            # clock. The manifest PUT may succeed but the subsequent marker"},{"line_number":665,"context_line":"            # DELETE will fail, so don\u0027t attempt either."}],"source_content_type":"text/x-python","patch_set":1,"id":"19d5d5f2_7d9e88dd","line":662,"in_reply_to":"134c22c4_a408f125","updated":"2026-02-26 17:47:47.000000000","message":"I think that neither version is strictly correct/accurate: the comparison should be with the request\u0027s x-timestamp, but I expect the request doesn\u0027t have one yet, so any approximation to \"now\" will do. NormalTimestamp is a better choice because it WILL be less than any Timestamp that is subsequently created.","commit_id":"b40c1646cebd41a40fdc16170fcc57781a760b3e"}],"swift/common/utils/timestamp.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"1a8e54ce42e8166af717e339f6ea3de0c37aa284","unresolved":true,"context_lines":[{"line_number":216,"context_line":"        try:"},{"line_number":217,"context_line":"            other_ts \u003d self._cast(other)"},{"line_number":218,"context_line":"        except ValueError:"},{"line_number":219,"context_line":"            return NotImplemented"},{"line_number":220,"context_line":"        if other_ts.timestamp \u003c 0:"},{"line_number":221,"context_line":"            return False"},{"line_number":222,"context_line":"        if other_ts.timestamp \u003e\u003d 10000000000:"}],"source_content_type":"text/x-python","patch_set":1,"id":"5d02c65d_d9a25b98","line":219,"updated":"2026-02-26 17:47:47.000000000","message":"I have fixed this in the next set of parent patches that I will push today","commit_id":"b40c1646cebd41a40fdc16170fcc57781a760b3e"}],"test/unit/common/middleware/s3api/test_multi_upload.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"a2ab1d6d6be1eb8c5fafb1e4729034c35e2bcc4b","unresolved":true,"context_lines":[{"line_number":2087,"context_line":""},{"line_number":2088,"context_line":"    def test_object_multipart_upload_complete_marker_ts_now(self):"},{"line_number":2089,"context_line":"        marker_timestamp \u003d Timestamp.now()"},{"line_number":2090,"context_line":"        now_timestamp \u003d NormalTimestamp(float(marker_timestamp))"},{"line_number":2091,"context_line":"        self._do_test_object_multipart_upload_complete_marker_in_future("},{"line_number":2092,"context_line":"            marker_timestamp, now_timestamp)"},{"line_number":2093,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"a51de310_13ce5b50","line":2090,"updated":"2026-02-27 11:59:11.000000000","message":"this results in now_timestamp being less than marker_timestamp (because it has same float but no jitter). Which is same as the next test.\n\nWe could force both to be NormalTimestamps to test the exact equality case (note: that markers written before upgrade will look like NormalTimestamps, so there is some coverage of the legacy case that way too.)","commit_id":"b40c1646cebd41a40fdc16170fcc57781a760b3e"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"a2ab1d6d6be1eb8c5fafb1e4729034c35e2bcc4b","unresolved":true,"context_lines":[{"line_number":2123,"context_line":"                                     \u0027Date\u0027: self.get_date_header(skew\u003d100), },"},{"line_number":2124,"context_line":"                            body\u003dXML)"},{"line_number":2125,"context_line":"        with patch(\u0027swift.common.middleware.s3api.controllers.multi_upload.\u0027"},{"line_number":2126,"context_line":"                   \u0027NormalTimestamp.now\u0027, return_value\u003dpast_time):"},{"line_number":2127,"context_line":"            status, headers, body \u003d self.call_s3api(req)"},{"line_number":2128,"context_line":"        self.assertEqual(status.split()[0], \u0027503\u0027)"},{"line_number":2129,"context_line":"        self.assertEqual(\u0027ServiceUnavailable\u0027, self._get_error_code(body))"}],"source_content_type":"text/x-python","patch_set":1,"id":"961d854e_34148e33","line":2126,"updated":"2026-02-27 11:59:11.000000000","message":"we can use ``mock_normal_timestamp_now``","commit_id":"b40c1646cebd41a40fdc16170fcc57781a760b3e"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"a2ab1d6d6be1eb8c5fafb1e4729034c35e2bcc4b","unresolved":true,"context_lines":[{"line_number":2126,"context_line":"                   \u0027NormalTimestamp.now\u0027, return_value\u003dpast_time):"},{"line_number":2127,"context_line":"            status, headers, body \u003d self.call_s3api(req)"},{"line_number":2128,"context_line":"        self.assertEqual(status.split()[0], \u0027503\u0027)"},{"line_number":2129,"context_line":"        self.assertEqual(\u0027ServiceUnavailable\u0027, self._get_error_code(body))"},{"line_number":2130,"context_line":""},{"line_number":2131,"context_line":"    def test_object_multipart_upload_complete_409_on_marker_delete(self):"},{"line_number":2132,"context_line":"        # verify that clock skew preventing an upload marker DELETE results in"}],"source_content_type":"text/x-python","patch_set":1,"id":"46a2fdcb_63b9a33a","line":2129,"updated":"2026-02-27 11:59:11.000000000","message":"+1 this test fails when I revert the code to use Timestamp","commit_id":"b40c1646cebd41a40fdc16170fcc57781a760b3e"}]}
