)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"4b788c29c01cd4ffd865a651f70c24a788f4e646","unresolved":true,"context_lines":[{"line_number":17,"context_line":"Timestamps will now have the following internal format:"},{"line_number":18,"context_line":""},{"line_number":19,"context_line":"\u003c  float part  \u003e \u003c   hex part   \u003e"},{"line_number":20,"context_line":"1234567890.12345_2jjjjjjjjjoooooo"},{"line_number":21,"context_line":""},{"line_number":22,"context_line":"The hex part is normally composed of:"},{"line_number":23,"context_line":"* a prefix \u00272\u0027 which indicates that jitter is present (2 ~ version)."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":38,"id":"b661f93d_0afcfc71","line":20,"updated":"2026-01-07 01:16:58.000000000","message":"how about adding a new field of ``reserved``? we may want to move the usage of versioned_writes to its own field, or add a new field for some future usages, let\u0027s reserve some bytes for them.\n\n```\n\u003c  float part  \u003e \u003c   hex part   \u003e\n1234567890.12345_2rrjjjjjjjjooooo\n\nThe hex part is normally composed of:\n* a prefix \u00272\u0027 which indicates that jitter is present (2 ~ version).\n* 2 digits of reserved (r).\n* 8 digits of random jitter (j).\n* 5 digits of offset (o).\n```","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"30771312418d73ed1b430d0447fa434e20073bd5","unresolved":true,"context_lines":[{"line_number":17,"context_line":"Timestamps will now have the following internal format:"},{"line_number":18,"context_line":""},{"line_number":19,"context_line":"\u003c  float part  \u003e \u003c   hex part   \u003e"},{"line_number":20,"context_line":"1234567890.12345_2jjjjjjjjjoooooo"},{"line_number":21,"context_line":""},{"line_number":22,"context_line":"The hex part is normally composed of:"},{"line_number":23,"context_line":"* a prefix \u00272\u0027 which indicates that jitter is present (2 ~ version)."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":38,"id":"b64b6b26_9daf5835","line":20,"in_reply_to":"4ca7c34f_2ac12a93","updated":"2026-02-03 15:47:54.000000000","message":"Existing timestamps should never have their hex parts modified as a result of parsing/serialising. So v3 timestamps can have a v3 format and v2 timestamps remain unchanged.","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"5da24b58dc2a47fa59928d81ff0a8599c4247876","unresolved":true,"context_lines":[{"line_number":17,"context_line":"Timestamps will now have the following internal format:"},{"line_number":18,"context_line":""},{"line_number":19,"context_line":"\u003c  float part  \u003e \u003c   hex part   \u003e"},{"line_number":20,"context_line":"1234567890.12345_2jjjjjjjjjoooooo"},{"line_number":21,"context_line":""},{"line_number":22,"context_line":"The hex part is normally composed of:"},{"line_number":23,"context_line":"* a prefix \u00272\u0027 which indicates that jitter is present (2 ~ version)."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":38,"id":"4ca7c34f_2ac12a93","line":20,"in_reply_to":"556dac78_1cd2b34a","updated":"2026-01-07 20:00:58.000000000","message":"when I look at those macro definitions of v1 and v2(https://review.opendev.org/c/openstack/swift/+/967738/40/swift/common/utils/timestamp.py#29), I wish version 2 is the final version if possible. It\u0027ll be even harder to introduce version 3, would need to consider all kinds of compatibility scenarios.\n\nFor example, if version 3 becomes ``1234567890.12345_3vvjjjjjjjjooooo``, then that means the existing version 2 timestamps in future cluster would have their top bytes of random jitter value removed during migration to version 3. And this is going to be a source for new kind of timestamp collisions.","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"df6b2084c3bcd0acfb7ed6311f43a348dad5dd93","unresolved":false,"context_lines":[{"line_number":17,"context_line":"Timestamps will now have the following internal format:"},{"line_number":18,"context_line":""},{"line_number":19,"context_line":"\u003c  float part  \u003e \u003c   hex part   \u003e"},{"line_number":20,"context_line":"1234567890.12345_2jjjjjjjjjoooooo"},{"line_number":21,"context_line":""},{"line_number":22,"context_line":"The hex part is normally composed of:"},{"line_number":23,"context_line":"* a prefix \u00272\u0027 which indicates that jitter is present (2 ~ version)."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":38,"id":"ab86da3f_6c35534f","line":20,"in_reply_to":"b64b6b26_9daf5835","updated":"2026-02-05 20:23:12.000000000","message":"Acknowledged","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"efc12f94fab26cecc7d58b4c39dbf44093af418c","unresolved":true,"context_lines":[{"line_number":17,"context_line":"Timestamps will now have the following internal format:"},{"line_number":18,"context_line":""},{"line_number":19,"context_line":"\u003c  float part  \u003e \u003c   hex part   \u003e"},{"line_number":20,"context_line":"1234567890.12345_2jjjjjjjjjoooooo"},{"line_number":21,"context_line":""},{"line_number":22,"context_line":"The hex part is normally composed of:"},{"line_number":23,"context_line":"* a prefix \u00272\u0027 which indicates that jitter is present (2 ~ version)."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":38,"id":"556dac78_1cd2b34a","line":20,"in_reply_to":"b661f93d_0afcfc71","updated":"2026-01-07 12:18:14.000000000","message":"See below\n\n\u003eThe hex part range 0x3000000000 to 0xffffffffff is reserved for\n\u003e future versions that may wish to carve the hex part up differently.\n\nIn future we could have (for example)\n\n1234567890.12345_3vvjjjjjjjjooooo\n\nwhere v is used by versioning\n\nNote that, so far, we never parse the hex part to use the individual components. All we care about is having a pseudo-unique number (jitter) that can be incremented (offset) to appear to appear to be \"newer\" without colliding with or appearing larger than another Timestamp.","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"171a6913b42490b877051cf840efbf28b055aec3","unresolved":true,"context_lines":[{"line_number":58,"context_line":""},{"line_number":59,"context_line":"I think there could be repeated (ssync) replication until object"},{"line_number":60,"context_line":"servers are all upgraded because meta files aren\u0027t handled"},{"line_number":61,"context_line":"consistently w.r.t. content type timestamps."},{"line_number":62,"context_line":""},{"line_number":63,"context_line":"Object servers and container object tables cannot yet accurately"},{"line_number":64,"context_line":"record the finer precision of timestamp jitter for user metadata and"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":53,"id":"c96aaeb1_5d4df5d0","line":61,"updated":"2026-02-13 22:27:10.000000000","message":"i\u0027m still trying to figure out what this means.","commit_id":"1964da5195e0d4fe96e89e98d418d1ded010a9ad"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"55472369dc2bdcd573b4ce2f213ef9d0f15823e3","unresolved":true,"context_lines":[{"line_number":58,"context_line":""},{"line_number":59,"context_line":"I think there could be repeated (ssync) replication until object"},{"line_number":60,"context_line":"servers are all upgraded because meta files aren\u0027t handled"},{"line_number":61,"context_line":"consistently w.r.t. content type timestamps."},{"line_number":62,"context_line":""},{"line_number":63,"context_line":"Object servers and container object tables cannot yet accurately"},{"line_number":64,"context_line":"record the finer precision of timestamp jitter for user metadata and"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":53,"id":"e72493a0_aa92e7ee","line":61,"in_reply_to":"c96aaeb1_5d4df5d0","updated":"2026-02-16 17:58:13.000000000","message":"deleted, stale comment from earlier patchsets when ALL timestamps got jitter.","commit_id":"1964da5195e0d4fe96e89e98d418d1ded010a9ad"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"e3f331fce7da982ef54ee283e8389e2c4dfad87e","unresolved":false,"context_lines":[{"line_number":58,"context_line":""},{"line_number":59,"context_line":"I think there could be repeated (ssync) replication until object"},{"line_number":60,"context_line":"servers are all upgraded because meta files aren\u0027t handled"},{"line_number":61,"context_line":"consistently w.r.t. content type timestamps."},{"line_number":62,"context_line":""},{"line_number":63,"context_line":"Object servers and container object tables cannot yet accurately"},{"line_number":64,"context_line":"record the finer precision of timestamp jitter for user metadata and"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":53,"id":"f72a7515_b8170b74","line":61,"in_reply_to":"e72493a0_aa92e7ee","updated":"2026-02-18 17:45:54.000000000","message":"Done","commit_id":"1964da5195e0d4fe96e89e98d418d1ded010a9ad"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"ff6903e4e1e780153e0241e95bd1734ee1074be5","unresolved":true,"context_lines":[{"line_number":4,"context_line":"Commit:     Alistair Coles \u003calistairncoles@gmail.com\u003e"},{"line_number":5,"context_line":"CommitDate: 2026-02-18 17:13:04 +0000"},{"line_number":6,"context_line":""},{"line_number":7,"context_line":"Timestamp: add random jitter to the hex field"},{"line_number":8,"context_line":""},{"line_number":9,"context_line":"This patch modifies the Timestamp class to support a randomly"},{"line_number":10,"context_line":"generated jitter component that increases the effective precision of"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":55,"id":"36961301_a69ad13a","line":7,"updated":"2026-02-19 05:25:05.000000000","message":"It\u0027s true this patch ``add random jitter to the hex field``, but I feel ``introduce version 2 timestamp and apply it on xxxx`` reflects adequately the large impact of this patch.","commit_id":"70256a85180c445a1d513c846d812d125d01a2b8"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"8ad3e0cc663af3a6a5c50b1e504eed3e7033bd52","unresolved":false,"context_lines":[{"line_number":4,"context_line":"Commit:     Alistair Coles \u003calistairncoles@gmail.com\u003e"},{"line_number":5,"context_line":"CommitDate: 2026-02-18 17:13:04 +0000"},{"line_number":6,"context_line":""},{"line_number":7,"context_line":"Timestamp: add random jitter to the hex field"},{"line_number":8,"context_line":""},{"line_number":9,"context_line":"This patch modifies the Timestamp class to support a randomly"},{"line_number":10,"context_line":"generated jitter component that increases the effective precision of"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":55,"id":"fb0a35e2_f41a0184","line":7,"in_reply_to":"36961301_a69ad13a","updated":"2026-02-19 15:19:37.000000000","message":"Good call. I don\u0027t think the use cases will fit on the commit subject line. I\u0027d like to keep jitter in the commit message.","commit_id":"70256a85180c445a1d513c846d812d125d01a2b8"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"a73c2a394723d1721a5847fead9e3ed3c5a5d0ab","unresolved":true,"context_lines":[{"line_number":72,"context_line":"previously returned Timestamps with no hex part whose internal form"},{"line_number":73,"context_line":"would have only the float part e.g. 1234567890.12345. With this patch,"},{"line_number":74,"context_line":"the Timestamp returned will always have a hex part with random jitter,"},{"line_number":75,"context_line":"and the internal formt will always be of the extended form"},{"line_number":76,"context_line":"e.g. 1234567890.12345_2abcdef123000000."},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"Callers wishing to construct a Timestamp with no jitter should pass a"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":65,"id":"e85e1f54_60965d18","line":75,"range":{"start_line":75,"start_character":17,"end_line":75,"end_character":22},"updated":"2026-03-02 06:09:12.000000000","message":"form","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"4763c4c88cfe0788f22fc72702f4c8818aa47166","unresolved":false,"context_lines":[{"line_number":72,"context_line":"previously returned Timestamps with no hex part whose internal form"},{"line_number":73,"context_line":"would have only the float part e.g. 1234567890.12345. With this patch,"},{"line_number":74,"context_line":"the Timestamp returned will always have a hex part with random jitter,"},{"line_number":75,"context_line":"and the internal formt will always be of the extended form"},{"line_number":76,"context_line":"e.g. 1234567890.12345_2abcdef123000000."},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"Callers wishing to construct a Timestamp with no jitter should pass a"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":65,"id":"0a9ae7c6_de86a0dd","line":75,"range":{"start_line":75,"start_character":17,"end_line":75,"end_character":22},"in_reply_to":"e85e1f54_60965d18","updated":"2026-03-02 16:19:00.000000000","message":"Done","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"}],"/PATCHSET_LEVEL":[{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"b6cd324be6987cc602af5b737ce3d73cdc8fa673","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"17aba5bc_c244cde1","updated":"2025-11-20 05:06:23.000000000","message":"Liking the idea of making it a part of Timestamp class and I see you went multiplication over bitshifting.. potato potato :P \n\nThere is a the version feild. How\u0027d we go about moving to semi unique values. Pass something in as new? I guess we could just change the extra value now that its a field (which is pretty awesome actually). Probably thinking too many steps ahead.","commit_id":"a65ed87d7438993fe6e65bbb05078a8d861a3b96"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"c90fc91ccc23dd3c3a12507879c1311623cfcdd6","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"a52e2b5d_ebb9706e","updated":"2025-11-19 19:38:52.000000000","message":"still work to do on tests","commit_id":"a65ed87d7438993fe6e65bbb05078a8d861a3b96"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"3ef2a74f2b491304ac80254f594c1a2067bc9019","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"a22505ef_9a98fe6f","in_reply_to":"17aba5bc_c244cde1","updated":"2025-11-20 19:05:11.000000000","message":"\u003e multiplication over bitshifting.\nI came to think that bit shifting made it easier to grok what\u0027s going on 😊\n\n\u003e How\u0027d we go about moving to semi unique values\nit\u0027d be good to think about the future (but not implement it 😊 ). Pass something in, bump the version digit if necessary etc.\n\nThe significance of the version digit is, for example, that we could change the split of jitter to offset digits.","commit_id":"a65ed87d7438993fe6e65bbb05078a8d861a3b96"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"129cb92c973a7f90a471dcd79f49e7d1d1dd037b","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"ac87ef88_90521e8d","updated":"2025-11-21 18:36:31.000000000","message":"still lots of failing tests\n\n```\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d 627 failed, 9434 passed, 3 skipped, 72 warnings, 18 errors, 3478 subtests passed in 1009.17s (0:16:49) \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\nthere’s some intermittent failures probably due to the random jitter in v2 timestamps, vtox re-tries failed tests:\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d 489 failed, 10 passed, 9437 deselected, 2 warnings, 18 errors, 90 subtests passed in 98.64s (0:01:38) \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n```","commit_id":"5d6eb8e12465b140461dc45af612c78c82a384d3"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"03cb8dafdd50124b777ee8df38d267b55d1fed38","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":11,"id":"d15645ca_a412974f","updated":"2025-11-26 23:22:35.000000000","message":"just push in draft comments I had.. not sure they\u0027re still relevent :P","commit_id":"ab618f00f244167ba0a6d59b14ff9b699a8073e1"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"d34ac0ffb4be1b40353f076225fba57f5fd75a34","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":31,"id":"1f245bfe_8b49c749","updated":"2026-01-02 10:50:20.000000000","message":"@jhuo@nvidia.com please see my comment on https://review.opendev.org/c/openstack/swift/+/972032","commit_id":"7d4ed41e472e34cc79240b28a1a3314808d403d1"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"0d3d84a4c9bd1c3f5255b1e54f6194e90e6fb3ca","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":31,"id":"a3282c1a_63e9e567","updated":"2026-01-01 01:31:13.000000000","message":"The idea of using a random number and the offset field is great, however I feel the current implementation is too invasive by introducing extra fields and versions to the Timestamp class, as a result every timestamp usage within swift will be impacted, which worries me a lot.\n\nWhile I was exploring ways to only apply the new timestamp to the object PUT path, I found out the object PUT path shouldn\u0027t have any conflicts with all existing use cases of Timestamp offset (reconciler and versioned write), and the design of existing Timestamp offset is flexible enough and we don\u0027t need to introduce extra fields, we can just assign a random jitter to the while offset field for object PUT:\n   1. for versioned write: as long as the new unqiue_time middleware is put after auth middleware and before versioned_writes, versioned_writes will be able to use offset+\u003d1 correctly for the symlink object.\n   2. for reconciler: since a new misplaced object will need to be added into misplaced container before reconciler daemon processes it (which is going to be at least many seconds), a new object PUT with offset won\u0027t have conflict with existing misplaced objects (larger timestamp value), and things will work fine too if the reconciler process an object with a random number as whole offset, since the offset +3/+2/+1 will be based on the existing offset (https://github.com/NVIDIA/swift/blob/master/swift/common/utils/timestamp.py#L105) and reconciler will only need the comparisons of these offset (no check of absolute value).\n\nHere is the approach I implemented on top of the previous work of Matt/Alistair/Tim: https://review.opendev.org/c/openstack/swift/+/972032","commit_id":"7d4ed41e472e34cc79240b28a1a3314808d403d1"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"ecd4361570c9020a1bf68d54304355011064f5cc","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":51,"id":"af4cd950_6f94eb65","updated":"2026-02-11 20:14:53.000000000","message":"i can\u0027t spot it in gerrit - but it seems like the rebase lost an import w/o fixing all the usage of Timestamp:\n\n```\ncgerrard@NVStation:~/Workspace/vagrant-swift-all-in-one/swift$ git diff 57d83462328c7d6a9cde6288fcaccad14e0a3711..25bdbf9676953b49ce5f3d7ff8056f87bac9f739 -- test/unit/obj/test_ssync_receiver.py\ndiff --git a/test/unit/obj/test_ssync_receiver.py b/test/unit/obj/test_ssync_receiver.py\nindex 33e99ae96..5de4c30a1 100644\n--- a/test/unit/obj/test_ssync_receiver.py\n+++ b/test/unit/obj/test_ssync_receiver.py\n@@ -32,7 +32,7 @@ from swift.common.storage_policy import POLICIES\n from swift.common import utils\n from swift.common.swob import HTTPException, HTTPCreated, Request, \\\n     HTTPNoContent\n-from swift.common.utils import public, Timestamp\n+from swift.common.utils import public\n from swift.obj import diskfile\n from swift.obj import server\n from swift.obj import ssync_receiver, ssync_sender\n@@ -1220,7 +1220,7 @@ class TestReceiver(unittest.TestCase):\n                          diskfile.get_data_dir(POLICIES[0])),\n             \u00271\u0027, self.hash1)\n         utils.mkdirs(object_dir)\n-        newer_ts1 \u003d Timestamp(float(self.ts1) + 1).internal\n+        newer_ts1 \u003d utils.normalize_timestamp(float(self.ts1) + 1)\n         self.metadata1[\u0027X-Timestamp\u0027] \u003d newer_ts1\n         fp \u003d open(os.path.join(object_dir, newer_ts1 + \u0027.data\u0027), \u0027w+\u0027)\n         fp.write(\u00271\u0027)\n@@ -1254,7 +1254,7 @@ class TestReceiver(unittest.TestCase):\n                          diskfile.get_data_dir(POLICIES[0])),\n             \u00271\u0027, self.hash1)\n         utils.mkdirs(object_dir)\n-        older_ts1 \u003d Timestamp(float(self.ts1) - 1).internal\n+        older_ts1 \u003d utils.normalize_timestamp(float(self.ts1) - 1)\n         self.metadata1[\u0027X-Timestamp\u0027] \u003d older_ts1\n         fp \u003d open(os.path.join(object_dir, older_ts1 + \u0027.data\u0027), \u0027w+\u0027)\n         fp.write(\u00271\u0027)\n@@ -1294,7 +1294,7 @@ class TestReceiver(unittest.TestCase):\n                          diskfile.get_data_dir(POLICIES[0])),\n             \u00271\u0027, self.hash1)\n         utils.mkdirs(object_dir)\n-        older_ts1 \u003d Timestamp(float(self.ts1) - 1).internal\n+        older_ts1 \u003d utils.normalize_timestamp(float(self.ts1) - 1)\n         self.metadata1[\u0027X-Timestamp\u0027] \u003d older_ts1\n         fp \u003d open(os.path.join(object_dir, older_ts1 + \u0027.data\u0027), \u0027w+\u0027)\n         fp.write(\u00271\u0027)\ncgerrard@NVStation:~/Workspace/vagrant-swift-all-in-one/swift$ git blame 25bdbf9676953b49ce5f3d7ff8056f87bac9f739 test/unit/obj/test_ssync_receiver.py | grep Timestamp\\(\n25bdbf9676 (Alistair Coles  2025-12-18 11:42:13 +0000 2685)         t_data \u003d Timestamp(next(ts_iter).normal)\n25bdbf9676 (Alistair Coles  2025-12-18 11:42:13 +0000 2686)         t_meta \u003d Timestamp(next(ts_iter).normal)\n25bdbf9676 (Alistair Coles  2025-12-18 11:42:13 +0000 2687)         t_ctype \u003d Timestamp(next(ts_iter).normal)\n4e74e7f558 (Tim Burke       2023-02-17 01:16:54 -0800 2729)         t_data_offset \u003d utils.Timestamp(t_data, offset\u003d99)\n4e74e7f558 (Tim Burke       2023-02-17 01:16:54 -0800 2730)         t_meta_offset \u003d utils.Timestamp(t_meta, offset\u003d1)\n4e74e7f558 (Tim Burke       2023-02-17 01:16:54 -0800 2731)         t_ctype_offset \u003d utils.Timestamp(t_ctype, offset\u003d2)\n25bdbf9676 (Alistair Coles  2025-12-18 11:42:13 +0000 2757)         t_data_offset \u003d utils.Timestamp(t_data.normal + \"_2feed12345000000\",\n25bdbf9676 (Alistair Coles  2025-12-18 11:42:13 +0000 2759)         t_meta_offset \u003d utils.Timestamp(t_meta.normal + \"_2cafe12345000000\",\n25bdbf9676 (Alistair Coles  2025-12-18 11:42:13 +0000 2761)         t_ctype_offset \u003d utils.Timestamp(t_ctype.normal + \u0027_2beef12345000000\u0027,\n```","commit_id":"25bdbf9676953b49ce5f3d7ff8056f87bac9f739"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"171a6913b42490b877051cf840efbf28b055aec3","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":53,"id":"6397e608_6ff3e449","updated":"2026-02-13 22:27:10.000000000","message":"I\u0027m still working on this - but i\u0027m not sure I\u0027ve been able to validate/understand the concern about ssync (maybe it only comes up once we let .meta have a hex_part?)","commit_id":"1964da5195e0d4fe96e89e98d418d1ded010a9ad"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"55472369dc2bdcd573b4ce2f213ef9d0f15823e3","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":53,"id":"a1901706_a0f3a45e","in_reply_to":"6397e608_6ff3e449","updated":"2026-02-16 17:58:13.000000000","message":"The concern about ssync repeating if object servers are on different versions does relate to when we (later) allow POST to have jitter and therefore meta and ctype timestamp can have hex parts.\n\nI have removed that concern from the commit. Sorry for the confusion.","commit_id":"1964da5195e0d4fe96e89e98d418d1ded010a9ad"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"178ebe75011383c5d1e4780bc1c5fd5452ce80d2","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":60,"id":"4d67cb10_f07ad162","updated":"2026-02-23 21:51:01.000000000","message":"I\u0027ve been spending a few days in the guts of the consistency engine with this change on my vsaio and I like it.  I\u0027ve mostly been playing with the object-reconstructor/replicator and container-reconciler.\n\nI\u0027ve gotten used to the new .data file names on disk and sort of like them.\n\n```\nvagrant@saio:~$ swift-object-info /srv/node3/sdb3/objects/645/35a/a161102fba1710ef912af194b8d4635a/1771883007.77599_29f45a7474000000.data\nPath: /AUTH_test/test/test\n  Account: AUTH_test\n  Container: test\n  Object: test\n  Object hash: a161102fba1710ef912af194b8d4635a\nContent-Type: application/octet-stream\nTimestamp: 2026-02-23T21:43:27.775990 (1771883007.77599_29f45a7474000000)\n\nvagrant@saio:~$ sqlite3 -line /srv/node2/sdb2/containers/450/afd/7089ab48d955ab0851fc51cc17a34afd/7089ab48d955ab0851fc51cc17a34afd.db \"select * from object\"\n               ROWID \u003d 5\n                name \u003d test\n          created_at \u003d 1771883315.44191_2c020ceb4a000000\n                size \u003d 8\n        content_type \u003d application/octet-stream\n                etag \u003d 70c1db56f301c9e337b0099bd4174b28\n             deleted \u003d 0\nstorage_policy_index \u003d 0\n\n```\n\nI keep expecting jitter to show up somewhere unexpected - but so far it\u0027s been limited to new .data files (which is good!)\n\n```\nvagrant@saio:~$ curl \"http://saio:8090/v1/.expiring_objects/1771804774?format\u003djson\" -s | jq .\n[\n  {\n    \"bytes\": 0,\n    \"hash\": \"d41d8cd98f00b204e9800998ecf8427e\",\n    \"name\": \"1771883307-AUTH_test/test/test\",\n    \"content_type\": \"text/plain;swift_expirer_bytes\u003d8\",\n    \"last_modified\": \"2026-02-23T21:43:27.775990\"\n  }\n]\n```\n\nI\u0027ll keep at it; but so far no blocking issues from my perspective - I think this is taking us in the right direction and I\u0027m just trying to get my head around all of it.","commit_id":"026fe0c57bae6f6bef354f2e627120834f9de446"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"b501820341b9e1d27250f21113e189a5ba7eb88c","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":63,"id":"3014986d_7288acda","updated":"2026-02-27 00:14:29.000000000","message":"I\u0027m still feeling very positive and quite comfortable with this change!\n\nI totally missed the issue with comparing [Normal]Timestamp\u0027s to strings - but that seems to have been sorted:\n\nmaster:\n```\n\u003e\u003e\u003e Timestamp.now() \u003c \u0027not a timestamp\u0027\nTraceback (most recent call last):\n  File \"\u003cstdin\u003e\", line 1, in \u003cmodule\u003e\n  File \"/vagrant/swift/swift/common/utils/timestamp.py\", line 252, in __lt__\n    other \u003d Timestamp(other, check_bounds\u003dFalse)\n            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/vagrant/swift/swift/common/utils/timestamp.py\", line 92, in __init__\n    float_timestamp \u003d float(base)\n                      ^^^^^^^^^^^\nValueError: could not convert string to float: \u0027not a timestamp\u0027\n```\n\npatch set 61:\n```\n\u003e\u003e\u003e \u0027not a timestmap\u0027 \u003c Timestamp.now()\nTrue\n```\n\nThis patchset:\n```\n\u003e\u003e\u003e \u0027not a timestmap\u0027 \u003c Timestamp.now()\nTraceback (most recent call last):\n  File \"\u003cstdin\u003e\", line 1, in \u003cmodule\u003e\nTypeError: \u0027\u003c\u0027 not supported between instances of \u0027str\u0027 and \u0027Timestamp\u0027\n```\n\nLately I\u0027ve been digging into jitter and `[de|en]code_timestamps` - trying to understand the XXX comment in there:\n\n978154: tests: expand .meta filename and POST content-type coverage | https://review.opendev.org/c/openstack/swift/+/978154\n\nI think I\u0027ll get to set that down for now, because I think it\u0027s all fine and make sense, and I better understand the implications and justification for the decision that :\n\na) POST should NOT include jitter for now (i.e. the behavior of this patch is correct) e.g.\n```\n/srv/node3/sdb3/objects/645/35a/a161102fba1710ef912af194b8d4635a/1772122441.76478_25c49546ae000000.data\n/srv/node3/sdb3/objects/645/35a/a161102fba1710ef912af194b8d4635a/1772122466.94830.meta\n/srv/node3/sdb3/objects/645/35a/a161102fba1710ef912af194b8d4635a/1772122454.41315-5b58b.meta\n```\n\nb) we can start testing backend services with v2 Timestamps *now* because a lot of stuff is going to \"just work\" and we need to start exploring that to gain confidence in an eventual complete rollout that doesn\u0027t include a bunch of distracting churn to put tests *back* to using native ts_iter w/ v2 Timestamps\n\n... I\u0027m noticing this change doesn\u0027t depend on or update the probe test for timestamp collision:\n\n927327: object-server: raise 500 on ec timestamp collision | https://review.opendev.org/c/openstack/swift/+/927327\n\n... and since that\u0027s in merge conflict maybe tomorrow I\u0027ll try stacking it up on this to sort of add some tests around verifying that we\u0027re like sort of actually fixing the problem?","commit_id":"bce3e42d3f31422218ab8b7a2130832870a3bf07"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"a73c2a394723d1721a5847fead9e3ed3c5a5d0ab","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":65,"id":"1c7a687e_05feb77c","updated":"2026-03-02 06:09:12.000000000","message":"Just some last minute finds, from running things in my SAIO and also decided to ask our AI overlords.","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"31725ee63ab7931ebfa13accf09aab4aea6fe801","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":66,"id":"87cafcb7_47de10d2","updated":"2026-03-02 18:14:29.000000000","message":"An misusage of ``Timestamp.normal`` manifest itself as a bug in reconstructor with v2 timestamp format: https://review.opendev.org/c/openstack/swift/+/978572/\n\nWhen reconstructor sends out fragment request during SYNC rebuild, header ``headers[\u0027X-Backend-Fragment-Preferences\u0027]`` used ``Timestamp.normal`` of the local fragment; and when partner object servers receive it, diskfile uses the normal timestamp string to look up a dict with internal timestamp strings as keys: https://github.com/NVIDIA/swift/blob/master/swift/obj/diskfile.py#L3768\n\nand this fail with the v2 timestamp jitter patch (but passed before it)\n```\nts \u003d Timestamp.now()\nfrag_sets \u003d {ts: \u0027data\u0027}\nlookup_ts \u003d Timestamp(ts.normal) \nlookup_ts in frag_sets       # → False (hash mismatch)\n```\n\nluckily, for the normal case which this partner fragment only has one durable file, the fallback of picking the latest fragment file still behavior correctly: https://github.com/NVIDIA/swift/blob/master/swift/obj/diskfile.py#L3783\n\nbut when this partner object-server has one old durable fragment and a new non-durable fragment (could be an aborted write), it will return the new non-durable fragment to the reconstructor. I am adding a probe test case for this case.","commit_id":"9ea0d0b5909fe47ea1c53fb8b84a350c566b2dca"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"ccdfffab8436c0ce7b18e726140dbe07ae5494d2","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":66,"id":"93bcdc1b_e31c25e4","updated":"2026-03-02 16:19:33.000000000","message":"recheck\n\nswift-tox-func-py312 \nPOST_FAILURE","commit_id":"9ea0d0b5909fe47ea1c53fb8b84a350c566b2dca"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"8ed91b04401831f893fc08c2386a4bd69c92c5c5","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":73,"id":"65a3d466_ff4d1b2d","updated":"2026-03-12 20:31:52.000000000","message":"still reeling from the out-of-tree code we had that was doing a bunch of `assertEqual(Timestamp(\u003cfloat\u003e).internal, resp.headers[\u0027x-backend-timestamp\u0027])` ... in particular it starts to become confusing/messy deciding if all this code should just start *sending* `Timestamp(\u003cfloat\u003e).normal` so that it can \"maintain compatibility\" across old swift and post-jitter Timestamps (until such a time as post-jitter Timestamps decide they want to include jitter on those container PUT/POST requests).\n\nI\u0027m not sure I\u0027ve been yet convinced that we must change anything; but I\u0027m at *least* starting to get used to the idea that `Timestamp(\u003cfloat\u003e)` probably shouldn\u0027t add jitter and can be deprecated soon anyway!","commit_id":"57c4bf8d7ad2d61e5d4ae92556cd88532e1e63f9"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"06f0d04f715a4e8f1ba0923471fb6ee27d9d031e","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":87,"id":"98aa90ce_73198f4f","updated":"2026-05-12 23:16:57.000000000","message":"Hopefully I didn\u0027t mess up the rebase too much!!!\n\nIt was a good excuse to spend some time with the new behaviors/interfaces in utils.timestamp and the new tests tho; esp having reviewed the offset property\n\n```\n-        ts \u003d timestamp.Timestamp(t, offset\u003dtimestamp.MAX_OFFSET)\n-        self.assertEqual(ts.internal, \u00271417462430.78693_ffffffffffffffff\u0027)\n+        ts \u003d timestamp.Timestamp(t, offset\u003dv1_max_offset)\n+        self.assertEqual(ts.internal, \u00271417462430.78693_1fffffffffffffff\u0027)\n```\n\n^ ok, we\u0027re arbitrarily reducing the v1 \"max offset\" to reserve the version bit anyway?  why not just go further and say neither version supports offset larger than `0xffffff` - 16M is a *lot* of reconciler moves!?\n\n```\n\u003e\u003e\u003e from swift.common.utils import Timestamp\n\u003e\u003e\u003e t \u003d Timestamp(0)\n\u003e\u003e\u003e t.offset \u003d 1.0\nTraceback (most recent call last):\n  File \"\u003cstdin\u003e\", line 1, in \u003cmodule\u003e\nAttributeError: can\u0027t set attribute \u0027offset\u0027\n\u003e\u003e\u003e t.hex_part \u003d 1.0\n\u003e\u003e\u003e t.internal\nTraceback (most recent call last):\n  File \"\u003cstdin\u003e\", line 1, in \u003cmodule\u003e\n  File \"/vagrant/swift/swift/common/utils/timestamp.py\", line 561, in internal\n    return self._internal_format(\n  File \"/vagrant/swift/swift/common/utils/timestamp.py\", line 555, in _internal_format\n    return INTERNAL_FORMAT % (timestamp, hex_part)\nTypeError: %x format: an integer is required, not float\n```\n\n^ I see the vision, but I\u0027m not sure we quite stick the landing\n\nN.B. True \u003d\u003d 1 will never not be funny to me!\n\n```\n\u003e\u003e\u003e t \u003d Timestamp(0, version\u003dTrue)\n\u003e\u003e\u003e t.hex_part \u003d True\n\u003e\u003e\u003e t.internal\n\u00270000000000.00000_0000000000000001\u0027\n```\n\nThis might just be \"consenting adults\":\n\n```\n\u003e\u003e\u003e t \u003d Timestamp(0, version\u003d2)\n\u003e\u003e\u003e t.hex_part \u003d True\n\u003e\u003e\u003e t.internal\n\u00270000000000.00000_0000000000000001\u0027\n```\n\n... but it makes the \"you can\u0027t set offset\" seem arbitrary esp given that `_offset` does what you want?","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f16dbc44ebf4c0abd242b5c03d18bac3d5016aaa","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":89,"id":"28332d2c_97e9e045","updated":"2026-05-14 14:00:23.000000000","message":"I\u0027ve addressed some, but not all, comments in follow-up patches that can be squashed into this","commit_id":"78c1a0586d612c83beca33a69867cf86096c1f19"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"11727de84411d9cfb332602135c26e2ed8ec0dfc","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":92,"id":"be00e482_7796de70","updated":"2026-06-25 07:16:03.000000000","message":"Still in the process of a final review of this. It\u0027s looking good to me. But we probably need to either squash in 990515 or at least make sure it lands before we do the next release. container-sync change should land before the next release too.\n\nJust digging into gaps an aversarial claude review gave me, just digging in to check to see if they\u0027re real or something we need to worry about. So will put my final vote on tomorrow.","commit_id":"be6009a882f9d9fc9ff2695928df6b0442dc53f3"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"69c82983d233b042f3c36540424edc3df05bf269","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":93,"id":"709c69b5_4acbc2c9","updated":"2026-06-26 07:50:15.000000000","message":"Rebased, all looks good to me.\n\nWhen we do land this, we also need the sq follow up (https://review.opendev.org/c/openstack/swift/+/990515) and container-sync patch (https://review.opendev.org/c/openstack/swift/+/989485) to land before we cut the next upstream release, so we don\u0027t have known gaps that could hurt clusters out there.\n\nAlso added some follow up test patches to cover some current design decisions, which may change when other VERBs get jitter.. but just so we have coverage and so some edgecases are covered, mostly thanks to in depth reviews from our french sounding AI friend (claude):\n\nhttps://review.opendev.org/c/openstack/swift/+/995007\nhttps://review.opendev.org/c/openstack/swift/+/995008","commit_id":"f9a2b30eed13bafec12fd14971a5fc2db4818af3"}],"swift/common/middleware/versioned_writes/legacy.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"73e45c7b21efdd335ae50dab6cbe616315b63b43","unresolved":true,"context_lines":[{"line_number":461,"context_line":"            \u0027x-timestamp\u0027,"},{"line_number":462,"context_line":"            str(calendar.timegm(time.strptime("},{"line_number":463,"context_line":"                get_resp.headers[\u0027last-modified\u0027],"},{"line_number":464,"context_line":"                DATE_HEADER_FORMAT_STRING))))"},{"line_number":465,"context_line":"        vers_obj_name \u003d self._build_versions_object_name("},{"line_number":466,"context_line":"            object_name, ts_source)"},{"line_number":467,"context_line":""}],"source_content_type":"text/x-python","patch_set":31,"id":"342f9411_14dbff17","line":464,"updated":"2025-12-24 16:46:06.000000000","message":"the str() prevents Timestamp adding jitter - ought to have a comment or refactor to be clear","commit_id":"7d4ed41e472e34cc79240b28a1a3314808d403d1"}],"swift/common/middleware/versioned_writes/object_versioning.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"73e45c7b21efdd335ae50dab6cbe616315b63b43","unresolved":true,"context_lines":[{"line_number":449,"context_line":"            \u0027x-timestamp\u0027,"},{"line_number":450,"context_line":"            str(calendar.timegm(time.strptime("},{"line_number":451,"context_line":"                get_resp.headers[\u0027last-modified\u0027],"},{"line_number":452,"context_line":"                DATE_HEADER_FORMAT_STRING))))"},{"line_number":453,"context_line":"        vers_obj_name \u003d self._build_versions_object_name("},{"line_number":454,"context_line":"            object_name, ts_source)"},{"line_number":455,"context_line":""}],"source_content_type":"text/x-python","patch_set":31,"id":"4ca3bc61_eb1bc0a6","line":452,"updated":"2025-12-24 16:46:06.000000000","message":"the str() prevents Timestamp adding jitter - ought to have a comment or refactor to be clear","commit_id":"7d4ed41e472e34cc79240b28a1a3314808d403d1"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"5130e4b40bda460ee7b138d1dd93026264d9d16d","unresolved":false,"context_lines":[{"line_number":449,"context_line":"            \u0027x-timestamp\u0027,"},{"line_number":450,"context_line":"            str(calendar.timegm(time.strptime("},{"line_number":451,"context_line":"                get_resp.headers[\u0027last-modified\u0027],"},{"line_number":452,"context_line":"                DATE_HEADER_FORMAT_STRING))))"},{"line_number":453,"context_line":"        vers_obj_name \u003d self._build_versions_object_name("},{"line_number":454,"context_line":"            object_name, ts_source)"},{"line_number":455,"context_line":""}],"source_content_type":"text/x-python","patch_set":31,"id":"57b452b8_94702813","line":452,"in_reply_to":"4ca3bc61_eb1bc0a6","updated":"2026-02-12 19:09:25.000000000","message":"Done\n\nTimestamp no longer adds jitter by default in the constructor","commit_id":"7d4ed41e472e34cc79240b28a1a3314808d403d1"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"8207f2c01aeea0343543be7050813999b080e58c","unresolved":true,"context_lines":[{"line_number":359,"context_line":"        req.method \u003d \u0027PUT\u0027"},{"line_number":360,"context_line":"        # inch x-timestamp forward, just in case"},{"line_number":361,"context_line":"        ts \u003d req.ensure_x_timestamp()"},{"line_number":362,"context_line":"        req.timestamp \u003d Timestamp(ts, offset\u003d1)"},{"line_number":363,"context_line":"        req.headers[TGT_ETAG_SYSMETA_SYMLINK_HDR] \u003d put_etag"},{"line_number":364,"context_line":"        req.headers[TGT_BYTES_SYSMETA_SYMLINK_HDR] \u003d put_bytes"},{"line_number":365,"context_line":"        # N.B. in stack mode DELETE we use content_type from listing"}],"source_content_type":"text/x-python","patch_set":54,"id":"e590b000_9873c361","line":362,"updated":"2026-02-17 17:03:10.000000000","message":"this can be written as ``ts.increment_offset(1)``\n\n...and maybe the arg should default to 1!","commit_id":"632c8359d8d5c3c3908cbe32a53e40b922acea50"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"8ad3e0cc663af3a6a5c50b1e504eed3e7033bd52","unresolved":false,"context_lines":[{"line_number":359,"context_line":"        req.method \u003d \u0027PUT\u0027"},{"line_number":360,"context_line":"        # inch x-timestamp forward, just in case"},{"line_number":361,"context_line":"        ts \u003d req.ensure_x_timestamp()"},{"line_number":362,"context_line":"        req.timestamp \u003d Timestamp(ts, offset\u003d1)"},{"line_number":363,"context_line":"        req.headers[TGT_ETAG_SYSMETA_SYMLINK_HDR] \u003d put_etag"},{"line_number":364,"context_line":"        req.headers[TGT_BYTES_SYSMETA_SYMLINK_HDR] \u003d put_bytes"},{"line_number":365,"context_line":"        # N.B. in stack mode DELETE we use content_type from listing"}],"source_content_type":"text/x-python","patch_set":54,"id":"c7ae76ed_e3257849","line":362,"in_reply_to":"e590b000_9873c361","updated":"2026-02-19 15:19:37.000000000","message":"Done","commit_id":"632c8359d8d5c3c3908cbe32a53e40b922acea50"}],"swift/common/swob.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"efc12f94fab26cecc7d58b4c39dbf44093af418c","unresolved":true,"context_lines":[{"line_number":172,"context_line":"    :returns: a Timestamp instance."},{"line_number":173,"context_line":"    \"\"\""},{"line_number":174,"context_line":"    seconds \u003d calendar.timegm(time.strptime(value, DATE_HEADER_FORMAT_STRING))"},{"line_number":175,"context_line":"    return Timestamp(seconds, with_jitter\u003dFalse)"},{"line_number":176,"context_line":""},{"line_number":177,"context_line":""},{"line_number":178,"context_line":"def _datetime_property(header):"}],"source_content_type":"text/x-python","patch_set":38,"id":"799a23e1_c076cbeb","line":175,"updated":"2026-01-07 12:18:14.000000000","message":"not necessary if we make with_jitter\u003dFalse by default","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"60454d2af76bc782c2f47bc84928e4d734449e27","unresolved":false,"context_lines":[{"line_number":172,"context_line":"    :returns: a Timestamp instance."},{"line_number":173,"context_line":"    \"\"\""},{"line_number":174,"context_line":"    seconds \u003d calendar.timegm(time.strptime(value, DATE_HEADER_FORMAT_STRING))"},{"line_number":175,"context_line":"    return Timestamp(seconds, with_jitter\u003dFalse)"},{"line_number":176,"context_line":""},{"line_number":177,"context_line":""},{"line_number":178,"context_line":"def _datetime_property(header):"}],"source_content_type":"text/x-python","patch_set":38,"id":"a5e53ef4_92c1c61b","line":175,"in_reply_to":"799a23e1_c076cbeb","updated":"2026-01-20 17:06:14.000000000","message":"Done","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"47bda4dfc6864e4e2460216ec9770b8941e64b0d","unresolved":true,"context_lines":[{"line_number":1053,"context_line":"    @timestamp.setter"},{"line_number":1054,"context_line":"    def timestamp(self, value):"},{"line_number":1055,"context_line":"        self._timestamp \u003d Timestamp(value)"},{"line_number":1056,"context_line":"        self.environ[\u0027HTTP_X_TIMESTAMP\u0027] \u003d self._timestamp.internal"},{"line_number":1057,"context_line":""},{"line_number":1058,"context_line":"    @property"},{"line_number":1059,"context_line":"    def path_qs(self):"}],"source_content_type":"text/x-python","patch_set":42,"id":"d6d5e142_8ff28318","line":1056,"updated":"2026-01-07 18:27:46.000000000","message":"this makes me even more concerned about the confusion w.r.t. S3Request.timestamp https://review.opendev.org/c/openstack/swift/+/972212\nbecause now there\u0027s a setter manipulating self._timestamp which has different meaning in the subclass","commit_id":"8e6e1cbfead0ffe06915beeab07fef3f1d0e01de"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"281a9af18fde7ff1be1bf191c2b503fae0f527f5","unresolved":false,"context_lines":[{"line_number":1053,"context_line":"    @timestamp.setter"},{"line_number":1054,"context_line":"    def timestamp(self, value):"},{"line_number":1055,"context_line":"        self._timestamp \u003d Timestamp(value)"},{"line_number":1056,"context_line":"        self.environ[\u0027HTTP_X_TIMESTAMP\u0027] \u003d self._timestamp.internal"},{"line_number":1057,"context_line":""},{"line_number":1058,"context_line":"    @property"},{"line_number":1059,"context_line":"    def path_qs(self):"}],"source_content_type":"text/x-python","patch_set":42,"id":"2af4ebd8_49f4a8b8","line":1056,"in_reply_to":"143e22be_997d70fa","updated":"2026-02-27 14:53:55.000000000","message":"Done","commit_id":"8e6e1cbfead0ffe06915beeab07fef3f1d0e01de"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"e3f331fce7da982ef54ee283e8389e2c4dfad87e","unresolved":true,"context_lines":[{"line_number":1053,"context_line":"    @timestamp.setter"},{"line_number":1054,"context_line":"    def timestamp(self, value):"},{"line_number":1055,"context_line":"        self._timestamp \u003d Timestamp(value)"},{"line_number":1056,"context_line":"        self.environ[\u0027HTTP_X_TIMESTAMP\u0027] \u003d self._timestamp.internal"},{"line_number":1057,"context_line":""},{"line_number":1058,"context_line":"    @property"},{"line_number":1059,"context_line":"    def path_qs(self):"}],"source_content_type":"text/x-python","patch_set":42,"id":"143e22be_997d70fa","line":1056,"in_reply_to":"8d1ab4f2_34d029e6","updated":"2026-02-18 17:45:54.000000000","message":"I added this to support object_versioning having a clean interface to increment offset and (re-)set the timestamp. I\u0027m backing this out because it\u0027s too scarey w.r.t. the overridden S3Request.timestamp. We\u0027ll just have to set \u0027x-timestamp\u0027 in the req.headers rather than using a nice helper property setter :/","commit_id":"8e6e1cbfead0ffe06915beeab07fef3f1d0e01de"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"8207f2c01aeea0343543be7050813999b080e58c","unresolved":true,"context_lines":[{"line_number":1053,"context_line":"    @timestamp.setter"},{"line_number":1054,"context_line":"    def timestamp(self, value):"},{"line_number":1055,"context_line":"        self._timestamp \u003d Timestamp(value)"},{"line_number":1056,"context_line":"        self.environ[\u0027HTTP_X_TIMESTAMP\u0027] \u003d self._timestamp.internal"},{"line_number":1057,"context_line":""},{"line_number":1058,"context_line":"    @property"},{"line_number":1059,"context_line":"    def path_qs(self):"}],"source_content_type":"text/x-python","patch_set":42,"id":"8d1ab4f2_34d029e6","line":1056,"in_reply_to":"d6d5e142_8ff28318","updated":"2026-02-17 17:03:10.000000000","message":"revisit this - it may be necessary to NOT add this setter to avoid subclass confusion","commit_id":"8e6e1cbfead0ffe06915beeab07fef3f1d0e01de"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"60454d2af76bc782c2f47bc84928e4d734449e27","unresolved":true,"context_lines":[{"line_number":1052,"context_line":""},{"line_number":1053,"context_line":"    @timestamp.setter"},{"line_number":1054,"context_line":"    def timestamp(self, value):"},{"line_number":1055,"context_line":"        self._timestamp \u003d Timestamp(value)"},{"line_number":1056,"context_line":"        self.environ[\u0027HTTP_X_TIMESTAMP\u0027] \u003d self._timestamp.internal"},{"line_number":1057,"context_line":""},{"line_number":1058,"context_line":"    @property"}],"source_content_type":"text/x-python","patch_set":46,"id":"36724e6d_c41a65de","line":1055,"updated":"2026-01-20 17:06:14.000000000","message":"it\u0027s weird/wrong that there\u0027s two places where this attribute is maintained","commit_id":"e67e85df951dc8dfdd2cea7446c193bdc161bf27"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"281a9af18fde7ff1be1bf191c2b503fae0f527f5","unresolved":false,"context_lines":[{"line_number":1052,"context_line":""},{"line_number":1053,"context_line":"    @timestamp.setter"},{"line_number":1054,"context_line":"    def timestamp(self, value):"},{"line_number":1055,"context_line":"        self._timestamp \u003d Timestamp(value)"},{"line_number":1056,"context_line":"        self.environ[\u0027HTTP_X_TIMESTAMP\u0027] \u003d self._timestamp.internal"},{"line_number":1057,"context_line":""},{"line_number":1058,"context_line":"    @property"}],"source_content_type":"text/x-python","patch_set":46,"id":"ca164758_33b4529d","line":1055,"in_reply_to":"36724e6d_c41a65de","updated":"2026-02-27 14:53:55.000000000","message":"Acknowledged","commit_id":"e67e85df951dc8dfdd2cea7446c193bdc161bf27"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"8207f2c01aeea0343543be7050813999b080e58c","unresolved":true,"context_lines":[{"line_number":1023,"context_line":"            # x-timestamp header in requests. If present this is checked and"},{"line_number":1024,"context_line":"            # preserved, otherwise a fresh timestamp is added."},{"line_number":1025,"context_line":"            try:"},{"line_number":1026,"context_line":"                self._timestamp \u003d Timestamp(self.environ[\u0027HTTP_X_TIMESTAMP\u0027])"},{"line_number":1027,"context_line":"            except ValueError:"},{"line_number":1028,"context_line":"                raise HTTPBadRequest("},{"line_number":1029,"context_line":"                    request\u003dself, content_type\u003d\u0027text/plain\u0027,"}],"source_content_type":"text/x-python","patch_set":54,"id":"d2ca2a75_29ca26d8","line":1026,"updated":"2026-02-17 17:03:10.000000000","message":"here we should also validate there is no jitter when there shouldn\u0027t be jitter!","commit_id":"632c8359d8d5c3c3908cbe32a53e40b922acea50"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"281a9af18fde7ff1be1bf191c2b503fae0f527f5","unresolved":false,"context_lines":[{"line_number":1023,"context_line":"            # x-timestamp header in requests. If present this is checked and"},{"line_number":1024,"context_line":"            # preserved, otherwise a fresh timestamp is added."},{"line_number":1025,"context_line":"            try:"},{"line_number":1026,"context_line":"                self._timestamp \u003d Timestamp(self.environ[\u0027HTTP_X_TIMESTAMP\u0027])"},{"line_number":1027,"context_line":"            except ValueError:"},{"line_number":1028,"context_line":"                raise HTTPBadRequest("},{"line_number":1029,"context_line":"                    request\u003dself, content_type\u003d\u0027text/plain\u0027,"}],"source_content_type":"text/x-python","patch_set":54,"id":"b06e1493_54fbd96a","line":1026,"in_reply_to":"d2ca2a75_29ca26d8","updated":"2026-02-27 14:53:55.000000000","message":"Done","commit_id":"632c8359d8d5c3c3908cbe32a53e40b922acea50"}],"swift/common/utils/timestamp.py":[{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"b6cd324be6987cc602af5b737ce3d73cdc8fa673","unresolved":true,"context_lines":[{"line_number":158,"context_line":"    @classmethod"},{"line_number":159,"context_line":"    def _make_extra(cls):"},{"line_number":160,"context_line":"        return random.randint(16 ** (V2_EXTRA_DIGITS - 1),"},{"line_number":161,"context_line":"                              (16 ** V2_EXTRA_DIGITS) - 1)"},{"line_number":162,"context_line":""},{"line_number":163,"context_line":"    def _make_hex_part(self):"},{"line_number":164,"context_line":"        hex_part \u003d self.offset"}],"source_content_type":"text/x-python","patch_set":1,"id":"4b649dfc_badec925","line":161,"updated":"2025-11-20 05:06:23.000000000","message":"These can obviously be changed for V2_EXTRA_BASE and V2_MAX_EXTRA","commit_id":"a65ed87d7438993fe6e65bbb05078a8d861a3b96"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"3ef2a74f2b491304ac80254f594c1a2067bc9019","unresolved":true,"context_lines":[{"line_number":158,"context_line":"    @classmethod"},{"line_number":159,"context_line":"    def _make_extra(cls):"},{"line_number":160,"context_line":"        return random.randint(16 ** (V2_EXTRA_DIGITS - 1),"},{"line_number":161,"context_line":"                              (16 ** V2_EXTRA_DIGITS) - 1)"},{"line_number":162,"context_line":""},{"line_number":163,"context_line":"    def _make_hex_part(self):"},{"line_number":164,"context_line":"        hex_part \u003d self.offset"}],"source_content_type":"text/x-python","patch_set":1,"id":"b3824bec_44afe347","line":161,"in_reply_to":"4b649dfc_badec925","updated":"2025-11-20 19:05:11.000000000","message":"thanks!","commit_id":"a65ed87d7438993fe6e65bbb05078a8d861a3b96"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"73e45c7b21efdd335ae50dab6cbe616315b63b43","unresolved":false,"context_lines":[{"line_number":158,"context_line":"    @classmethod"},{"line_number":159,"context_line":"    def _make_extra(cls):"},{"line_number":160,"context_line":"        return random.randint(16 ** (V2_EXTRA_DIGITS - 1),"},{"line_number":161,"context_line":"                              (16 ** V2_EXTRA_DIGITS) - 1)"},{"line_number":162,"context_line":""},{"line_number":163,"context_line":"    def _make_hex_part(self):"},{"line_number":164,"context_line":"        hex_part \u003d self.offset"}],"source_content_type":"text/x-python","patch_set":1,"id":"231a70d1_da328ed0","line":161,"in_reply_to":"b3824bec_44afe347","updated":"2025-12-24 16:46:06.000000000","message":"Done","commit_id":"a65ed87d7438993fe6e65bbb05078a8d861a3b96"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"c90fc91ccc23dd3c3a12507879c1311623cfcdd6","unresolved":true,"context_lines":[{"line_number":303,"context_line":"            inv_extra \u003d 0"},{"line_number":304,"context_line":"        else:"},{"line_number":305,"context_line":"            inv_offset \u003d V2_MAX_OFFSET - self.offset"},{"line_number":306,"context_line":"            inv_extra \u003d V2_MAX_EXTRA - self.extra"},{"line_number":307,"context_line":"        return Timestamp(inv_raw, offset\u003dinv_offset, extra\u003dinv_extra)"},{"line_number":308,"context_line":""},{"line_number":309,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"b8966eec_3871db10","line":306,"updated":"2025-11-19 19:38:52.000000000","message":"I really want to check this over some more","commit_id":"a65ed87d7438993fe6e65bbb05078a8d861a3b96"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"73e45c7b21efdd335ae50dab6cbe616315b63b43","unresolved":false,"context_lines":[{"line_number":303,"context_line":"            inv_extra \u003d 0"},{"line_number":304,"context_line":"        else:"},{"line_number":305,"context_line":"            inv_offset \u003d V2_MAX_OFFSET - self.offset"},{"line_number":306,"context_line":"            inv_extra \u003d V2_MAX_EXTRA - self.extra"},{"line_number":307,"context_line":"        return Timestamp(inv_raw, offset\u003dinv_offset, extra\u003dinv_extra)"},{"line_number":308,"context_line":""},{"line_number":309,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"a966a592_12dae407","line":306,"in_reply_to":"b8966eec_3871db10","updated":"2025-12-24 16:46:06.000000000","message":"Done","commit_id":"a65ed87d7438993fe6e65bbb05078a8d861a3b96"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"b8f2ca46ae6e9e9e0fdc0499b506354281ae1910","unresolved":true,"context_lines":[{"line_number":464,"context_line":"    :param timestamp: unix timestamp"},{"line_number":465,"context_line":"    :returns: normalized timestamp as a string"},{"line_number":466,"context_line":"    \"\"\""},{"line_number":467,"context_line":"    # TODO: what should high precision lookk like w.r.t. hex_part??"},{"line_number":468,"context_line":"    fmt \u003d \u0027%016.5f\u0027 if high_precision else \u0027%010d\u0027"},{"line_number":469,"context_line":"    return fmt % min(max(0, float(timestamp)), 9999999999.99999)"}],"source_content_type":"text/x-python","patch_set":3,"id":"21bf8d51_20088394","line":467,"updated":"2025-11-24 06:22:02.000000000","message":"good question, and does it make a difference? I mean if we have a \"collision\" will we still have possibly 2 different x-delete-at values (if they pick different delete_at times). So it\u0027s the same problem as a any race to write the the expirer queue. When the wrong one goes an attempts a delete, it\u0027ll find a different x-delete-at and not trigger it.\nSo I wonder if this is a case adding offset isn\u0027t strictly required.. but maybe I need to think on it some more.","commit_id":"5d6eb8e12465b140461dc45af612c78c82a384d3"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"e41a46f19c1be90e469e509cd6d32dbad7a1d809","unresolved":false,"context_lines":[{"line_number":464,"context_line":"    :param timestamp: unix timestamp"},{"line_number":465,"context_line":"    :returns: normalized timestamp as a string"},{"line_number":466,"context_line":"    \"\"\""},{"line_number":467,"context_line":"    # TODO: what should high precision lookk like w.r.t. hex_part??"},{"line_number":468,"context_line":"    fmt \u003d \u0027%016.5f\u0027 if high_precision else \u0027%010d\u0027"},{"line_number":469,"context_line":"    return fmt % min(max(0, float(timestamp)), 9999999999.99999)"}],"source_content_type":"text/x-python","patch_set":3,"id":"eb644230_a1566b94","line":467,"in_reply_to":"21bf8d51_20088394","updated":"2026-02-26 17:58:28.000000000","message":"Done","commit_id":"5d6eb8e12465b140461dc45af612c78c82a384d3"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"1f4a7e54a87065a56da9b18340e9b3e442211075","unresolved":true,"context_lines":[{"line_number":225,"context_line":"        return self._internal_format("},{"line_number":226,"context_line":"            self.timestamp, self._hex_part, FORCE_INTERNAL)"},{"line_number":227,"context_line":""},{"line_number":228,"context_line":"    def jitterless(self):"},{"line_number":229,"context_line":"        # TODO: this is a quick\u0027n\u0027dirty shortcut and at least needs a better"},{"line_number":230,"context_line":"        #  name"},{"line_number":231,"context_line":"        \"\"\""}],"source_content_type":"text/x-python","patch_set":8,"id":"c4990c5d_387a301f","line":228,"range":{"start_line":228,"start_character":8,"end_line":228,"end_character":18},"updated":"2025-11-26 23:22:04.000000000","message":"common, vanilla, imprecise, uniform.. or opposite of jitter: calm, stable.\n\nor v1 or version_1, to indicate it\u0027s a V1 (no jitter).","commit_id":"ac59feec5c51e8eee765bc4adb88b73469bfeecd"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"efc12f94fab26cecc7d58b4c39dbf44093af418c","unresolved":false,"context_lines":[{"line_number":225,"context_line":"        return self._internal_format("},{"line_number":226,"context_line":"            self.timestamp, self._hex_part, FORCE_INTERNAL)"},{"line_number":227,"context_line":""},{"line_number":228,"context_line":"    def jitterless(self):"},{"line_number":229,"context_line":"        # TODO: this is a quick\u0027n\u0027dirty shortcut and at least needs a better"},{"line_number":230,"context_line":"        #  name"},{"line_number":231,"context_line":"        \"\"\""}],"source_content_type":"text/x-python","patch_set":8,"id":"c1152704_4e1c82d9","line":228,"range":{"start_line":228,"start_character":8,"end_line":228,"end_character":18},"in_reply_to":"c4990c5d_387a301f","updated":"2026-01-07 12:18:14.000000000","message":"Acknowledged","commit_id":"ac59feec5c51e8eee765bc4adb88b73469bfeecd"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"1f4a7e54a87065a56da9b18340e9b3e442211075","unresolved":true,"context_lines":[{"line_number":433,"context_line":"        if delta:"},{"line_number":434,"context_line":"            # XXX we\u0027ve lost the original hexpart when encoding, so we have no"},{"line_number":435,"context_line":"            # choice other than to force zero jitter to avoid varying results"},{"line_number":436,"context_line":"            t2 \u003d Timestamp((t1.raw + delta) * PRECISION, jitter\u003d0)"},{"line_number":437,"context_line":"    elif not explicit:"},{"line_number":438,"context_line":"        t2 \u003d t1"},{"line_number":439,"context_line":"    if len(parts) \u003e 2:"}],"source_content_type":"text/x-python","patch_set":8,"id":"317d7979_3abb90c1","line":436,"updated":"2025-11-26 23:22:04.000000000","message":"can use the new \"jitterless\" here.","commit_id":"ac59feec5c51e8eee765bc4adb88b73469bfeecd"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"efc12f94fab26cecc7d58b4c39dbf44093af418c","unresolved":false,"context_lines":[{"line_number":433,"context_line":"        if delta:"},{"line_number":434,"context_line":"            # XXX we\u0027ve lost the original hexpart when encoding, so we have no"},{"line_number":435,"context_line":"            # choice other than to force zero jitter to avoid varying results"},{"line_number":436,"context_line":"            t2 \u003d Timestamp((t1.raw + delta) * PRECISION, jitter\u003d0)"},{"line_number":437,"context_line":"    elif not explicit:"},{"line_number":438,"context_line":"        t2 \u003d t1"},{"line_number":439,"context_line":"    if len(parts) \u003e 2:"}],"source_content_type":"text/x-python","patch_set":8,"id":"bd6d67ef_ab007bcd","line":436,"in_reply_to":"317d7979_3abb90c1","updated":"2026-01-07 12:18:14.000000000","message":"Done","commit_id":"ac59feec5c51e8eee765bc4adb88b73469bfeecd"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"92c35f72fc221504be67141926a79b229679d80b","unresolved":true,"context_lines":[{"line_number":383,"context_line":"    @classmethod"},{"line_number":384,"context_line":"    def zero(cls):"},{"line_number":385,"context_line":"        \"\"\""},{"line_number":386,"context_line":"        Returns an instance of the smallest possible Timestamp."},{"line_number":387,"context_line":"        \"\"\""},{"line_number":388,"context_line":"        return cls(0, jitter\u003d0)"},{"line_number":389,"context_line":""}],"source_content_type":"text/x-python","patch_set":27,"id":"a6591d2f_9ab0216f","line":386,"updated":"2025-12-18 19:57:32.000000000","message":"@tburke@nvidia.com mentioned in other patch that this is not accurate, since\n```\nrepr(Timestamp(\u00270\u0027)) \u003d\u003d \u00270000000000.00000\u0027\nrepr(Timestamp(0)) \u003d\u003d \u00270000000000.00000_2abcdef123000000\u0027  # random jitter\nrepr(Timestamp.zero()) \u003d\u003d \u00270000000000.00000_2000000000000000\u0027  # explicit zero jitter\n```\n\nso ``Timestamp.zero()`` should be ``Returns an instance of the smallest possible Timestamp in version 2.``","commit_id":"838bb722b863daecebeb54181a17b322d45bdb38"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"efc12f94fab26cecc7d58b4c39dbf44093af418c","unresolved":false,"context_lines":[{"line_number":383,"context_line":"    @classmethod"},{"line_number":384,"context_line":"    def zero(cls):"},{"line_number":385,"context_line":"        \"\"\""},{"line_number":386,"context_line":"        Returns an instance of the smallest possible Timestamp."},{"line_number":387,"context_line":"        \"\"\""},{"line_number":388,"context_line":"        return cls(0, jitter\u003d0)"},{"line_number":389,"context_line":""}],"source_content_type":"text/x-python","patch_set":27,"id":"c7cf33c4_8879311f","line":386,"in_reply_to":"a6591d2f_9ab0216f","updated":"2026-01-07 12:18:14.000000000","message":"Done","commit_id":"838bb722b863daecebeb54181a17b322d45bdb38"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"efc12f94fab26cecc7d58b4c39dbf44093af418c","unresolved":true,"context_lines":[{"line_number":86,"context_line":"        Returns an instance of the smallest possible Timestamp."},{"line_number":87,"context_line":"        \"\"\""},{"line_number":88,"context_line":"        # Deliberately pass a string to the construct so that subclasses do not"},{"line_number":89,"context_line":"        # add extra default parts (e.g. jitter)."},{"line_number":90,"context_line":"        return cls(\u00270000000000.00000\u0027)"},{"line_number":91,"context_line":""},{"line_number":92,"context_line":"    def __repr__(self):"}],"source_content_type":"text/x-python","patch_set":38,"id":"f57f97c9_a7e32d9c","line":89,"updated":"2026-01-07 12:18:14.000000000","message":"not necessary if we make with_jitter default to False","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"e3f331fce7da982ef54ee283e8389e2c4dfad87e","unresolved":false,"context_lines":[{"line_number":86,"context_line":"        Returns an instance of the smallest possible Timestamp."},{"line_number":87,"context_line":"        \"\"\""},{"line_number":88,"context_line":"        # Deliberately pass a string to the construct so that subclasses do not"},{"line_number":89,"context_line":"        # add extra default parts (e.g. jitter)."},{"line_number":90,"context_line":"        return cls(\u00270000000000.00000\u0027)"},{"line_number":91,"context_line":""},{"line_number":92,"context_line":"    def __repr__(self):"}],"source_content_type":"text/x-python","patch_set":38,"id":"b76b30e7_228f6c2e","line":89,"in_reply_to":"f57f97c9_a7e32d9c","updated":"2026-02-18 17:45:54.000000000","message":"Done","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"efc12f94fab26cecc7d58b4c39dbf44093af418c","unresolved":true,"context_lines":[{"line_number":163,"context_line":"        # This calculation is based on Python 2.7\u0027s Modules/datetimemodule.c,"},{"line_number":164,"context_line":"        # function delta_to_microseconds(), but written in Python."},{"line_number":165,"context_line":"        # Deliberately pass a string to the construct so that subclasses do not"},{"line_number":166,"context_line":"        # add extra default parts (e.g. jitter)."},{"line_number":167,"context_line":"        return cls(str(delta.total_seconds()))"},{"line_number":168,"context_line":""},{"line_number":169,"context_line":"    def ceil(self):"}],"source_content_type":"text/x-python","patch_set":38,"id":"0e52f939_9d20664c","line":166,"updated":"2026-01-07 12:18:14.000000000","message":"not necessary if we make with_jitter default to False","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"e3f331fce7da982ef54ee283e8389e2c4dfad87e","unresolved":false,"context_lines":[{"line_number":163,"context_line":"        # This calculation is based on Python 2.7\u0027s Modules/datetimemodule.c,"},{"line_number":164,"context_line":"        # function delta_to_microseconds(), but written in Python."},{"line_number":165,"context_line":"        # Deliberately pass a string to the construct so that subclasses do not"},{"line_number":166,"context_line":"        # add extra default parts (e.g. jitter)."},{"line_number":167,"context_line":"        return cls(str(delta.total_seconds()))"},{"line_number":168,"context_line":""},{"line_number":169,"context_line":"    def ceil(self):"}],"source_content_type":"text/x-python","patch_set":38,"id":"7dc8f89a_369db685","line":166,"in_reply_to":"0e52f939_9d20664c","updated":"2026-02-18 17:45:54.000000000","message":"Done","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"4b788c29c01cd4ffd865a651f70c24a788f4e646","unresolved":true,"context_lines":[{"line_number":297,"context_line":""},{"line_number":298,"context_line":"    def __init__(self, timestamp, offset\u003d0, delta\u003d0, check_bounds\u003dTrue,"},{"line_number":299,"context_line":"                 with_jitter\u003dTrue):"},{"line_number":300,"context_line":"        # TODO: I\u0027m inclined make the default False and have"},{"line_number":301,"context_line":"        #  Timestamp.now() set with_jitter\u003dTrue. That would be more explicit"},{"line_number":302,"context_line":"        #  than init\u0027ing with a float but getting a float plus some jitter as"},{"line_number":303,"context_line":"        #  a side-effect."}],"source_content_type":"text/x-python","patch_set":38,"id":"e8bde13b_d2062d68","line":300,"updated":"2026-01-07 01:16:58.000000000","message":"yes, the default needs to be ``False`` and we only enable it for the object creation path.","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"30771312418d73ed1b430d0447fa434e20073bd5","unresolved":true,"context_lines":[{"line_number":297,"context_line":""},{"line_number":298,"context_line":"    def __init__(self, timestamp, offset\u003d0, delta\u003d0, check_bounds\u003dTrue,"},{"line_number":299,"context_line":"                 with_jitter\u003dTrue):"},{"line_number":300,"context_line":"        # TODO: I\u0027m inclined make the default False and have"},{"line_number":301,"context_line":"        #  Timestamp.now() set with_jitter\u003dTrue. That would be more explicit"},{"line_number":302,"context_line":"        #  than init\u0027ing with a float but getting a float plus some jitter as"},{"line_number":303,"context_line":"        #  a side-effect."}],"source_content_type":"text/x-python","patch_set":38,"id":"be5e7c0a_0c7b7224","line":300,"in_reply_to":"41853a16_aba0ff95","updated":"2026-02-03 15:47:54.000000000","message":"\u003eI still have a different view on this. First, only timestamp collision with PUTs manifest the problem of data corruptions, let\u0027s only solve them. those timestamp collision cases of obj_POST/container_PUT/container_POST/etc with different data only exist in theory for now, the YAGNI principle applies well here.\n\nthe latest patch set only applies jitter to object PUTs\n\n\u003e I think a similar middleware approach like this makes a lot sense\nIMHO middlewares imply a function that is either optional or needs to happen at a specific place in the pipeline. I think we want to ensure the correct timestamp at the final app in the pipeline.","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"e3f331fce7da982ef54ee283e8389e2c4dfad87e","unresolved":false,"context_lines":[{"line_number":297,"context_line":""},{"line_number":298,"context_line":"    def __init__(self, timestamp, offset\u003d0, delta\u003d0, check_bounds\u003dTrue,"},{"line_number":299,"context_line":"                 with_jitter\u003dTrue):"},{"line_number":300,"context_line":"        # TODO: I\u0027m inclined make the default False and have"},{"line_number":301,"context_line":"        #  Timestamp.now() set with_jitter\u003dTrue. That would be more explicit"},{"line_number":302,"context_line":"        #  than init\u0027ing with a float but getting a float plus some jitter as"},{"line_number":303,"context_line":"        #  a side-effect."}],"source_content_type":"text/x-python","patch_set":38,"id":"cecd36b9_d1ff4b10","line":300,"in_reply_to":"be5e7c0a_0c7b7224","updated":"2026-02-18 17:45:54.000000000","message":"Done","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"5da24b58dc2a47fa59928d81ff0a8599c4247876","unresolved":true,"context_lines":[{"line_number":297,"context_line":""},{"line_number":298,"context_line":"    def __init__(self, timestamp, offset\u003d0, delta\u003d0, check_bounds\u003dTrue,"},{"line_number":299,"context_line":"                 with_jitter\u003dTrue):"},{"line_number":300,"context_line":"        # TODO: I\u0027m inclined make the default False and have"},{"line_number":301,"context_line":"        #  Timestamp.now() set with_jitter\u003dTrue. That would be more explicit"},{"line_number":302,"context_line":"        #  than init\u0027ing with a float but getting a float plus some jitter as"},{"line_number":303,"context_line":"        #  a side-effect."}],"source_content_type":"text/x-python","patch_set":38,"id":"41853a16_aba0ff95","line":300,"in_reply_to":"c16085b7_c4f0fcc6","updated":"2026-01-07 20:00:58.000000000","message":"\u003eI prefer to think of object POST as being the exception, and all other requests types have jitter.\n\nI still have a different view on this. First, only timestamp collision with PUTs manifest the problem of data corruptions, let\u0027s only solve them. those timestamp collision cases of obj_POST/container_PUT/container_POST/etc with different data only exist in theory for now, the YAGNI principle applies well here.\n\nSecond, if we decide to use timestamp with random jitter everywhere (other than container shard), incremental migration would minimize the risks. if we change all timestamp usages with one patch, if even all existing test cases are happy, I (merely me probably) don\u0027t know if there is one bug lurking somewhere would trigger an incident on prod. but if we upgrade the timestamp step by step, like obj_PUT --\u003e container_PUT/POST --\u003e obj_POST --\u003e others in different releases, it would be easier to verify each release in the staging clusters and spot potential issues with limited changes. \n\nAnd given the discovery that s3api won\u0027t alter the original ``x-timestamp``, I think a similar middleware approach like this makes a lot sense: https://review.opendev.org/c/openstack/swift/+/972032. The middleware can specify which methods to use the version 2 timestamp with ``with_jitter\u003dTrue``.","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"efc12f94fab26cecc7d58b4c39dbf44093af418c","unresolved":true,"context_lines":[{"line_number":297,"context_line":""},{"line_number":298,"context_line":"    def __init__(self, timestamp, offset\u003d0, delta\u003d0, check_bounds\u003dTrue,"},{"line_number":299,"context_line":"                 with_jitter\u003dTrue):"},{"line_number":300,"context_line":"        # TODO: I\u0027m inclined make the default False and have"},{"line_number":301,"context_line":"        #  Timestamp.now() set with_jitter\u003dTrue. That would be more explicit"},{"line_number":302,"context_line":"        #  than init\u0027ing with a float but getting a float plus some jitter as"},{"line_number":303,"context_line":"        #  a side-effect."}],"source_content_type":"text/x-python","patch_set":38,"id":"c16085b7_c4f0fcc6","line":300,"in_reply_to":"e8bde13b_d2062d68","updated":"2026-01-07 12:18:14.000000000","message":"ack - see follow-on sq? patch\n\nupdate: OIC you have seen that, great!\n\n\u003e only enable it for the object creation path.\n\nI prefer to think of object POST as being the exception, and all other requests types have jitter.","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"6ad3c8a7b5423f98c08820a4f3156577e92e36b0","unresolved":true,"context_lines":[{"line_number":425,"context_line":""},{"line_number":426,"context_line":"        :returns: an instance of Timestamp."},{"line_number":427,"context_line":"        \"\"\""},{"line_number":428,"context_line":"        return Timestamp(float(self), offset\u003d0, with_jitter\u003dFalse)"},{"line_number":429,"context_line":""},{"line_number":430,"context_line":""},{"line_number":431,"context_line":"def encode_timestamps(t1, t2\u003dNone, t3\u003dNone, explicit\u003dFalse):"}],"source_content_type":"text/x-python","patch_set":49,"id":"7004ddee_cdc1827c","line":428,"updated":"2026-02-03 05:50:38.000000000","message":"So we loose the ability to simplify to the \"simple\" timestamp? So does SimpleTimestamp need some way of converting a timestamp into a simple one, ie class method or contructor?","commit_id":"c45563740c4d84db48381e8038d660879b4c30dc"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"30771312418d73ed1b430d0447fa434e20073bd5","unresolved":true,"context_lines":[{"line_number":425,"context_line":""},{"line_number":426,"context_line":"        :returns: an instance of Timestamp."},{"line_number":427,"context_line":"        \"\"\""},{"line_number":428,"context_line":"        return Timestamp(float(self), offset\u003d0, with_jitter\u003dFalse)"},{"line_number":429,"context_line":""},{"line_number":430,"context_line":""},{"line_number":431,"context_line":"def encode_timestamps(t1, t2\u003dNone, t3\u003dNone, explicit\u003dFalse):"}],"source_content_type":"text/x-python","patch_set":49,"id":"c4e735dc_6113b081","line":428,"in_reply_to":"7004ddee_cdc1827c","updated":"2026-02-03 15:47:54.000000000","message":"I changed this because there are tests that assert that a request timestamp is a ``Timestamp``, but then when we restricted jitter to *only* object PUTs, all the other request types were getting a Timestamp.simplify() and breaking that assertion.\n\nsimplify() now has the same outcome but preserves the type of the Timestamp, which IMHO is less confusing (All request timestamps are instances of Timestamp. Some instances have jitter.)\n\nBUT, maybe the method name ``simplify`` is no longer appropriate because it implies...making it Simple 😄\n\nI don\u0027t want to name it ``jitterless`` because in the future we may not think of the hex part as jitter.\n\n``simplify`` is only used once in code but used lots in tests :/\n\nI will ponder some more...","commit_id":"c45563740c4d84db48381e8038d660879b4c30dc"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"47c053013ee0ead5ce4fbfb0404ec34fa30e9d93","unresolved":true,"context_lines":[{"line_number":425,"context_line":""},{"line_number":426,"context_line":"        :returns: an instance of Timestamp."},{"line_number":427,"context_line":"        \"\"\""},{"line_number":428,"context_line":"        return Timestamp(float(self), offset\u003d0, with_jitter\u003dFalse)"},{"line_number":429,"context_line":""},{"line_number":430,"context_line":""},{"line_number":431,"context_line":"def encode_timestamps(t1, t2\u003dNone, t3\u003dNone, explicit\u003dFalse):"}],"source_content_type":"text/x-python","patch_set":49,"id":"c8b65da8_84b32aff","line":428,"in_reply_to":"c4e735dc_6113b081","updated":"2026-02-13 15:50:19.000000000","message":"I renamed the method to ``normalized`` because it changes the Timestamp to have only its normalized form","commit_id":"c45563740c4d84db48381e8038d660879b4c30dc"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"e41a46f19c1be90e469e509cd6d32dbad7a1d809","unresolved":false,"context_lines":[{"line_number":425,"context_line":""},{"line_number":426,"context_line":"        :returns: an instance of Timestamp."},{"line_number":427,"context_line":"        \"\"\""},{"line_number":428,"context_line":"        return Timestamp(float(self), offset\u003d0, with_jitter\u003dFalse)"},{"line_number":429,"context_line":""},{"line_number":430,"context_line":""},{"line_number":431,"context_line":"def encode_timestamps(t1, t2\u003dNone, t3\u003dNone, explicit\u003dFalse):"}],"source_content_type":"text/x-python","patch_set":49,"id":"fdb38430_10a2083e","line":428,"in_reply_to":"c8b65da8_84b32aff","updated":"2026-02-26 17:58:28.000000000","message":"Done","commit_id":"c45563740c4d84db48381e8038d660879b4c30dc"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"47c053013ee0ead5ce4fbfb0404ec34fa30e9d93","unresolved":true,"context_lines":[{"line_number":28,"context_line":"MAX_HEX_PART \u003d (16 ** HEX_PART_DIGITS) - 1"},{"line_number":29,"context_line":"# Note: Previous versions of the Timestamp allocated the entire 16 digit hex"},{"line_number":30,"context_line":"# part to offset. To accommodate v2 Timestamps with jitter, the maximum offset"},{"line_number":31,"context_line":"# of a v1 Timestamp has now been restricted to a maximum of 0x1fffffffffffffff,"},{"line_number":32,"context_line":"# based on the assumption that no existing v1 Timestamp has a larger offset."},{"line_number":33,"context_line":"V1_MAX_OFFSET \u003d 2 * (16 ** 15) - 1"},{"line_number":34,"context_line":"# v2 Timestamp offsets are restricted to 6 hex digits"}],"source_content_type":"text/x-python","patch_set":52,"id":"06df339e_e538327d","line":31,"range":{"start_line":31,"start_character":49,"end_line":31,"end_character":56},"updated":"2026-02-13 15:50:19.000000000","message":"too many maximums in this sentence","commit_id":"0e79af4aa0471ef114dbc56669537c7aef39f404"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"e3f331fce7da982ef54ee283e8389e2c4dfad87e","unresolved":false,"context_lines":[{"line_number":28,"context_line":"MAX_HEX_PART \u003d (16 ** HEX_PART_DIGITS) - 1"},{"line_number":29,"context_line":"# Note: Previous versions of the Timestamp allocated the entire 16 digit hex"},{"line_number":30,"context_line":"# part to offset. To accommodate v2 Timestamps with jitter, the maximum offset"},{"line_number":31,"context_line":"# of a v1 Timestamp has now been restricted to a maximum of 0x1fffffffffffffff,"},{"line_number":32,"context_line":"# based on the assumption that no existing v1 Timestamp has a larger offset."},{"line_number":33,"context_line":"V1_MAX_OFFSET \u003d 2 * (16 ** 15) - 1"},{"line_number":34,"context_line":"# v2 Timestamp offsets are restricted to 6 hex digits"}],"source_content_type":"text/x-python","patch_set":52,"id":"c1e57445_fcb1c2f1","line":31,"range":{"start_line":31,"start_character":49,"end_line":31,"end_character":56},"in_reply_to":"06df339e_e538327d","updated":"2026-02-18 17:45:54.000000000","message":"Done","commit_id":"0e79af4aa0471ef114dbc56669537c7aef39f404"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"1fca10a6c99d8e2b631dc286c47acc4ff322113d","unresolved":true,"context_lines":[{"line_number":292,"context_line":""},{"line_number":293,"context_line":"        1402464677.04188_2123456789000000"},{"line_number":294,"context_line":""},{"line_number":295,"context_line":"    The fixed width of the parts ensures stable sort order."},{"line_number":296,"context_line":""},{"line_number":297,"context_line":"    The normalized representation of an extended timestamp has just the float"},{"line_number":298,"context_line":"    part, and therefore looks the same as a SimpleTimestamp, e.g:"}],"source_content_type":"text/x-python","patch_set":53,"id":"ca90a7bf_2092d652","line":295,"updated":"2026-02-13 17:15:38.000000000","message":"Add more here, in particular draw attention to the fact that Timestamps created sequentially in the *same deca microsecond* are NOT guaranteed to be monotonically increasing because the jitter is random. This is likely to be true even if we move to something more deterministic like node/process ids","commit_id":"1964da5195e0d4fe96e89e98d418d1ded010a9ad"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"55472369dc2bdcd573b4ce2f213ef9d0f15823e3","unresolved":false,"context_lines":[{"line_number":292,"context_line":""},{"line_number":293,"context_line":"        1402464677.04188_2123456789000000"},{"line_number":294,"context_line":""},{"line_number":295,"context_line":"    The fixed width of the parts ensures stable sort order."},{"line_number":296,"context_line":""},{"line_number":297,"context_line":"    The normalized representation of an extended timestamp has just the float"},{"line_number":298,"context_line":"    part, and therefore looks the same as a SimpleTimestamp, e.g:"}],"source_content_type":"text/x-python","patch_set":53,"id":"c166930d_a84ca9ba","line":295,"in_reply_to":"ca90a7bf_2092d652","updated":"2026-02-16 17:58:13.000000000","message":"Done","commit_id":"1964da5195e0d4fe96e89e98d418d1ded010a9ad"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"1fca10a6c99d8e2b631dc286c47acc4ff322113d","unresolved":true,"context_lines":[{"line_number":652,"context_line":"    :param timestamp: unix timestamp"},{"line_number":653,"context_line":"    :returns: normalized timestamp as a string"},{"line_number":654,"context_line":"    \"\"\""},{"line_number":655,"context_line":"    # TODO: what should high precision look like w.r.t. hex_part??"},{"line_number":656,"context_line":"    fmt \u003d \u0027%016.5f\u0027 if high_precision else \u0027%010d\u0027"},{"line_number":657,"context_line":"    return fmt % min(max(0, float(timestamp)), 9999999999.99999)"},{"line_number":658,"context_line":""}],"source_content_type":"text/x-python","patch_set":53,"id":"939ebe79_8ad4ebba","line":655,"updated":"2026-02-13 17:15:38.000000000","message":"I think it\u0027s ok to leave this as it is i.e. just use the normal format.\nAdding the hex part (with jitter) doesn\u0027t help guarantee that a delete timestamp is *greater* than a put timestamp *in the same deca-microsecond* (because the jitter is random)","commit_id":"1964da5195e0d4fe96e89e98d418d1ded010a9ad"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"55472369dc2bdcd573b4ce2f213ef9d0f15823e3","unresolved":false,"context_lines":[{"line_number":652,"context_line":"    :param timestamp: unix timestamp"},{"line_number":653,"context_line":"    :returns: normalized timestamp as a string"},{"line_number":654,"context_line":"    \"\"\""},{"line_number":655,"context_line":"    # TODO: what should high precision look like w.r.t. hex_part??"},{"line_number":656,"context_line":"    fmt \u003d \u0027%016.5f\u0027 if high_precision else \u0027%010d\u0027"},{"line_number":657,"context_line":"    return fmt % min(max(0, float(timestamp)), 9999999999.99999)"},{"line_number":658,"context_line":""}],"source_content_type":"text/x-python","patch_set":53,"id":"ff0998df_362d3813","line":655,"in_reply_to":"939ebe79_8ad4ebba","updated":"2026-02-16 17:58:13.000000000","message":"Done","commit_id":"1964da5195e0d4fe96e89e98d418d1ded010a9ad"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"8207f2c01aeea0343543be7050813999b080e58c","unresolved":true,"context_lines":[{"line_number":682,"context_line":"    if resource_type and resource_type.lower() \u003d\u003d \u0027object\u0027 and method \u003d\u003d \u0027PUT\u0027:"},{"line_number":683,"context_line":"        return ts"},{"line_number":684,"context_line":"    else:"},{"line_number":685,"context_line":"        return ts.normalized()"}],"source_content_type":"text/x-python","patch_set":54,"id":"c129073e_f019603f","line":685,"updated":"2026-02-17 17:03:10.000000000","message":"we should have the proxy app validate that any *existing* x-timestamp conforms with the conditions in this function, perhaps in ensure_x_timestamp","commit_id":"632c8359d8d5c3c3908cbe32a53e40b922acea50"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"e3f331fce7da982ef54ee283e8389e2c4dfad87e","unresolved":false,"context_lines":[{"line_number":682,"context_line":"    if resource_type and resource_type.lower() \u003d\u003d \u0027object\u0027 and method \u003d\u003d \u0027PUT\u0027:"},{"line_number":683,"context_line":"        return ts"},{"line_number":684,"context_line":"    else:"},{"line_number":685,"context_line":"        return ts.normalized()"}],"source_content_type":"text/x-python","patch_set":54,"id":"012d643f_6686e0bd","line":685,"in_reply_to":"c129073e_f019603f","updated":"2026-02-18 17:45:54.000000000","message":"Done","commit_id":"632c8359d8d5c3c3908cbe32a53e40b922acea50"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"ff6903e4e1e780153e0241e95bd1734ee1074be5","unresolved":true,"context_lines":[{"line_number":324,"context_line":"    \"\"\""},{"line_number":325,"context_line":""},{"line_number":326,"context_line":"    def __init__(self, timestamp, offset\u003d0, delta\u003d0, check_bounds\u003dTrue,"},{"line_number":327,"context_line":"                 extended\u003dFalse):"},{"line_number":328,"context_line":"        \"\"\""},{"line_number":329,"context_line":"        Create a new Timestamp."},{"line_number":330,"context_line":""}],"source_content_type":"text/x-python","patch_set":55,"id":"40ffc580_e340a49f","line":327,"updated":"2026-02-19 05:25:05.000000000","message":"``extended \u003d\u003d False`` means version 1 timestamp, and ``extended \u003d\u003d True`` means version 2 timestamp. since we are not going to migrate the existing objects with version 1 timestamp in prod to newer version, our code base needs to understand version 1 timestamp forever.\n\nwhen I look at different places of ``Timestamp`` objects, it\u0027s not that direct to tell if they are version 1 or version 2. I like the new class ``NormalTimestamp``, I can just tell what it is from its name, that\u0027ll be great if we can tell v2 timestamp from v1 simply like that.","commit_id":"70256a85180c445a1d513c846d812d125d01a2b8"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"8ad3e0cc663af3a6a5c50b1e504eed3e7033bd52","unresolved":true,"context_lines":[{"line_number":324,"context_line":"    \"\"\""},{"line_number":325,"context_line":""},{"line_number":326,"context_line":"    def __init__(self, timestamp, offset\u003d0, delta\u003d0, check_bounds\u003dTrue,"},{"line_number":327,"context_line":"                 extended\u003dFalse):"},{"line_number":328,"context_line":"        \"\"\""},{"line_number":329,"context_line":"        Create a new Timestamp."},{"line_number":330,"context_line":""}],"source_content_type":"text/x-python","patch_set":55,"id":"ab1d2e5d_f7868154","line":327,"in_reply_to":"40ffc580_e340a49f","updated":"2026-02-19 15:19:37.000000000","message":"I don\u0027t think we reached consensus on a new name for Timestamp (and we have to preserve Timestamp even if just an alias, for out-of-tree compatibility). But, to be clear, a \"v1Timestamp\" class would never be used appear in-tree. We\u0027re not adding a new version, we\u0027re evolving the existing.","commit_id":"70256a85180c445a1d513c846d812d125d01a2b8"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"2dabba38f8f83782a444d71c3695e074ad5f6d9f","unresolved":true,"context_lines":[{"line_number":324,"context_line":"    \"\"\""},{"line_number":325,"context_line":""},{"line_number":326,"context_line":"    def __init__(self, timestamp, offset\u003d0, delta\u003d0, check_bounds\u003dTrue,"},{"line_number":327,"context_line":"                 extended\u003dFalse):"},{"line_number":328,"context_line":"        \"\"\""},{"line_number":329,"context_line":"        Create a new Timestamp."},{"line_number":330,"context_line":""}],"source_content_type":"text/x-python","patch_set":55,"id":"ce499a85_c6cbd964","line":327,"in_reply_to":"ab1d2e5d_f7868154","updated":"2026-02-23 06:19:09.000000000","message":"the refactoring to add new class, if consensus reached later, can be follow-up patches","commit_id":"70256a85180c445a1d513c846d812d125d01a2b8"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"e41a46f19c1be90e469e509cd6d32dbad7a1d809","unresolved":false,"context_lines":[{"line_number":324,"context_line":"    \"\"\""},{"line_number":325,"context_line":""},{"line_number":326,"context_line":"    def __init__(self, timestamp, offset\u003d0, delta\u003d0, check_bounds\u003dTrue,"},{"line_number":327,"context_line":"                 extended\u003dFalse):"},{"line_number":328,"context_line":"        \"\"\""},{"line_number":329,"context_line":"        Create a new Timestamp."},{"line_number":330,"context_line":""}],"source_content_type":"text/x-python","patch_set":55,"id":"0fdb7aaa_180a14b5","line":327,"in_reply_to":"ce499a85_c6cbd964","updated":"2026-02-26 17:58:28.000000000","message":"Acknowledged","commit_id":"70256a85180c445a1d513c846d812d125d01a2b8"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"8ad3e0cc663af3a6a5c50b1e504eed3e7033bd52","unresolved":true,"context_lines":[{"line_number":343,"context_line":"            * another instance of a Timestamp (offset is preserved when"},{"line_number":344,"context_line":"              present)"},{"line_number":345,"context_line":""},{"line_number":346,"context_line":"        :param offset: (int) the second internal offset vector"},{"line_number":347,"context_line":"        :param delta: (int) deca-microsecond difference to be added to the"},{"line_number":348,"context_line":"            ``timestamp`` value"},{"line_number":349,"context_line":"        :param check_bounds: if True (default) then a ValueError will be raised"}],"source_content_type":"text/x-python","patch_set":55,"id":"ce5f712f_95dc5bde","line":346,"updated":"2026-02-19 15:19:37.000000000","message":"this should say it is an increment on any existing offset.","commit_id":"70256a85180c445a1d513c846d812d125d01a2b8"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"e41a46f19c1be90e469e509cd6d32dbad7a1d809","unresolved":false,"context_lines":[{"line_number":343,"context_line":"            * another instance of a Timestamp (offset is preserved when"},{"line_number":344,"context_line":"              present)"},{"line_number":345,"context_line":""},{"line_number":346,"context_line":"        :param offset: (int) the second internal offset vector"},{"line_number":347,"context_line":"        :param delta: (int) deca-microsecond difference to be added to the"},{"line_number":348,"context_line":"            ``timestamp`` value"},{"line_number":349,"context_line":"        :param check_bounds: if True (default) then a ValueError will be raised"}],"source_content_type":"text/x-python","patch_set":55,"id":"6ef33361_e3382828","line":346,"in_reply_to":"ce5f712f_95dc5bde","updated":"2026-02-26 17:58:28.000000000","message":"Done","commit_id":"70256a85180c445a1d513c846d812d125d01a2b8"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"ff6903e4e1e780153e0241e95bd1734ee1074be5","unresolved":true,"context_lines":[{"line_number":349,"context_line":"        :param check_bounds: if True (default) then a ValueError will be raised"},{"line_number":350,"context_line":"            if the given timestamp is less than 0 or greater than the maximum"},{"line_number":351,"context_line":"            time that can be represented by this class."},{"line_number":352,"context_line":"        :param extended: If True then a random jitter amount will be"},{"line_number":353,"context_line":"            inserted into the hex part of the timestamp. Default is False."},{"line_number":354,"context_line":"        \"\"\""},{"line_number":355,"context_line":"        hex_part \u003d 0"}],"source_content_type":"text/x-python","patch_set":55,"id":"58ebfaa6_99533702","line":352,"updated":"2026-02-19 05:25:05.000000000","message":"so even if ``timestamp`` has a random jitter, but the hex part will be 0 if ``extended`` is true. I feel this choice is hard to understand.","commit_id":"70256a85180c445a1d513c846d812d125d01a2b8"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"8ad3e0cc663af3a6a5c50b1e504eed3e7033bd52","unresolved":false,"context_lines":[{"line_number":349,"context_line":"        :param check_bounds: if True (default) then a ValueError will be raised"},{"line_number":350,"context_line":"            if the given timestamp is less than 0 or greater than the maximum"},{"line_number":351,"context_line":"            time that can be represented by this class."},{"line_number":352,"context_line":"        :param extended: If True then a random jitter amount will be"},{"line_number":353,"context_line":"            inserted into the hex part of the timestamp. Default is False."},{"line_number":354,"context_line":"        \"\"\""},{"line_number":355,"context_line":"        hex_part \u003d 0"}],"source_content_type":"text/x-python","patch_set":55,"id":"d6a0a6ff_d3bdbc9d","line":352,"in_reply_to":"58ebfaa6_99533702","updated":"2026-02-19 15:19:37.000000000","message":"It\u0027s hard to document because the constructor is used to create \"new\" timestamps from a float and to parse existing timestamps.\n\nWe don\u0027t add/remove jitter to/from an existing timestamp str. If extended is True we\u0027ll add jitter to a new Timestamp constructed with a float.","commit_id":"70256a85180c445a1d513c846d812d125d01a2b8"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"ff6903e4e1e780153e0241e95bd1734ee1074be5","unresolved":true,"context_lines":[{"line_number":663,"context_line":"            and request_method \u003d\u003d \u0027PUT\u0027)"},{"line_number":664,"context_line":""},{"line_number":665,"context_line":""},{"line_number":666,"context_line":"def make_timestamp_now(resource_type, request_method):"},{"line_number":667,"context_line":"    \"\"\""},{"line_number":668,"context_line":"    Returns a timestamp of the appropriate type for the given resource_type and"},{"line_number":669,"context_line":"    request method."}],"source_content_type":"text/x-python","patch_set":55,"id":"bdcf97e0_f9f1f4d3","line":666,"updated":"2026-02-19 05:25:05.000000000","message":"this patch applies the version 2 timestamp on the object PUT path only, but ``make_timestamp_now`` won\u0027t return any version 1 timestamp anymore?","commit_id":"70256a85180c445a1d513c846d812d125d01a2b8"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"8ad3e0cc663af3a6a5c50b1e504eed3e7033bd52","unresolved":true,"context_lines":[{"line_number":663,"context_line":"            and request_method \u003d\u003d \u0027PUT\u0027)"},{"line_number":664,"context_line":""},{"line_number":665,"context_line":""},{"line_number":666,"context_line":"def make_timestamp_now(resource_type, request_method):"},{"line_number":667,"context_line":"    \"\"\""},{"line_number":668,"context_line":"    Returns a timestamp of the appropriate type for the given resource_type and"},{"line_number":669,"context_line":"    request method."}],"source_content_type":"text/x-python","patch_set":55,"id":"e5730ab4_ccedccd3","line":666,"in_reply_to":"bdcf97e0_f9f1f4d3","updated":"2026-02-19 15:19:37.000000000","message":"Correct.\n\nNormalTimestamp is sufficient unless a request type requires offset. If offset is required then (v2) Timestamp is appropriate.\n\nOr, to put it another way, if you don\u0027t want jitter then you don\u0027t need offset so a NormalTimestamp is sufficient.\n\nI don\u0027t believe there are any use cases where a v2 Timestamp requires offset but we do not yet want to add jitter, because offset is only used with object PUTs, and reconciler object DELETEs which inherit the PUT timestamp.\n\nReturning NormalTimestamps here has the advantage of preventing the caller subsequently adding offset to a request type that doesn\u0027t support offset.","commit_id":"70256a85180c445a1d513c846d812d125d01a2b8"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"e41a46f19c1be90e469e509cd6d32dbad7a1d809","unresolved":false,"context_lines":[{"line_number":663,"context_line":"            and request_method \u003d\u003d \u0027PUT\u0027)"},{"line_number":664,"context_line":""},{"line_number":665,"context_line":""},{"line_number":666,"context_line":"def make_timestamp_now(resource_type, request_method):"},{"line_number":667,"context_line":"    \"\"\""},{"line_number":668,"context_line":"    Returns a timestamp of the appropriate type for the given resource_type and"},{"line_number":669,"context_line":"    request method."}],"source_content_type":"text/x-python","patch_set":55,"id":"9dc42906_7e711b5f","line":666,"in_reply_to":"c545dad7_006f4bb1","updated":"2026-02-26 17:58:28.000000000","message":"thanks!","commit_id":"70256a85180c445a1d513c846d812d125d01a2b8"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"2dabba38f8f83782a444d71c3695e074ad5f6d9f","unresolved":true,"context_lines":[{"line_number":663,"context_line":"            and request_method \u003d\u003d \u0027PUT\u0027)"},{"line_number":664,"context_line":""},{"line_number":665,"context_line":""},{"line_number":666,"context_line":"def make_timestamp_now(resource_type, request_method):"},{"line_number":667,"context_line":"    \"\"\""},{"line_number":668,"context_line":"    Returns a timestamp of the appropriate type for the given resource_type and"},{"line_number":669,"context_line":"    request method."}],"source_content_type":"text/x-python","patch_set":55,"id":"c545dad7_006f4bb1","line":666,"in_reply_to":"e5730ab4_ccedccd3","updated":"2026-02-23 06:19:09.000000000","message":"I found another place which we don\u0027t need to use v2 Timestamp: https://review.opendev.org/c/openstack/swift/+/977594","commit_id":"70256a85180c445a1d513c846d812d125d01a2b8"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"178ebe75011383c5d1e4780bc1c5fd5452ce80d2","unresolved":true,"context_lines":[{"line_number":692,"context_line":"    elif resource_type.lower() \u003d\u003d \u0027object\u0027 and request_method \u003d\u003d \u0027PUT\u0027:"},{"line_number":693,"context_line":"        return Timestamp.now()"},{"line_number":694,"context_line":"    else:"},{"line_number":695,"context_line":"        return NormalTimestamp.now()"},{"line_number":696,"context_line":""},{"line_number":697,"context_line":""},{"line_number":698,"context_line":"def parse_timestamp(value, resource_type, request_method):"}],"source_content_type":"text/x-python","patch_set":60,"id":"791d1636_0a54fbab","line":695,"updated":"2026-02-23 21:51:01.000000000","message":"one side effect of this choice that I didn\u0027t realize at first was that since we use NormalTimestamp with POST you can end up with a v0\u003d\u003ev1 timestamp on a .data file for a *time.time* float value *after* the release of this change b/c of the way the reconclier will \"promote\" the x-timestamp of the .meta to be the x-timestamp (plus some offset) of the resulting migrated object.\n\nI think this is ... more or less basically still \"correct\" WRT to how to we define the reconciler\u0027s behavior; and also about as good as we can do until we\u0027re ready to add jitter to object POST (which we should do, but NOT as part of this patch)","commit_id":"026fe0c57bae6f6bef354f2e627120834f9de446"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"e41a46f19c1be90e469e509cd6d32dbad7a1d809","unresolved":false,"context_lines":[{"line_number":692,"context_line":"    elif resource_type.lower() \u003d\u003d \u0027object\u0027 and request_method \u003d\u003d \u0027PUT\u0027:"},{"line_number":693,"context_line":"        return Timestamp.now()"},{"line_number":694,"context_line":"    else:"},{"line_number":695,"context_line":"        return NormalTimestamp.now()"},{"line_number":696,"context_line":""},{"line_number":697,"context_line":""},{"line_number":698,"context_line":"def parse_timestamp(value, resource_type, request_method):"}],"source_content_type":"text/x-python","patch_set":60,"id":"fc40b894_d29269c3","line":695,"in_reply_to":"791d1636_0a54fbab","updated":"2026-02-26 17:58:28.000000000","message":"Acknowledged","commit_id":"026fe0c57bae6f6bef354f2e627120834f9de446"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"b501820341b9e1d27250f21113e189a5ba7eb88c","unresolved":true,"context_lines":[{"line_number":23,"context_line":""},{"line_number":24,"context_line":"NORMAL_FORMAT \u003d \"%016.05f\""},{"line_number":25,"context_line":"INTERNAL_FORMAT \u003d NORMAL_FORMAT + \u0027_%016x\u0027"},{"line_number":26,"context_line":"SHORT_FORMAT \u003d NORMAL_FORMAT + \u0027_%x\u0027"},{"line_number":27,"context_line":"HEX_PART_DIGITS \u003d 16"},{"line_number":28,"context_line":"MAX_HEX_PART \u003d (16 ** HEX_PART_DIGITS) - 1"},{"line_number":29,"context_line":"# Note: Previous versions of the Timestamp class allocated the entire 16 digit"}],"source_content_type":"text/x-python","patch_set":61,"id":"29c970f0_562ebf2f","line":26,"updated":"2026-02-27 00:14:29.000000000","message":"\"short\" doesn\u0027t seem like a very good name anymore if all v2 timestamps are going to be greater than 2305843009213693952 the \"short\" form isn\u0027t really saving us much","commit_id":"3b4cdeed1c1f9456e04bed2f54e644d342128b85"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"281a9af18fde7ff1be1bf191c2b503fae0f527f5","unresolved":true,"context_lines":[{"line_number":23,"context_line":""},{"line_number":24,"context_line":"NORMAL_FORMAT \u003d \"%016.05f\""},{"line_number":25,"context_line":"INTERNAL_FORMAT \u003d NORMAL_FORMAT + \u0027_%016x\u0027"},{"line_number":26,"context_line":"SHORT_FORMAT \u003d NORMAL_FORMAT + \u0027_%x\u0027"},{"line_number":27,"context_line":"HEX_PART_DIGITS \u003d 16"},{"line_number":28,"context_line":"MAX_HEX_PART \u003d (16 ** HEX_PART_DIGITS) - 1"},{"line_number":29,"context_line":"# Note: Previous versions of the Timestamp class allocated the entire 16 digit"}],"source_content_type":"text/x-python","patch_set":61,"id":"7cfb85b5_80c071cb","line":26,"in_reply_to":"29c970f0_562ebf2f","updated":"2026-02-27 14:53:55.000000000","message":"Most existing/legacy timestamps would still have a shorter form. It\u0027s only used in encode_timestamps, and I\u0027m not sure we\u0027ll ever *encode* a legacy timestamp again. We could stop using it, but we probably want to be making more significant changes to encode_timestamps to support POSTs-with-jitter, so best left as a follow-on task?","commit_id":"3b4cdeed1c1f9456e04bed2f54e644d342128b85"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"9c233daf2cfad82e73cb72e81b7283dbb247fe4f","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 False"},{"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":61,"id":"e8ba8d52_a389a67e","line":219,"updated":"2026-02-25 17:32:59.000000000","message":"when I compare a larger v2 timestamp full string to a smaller NormalTimestamp, got a surprise result:\n```\n\u003e\u003e\u003e nts \u003d NormalTimestamp(\"1772036892.54575\")\n\u003e\u003e\u003e v2_str \u003d \"1972036909.08134_2abc000000000000\"\n\u003e\u003e\u003e v2_str \u003e\u003d nts\nFalse\n```\n\nI think this is because ``self._cast(other)`` returns ``ValueError`` when ``NormalTimestamp._parse()`` was called. NormalTimestamp does the correct thing, so maybe we just disallow this kind of comparison by raising the exception?","commit_id":"3b4cdeed1c1f9456e04bed2f54e644d342128b85"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"2aed517795ae85b3efed1cffbac61dc31987b610","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 False"},{"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":61,"id":"d644ee64_f69dffef","line":219,"in_reply_to":"6fb7b575_a4d78879","updated":"2026-02-26 06:16:50.000000000","message":"Maybe another option is something like: https://review.opendev.org/c/openstack/swift/+/978042\n\nWhich expands the _parse in the other timestamps for match the sig of Timestamp and just ignore extra data after the `_`.\n\nI just don\u0027t like the fact that the answer would be we cant compare strings only concrete classes of the BaseTimestamp.. but I could live with it I guess.","commit_id":"3b4cdeed1c1f9456e04bed2f54e644d342128b85"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"e41a46f19c1be90e469e509cd6d32dbad7a1d809","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 False"},{"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":61,"id":"5c45aee2_9796d7ba","line":219,"in_reply_to":"d644ee64_f69dffef","updated":"2026-02-26 17:58:28.000000000","message":"I hope I have fixed this issue, in two ways:\n\n1. The comparison of a Timestamp.internal *string* with a NormalTimestamp *instance* should now work\n\n2. Comparison of a [Normal]Timestamp instance with an unsupported object now raises an exception","commit_id":"3b4cdeed1c1f9456e04bed2f54e644d342128b85"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"71c98a018b23647da74a67beb9ebfdab38d243b2","unresolved":false,"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 False"},{"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":61,"id":"6fb7b575_a4d78879","line":219,"in_reply_to":"e8ba8d52_a389a67e","updated":"2026-02-25 21:33:45.000000000","message":"I got this fixed in the patch where I ran into this: https://review.opendev.org/c/openstack/swift/+/978029","commit_id":"3b4cdeed1c1f9456e04bed2f54e644d342128b85"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"b501820341b9e1d27250f21113e189a5ba7eb88c","unresolved":true,"context_lines":[{"line_number":488,"context_line":"        if self.hex_part or FORCE_INTERNAL:"},{"line_number":489,"context_line":"            return SHORT_FORMAT % (self.timestamp, self.hex_part)"},{"line_number":490,"context_line":"        else:"},{"line_number":491,"context_line":"            return self.normal"},{"line_number":492,"context_line":""},{"line_number":493,"context_line":"    def __invert__(self):"},{"line_number":494,"context_line":"        inv_raw \u003d (MAX_RAW_TIME - self.raw) * PRECISION"}],"source_content_type":"text/x-python","patch_set":61,"id":"a7cd2c68_e73200bb","line":491,"updated":"2026-02-27 00:14:29.000000000","message":"I think `.internal` will sort of already render NormalTimestamps like this and, since v2 will always have a full `hex_part` this is starting to become equivalent to `.internal`\n\nI\u0027d like to add a comment that the `.short` property should be removed/avoided/deprecated and new code should use `.internal` instead.\n\nAlternatively since it\u0027s only used in encode_timestamp we could remove it - but that starts to feel like scope creep.","commit_id":"3b4cdeed1c1f9456e04bed2f54e644d342128b85"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"281a9af18fde7ff1be1bf191c2b503fae0f527f5","unresolved":true,"context_lines":[{"line_number":488,"context_line":"        if self.hex_part or FORCE_INTERNAL:"},{"line_number":489,"context_line":"            return SHORT_FORMAT % (self.timestamp, self.hex_part)"},{"line_number":490,"context_line":"        else:"},{"line_number":491,"context_line":"            return self.normal"},{"line_number":492,"context_line":""},{"line_number":493,"context_line":"    def __invert__(self):"},{"line_number":494,"context_line":"        inv_raw \u003d (MAX_RAW_TIME - self.raw) * PRECISION"}],"source_content_type":"text/x-python","patch_set":61,"id":"bbad57fa_c5bab8d6","line":491,"in_reply_to":"a7cd2c68_e73200bb","updated":"2026-02-27 14:53:55.000000000","message":"newly minted Timestamps will always have a hex part, but parsed legacy timestamps may not and will still have a shorter format. But same as earlier comment, I\u0027m not sure we will ever encode a legacy timestamp again (unless somehow in metadata replication, but I don\u0027t think so???).\n\nAgree, comment for now, remove later.","commit_id":"3b4cdeed1c1f9456e04bed2f54e644d342128b85"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"b501820341b9e1d27250f21113e189a5ba7eb88c","unresolved":true,"context_lines":[{"line_number":522,"context_line":"    then the offsets for t2 and t3 are always appended even if zero."},{"line_number":523,"context_line":""},{"line_number":524,"context_line":"    Note: any offset value in t1 will be preserved, but offsets on t2 and t3"},{"line_number":525,"context_line":"    are not preserved. In the anticipated use cases for this method (and the"},{"line_number":526,"context_line":"    inverse decode_timestamps method) the timestamps passed as t2 and t3 are"},{"line_number":527,"context_line":"    not expected to have offsets as they will be timestamps associated with a"},{"line_number":528,"context_line":"    POST request. In the case where the encoding is used in a container objects"}],"source_content_type":"text/x-python","patch_set":63,"id":"56595713_f4f486b9","line":525,"updated":"2026-02-27 00:14:29.000000000","message":"s/offset/hex_part/","commit_id":"bce3e42d3f31422218ab8b7a2130832870a3bf07"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"281a9af18fde7ff1be1bf191c2b503fae0f527f5","unresolved":false,"context_lines":[{"line_number":522,"context_line":"    then the offsets for t2 and t3 are always appended even if zero."},{"line_number":523,"context_line":""},{"line_number":524,"context_line":"    Note: any offset value in t1 will be preserved, but offsets on t2 and t3"},{"line_number":525,"context_line":"    are not preserved. In the anticipated use cases for this method (and the"},{"line_number":526,"context_line":"    inverse decode_timestamps method) the timestamps passed as t2 and t3 are"},{"line_number":527,"context_line":"    not expected to have offsets as they will be timestamps associated with a"},{"line_number":528,"context_line":"    POST request. In the case where the encoding is used in a container objects"}],"source_content_type":"text/x-python","patch_set":63,"id":"3f3d280d_682041cb","line":525,"in_reply_to":"56595713_f4f486b9","updated":"2026-02-27 14:53:55.000000000","message":"Done","commit_id":"bce3e42d3f31422218ab8b7a2130832870a3bf07"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"b501820341b9e1d27250f21113e189a5ba7eb88c","unresolved":true,"context_lines":[{"line_number":526,"context_line":"    inverse decode_timestamps method) the timestamps passed as t2 and t3 are"},{"line_number":527,"context_line":"    not expected to have offsets as they will be timestamps associated with a"},{"line_number":528,"context_line":"    POST request. In the case where the encoding is used in a container objects"},{"line_number":529,"context_line":"    table row, t1 could be the PUT or DELETE time but t2 and t3 represent the"},{"line_number":530,"context_line":"    content type and metadata times (if different from the data file) i.e."},{"line_number":531,"context_line":"    correspond to POST timestamps. In the case where the encoded form is used"},{"line_number":532,"context_line":"    in a .meta file name, t1 and t2 both correspond to POST timestamps."}],"source_content_type":"text/x-python","patch_set":63,"id":"d2a4781a_8a85e004","line":529,"updated":"2026-02-27 00:14:29.000000000","message":"in my testing it\u0027s the container objects table row case is where this is going to matter\n\nsince in diskfile we never use the t3 case the hex_part of t1 *IS* preserved in the file name and the delta isn\u0027t really significant b/c the *actual* ctype-timestamp is preserved *in* the metadata as Content-Type-Timestamp with full v2 resolution - and since don\u0027t obsolete the ctype +0.meta file in cleanup_ondisk_files it\u0027s v2 jitter should be sufficient to force a hash-mismatch and trigger rsync.","commit_id":"bce3e42d3f31422218ab8b7a2130832870a3bf07"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"b501820341b9e1d27250f21113e189a5ba7eb88c","unresolved":true,"context_lines":[{"line_number":534,"context_line":"    form \u003d \u0027{0}\u0027"},{"line_number":535,"context_line":"    values \u003d [t1.short]"},{"line_number":536,"context_line":"    if t2 is not None:"},{"line_number":537,"context_line":"        t2_t1_delta \u003d t2.raw - t1.raw"},{"line_number":538,"context_line":"        explicit \u003d explicit or (t2_t1_delta !\u003d 0)"},{"line_number":539,"context_line":"        values.append(t2_t1_delta)"},{"line_number":540,"context_line":"        if t3 is not None:"}],"source_content_type":"text/x-python","patch_set":63,"id":"b1ac4e1f_865d9625","line":537,"updated":"2026-02-27 00:14:29.000000000","message":"probably *this* is closer to the `# XXX ...` in my mind, the delta between `t2.raw and t1.raw` doesn\u0027t account for their hexparts.  This `t2_t1_delta` value is purposefully discarding timing data (exactly as described in the doc string!)\n\nI would say that we should raise an Error if t2 has an offset (to keep us honest) but a lot of object-server tests are already testing .meta\u0027s with v2 timestamps.","commit_id":"bce3e42d3f31422218ab8b7a2130832870a3bf07"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"281a9af18fde7ff1be1bf191c2b503fae0f527f5","unresolved":false,"context_lines":[{"line_number":534,"context_line":"    form \u003d \u0027{0}\u0027"},{"line_number":535,"context_line":"    values \u003d [t1.short]"},{"line_number":536,"context_line":"    if t2 is not None:"},{"line_number":537,"context_line":"        t2_t1_delta \u003d t2.raw - t1.raw"},{"line_number":538,"context_line":"        explicit \u003d explicit or (t2_t1_delta !\u003d 0)"},{"line_number":539,"context_line":"        values.append(t2_t1_delta)"},{"line_number":540,"context_line":"        if t3 is not None:"}],"source_content_type":"text/x-python","patch_set":63,"id":"9e1d8166_ab9f52e8","line":537,"in_reply_to":"b1ac4e1f_865d9625","updated":"2026-02-27 14:53:55.000000000","message":"adding an XXX comment here","commit_id":"bce3e42d3f31422218ab8b7a2130832870a3bf07"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"b501820341b9e1d27250f21113e189a5ba7eb88c","unresolved":true,"context_lines":[{"line_number":586,"context_line":"        if delta:"},{"line_number":587,"context_line":"            # XXX we\u0027ve lost the original hexpart when encoding, so we have no"},{"line_number":588,"context_line":"            # choice other than to force zero jitter to avoid varying results"},{"line_number":589,"context_line":"            t2 \u003d Timestamp(str((t1.raw + delta) * PRECISION))"},{"line_number":590,"context_line":"    elif not explicit:"},{"line_number":591,"context_line":"        t2 \u003d t1"},{"line_number":592,"context_line":"    if len(parts) \u003e 2:"}],"source_content_type":"text/x-python","patch_set":63,"id":"63ec6d77_77700954","line":589,"updated":"2026-02-27 00:14:29.000000000","message":"\"we\u0027ve lost\" had me sort of confused, partially b/c I didn\u0027t understand this method - like I\u0027d thought `# XXX ...` was pointing to a bug HERE, in *this* diff hunk; but the \"when encoding\" part of the comment is REALLY important: \"we have no choice\" - the timing data we\u0027re not including with this Timestamp instance has ALREADY been lost.\n\nWe\u0027re about to *make up* a Timestamp instance - and we don\u0027t get to create it from a `.internal` string representing a Timestamp instance - b/c the string we\u0027ve encoded is composed of a real/full v2 timestamp `.internal` (with jitter) and a \"delta\" of the \"raw\" (float) part of t1 \u0026 t2\n\ne.g.\n\n```\n(Pdb) decode_timestamps(\u00271772144767.00000_216bdd68eb000000-1b207\u0027)\n(1772144767.00000_216bdd68eb000000, 1772144765.88889_0000000000000000, 1772144765.88889_0000000000000000)\n```\n\nso that weird/dangly `-1b207` bit is saying:\n\nthere exists another timestamp in addition to the `.internal`/literal Timestamp, and this string communicates that the timestamp in question is equal to the NormalTimestamp that is `0x1b207` decamicroseconds before the `.internal`/literal Timestamp.\n\nAnd we \"make up\" that timestamp using math (`t1.raw + delta`) and we are admitting \"this t2 Timestamp we\u0027re making up is not *neccessarily* the t2 Timestamp that was encoded - because we\u0027ve lost it\u0027s hexpart when encoding\"","commit_id":"bce3e42d3f31422218ab8b7a2130832870a3bf07"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"281a9af18fde7ff1be1bf191c2b503fae0f527f5","unresolved":false,"context_lines":[{"line_number":586,"context_line":"        if delta:"},{"line_number":587,"context_line":"            # XXX we\u0027ve lost the original hexpart when encoding, so we have no"},{"line_number":588,"context_line":"            # choice other than to force zero jitter to avoid varying results"},{"line_number":589,"context_line":"            t2 \u003d Timestamp(str((t1.raw + delta) * PRECISION))"},{"line_number":590,"context_line":"    elif not explicit:"},{"line_number":591,"context_line":"        t2 \u003d t1"},{"line_number":592,"context_line":"    if len(parts) \u003e 2:"}],"source_content_type":"text/x-python","patch_set":63,"id":"0f564244_8994a896","line":589,"in_reply_to":"63ec6d77_77700954","updated":"2026-02-27 14:53:55.000000000","message":"dropping the XXX from comments here to avoid suggestion of this being the site of any \"bug\"","commit_id":"bce3e42d3f31422218ab8b7a2130832870a3bf07"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"a73c2a394723d1721a5847fead9e3ed3c5a5d0ab","unresolved":true,"context_lines":[{"line_number":131,"context_line":"        return int(self.timestamp)"},{"line_number":132,"context_line":""},{"line_number":133,"context_line":"    def __bool__(self):"},{"line_number":134,"context_line":"        return self.internal !\u003d self.zero()"},{"line_number":135,"context_line":""},{"line_number":136,"context_line":"    @property"},{"line_number":137,"context_line":"    def normal(self):"}],"source_content_type":"text/x-python","patch_set":65,"id":"3b454775_1ae24330","line":134,"updated":"2026-03-02 06:09:12.000000000","message":"Isn\u0027t this now comparing a string with one of the Timestamp objects. Should this be something like:\n\n```\nreturn self.internal !\u003d self.zero().internal\n```\nOR\n```\nreturn self !\u003d self.zero()\n```\n\nTo make it cleaner?","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"4763c4c88cfe0788f22fc72702f4c8818aa47166","unresolved":true,"context_lines":[{"line_number":131,"context_line":"        return int(self.timestamp)"},{"line_number":132,"context_line":""},{"line_number":133,"context_line":"    def __bool__(self):"},{"line_number":134,"context_line":"        return self.internal !\u003d self.zero()"},{"line_number":135,"context_line":""},{"line_number":136,"context_line":"    @property"},{"line_number":137,"context_line":"    def normal(self):"}],"source_content_type":"text/x-python","patch_set":65,"id":"e50b3095_2e568f40","line":134,"in_reply_to":"3b454775_1ae24330","updated":"2026-03-02 16:19:00.000000000","message":"good catch. I think it might be best to revert this to how it was and have the Timestamp subclass override with\n\n```\nreturn super().__bool__() or bool(self.hex_part)\n```\n\nThe call to .zero() is a bit OTT (i.e. construct a whole other instance just to evaluate bool)","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"ba4b78e9d7aa3053bdc678aa75804d3a4713b0b6","unresolved":false,"context_lines":[{"line_number":131,"context_line":"        return int(self.timestamp)"},{"line_number":132,"context_line":""},{"line_number":133,"context_line":"    def __bool__(self):"},{"line_number":134,"context_line":"        return self.internal !\u003d self.zero()"},{"line_number":135,"context_line":""},{"line_number":136,"context_line":"    @property"},{"line_number":137,"context_line":"    def normal(self):"}],"source_content_type":"text/x-python","patch_set":65,"id":"5ac449b8_087b7891","line":134,"in_reply_to":"e50b3095_2e568f40","updated":"2026-03-11 05:01:52.000000000","message":"Done","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"a73c2a394723d1721a5847fead9e3ed3c5a5d0ab","unresolved":true,"context_lines":[{"line_number":379,"context_line":"        jitter_value \u003d 0x2000000000 + random.randint(0x000000000, 0xfffffffff)"},{"line_number":380,"context_line":"        # jitter is shifted to the left of the 6 offset digits"},{"line_number":381,"context_line":"        self.hex_part \u003d jitter_value \u003c\u003c (4 * NUM_OFFSET_DIGITS)"},{"line_number":382,"context_line":"        return float(timestamp)"},{"line_number":383,"context_line":""},{"line_number":384,"context_line":"    def _parse(self, timestamp_str, **kwargs):"},{"line_number":385,"context_line":"        float_str, hex_str \u003d timestamp_str.partition(\u0027_\u0027)[::2]"}],"source_content_type":"text/x-python","patch_set":65,"id":"233de41b_f9bb92b8","line":382,"updated":"2026-03-02 06:09:12.000000000","message":"This `_create` is called in from the parent constructor, but in essence sets the jitter. So if we don\u0027t use `.zero()` a `Timestamp(0.0)` now isn\u0027t falsey:\n\n```\n\nIn [1]: from swift.common.utils.timestamp import Timestamp\n\nIn [2]: z \u003d Timestamp.zero()\n\nIn [3]: bool(z)\nOut[3]: False\n\nIn [4]: t \u003d Timestamp(0.0)\n\nIn [5]: bool(t)\nOut[5]: True\n\n```\n\nI mean we should be using .zero, that\u0027s the idea, but if someone expects a Timestamp(0) to be falsey it wont.. might not be something we need to fix now. But probably is something unexpected.","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"4763c4c88cfe0788f22fc72702f4c8818aa47166","unresolved":true,"context_lines":[{"line_number":379,"context_line":"        jitter_value \u003d 0x2000000000 + random.randint(0x000000000, 0xfffffffff)"},{"line_number":380,"context_line":"        # jitter is shifted to the left of the 6 offset digits"},{"line_number":381,"context_line":"        self.hex_part \u003d jitter_value \u003c\u003c (4 * NUM_OFFSET_DIGITS)"},{"line_number":382,"context_line":"        return float(timestamp)"},{"line_number":383,"context_line":""},{"line_number":384,"context_line":"    def _parse(self, timestamp_str, **kwargs):"},{"line_number":385,"context_line":"        float_str, hex_str \u003d timestamp_str.partition(\u0027_\u0027)[::2]"}],"source_content_type":"text/x-python","patch_set":65,"id":"9757acfb_d66744b2","line":382,"in_reply_to":"233de41b_f9bb92b8","updated":"2026-03-02 16:19:00.000000000","message":"I\u0027ll add a comment to the docstring to point out that Timestamp(0.0) is not at zero.\n\nIn the ideal world the constructor would not accept a float, and ``Timestamp()`` would be the equivalent to ``Timestamp.now()``, but backwards compatibility etc etc.","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"ba4b78e9d7aa3053bdc678aa75804d3a4713b0b6","unresolved":false,"context_lines":[{"line_number":379,"context_line":"        jitter_value \u003d 0x2000000000 + random.randint(0x000000000, 0xfffffffff)"},{"line_number":380,"context_line":"        # jitter is shifted to the left of the 6 offset digits"},{"line_number":381,"context_line":"        self.hex_part \u003d jitter_value \u003c\u003c (4 * NUM_OFFSET_DIGITS)"},{"line_number":382,"context_line":"        return float(timestamp)"},{"line_number":383,"context_line":""},{"line_number":384,"context_line":"    def _parse(self, timestamp_str, **kwargs):"},{"line_number":385,"context_line":"        float_str, hex_str \u003d timestamp_str.partition(\u0027_\u0027)[::2]"}],"source_content_type":"text/x-python","patch_set":65,"id":"751ef9cf_9f421d15","line":382,"in_reply_to":"9757acfb_d66744b2","updated":"2026-03-11 05:01:52.000000000","message":"Done","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"a73c2a394723d1721a5847fead9e3ed3c5a5d0ab","unresolved":true,"context_lines":[{"line_number":418,"context_line":"            # we don\u0027t know how many hex digits have been allocated to offset"},{"line_number":419,"context_line":"            # in a future version of this class so it is not safe to modify it"},{"line_number":420,"context_line":"            raise ValueError(\u0027Cannot modify offset in an unrecognised hex \u0027"},{"line_number":421,"context_line":"                             \u0027part encoding\u0027)"},{"line_number":422,"context_line":""},{"line_number":423,"context_line":"        if value \u003e max_offset:"},{"line_number":424,"context_line":"            raise ValueError(\u0027offset must be less than or equal to %d\u0027"}],"source_content_type":"text/x-python","patch_set":65,"id":"cdadf913_c93bbfe3","line":421,"updated":"2026-03-02 06:09:12.000000000","message":"Nice love it","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"4763c4c88cfe0788f22fc72702f4c8818aa47166","unresolved":false,"context_lines":[{"line_number":418,"context_line":"            # we don\u0027t know how many hex digits have been allocated to offset"},{"line_number":419,"context_line":"            # in a future version of this class so it is not safe to modify it"},{"line_number":420,"context_line":"            raise ValueError(\u0027Cannot modify offset in an unrecognised hex \u0027"},{"line_number":421,"context_line":"                             \u0027part encoding\u0027)"},{"line_number":422,"context_line":""},{"line_number":423,"context_line":"        if value \u003e max_offset:"},{"line_number":424,"context_line":"            raise ValueError(\u0027offset must be less than or equal to %d\u0027"}],"source_content_type":"text/x-python","patch_set":65,"id":"7f1fe3d7_99958244","line":421,"in_reply_to":"cdadf913_c93bbfe3","updated":"2026-03-02 16:19:00.000000000","message":"Acknowledged","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"a73c2a394723d1721a5847fead9e3ed3c5a5d0ab","unresolved":true,"context_lines":[{"line_number":436,"context_line":"            # in a future version of this class so it is not safe to return it"},{"line_number":437,"context_line":"            raise AttributeError(\u0027Cannot access offset in an unrecognised \u0027"},{"line_number":438,"context_line":"                                 \u0027hex_part encoding\u0027)"},{"line_number":439,"context_line":"        return self._offset"},{"line_number":440,"context_line":""},{"line_number":441,"context_line":"    def increment_offset(self, value):"},{"line_number":442,"context_line":"        \"\"\""}],"source_content_type":"text/x-python","patch_set":65,"id":"7aff719a_75a3f689","line":439,"updated":"2026-03-02 06:09:12.000000000","message":"So this is just for backwards compatibility, we don\u0027t need an offset setter property. I guess no one out there should be using the `TS.offset \u003d some_offset` out there so maybe it\u0027s fine?","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"ba4b78e9d7aa3053bdc678aa75804d3a4713b0b6","unresolved":false,"context_lines":[{"line_number":436,"context_line":"            # in a future version of this class so it is not safe to return it"},{"line_number":437,"context_line":"            raise AttributeError(\u0027Cannot access offset in an unrecognised \u0027"},{"line_number":438,"context_line":"                                 \u0027hex_part encoding\u0027)"},{"line_number":439,"context_line":"        return self._offset"},{"line_number":440,"context_line":""},{"line_number":441,"context_line":"    def increment_offset(self, value):"},{"line_number":442,"context_line":"        \"\"\""}],"source_content_type":"text/x-python","patch_set":65,"id":"da921b90_fdc7218c","line":439,"in_reply_to":"32dbf98a_30de6d6e","updated":"2026-03-11 05:01:52.000000000","message":"Acknowledged","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"4763c4c88cfe0788f22fc72702f4c8818aa47166","unresolved":true,"context_lines":[{"line_number":436,"context_line":"            # in a future version of this class so it is not safe to return it"},{"line_number":437,"context_line":"            raise AttributeError(\u0027Cannot access offset in an unrecognised \u0027"},{"line_number":438,"context_line":"                                 \u0027hex_part encoding\u0027)"},{"line_number":439,"context_line":"        return self._offset"},{"line_number":440,"context_line":""},{"line_number":441,"context_line":"    def increment_offset(self, value):"},{"line_number":442,"context_line":"        \"\"\""}],"source_content_type":"text/x-python","patch_set":65,"id":"32dbf98a_30de6d6e","line":439,"in_reply_to":"7aff719a_75a3f689","updated":"2026-03-02 16:19:00.000000000","message":"Callers really shouldn\u0027t be setting offset to an absolute value (although there may be some perverse reason to deliberately want time to go backwards). It\u0027s a little unfortunate that existing constructor ``offset`` arg could be confused as having an absolute or relative meaning (it is treated as relative). \n\nThe new ``increment_offset`` is the way to go.","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"a73c2a394723d1721a5847fead9e3ed3c5a5d0ab","unresolved":false,"context_lines":[{"line_number":603,"context_line":"        delta \u003d signs[2] * int(parts[2], 16)"},{"line_number":604,"context_line":"        if delta:"},{"line_number":605,"context_line":"            # we lost the original hex_part of t2 when encoding, so we have no"},{"line_number":606,"context_line":"            # choice other than to force it to zero when decoding"},{"line_number":607,"context_line":"            t3 \u003d Timestamp(str((t2.raw + delta) * PRECISION))"},{"line_number":608,"context_line":"    elif not explicit:"},{"line_number":609,"context_line":"        t3 \u003d t2"}],"source_content_type":"text/x-python","patch_set":65,"id":"43012333_8e56f686","line":606,"updated":"2026-03-02 06:09:12.000000000","message":"I like that we have comments to explain that the hex_part is lost.","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"a73c2a394723d1721a5847fead9e3ed3c5a5d0ab","unresolved":true,"context_lines":[{"line_number":669,"context_line":"    return fmt % min(max(0, float(timestamp)), 9999999999.99999)"},{"line_number":670,"context_line":""},{"line_number":671,"context_line":""},{"line_number":672,"context_line":"def _request_type_allows_extended_timestamp(resource_type, request_method):"},{"line_number":673,"context_line":"    return (resource_type.lower() \u003d\u003d \u0027object\u0027"},{"line_number":674,"context_line":"            and request_method \u003d\u003d \u0027PUT\u0027)"},{"line_number":675,"context_line":""}],"source_content_type":"text/x-python","patch_set":65,"id":"9bc4d94a_ea5be76f","line":672,"range":{"start_line":672,"start_character":4,"end_line":672,"end_character":43},"updated":"2026-03-02 06:09:12.000000000","message":"Is this used anywhere, I can\u0027t see it used in this change at least. So is this dead code?","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"ba4b78e9d7aa3053bdc678aa75804d3a4713b0b6","unresolved":false,"context_lines":[{"line_number":669,"context_line":"    return fmt % min(max(0, float(timestamp)), 9999999999.99999)"},{"line_number":670,"context_line":""},{"line_number":671,"context_line":""},{"line_number":672,"context_line":"def _request_type_allows_extended_timestamp(resource_type, request_method):"},{"line_number":673,"context_line":"    return (resource_type.lower() \u003d\u003d \u0027object\u0027"},{"line_number":674,"context_line":"            and request_method \u003d\u003d \u0027PUT\u0027)"},{"line_number":675,"context_line":""}],"source_content_type":"text/x-python","patch_set":65,"id":"ddcd5cde_45ef62f2","line":672,"range":{"start_line":672,"start_character":4,"end_line":672,"end_character":43},"in_reply_to":"0a062a8e_03c1e1f5","updated":"2026-03-11 05:01:52.000000000","message":"Done","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"4763c4c88cfe0788f22fc72702f4c8818aa47166","unresolved":true,"context_lines":[{"line_number":669,"context_line":"    return fmt % min(max(0, float(timestamp)), 9999999999.99999)"},{"line_number":670,"context_line":""},{"line_number":671,"context_line":""},{"line_number":672,"context_line":"def _request_type_allows_extended_timestamp(resource_type, request_method):"},{"line_number":673,"context_line":"    return (resource_type.lower() \u003d\u003d \u0027object\u0027"},{"line_number":674,"context_line":"            and request_method \u003d\u003d \u0027PUT\u0027)"},{"line_number":675,"context_line":""}],"source_content_type":"text/x-python","patch_set":65,"id":"0a062a8e_03c1e1f5","line":672,"range":{"start_line":672,"start_character":4,"end_line":672,"end_character":43},"in_reply_to":"9bc4d94a_ea5be76f","updated":"2026-03-02 16:19:00.000000000","message":"good catch, not used","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"4763c4c88cfe0788f22fc72702f4c8818aa47166","unresolved":true,"context_lines":[{"line_number":687,"context_line":"    \"\"\""},{"line_number":688,"context_line":"    # note: always use Timestamp.now() to generate the timestamp, and then cast"},{"line_number":689,"context_line":"    # to a NormalTimestamp when necessary, so that tests can mock only"},{"line_number":690,"context_line":"    # Timestamp.now() to mock timestamps for *all request types*."},{"line_number":691,"context_line":"    if not resource_type:"},{"line_number":692,"context_line":"        return NormalTimestamp.now()"},{"line_number":693,"context_line":"    elif resource_type.lower() \u003d\u003d \u0027object\u0027 and request_method \u003d\u003d \u0027PUT\u0027:"}],"source_content_type":"text/x-python","patch_set":65,"id":"8e4ba81c_8205d074","line":690,"updated":"2026-03-02 16:19:00.000000000","message":"this comment is stale but I still wonder if this is a better way, in order to reduce future test churn as we migrate other request types to get a Timestamp with jitter.","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"a73c2a394723d1721a5847fead9e3ed3c5a5d0ab","unresolved":true,"context_lines":[{"line_number":689,"context_line":"    # to a NormalTimestamp when necessary, so that tests can mock only"},{"line_number":690,"context_line":"    # Timestamp.now() to mock timestamps for *all request types*."},{"line_number":691,"context_line":"    if not resource_type:"},{"line_number":692,"context_line":"        return NormalTimestamp.now()"},{"line_number":693,"context_line":"    elif resource_type.lower() \u003d\u003d \u0027object\u0027 and request_method \u003d\u003d \u0027PUT\u0027:"},{"line_number":694,"context_line":"        return Timestamp.now()"},{"line_number":695,"context_line":"    else:"}],"source_content_type":"text/x-python","patch_set":65,"id":"71acd872_b3ad494b","line":692,"updated":"2026-03-02 06:09:12.000000000","message":"This is a little redundent because it\u0027s also captured in the else, so we could:\n\n```\nif resource_type and resource_type.lower() \u003d\u003d \u0027object\u0027 and request_method \u003d\u003d \u0027PUT\u0027:\n    return Timestamp.now()\nelse:\n    return NormalTimestamp.now()\n```\n\nBut I feel this is just temp code which we\u0027ll keep coming back to, plus maybe the extra check or resource_type makes the line to long, so meh, not a blocker.","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"4763c4c88cfe0788f22fc72702f4c8818aa47166","unresolved":false,"context_lines":[{"line_number":689,"context_line":"    # to a NormalTimestamp when necessary, so that tests can mock only"},{"line_number":690,"context_line":"    # Timestamp.now() to mock timestamps for *all request types*."},{"line_number":691,"context_line":"    if not resource_type:"},{"line_number":692,"context_line":"        return NormalTimestamp.now()"},{"line_number":693,"context_line":"    elif resource_type.lower() \u003d\u003d \u0027object\u0027 and request_method \u003d\u003d \u0027PUT\u0027:"},{"line_number":694,"context_line":"        return Timestamp.now()"},{"line_number":695,"context_line":"    else:"}],"source_content_type":"text/x-python","patch_set":65,"id":"1b41cb9b_530cc643","line":692,"in_reply_to":"71acd872_b3ad494b","updated":"2026-03-02 16:19:00.000000000","message":"Acknowledged","commit_id":"4713725f37e090c9ea96a7f4dd2707cacb72b9f6"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"8ed91b04401831f893fc08c2386a4bd69c92c5c5","unresolved":true,"context_lines":[{"line_number":385,"context_line":"    def _create(self, timestamp, **kwargs):"},{"line_number":386,"context_line":"        jitter_value \u003d 0x2000000000 + random.randint(0x000000000, 0xfffffffff)"},{"line_number":387,"context_line":"        # jitter is shifted to the left of the 6 offset digits"},{"line_number":388,"context_line":"        self.hex_part \u003d jitter_value \u003c\u003c (4 * NUM_OFFSET_DIGITS)"},{"line_number":389,"context_line":"        return float(timestamp)"},{"line_number":390,"context_line":""},{"line_number":391,"context_line":"    def _parse(self, timestamp_str, **kwargs):"}],"source_content_type":"text/x-python","patch_set":73,"id":"fbb469fb_8f7c60c6","line":388,"updated":"2026-03-12 20:31:52.000000000","message":"I\u0027d like to explore ways we can make \"add extended immutable transaction hex part\" *conditional* beyond just the difference between `_create` and `_parse`\n\n```\ndef uuid4():\n    \"\"\"Generate a random UUID.\"\"\"\n    return UUID(bytes\u003dos.urandom(16), version\u003d4)\n```\n\nPerhaps eventually moving all `Timestamp(\u003cfloat\u003e)` creation to an explicit:\n\n`Timestamp(normal_float\u003d\u003cfloat\u003e, version\u003d1)` or `Timestamp(normal_float\u003d\u003cfloat\u003e, version\u003d2)`\n\n... and NEVER just `Timestamp(\u003cfloat\u003e)` since a single un-named argument to a `Timestamp.__init__` should *always* be the internal format of a `timestamp_str`","commit_id":"57c4bf8d7ad2d61e5d4ae92556cd88532e1e63f9"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"ba4b78e9d7aa3053bdc678aa75804d3a4713b0b6","unresolved":false,"context_lines":[{"line_number":495,"context_line":"        #  identical to the internal format. Furthermore, this format is only"},{"line_number":496,"context_line":"        #  used to encode object data timestamps (i.e. PUT x-timestamps) in the"},{"line_number":497,"context_line":"        #  encode_timestamps function. As such it has become redundant and"},{"line_number":498,"context_line":"        #  should be removed/deprecated."},{"line_number":499,"context_line":"        if self.hex_part or FORCE_INTERNAL:"},{"line_number":500,"context_line":"            return SHORT_FORMAT % (self.timestamp, self.hex_part)"},{"line_number":501,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":73,"id":"128d7adf_c5a262d5","line":498,"updated":"2026-03-11 05:01:52.000000000","message":"So the TODO here is basically to remove/deprecate short later. We wonder if these are good candidates for adding bug/feature requests for (for newbies) and/or downstream tickets. But I guess we can also just `git grep TODO` to find them in the code base 😊\n\nSo meh","commit_id":"57c4bf8d7ad2d61e5d4ae92556cd88532e1e63f9"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"8ed91b04401831f893fc08c2386a4bd69c92c5c5","unresolved":true,"context_lines":[{"line_number":727,"context_line":"        # e.g. a container GET for shard ranges."},{"line_number":728,"context_line":"        return Timestamp(value)"},{"line_number":729,"context_line":"    else:"},{"line_number":730,"context_line":"        return NormalTimestamp(value)"}],"source_content_type":"text/x-python","patch_set":73,"id":"669e806a_a3daa394","line":730,"updated":"2026-03-12 20:31:52.000000000","message":"I wonder if we could log a warning when this is lossy?\n\ni.e. \"Hey we just got a object POST with jitter\"\n\nActually, should we even be converting to NormalTimestamp here?  Would it be better to always aim for `Timestamp(value)` and on `if ts.hexpart: return ts.normalize()` or something?","commit_id":"57c4bf8d7ad2d61e5d4ae92556cd88532e1e63f9"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"80e8ee62934af1a3b9fb8091c25f17d8f7919131","unresolved":true,"context_lines":[{"line_number":562,"context_line":""},{"line_number":563,"context_line":"    :returns: an instance of Timestamp."},{"line_number":564,"context_line":"    \"\"\""},{"line_number":565,"context_line":"    return Timestamp.now(version\u003d2)"},{"line_number":566,"context_line":""},{"line_number":567,"context_line":""},{"line_number":568,"context_line":"def encode_timestamps(t1, t2\u003dNone, t3\u003dNone, explicit\u003dFalse):"}],"source_content_type":"text/x-python","patch_set":76,"id":"cc80c71a_f38ad4f3","line":565,"updated":"2026-03-18 11:05:48.000000000","message":"I\u0027m tempted to add:\n\n```\ndef timestamp0():\n    return NormalTimestamp.now()\n```\n\nbecause with hindsight I think having \"factory functions\" that hide the particular class from the caller is better than having to call class methods which require the caller to know about a specific class.","commit_id":"d09e4b5579a3db80f8bfee8486b9b9d7b2b00da7"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"0a29f83144f10fbf94689cec6bce41179dab077b","unresolved":true,"context_lines":[{"line_number":321,"context_line":"    operations will not create a timestamp with an offset."},{"line_number":322,"context_line":""},{"line_number":323,"context_line":"    Modern version 2 Timestamps encode the offset in the final 6 digits of the"},{"line_number":324,"context_line":"    hex part. The first 10 digits of the hex part encode a randomly generated"},{"line_number":325,"context_line":"    \"jitter\" component that differentiates timestamps within the same"},{"line_number":326,"context_line":"    deca-microsecond. For example, a version 2 Timestamp with offset 1 has the"},{"line_number":327,"context_line":"    following internalized form:"}],"source_content_type":"text/x-python","patch_set":77,"id":"5321d7cd_02156127","line":324,"updated":"2026-03-23 05:47:16.000000000","message":"``The first 10 digits of the hex part``, except the first digit?","commit_id":"1d6813c656835bc81acba9504c428c5c41e988c8"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"0a29f83144f10fbf94689cec6bce41179dab077b","unresolved":true,"context_lines":[{"line_number":533,"context_line":"        else:"},{"line_number":534,"context_line":"            return self.normal"},{"line_number":535,"context_line":""},{"line_number":536,"context_line":"    def __invert__(self):"},{"line_number":537,"context_line":"        inv_raw \u003d (MAX_RAW_TIME - self.raw) * PRECISION"},{"line_number":538,"context_line":"        if not self.hex_part:"},{"line_number":539,"context_line":"            # legacy Timestamps did not allow inversion of hex_parts"}],"source_content_type":"text/x-python","patch_set":77,"id":"3d600406_3c5df36f","line":536,"updated":"2026-03-23 05:47:16.000000000","message":"@alistairncoles@gmail.com discovered an issue in the upgrade path, and I confirmed that in my vsaio: during upgrade, old proxy server won\u0027t see versioned objects in the listing and object download with version_id would fail.\n\nthis is with v2 timestamp\n```\nvagrant@saio:~$ swift list --versions vertest --json\n[\n  {\n    \"bytes\": 15,\n    \"content_type\": \"application/octet-stream\",\n    \"hash\": \"ef82fae291e747a3fe45e2825161a7aa\",\n    \"is_latest\": true,\n    \"last_modified\": \"2026-03-21T00:17:42.459320\",\n    \"name\": \"vobj\",\n    \"version_id\": \"1774052262.45932_210c79c30f000000\"\n  },\n  {\n    \"bytes\": 15,\n    \"content_type\": \"application/octet-stream\",\n    \"hash\": \"076c56a17d54b82268e3418e778279aa\",\n    \"is_latest\": false,\n    \"last_modified\": \"2026-03-21T00:17:35.006260\",\n    \"name\": \"vobj\",\n    \"version_id\": \"1774052255.00626_247a4f81c8000000\"\n  }\n]\nvagrant@saio:~$ swift download vertest vobj -o -\nversion-2-data\nvagrant@saio:~$ swift download vertest vobj --version-id \"1774052262.45932_210c79c30f000000\" -o -\nversion-2-data\nvagrant@saio:~$ swift download vertest vobj --version-id \"1774052255.00626_247a4f81c8000000\" -o -\nversion-1-data\n...\nvagrant@saio:~$ ls -la /srv/node1/sdb1/objects/341/d46/5559c04d3e84035e9ccddd53c6fb4d46/1774052262.45932_210c79c30f000001.data\n-rwxr-xr-x 1 vagrant vagrant 0 Mar 21 00:17 /srv/node1/sdb1/objects/341/d46/5559c04d3e84035e9ccddd53c6fb4d46/1774052262.45932_210c79c30f000001.data\n```\n\nswitched to current master and reinstallswift\n```\nvagrant@saio:~$ swift list --versions vertest --json\n[]\nvagrant@saio:~$ swift download vertest vobj -o -\nversion-2-data\nvagrant@saio:~$ swift download vertest vobj --version-id \"1774052262.45932_210c79c30f000000\" -o -\nError downloading object \u0027vertest/vobj\u0027: Object GET failed: http://saio:8080/v1/AUTH_test/vertest/vobj?version-id\u003d1774052262.45932_210c79c30f000000 500 Internal Error   b\u0027An error occurred\u0027 (txn: txae16ff3ed31049fb9b8f1-0069bde8e7)\nFailed Transaction ID: txae16ff3ed31049fb9b8f1-0069bde8e7\n```\n\nFor the version id, I think it doesn\u0027t need hex(jitter and offset) in principle, as the current design works on master branch. With the jitters, when timestamp collisions happened, the upload with larger jitter will remove previous stored object with same timestamp but smaller jitter, so eventually the version id would just need the raw timestamp to identify an version of an object. See: https://review.opendev.org/c/openstack/swift/+/981704","commit_id":"1d6813c656835bc81acba9504c428c5c41e988c8"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"855b30fe5389d5775fed1582d8b68d82505541d2","unresolved":true,"context_lines":[{"line_number":533,"context_line":"        else:"},{"line_number":534,"context_line":"            return self.normal"},{"line_number":535,"context_line":""},{"line_number":536,"context_line":"    def __invert__(self):"},{"line_number":537,"context_line":"        inv_raw \u003d (MAX_RAW_TIME - self.raw) * PRECISION"},{"line_number":538,"context_line":"        if not self.hex_part:"},{"line_number":539,"context_line":"            # legacy Timestamps did not allow inversion of hex_parts"}],"source_content_type":"text/x-python","patch_set":77,"id":"62c20a6e_adf92d11","line":536,"in_reply_to":"3d600406_3c5df36f","updated":"2026-03-23 15:45:24.000000000","message":"think it through again, I think version id does need hex (jitter and offset), because the version object which is stored in version container and use version_id as suffix of its name may ran into timestamp collision too. We need jitter to prevent timestamp collision for both version object and symlink object.\n\nSeem like we have to backport hexpart-aware __invert__ back to the master, upgrade the cluster, before buidling timestamp jitter v2 into new release.","commit_id":"1d6813c656835bc81acba9504c428c5c41e988c8"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"5a14c7cf8eff1115d705db71e665e6eea9873f35","unresolved":false,"context_lines":[{"line_number":533,"context_line":"        else:"},{"line_number":534,"context_line":"            return self.normal"},{"line_number":535,"context_line":""},{"line_number":536,"context_line":"    def __invert__(self):"},{"line_number":537,"context_line":"        inv_raw \u003d (MAX_RAW_TIME - self.raw) * PRECISION"},{"line_number":538,"context_line":"        if not self.hex_part:"},{"line_number":539,"context_line":"            # legacy Timestamps did not allow inversion of hex_parts"}],"source_content_type":"text/x-python","patch_set":77,"id":"cd6264ac_6ce79440","line":536,"in_reply_to":"62c20a6e_adf92d11","updated":"2026-03-24 00:54:11.000000000","message":"https://review.opendev.org/c/openstack/swift/+/981825 is going to fix this!","commit_id":"1d6813c656835bc81acba9504c428c5c41e988c8"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"06f0d04f715a4e8f1ba0923471fb6ee27d9d031e","unresolved":true,"context_lines":[{"line_number":413,"context_line":"            a float this value initialises the offset, otherwise this value"},{"line_number":414,"context_line":"            will be added to any existing offset of the parsed ``timestamp``."},{"line_number":415,"context_line":"        :param delta: deca-microsecond difference to be added to the"},{"line_number":416,"context_line":"            ``timestamp`` value; should be an integer value."},{"line_number":417,"context_line":"        :param check_bounds: if True (default) then a ValueError will be raised"},{"line_number":418,"context_line":"            if the given timestamp is less than 0 or greater than the maximum"},{"line_number":419,"context_line":"            time that can be represented by this class."}],"source_content_type":"text/x-python","patch_set":87,"id":"4aea54d8_816c3d62","line":416,"updated":"2026-05-12 23:16:57.000000000","message":"is *this* allowed in parse!?\n\n```\n\u003e\u003e\u003e Timestamp(\u00270\u0027, delta\u003d2)\n0000000000.00002_0000000000000000\n```\n\n^ that\u0027s mutation of the immutable part?  I assume it\u0027s useful when you use it correctly?\n\n```\n\u003e\u003e\u003e Timestamp(\u00270\u0027, offset\u003d2)\n0000000000.00000_0000000000000002\n\u003e\u003e\u003e Timestamp(\u00270\u0027, delta\u003d2)\n0000000000.00002_0000000000000000\n\u003e\u003e\u003e Timestamp(\u00270\u0027, version\u003d2)\nTraceback (most recent call last):\n  File \"\u003cstdin\u003e\", line 1, in \u003cmodule\u003e\n  File \"/vagrant/swift/swift/common/utils/timestamp.py\", line 426, in __init__\n    super().__init__(timestamp, delta\u003ddelta, check_bounds\u003dcheck_bounds,\n  File \"/vagrant/swift/swift/common/utils/timestamp.py\", line 100, in __init__\n    float_timestamp \u003d self._parse(timestamp_str, **kwargs)\n  File \"/vagrant/swift/swift/common/utils/timestamp.py\", line 445, in _parse\n    raise TypeError(\u0027version not allowed when parsing timestamp\u0027)\nTypeError: version not allowed when parsing timestamp\n```\n\n\"parse will TypeError version\u003d kwarg\" \u003d\u003e holier than the pope!","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"06f0d04f715a4e8f1ba0923471fb6ee27d9d031e","unresolved":true,"context_lines":[{"line_number":442,"context_line":""},{"line_number":443,"context_line":"    def _parse(self, timestamp_str, version\u003dNone, **kwargs):"},{"line_number":444,"context_line":"        if version is not None:"},{"line_number":445,"context_line":"            raise TypeError(\u0027version not allowed when parsing timestamp\u0027)"},{"line_number":446,"context_line":"        float_str, hex_str \u003d timestamp_str.partition(\u0027_\u0027)[::2]"},{"line_number":447,"context_line":"        if \u0027_\u0027 in hex_str:"},{"line_number":448,"context_line":"            raise ValueError(\u0027invalid literal for int() with base 16: \u0027"}],"source_content_type":"text/x-python","patch_set":87,"id":"2d1bdce9_67d63846","line":445,"updated":"2026-05-12 23:16:57.000000000","message":"I think this is too strict; it showed up when I was auditing a test that should have only v2 timestamps but one such v2 timestamp was created like:\n\n```\nnew_v2_ts \u003d Timestamp(v2_ts.internal, offset\u003dN)\n```\n\n*is* that a v2 timestamp?  It depends on vf v2_ts.internal was v1 or v2?  Test would be more obvious with `Timestamp(v2_ts.internal, offset\u003dN, version\u003d2)` even if the `version\u003d2` was just a noop/sanity/explicit-is-better-than-implicit\n\ni.e. we allow parse to modify offset - maybe it should allow version manipulation too?\n\n```\n\u003e\u003e\u003e n \u003d NormalTimestamp.now()\n\u003e\u003e\u003e n.normal\n\u00271778619633.32970\u0027\n\u003e\u003e\u003e Timestamp(n.normal, offset\u003d3)\n1778619633.32970_0000000000000003\n\u003e\u003e\u003e Timestamp(n.normal, version\u003d2, offset\u003d3)\nTraceback (most recent call last):\n  File \"\u003cstdin\u003e\", line 1, in \u003cmodule\u003e\n  File \"/vagrant/swift/swift/common/utils/timestamp.py\", line 426, in __init__\n    super().__init__(timestamp, delta\u003ddelta, check_bounds\u003dcheck_bounds,\n  File \"/vagrant/swift/swift/common/utils/timestamp.py\", line 100, in __init__\n    float_timestamp \u003d self._parse(timestamp_str, **kwargs)\n  File \"/vagrant/swift/swift/common/utils/timestamp.py\", line 445, in _parse\n    raise TypeError(\u0027version not allowed when parsing timestamp\u0027)\nTypeError: version not allowed when parsing timestamp\n```\n\n*or* maybe it needs to be more mindful of \"upgrading\" v1\u003d\u003ev2 timestamps; but should still allow me to write tests like:\n\n```\n\u003e\u003e\u003e Timestamp(\u00271778619602.00842_0000000000000003\u0027, version\u003d1, offset\u003d1)\n1778619602.00842_0000000000000004\n\u003e\u003e\u003e Timestamp(\u00271778619854.32991_29f3244e38000000\u0027, version\u003d2, offset\u003d1)\n1778619854.32991_29f3244e38000001\n```\n\n... just like I can write tests like:\n\n```\n\u003e\u003e\u003e Timestamp(\u00271778619602.00842_0000000000000003\u0027, offset\u003d1)\n1778619602.00842_0000000000000004\n\u003e\u003e\u003e Timestamp(\u00271778619854.32991_29f3244e38000000\u0027, offset\u003d1)\n1778619854.32991_29f3244e38000001\n```\n\nTBD if `Timestamp(\u00271778619633.32970\u0027, version\u003d2)` should blow up with \"version mis-match\" or do exactly what you might hope/expect.","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"06f0d04f715a4e8f1ba0923471fb6ee27d9d031e","unresolved":true,"context_lines":[{"line_number":456,"context_line":"    def _offset(self):"},{"line_number":457,"context_line":"        if self.hex_part \u003c 0x2000000000000000:"},{"line_number":458,"context_line":"            # Previous versions of this class allowed offset to grow beyond the"},{"line_number":459,"context_line":"            # current limit of 0xffffff so make some allowance for larger"},{"line_number":460,"context_line":"            # values."},{"line_number":461,"context_line":"            return self.hex_part"},{"line_number":462,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":87,"id":"abbcdd2d_d5ed3265","line":459,"updated":"2026-05-12 23:16:57.000000000","message":"they allowed values right on up to `ffffffffffffffff` - I feel like \"make some allowance\" is a kind of weaseling to get out of feeling bad for changing the v1 offset limit but not *too* bad because `0xfffffffffffffff` is still a LOT bigger than 16M?\n\nBut I think if you\u0027ve gone beyond an offset of 16M you\u0027re already off the rails and we don\u0027t really need to be super coy about it; and certainly not act like it\u0027s reasonable or that this class knows how to handle that \"correctly\"\n\nRather instead of `return self.hex_part` we might say:\n\n```\nif t.version \u003d\u003d 1 and t.hex_part !\u003d t.offset:\n   raise RuntimeError(\u0027you have found the golden timestamp!\u0027)\n```\n\n^ that might suck, b/c it shows us very clearly where our assumptions were wrong - but it might suck less than \"the things you\u0027re sure of that just ain\u0027t so\"\n\nheh, maybe we could *start* with:\n\n```\nv1_max_offset \u003d MAX_OFFSET\n# uncomment this we screwed up but we can still have the first character!\n# v1_max_offset \u003d 0xfffffffffffffff\n```","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"06f0d04f715a4e8f1ba0923471fb6ee27d9d031e","unresolved":true,"context_lines":[{"line_number":462,"context_line":"        else:"},{"line_number":463,"context_line":"            # The upper hex digits are used for jitter so offset is constrained"},{"line_number":464,"context_line":"            # to the rightmost six digits."},{"line_number":465,"context_line":"            return self.hex_part \u0026 0xffffff"},{"line_number":466,"context_line":""},{"line_number":467,"context_line":"    @_offset.setter"},{"line_number":468,"context_line":"    def _offset(self, value):"}],"source_content_type":"text/x-python","patch_set":87,"id":"5b015443_4ad9c79d","line":465,"updated":"2026-05-12 23:16:57.000000000","message":"possibly this should blow up with `self.hex_part \u003c 0x3000000000000000`","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f16dbc44ebf4c0abd242b5c03d18bac3d5016aaa","unresolved":true,"context_lines":[{"line_number":462,"context_line":"        else:"},{"line_number":463,"context_line":"            # The upper hex digits are used for jitter so offset is constrained"},{"line_number":464,"context_line":"            # to the rightmost six digits."},{"line_number":465,"context_line":"            return self.hex_part \u0026 0xffffff"},{"line_number":466,"context_line":""},{"line_number":467,"context_line":"    @_offset.setter"},{"line_number":468,"context_line":"    def _offset(self, value):"}],"source_content_type":"text/x-python","patch_set":87,"id":"068480c5_93ece804","line":465,"in_reply_to":"5b015443_4ad9c79d","updated":"2026-05-14 14:00:23.000000000","message":"Assuming you mean \"blow up with self.hex_part \u003e\u003d 0x3000000000000000\"  ;-) ...\nthere\u0027s an annoying reason it mustn\u0027t which is to do with the ``___invert__`` method needing to construct a Timestamp that has hex_part \u003e\u003d 0x3000000000000000, and the constructor accesses ``_offset`` via ``increment_offset()``.\n\nHowever, I can see a workaround: see https://review.opendev.org/c/openstack/swift/+/988632/1/swift/common/utils/timestamp.py \n\nI have realised that this patch removes the previously supported ``Timestamp.offset \u003d \u003cmy value\u003e`` from the interface, which we should probably leave for backwards compat. So I\u0027m going to work around the invert issue and combine ``_offset`` with ``offset``. Simpler 🤞\n\nFWIW I do wonder if we\u0027d be better off removing the invert method from Timestamp and just have object_versioning do its own manipulation. I\u0027m not sure how to reason about:\n\n```\ninv_ts \u003d ~Timestamp(123, version\u003d2)\ninv_ts.version is None\nTrue\ninv_ts.increment_offset(1)\nTraceback (most recent call last):\n  File \"/Applications/PyCharm.app/Contents/plugins/python-ce/helpers/pydev/pydevconsole.py\", line 364, in runcode\n    coro \u003d func()\n  File \"\u003cinput\u003e\", line 1, in \u003cmodule\u003e\n  File \"/Users/acoles/0dev/openstack/swift/swift/common/utils/timestamp.py\", line 533, in increment_offset\n    self.offset +\u003d value\n  File \"/Users/acoles/0dev/openstack/swift/swift/common/utils/timestamp.py\", line 484, in offset\n    raise AttributeError(\u0027Cannot access offset in an unrecognised \u0027\nAttributeError: Cannot access offset in an unrecognised hex_part encoding\n```","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"06f0d04f715a4e8f1ba0923471fb6ee27d9d031e","unresolved":true,"context_lines":[{"line_number":471,"context_line":"        if value \u003c 0:"},{"line_number":472,"context_line":"            raise ValueError(\u0027offset must be non-negative\u0027)"},{"line_number":473,"context_line":""},{"line_number":474,"context_line":"        if self.hex_part \u003c 0x2000000000000000:"},{"line_number":475,"context_line":"            max_offset \u003d 0x1fffffffffffffff"},{"line_number":476,"context_line":"        elif self.hex_part \u003c 0x3000000000000000:"},{"line_number":477,"context_line":"            max_offset \u003d 0xffffff"}],"source_content_type":"text/x-python","patch_set":87,"id":"043f8131_35f80d5a","line":474,"updated":"2026-05-12 23:16:57.000000000","message":"this is not a great way to spell `if self.version \u003e\u003d 2`\n\nwe should make `version` a property even if it\u0027s \"just another view over `self.hex_part`\" in order to help us write idiomatic and obvious code/tests","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f16dbc44ebf4c0abd242b5c03d18bac3d5016aaa","unresolved":true,"context_lines":[{"line_number":471,"context_line":"        if value \u003c 0:"},{"line_number":472,"context_line":"            raise ValueError(\u0027offset must be non-negative\u0027)"},{"line_number":473,"context_line":""},{"line_number":474,"context_line":"        if self.hex_part \u003c 0x2000000000000000:"},{"line_number":475,"context_line":"            max_offset \u003d 0x1fffffffffffffff"},{"line_number":476,"context_line":"        elif self.hex_part \u003c 0x3000000000000000:"},{"line_number":477,"context_line":"            max_offset \u003d 0xffffff"}],"source_content_type":"text/x-python","patch_set":87,"id":"0309c9cc_28fc751a","line":474,"in_reply_to":"043f8131_35f80d5a","updated":"2026-05-14 14:00:23.000000000","message":"+1 pretty sure I had a ``version`` property in an earlier patchset but then I sensed a lot of confusion/pushback about the notion of version as an attribute vs class-per-version, and so I removed ``.version``\n\nSee https://review.opendev.org/c/openstack/swift/+/988631","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"06f0d04f715a4e8f1ba0923471fb6ee27d9d031e","unresolved":true,"context_lines":[{"line_number":472,"context_line":"            raise ValueError(\u0027offset must be non-negative\u0027)"},{"line_number":473,"context_line":""},{"line_number":474,"context_line":"        if self.hex_part \u003c 0x2000000000000000:"},{"line_number":475,"context_line":"            max_offset \u003d 0x1fffffffffffffff"},{"line_number":476,"context_line":"        elif self.hex_part \u003c 0x3000000000000000:"},{"line_number":477,"context_line":"            max_offset \u003d 0xffffff"},{"line_number":478,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":87,"id":"af4e564a_d3aec12a","line":475,"updated":"2026-05-12 23:16:57.000000000","message":"I *really* don\u0027t like this; a *key* part of our observation that \"hex_part is huge!\" and the conclusion \"we can steal some bytes for jitter!\" rests on the idea that no timestamp anywhere has ever gotten even *close* to `offset \u003e\u003d 0xffffff`\n\nWe *could* JUST release a change the capped v1timestamp offset to `0xffffff` - if that breaks anything we\u0027re already lost and need to rethink the whole of the strategy with v2timestamp using existing hexpart with room left over for \"enough\" offset.","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f16dbc44ebf4c0abd242b5c03d18bac3d5016aaa","unresolved":true,"context_lines":[{"line_number":472,"context_line":"            raise ValueError(\u0027offset must be non-negative\u0027)"},{"line_number":473,"context_line":""},{"line_number":474,"context_line":"        if self.hex_part \u003c 0x2000000000000000:"},{"line_number":475,"context_line":"            max_offset \u003d 0x1fffffffffffffff"},{"line_number":476,"context_line":"        elif self.hex_part \u003c 0x3000000000000000:"},{"line_number":477,"context_line":"            max_offset \u003d 0xffffff"},{"line_number":478,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":87,"id":"f206d78a_51368446","line":475,"in_reply_to":"af4e564a_d3aec12a","updated":"2026-05-14 14:00:23.000000000","message":"\"we can steal some bytes for jitter!\" matured into \"we can reserve a slab of the number space for v2 timestamps\". This patch is not unreservedly stealing hex-digits for jitter: the number space above 0x2fffffffffffffff remains unused; the number space below 0x2000000000000000 remains available for v1 timestamps. It is only v2 timestamps whose offsets are *necessarily* restricted to 0xffffff.\n\nI\u0027m mostly making this point to be sure that we have a common understanding of what this patch is doing w.r.t. the number space i.e. we\u0027re reserving number ranges, not digits.\n\nFrom a correctness perspective there is no requirement to restrict v1 offsets to the same limit as v2. From a maintenance/understanding perspective I see the choice of cognitive burden as:\n\n1) v1 offset limit is different to v2 because v2 mixes in jitter\n2) v1 offset limit is arbitrarily the same as v2 and there\u0027s a range of numbers between v1 and v2 that are unused.\n\nIf (2) proves most popular, I\u0027d caution against any expectation that future versions are also required to also have max offset of ``0xffffff``.","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"06f0d04f715a4e8f1ba0923471fb6ee27d9d031e","unresolved":true,"context_lines":[{"line_number":484,"context_line":"        if value \u003e max_offset:"},{"line_number":485,"context_line":"            raise ValueError(\u0027offset must be less than or equal to %d\u0027"},{"line_number":486,"context_line":"                             % max_offset)"},{"line_number":487,"context_line":"        self.hex_part \u003d (self.hex_part \u0026 ~max_offset) + value"},{"line_number":488,"context_line":""},{"line_number":489,"context_line":"    @property"},{"line_number":490,"context_line":"    def offset(self):"}],"source_content_type":"text/x-python","patch_set":87,"id":"04a98d94_bf205aed","line":487,"updated":"2026-05-12 23:16:57.000000000","message":"are we opening ourselves up to basically the same problems as `self.offset \u003d 1.0` by making this public?\n\n```\n\u003e\u003e\u003e t.hex_part \u003d 1.0\n\u003e\u003e\u003e t.internal\nTraceback (most recent call last):\n  File \"\u003cstdin\u003e\", line 1, in \u003cmodule\u003e\n  File \"/vagrant/swift/swift/common/utils/timestamp.py\", line 561, in internal\n    return self._internal_format(\n  File \"/vagrant/swift/swift/common/utils/timestamp.py\", line 555, in _internal_format\n    return INTERNAL_FORMAT % (timestamp, hex_part)\nTypeError: %x format: an integer is required, not float\n```","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f16dbc44ebf4c0abd242b5c03d18bac3d5016aaa","unresolved":true,"context_lines":[{"line_number":484,"context_line":"        if value \u003e max_offset:"},{"line_number":485,"context_line":"            raise ValueError(\u0027offset must be less than or equal to %d\u0027"},{"line_number":486,"context_line":"                             % max_offset)"},{"line_number":487,"context_line":"        self.hex_part \u003d (self.hex_part \u0026 ~max_offset) + value"},{"line_number":488,"context_line":""},{"line_number":489,"context_line":"    @property"},{"line_number":490,"context_line":"    def offset(self):"}],"source_content_type":"text/x-python","patch_set":87,"id":"ba297557_b225f877","line":487,"in_reply_to":"04a98d94_bf205aed","updated":"2026-05-14 14:00:23.000000000","message":"agree, we should tighten this up. hex_part is annoyingly only set from outside the Timestamp class in one place (ssync_receiver) - we could perhaps provide a constructor variant for that use case and make the class variable private?? and check it is an int in the constructor.","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"06f0d04f715a4e8f1ba0923471fb6ee27d9d031e","unresolved":true,"context_lines":[{"line_number":492,"context_line":"        This is here for backwards compatibility only. Callers should not"},{"line_number":493,"context_line":"        attach any meaning to the absolute value of offset."},{"line_number":494,"context_line":"        \"\"\""},{"line_number":495,"context_line":"        if self.hex_part \u003e\u003d 0x3000000000000000:"},{"line_number":496,"context_line":"            # we don\u0027t know how many hex digits have been allocated to offset"},{"line_number":497,"context_line":"            # in a future version of this class so it is not safe to return it"},{"line_number":498,"context_line":"            raise AttributeError(\u0027Cannot access offset in an unrecognised \u0027"}],"source_content_type":"text/x-python","patch_set":87,"id":"81942261_e935f043","line":495,"updated":"2026-05-12 23:16:57.000000000","message":"oic, we don\u0027t have to blowup reading `_offset` when `hex_part \u003e v2 cause we blow up in  `offset` when `hex_part \u003e v2`\n\nMaybe now that we have `_validate_offset` we don\u0027t really need `_offset`-the-property anymore?  because `self._offset \u003d ...` has exactly the semantics of the old `self.offset \u003d 3`?","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f16dbc44ebf4c0abd242b5c03d18bac3d5016aaa","unresolved":true,"context_lines":[{"line_number":492,"context_line":"        This is here for backwards compatibility only. Callers should not"},{"line_number":493,"context_line":"        attach any meaning to the absolute value of offset."},{"line_number":494,"context_line":"        \"\"\""},{"line_number":495,"context_line":"        if self.hex_part \u003e\u003d 0x3000000000000000:"},{"line_number":496,"context_line":"            # we don\u0027t know how many hex digits have been allocated to offset"},{"line_number":497,"context_line":"            # in a future version of this class so it is not safe to return it"},{"line_number":498,"context_line":"            raise AttributeError(\u0027Cannot access offset in an unrecognised \u0027"}],"source_content_type":"text/x-python","patch_set":87,"id":"57333701_81329cf1","line":495,"in_reply_to":"81942261_e935f043","updated":"2026-05-14 14:00:23.000000000","message":"See https://review.opendev.org/c/openstack/swift/+/988630","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"}],"swift/container/backend.py":[{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"1f4a7e54a87065a56da9b18340e9b3e442211075","unresolved":true,"context_lines":[{"line_number":871,"context_line":"            info \u003d conn.execute(\u0027\u0027\u0027"},{"line_number":872,"context_line":"                SELECT put_timestamp, delete_timestamp"},{"line_number":873,"context_line":"                FROM container_stat\u0027\u0027\u0027).fetchone()"},{"line_number":874,"context_line":"        return (Timestamp(now - reclaim_age, jitter\u003d0) \u003e"},{"line_number":875,"context_line":"                Timestamp(info[\u0027delete_timestamp\u0027]) \u003e"},{"line_number":876,"context_line":"                Timestamp(info[\u0027put_timestamp\u0027]))"},{"line_number":877,"context_line":""}],"source_content_type":"text/x-python","patch_set":8,"id":"d159c82c_7320bdc5","line":874,"range":{"start_line":874,"start_character":16,"end_line":874,"end_character":54},"updated":"2025-11-26 23:22:04.000000000","message":"Maybe\n```\nTimestamp(now - reclaim_age).jitterless()\n```","commit_id":"ac59feec5c51e8eee765bc4adb88b73469bfeecd"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"efc12f94fab26cecc7d58b4c39dbf44093af418c","unresolved":false,"context_lines":[{"line_number":871,"context_line":"            info \u003d conn.execute(\u0027\u0027\u0027"},{"line_number":872,"context_line":"                SELECT put_timestamp, delete_timestamp"},{"line_number":873,"context_line":"                FROM container_stat\u0027\u0027\u0027).fetchone()"},{"line_number":874,"context_line":"        return (Timestamp(now - reclaim_age, jitter\u003d0) \u003e"},{"line_number":875,"context_line":"                Timestamp(info[\u0027delete_timestamp\u0027]) \u003e"},{"line_number":876,"context_line":"                Timestamp(info[\u0027put_timestamp\u0027]))"},{"line_number":877,"context_line":""}],"source_content_type":"text/x-python","patch_set":8,"id":"3fe2e4c3_a09758ed","line":874,"range":{"start_line":874,"start_character":16,"end_line":874,"end_character":54},"in_reply_to":"d159c82c_7320bdc5","updated":"2026-01-07 12:18:14.000000000","message":"Done","commit_id":"ac59feec5c51e8eee765bc4adb88b73469bfeecd"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"05728c198cbcabd1828d53cdca03bc4926f46ab1","unresolved":true,"context_lines":[{"line_number":860,"context_line":"            info \u003d conn.execute(\u0027\u0027\u0027"},{"line_number":861,"context_line":"                SELECT put_timestamp, delete_timestamp"},{"line_number":862,"context_line":"                FROM container_stat\u0027\u0027\u0027).fetchone()"},{"line_number":863,"context_line":"        return (SimpleTimestamp(now - reclaim_age) \u003e"},{"line_number":864,"context_line":"                Timestamp(info[\u0027delete_timestamp\u0027]) \u003e"},{"line_number":865,"context_line":"                Timestamp(info[\u0027put_timestamp\u0027]))"},{"line_number":866,"context_line":""}],"source_content_type":"text/x-python","patch_set":49,"id":"873b9d7a_5e3fa773","line":863,"updated":"2026-02-05 14:56:12.000000000","message":"this could go in the parent patch. I\u0027m not sure we even need to create a timestamp object here","commit_id":"c45563740c4d84db48381e8038d660879b4c30dc"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"5130e4b40bda460ee7b138d1dd93026264d9d16d","unresolved":false,"context_lines":[{"line_number":860,"context_line":"            info \u003d conn.execute(\u0027\u0027\u0027"},{"line_number":861,"context_line":"                SELECT put_timestamp, delete_timestamp"},{"line_number":862,"context_line":"                FROM container_stat\u0027\u0027\u0027).fetchone()"},{"line_number":863,"context_line":"        return (SimpleTimestamp(now - reclaim_age) \u003e"},{"line_number":864,"context_line":"                Timestamp(info[\u0027delete_timestamp\u0027]) \u003e"},{"line_number":865,"context_line":"                Timestamp(info[\u0027put_timestamp\u0027]))"},{"line_number":866,"context_line":""}],"source_content_type":"text/x-python","patch_set":49,"id":"78c107eb_d37c8581","line":863,"in_reply_to":"873b9d7a_5e3fa773","updated":"2026-02-12 19:09:25.000000000","message":"Done","commit_id":"c45563740c4d84db48381e8038d660879b4c30dc"}],"swift/obj/ssync_receiver.py":[{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"5a14c7cf8eff1115d705db71e665e6eea9873f35","unresolved":true,"context_lines":[{"line_number":64,"context_line":"            k, v \u003d item.split(\u0027:\u0027)"},{"line_number":65,"context_line":"            if k \u003d\u003d \u0027m\u0027:"},{"line_number":66,"context_line":"                v, _, o \u003d v.partition(\u0027__\u0027)"},{"line_number":67,"context_line":"                # ignore ts_data offset when calculating ts_meta"},{"line_number":68,"context_line":"                result[\u0027ts_meta\u0027] \u003d ts_delta_hex_to_timestamp(ts_data, v, o)"},{"line_number":69,"context_line":"            elif k \u003d\u003d \u0027t\u0027:"},{"line_number":70,"context_line":"                v, _, o \u003d v.partition(\u0027__\u0027)"}],"source_content_type":"text/x-python","patch_set":77,"id":"29354c07_b31fd373","line":67,"updated":"2026-03-24 00:54:11.000000000","message":"comment in ``ts_delta_hex_to_timestamp`` already says this, this line can be removed","commit_id":"1d6813c656835bc81acba9504c428c5c41e988c8"}],"swift/obj/ssync_sender.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"171a6913b42490b877051cf840efbf28b055aec3","unresolved":true,"context_lines":[{"line_number":37,"context_line":"    \"\"\""},{"line_number":38,"context_line":"    msg \u003d (\u0027%s %s\u0027"},{"line_number":39,"context_line":"           % (urllib.parse.quote(object_hash),"},{"line_number":40,"context_line":"              urllib.parse.quote(ts_data.internal)))"},{"line_number":41,"context_line":"    extra_parts \u003d []"},{"line_number":42,"context_line":"    if ts_meta and ts_meta !\u003d ts_data:"},{"line_number":43,"context_line":"        delta \u003d ts_meta.raw - ts_data.raw"}],"source_content_type":"text/x-python","patch_set":52,"id":"9ba8ca10_11499cfe","line":40,"updated":"2026-02-13 22:27:10.000000000","message":"old:\n\n```\nobject-reconstructor-6030: ssync_sender sending: b\u0027d3c241e5b8b12c7ed98b251a388b749a 1771001347.78488 m:c57a4\\r\\n\u0027\n```\n\nnew:\n\n```\nssync_sender sending: b\u0027d3c241e5b8b12c7ed98b251a388b749a 1771007850.18624_20ed36c5bc000000 m:408d2c5\\r\\n\u0027\n```\n\nok, I think I was able to get into an interesting scenario with out-of-sync ctype_ts:\n\n```\nFeb 13 22:13:21 saio object-reconstructor-6030: ssync_sender sending: b\u00272acab65d25859ee7684be59b3198abf8 1771016187.97394_22e71e43e4000000 m:1ac74d13,t:94870b1\\r\\n\u0027\nFeb 13 22:13:21 saio object-reconstructor-6030: ssync_sender sending: b\u0027:MISSING_CHECK: END\\r\\n\u0027\nFeb 13 22:13:21 saio object-server-6020: STDERR: (48676) accepted (\u0027127.0.0.1\u0027, 59694)\nFeb 13 22:13:21 saio object-reconstructor-6020: ssync_sender sending: b\u0027:MISSING_CHECK: START\\r\\n\u0027\nFeb 13 22:13:21 saio object-reconstructor-6020: ssync_sender sending: b\u00272acab65d25859ee7684be59b3198abf8 1771016187.97394_22e71e43e4000000 m:1ac74d13\\r\\n\u0027\nFeb 13 22:13:21 saio object-reconstructor-6020: ssync_sender sending: b\u0027:MISSING_CHECK: END\\r\\n\u0027\nFeb 13 22:13:21 saio object-server-6030: recv_line: b\u0027:MISSING_CHECK: START\\r\\n\u0027\nFeb 13 22:13:21 saio object-server-6030: recv_line: b\u00272acab65d25859ee7684be59b3198abf8 1771016187.97394_22e71e43e4000000 m:1ac74d13\\r\\n\u0027\nFeb 13 22:13:21 saio object-reconstructor-6030: ssync_sender sending: b\u0027:UPDATES: START\\r\\n\u0027\nFeb 13 22:13:21 saio object-reconstructor-6030: ssync_sender sending: b\u0027POST /AUTH_test/test_ctype_meta/ctype_test.txt\\r\\nContent-Type: application/octet-stream\\r\\nContent-Type-Timestamp: 1771017745.39779\\r\\nX-Delete-At: 1771021580\\r\\nX-Timestamp: \n1771020680.66405\\r\\n\\r\\n\u0027\nFeb 13 22:13:21 saio object-reconstructor-6030: ssync_sender sending: b\u0027:UPDATES: END\\r\\n\u0027\nFeb 13 22:13:21 saio object-server-6030: recv_line: b\u0027:MISSING_CHECK: END\\r\\n\u0027\nFeb 13 22:13:21 saio object-reconstructor-6020: ssync_sender sending: b\u0027:UPDATES: START\\r\\n\u0027\nFeb 13 22:13:21 saio object-reconstructor-6020: ssync_sender sending: b\u0027:UPDATES: END\\r\\n\u0027\nFeb 13 22:13:21 saio object-server-6030: recv_line: b\u0027:UPDATES: START\\r\\n\u0027\nFeb 13 22:13:21 saio object-server-6030: recv_line: b\u0027:UPDATES: END\\r\\n\u0027\nFeb 13 22:13:21 saio object-reconstructor-6020: ssync completed ok: dev: sdb2, part: 171, policy: 1, num suffixes: 1, available: 1, sent: 0, deletable: 1\nFeb 13 22:13:21 saio object-reconstructor-6020: ssync_sender sending: b\u0027\u0027\nFeb 13 22:13:21 saio object-server-6030: STDERR: 127.0.0.1 - - [13/Feb/2026 22:13:21] \"SSYNC /sdb3/171 HTTP/1.1\" 200 276 0.016971\nFeb 13 22:13:21 saio object-server-6030: STDERR: (48040) accepted (\u0027127.0.0.1\u0027, 48782)\nFeb 13 22:13:21 saio object-server-6020: 127.0.0.1 - - [13/Feb/2026:22:13:21 +0000] \"SSYNC /sdb6/171\" 200 - \"-\" \"-\" \"-\" 0.0006 \"-\" 48676 1\nFeb 13 22:13:21 saio object-reconstructor-6010: ssync_sender sending: b\u0027:MISSING_CHECK: START\\r\\n\u0027\nFeb 13 22:13:21 saio object-reconstructor-6010: ssync_sender sending: b\u00272acab65d25859ee7684be59b3198abf8 1771016187.97394_22e71e43e4000000 m:1ac74d13,t:94870b1\\r\\n\u0027\nFeb 13 22:13:21 saio object-reconstructor-6010: ssync_sender sending: b\u0027:MISSING_CHECK: END\\r\\n\u0027\n```","commit_id":"0e79af4aa0471ef114dbc56669537c7aef39f404"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"281a9af18fde7ff1be1bf191c2b503fae0f527f5","unresolved":false,"context_lines":[{"line_number":37,"context_line":"    \"\"\""},{"line_number":38,"context_line":"    msg \u003d (\u0027%s %s\u0027"},{"line_number":39,"context_line":"           % (urllib.parse.quote(object_hash),"},{"line_number":40,"context_line":"              urllib.parse.quote(ts_data.internal)))"},{"line_number":41,"context_line":"    extra_parts \u003d []"},{"line_number":42,"context_line":"    if ts_meta and ts_meta !\u003d ts_data:"},{"line_number":43,"context_line":"        delta \u003d ts_meta.raw - ts_data.raw"}],"source_content_type":"text/x-python","patch_set":52,"id":"ac9e12a1_d9218ab8","line":40,"in_reply_to":"9ba8ca10_11499cfe","updated":"2026-02-27 14:53:55.000000000","message":"Acknowledged","commit_id":"0e79af4aa0471ef114dbc56669537c7aef39f404"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"171a6913b42490b877051cf840efbf28b055aec3","unresolved":true,"context_lines":[{"line_number":42,"context_line":"    if ts_meta and ts_meta !\u003d ts_data:"},{"line_number":43,"context_line":"        delta \u003d ts_meta.raw - ts_data.raw"},{"line_number":44,"context_line":"        extra_parts.append(\u0027m:%x\u0027 % delta)"},{"line_number":45,"context_line":"        if ts_meta.hex_part:"},{"line_number":46,"context_line":"            extra_parts[-1] +\u003d \u0027__%x\u0027 % ts_meta.hex_part"},{"line_number":47,"context_line":"        if ts_ctype and ts_ctype !\u003d ts_data:"},{"line_number":48,"context_line":"            delta \u003d ts_ctype.raw - ts_data.raw"}],"source_content_type":"text/x-python","patch_set":53,"id":"6e4a370b_3d2fe4e6","line":45,"updated":"2026-02-13 22:27:10.000000000","message":"ok, the s/offset/hex_part is *just* a mechanical API change","commit_id":"1964da5195e0d4fe96e89e98d418d1ded010a9ad"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"281a9af18fde7ff1be1bf191c2b503fae0f527f5","unresolved":false,"context_lines":[{"line_number":42,"context_line":"    if ts_meta and ts_meta !\u003d ts_data:"},{"line_number":43,"context_line":"        delta \u003d ts_meta.raw - ts_data.raw"},{"line_number":44,"context_line":"        extra_parts.append(\u0027m:%x\u0027 % delta)"},{"line_number":45,"context_line":"        if ts_meta.hex_part:"},{"line_number":46,"context_line":"            extra_parts[-1] +\u003d \u0027__%x\u0027 % ts_meta.hex_part"},{"line_number":47,"context_line":"        if ts_ctype and ts_ctype !\u003d ts_data:"},{"line_number":48,"context_line":"            delta \u003d ts_ctype.raw - ts_data.raw"}],"source_content_type":"text/x-python","patch_set":53,"id":"58f98673_02699f05","line":45,"in_reply_to":"4a81dfe3_0e4dc51a","updated":"2026-02-27 14:53:55.000000000","message":"Acknowledged","commit_id":"1964da5195e0d4fe96e89e98d418d1ded010a9ad"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"55472369dc2bdcd573b4ce2f213ef9d0f15823e3","unresolved":true,"context_lines":[{"line_number":42,"context_line":"    if ts_meta and ts_meta !\u003d ts_data:"},{"line_number":43,"context_line":"        delta \u003d ts_meta.raw - ts_data.raw"},{"line_number":44,"context_line":"        extra_parts.append(\u0027m:%x\u0027 % delta)"},{"line_number":45,"context_line":"        if ts_meta.hex_part:"},{"line_number":46,"context_line":"            extra_parts[-1] +\u003d \u0027__%x\u0027 % ts_meta.hex_part"},{"line_number":47,"context_line":"        if ts_ctype and ts_ctype !\u003d ts_data:"},{"line_number":48,"context_line":"            delta \u003d ts_ctype.raw - ts_data.raw"}],"source_content_type":"text/x-python","patch_set":53,"id":"4a81dfe3_0e4dc51a","line":45,"in_reply_to":"6e4a370b_3d2fe4e6","updated":"2026-02-16 17:58:13.000000000","message":"Yes. The \"bug\" wasn\u0027t a real bug on master, just a bug introduced by changing the meaning of offset such that offset no longer give the entire hex part.\n\nMy belief is that it is FINE for either end of ssync to be old code: old code reads the entire hex part using ``.offset``, new code reads the entire hex part using ``.hex_part``.","commit_id":"1964da5195e0d4fe96e89e98d418d1ded010a9ad"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"171a6913b42490b877051cf840efbf28b055aec3","unresolved":true,"context_lines":[{"line_number":45,"context_line":"        if ts_meta.hex_part:"},{"line_number":46,"context_line":"            extra_parts[-1] +\u003d \u0027__%x\u0027 % ts_meta.hex_part"},{"line_number":47,"context_line":"        if ts_ctype and ts_ctype !\u003d ts_data:"},{"line_number":48,"context_line":"            delta \u003d ts_ctype.raw - ts_data.raw"},{"line_number":49,"context_line":"            extra_parts.append(\u0027t:%x\u0027 % delta)"},{"line_number":50,"context_line":"            if ts_ctype.hex_part:"},{"line_number":51,"context_line":"                extra_parts[-1] +\u003d \u0027__%x\u0027 % ts_ctype.hex_part"}],"source_content_type":"text/x-python","patch_set":53,"id":"1735f793_99196053","line":48,"updated":"2026-02-13 22:27:10.000000000","message":"this has to be what we should be worried about - WTF is \".raw\" going to do with jitter or does that not matter?","commit_id":"1964da5195e0d4fe96e89e98d418d1ded010a9ad"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"55472369dc2bdcd573b4ce2f213ef9d0f15823e3","unresolved":true,"context_lines":[{"line_number":45,"context_line":"        if ts_meta.hex_part:"},{"line_number":46,"context_line":"            extra_parts[-1] +\u003d \u0027__%x\u0027 % ts_meta.hex_part"},{"line_number":47,"context_line":"        if ts_ctype and ts_ctype !\u003d ts_data:"},{"line_number":48,"context_line":"            delta \u003d ts_ctype.raw - ts_data.raw"},{"line_number":49,"context_line":"            extra_parts.append(\u0027t:%x\u0027 % delta)"},{"line_number":50,"context_line":"            if ts_ctype.hex_part:"},{"line_number":51,"context_line":"                extra_parts[-1] +\u003d \u0027__%x\u0027 % ts_ctype.hex_part"}],"source_content_type":"text/x-python","patch_set":53,"id":"c2c1a7d3_6f84415b","line":48,"in_reply_to":"1735f793_99196053","updated":"2026-02-16 17:58:13.000000000","message":"it doesn\u0027t matter because the hex part is encoded separately from the raw/float with the ``__`` underscore.\n\nIt\u0027s weird, I\u0027m not defending it, I don\u0027t recall the history, but it works, even when later we add hex parts to meta timestamps.\n\n\nOff-topic for this patch:\n\nWhat doesn\u0027t work is the *diskfile* on disk filename encoding on master. That loses hex parts on meta filenames. As a result, depending on the order in which meta files with/without latest content-type are received on an object server, different object servers could end up with different file names for the *same* data/metadata. A client HEAD would get the same response from either object server, but replication (which ias based on file names) thinks the replicas are out of sysnc so would keep sync\u0027ing the object.\n\nThat was the concern I alluded to in the now-deleted commit message paragraph.","commit_id":"1964da5195e0d4fe96e89e98d418d1ded010a9ad"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"171a6913b42490b877051cf840efbf28b055aec3","unresolved":true,"context_lines":[{"line_number":49,"context_line":"            extra_parts.append(\u0027t:%x\u0027 % delta)"},{"line_number":50,"context_line":"            if ts_ctype.hex_part:"},{"line_number":51,"context_line":"                extra_parts[-1] +\u003d \u0027__%x\u0027 % ts_ctype.hex_part"},{"line_number":52,"context_line":"    if \u0027durable\u0027 in kwargs and kwargs[\u0027durable\u0027] is False:"},{"line_number":53,"context_line":"        # only send durable in the less common case that it is False"},{"line_number":54,"context_line":"        extra_parts.append(\u0027durable:%s\u0027 % kwargs[\u0027durable\u0027])"},{"line_number":55,"context_line":"    if extra_parts:"}],"source_content_type":"text/x-python","patch_set":53,"id":"5301e21a_38b2bd70","line":52,"updated":"2026-02-13 22:27:10.000000000","message":"so for my tests the .meta updates don\u0027t get a .hex_part - the worst I saw was something like:\n\n```\nFeb 13 22:13:21 saio object-reconstructor-6030: ssync_sender sending: b\u00272acab65d25859ee7684be59b3198abf8 1771016187.97394_22e71e43e4000000 m:1ac74d13,t:94870b1\\r\\n\u0027\n```\n\nwhich seems like just the difference between the raw part of the t0 put and the t1 and t2 post","commit_id":"1964da5195e0d4fe96e89e98d418d1ded010a9ad"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"55472369dc2bdcd573b4ce2f213ef9d0f15823e3","unresolved":true,"context_lines":[{"line_number":49,"context_line":"            extra_parts.append(\u0027t:%x\u0027 % delta)"},{"line_number":50,"context_line":"            if ts_ctype.hex_part:"},{"line_number":51,"context_line":"                extra_parts[-1] +\u003d \u0027__%x\u0027 % ts_ctype.hex_part"},{"line_number":52,"context_line":"    if \u0027durable\u0027 in kwargs and kwargs[\u0027durable\u0027] is False:"},{"line_number":53,"context_line":"        # only send durable in the less common case that it is False"},{"line_number":54,"context_line":"        extra_parts.append(\u0027durable:%s\u0027 % kwargs[\u0027durable\u0027])"},{"line_number":55,"context_line":"    if extra_parts:"}],"source_content_type":"text/x-python","patch_set":53,"id":"93083417_f198cfcf","line":52,"in_reply_to":"5301e21a_38b2bd70","updated":"2026-02-16 17:58:13.000000000","message":"That looks like I\u0027d expect: ts_data has a hex part, ts_meta and ts_ctype don\u0027t and so they are encoded as deltas from the ts_data float.\n\nI guess the idea of the encoding was to save bytes on the wire??? IDK, maybe I should be blaming myself. The current version of myself would just send three internal format timestamps! But again, ssync is ok I think, even when we add hex parts to the meta files. The encoding is just weird.","commit_id":"1964da5195e0d4fe96e89e98d418d1ded010a9ad"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"281a9af18fde7ff1be1bf191c2b503fae0f527f5","unresolved":false,"context_lines":[{"line_number":49,"context_line":"            extra_parts.append(\u0027t:%x\u0027 % delta)"},{"line_number":50,"context_line":"            if ts_ctype.hex_part:"},{"line_number":51,"context_line":"                extra_parts[-1] +\u003d \u0027__%x\u0027 % ts_ctype.hex_part"},{"line_number":52,"context_line":"    if \u0027durable\u0027 in kwargs and kwargs[\u0027durable\u0027] is False:"},{"line_number":53,"context_line":"        # only send durable in the less common case that it is False"},{"line_number":54,"context_line":"        extra_parts.append(\u0027durable:%s\u0027 % kwargs[\u0027durable\u0027])"},{"line_number":55,"context_line":"    if extra_parts:"}],"source_content_type":"text/x-python","patch_set":53,"id":"c423e5a2_85b68a3d","line":52,"in_reply_to":"93083417_f198cfcf","updated":"2026-02-27 14:53:55.000000000","message":"Acknowledged","commit_id":"1964da5195e0d4fe96e89e98d418d1ded010a9ad"}],"test/unit/__init__.py":[{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"b8f2ca46ae6e9e9e0fdc0499b506354281ae1910","unresolved":true,"context_lines":[{"line_number":1111,"context_line":""},{"line_number":1112,"context_line":"    Note: the float part of each yielded Timestamp will increase by 1 but the"},{"line_number":1113,"context_line":"    hex part will have random jitter."},{"line_number":1114,"context_line":"    \"\"\""},{"line_number":1115,"context_line":"    return iter(Timestamp(t)"},{"line_number":1116,"context_line":"                for t in itertools.count(int(time.time()) + offset))"},{"line_number":1117,"context_line":""}],"source_content_type":"text/x-python","patch_set":3,"id":"2163cac2_6d68f200","line":1114,"updated":"2025-11-24 06:22:02.000000000","message":"Do we need to have the ability to \"lock in\" a extra, or give a list of extras (plus itertools.cycle) so we can have have control of the jitter?","commit_id":"5d6eb8e12465b140461dc45af612c78c82a384d3"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"30771312418d73ed1b430d0447fa434e20073bd5","unresolved":true,"context_lines":[{"line_number":1111,"context_line":""},{"line_number":1112,"context_line":"    Note: the float part of each yielded Timestamp will increase by 1 but the"},{"line_number":1113,"context_line":"    hex part will have random jitter."},{"line_number":1114,"context_line":"    \"\"\""},{"line_number":1115,"context_line":"    return iter(Timestamp(t)"},{"line_number":1116,"context_line":"                for t in itertools.count(int(time.time()) + offset))"},{"line_number":1117,"context_line":""}],"source_content_type":"text/x-python","patch_set":3,"id":"a484ce9f_4128db24","line":1114,"in_reply_to":"2163cac2_6d68f200","updated":"2026-02-03 15:47:54.000000000","message":"I\u0027ve not come across a need for that yet\n\nBut we may need some smarter way to yield timestamps that are for object PUTs (with jitter) vs other request types (without jitter)","commit_id":"5d6eb8e12465b140461dc45af612c78c82a384d3"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"e3f331fce7da982ef54ee283e8389e2c4dfad87e","unresolved":false,"context_lines":[{"line_number":1111,"context_line":""},{"line_number":1112,"context_line":"    Note: the float part of each yielded Timestamp will increase by 1 but the"},{"line_number":1113,"context_line":"    hex part will have random jitter."},{"line_number":1114,"context_line":"    \"\"\""},{"line_number":1115,"context_line":"    return iter(Timestamp(t)"},{"line_number":1116,"context_line":"                for t in itertools.count(int(time.time()) + offset))"},{"line_number":1117,"context_line":""}],"source_content_type":"text/x-python","patch_set":3,"id":"604bceb9_671b9df8","line":1114,"in_reply_to":"a484ce9f_4128db24","updated":"2026-02-18 17:45:54.000000000","message":"Done","commit_id":"5d6eb8e12465b140461dc45af612c78c82a384d3"}],"test/unit/common/test_utils.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"73e45c7b21efdd335ae50dab6cbe616315b63b43","unresolved":true,"context_lines":[{"line_number":346,"context_line":"        self.assertTrue(success)"},{"line_number":347,"context_line":""},{"line_number":348,"context_line":"    @mock_timestamp_randint(0xa)"},{"line_number":349,"context_line":"    def test_normalize_timestamp(self):"},{"line_number":350,"context_line":"        # Test swift.common.utils.normalize_timestamp"},{"line_number":351,"context_line":"        self.assertEqual(utils.normalize_timestamp(\u00271253327593.48174\u0027),"},{"line_number":352,"context_line":"                         \"1253327593.48174\")"}],"source_content_type":"text/x-python","patch_set":31,"id":"e0c0b111_0b1cdbee","line":349,"updated":"2025-12-24 16:46:06.000000000","message":"these tests ought to move to test_timestamp","commit_id":"7d4ed41e472e34cc79240b28a1a3314808d403d1"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"efc12f94fab26cecc7d58b4c39dbf44093af418c","unresolved":false,"context_lines":[{"line_number":346,"context_line":"        self.assertTrue(success)"},{"line_number":347,"context_line":""},{"line_number":348,"context_line":"    @mock_timestamp_randint(0xa)"},{"line_number":349,"context_line":"    def test_normalize_timestamp(self):"},{"line_number":350,"context_line":"        # Test swift.common.utils.normalize_timestamp"},{"line_number":351,"context_line":"        self.assertEqual(utils.normalize_timestamp(\u00271253327593.48174\u0027),"},{"line_number":352,"context_line":"                         \"1253327593.48174\")"}],"source_content_type":"text/x-python","patch_set":31,"id":"1420a5e7_b5e0649d","line":349,"in_reply_to":"e0c0b111_0b1cdbee","updated":"2026-01-07 12:18:14.000000000","message":"Done","commit_id":"7d4ed41e472e34cc79240b28a1a3314808d403d1"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"efc12f94fab26cecc7d58b4c39dbf44093af418c","unresolved":true,"context_lines":[{"line_number":5797,"context_line":"        self.assertEqual(\u0027a\u0027, parsed.account)"},{"line_number":5798,"context_line":"        self.assertEqual(\u0027root-has-some-hyphens\u0027, parsed.root_container)"},{"line_number":5799,"context_line":"        self.assertEqual(\u0027hash\u0027, parsed.parent_container_hash)"},{"line_number":5800,"context_line":"        self.assertEqual(utils.Timestamp(\u00271234\u0027), parsed.timestamp)"},{"line_number":5801,"context_line":"        self.assertEqual(99, parsed.index)"},{"line_number":5802,"context_line":""},{"line_number":5803,"context_line":"    def test_realistic_shard_range_names(self):"}],"source_content_type":"text/x-python","patch_set":38,"id":"7e0eef0d_6a615b2d","line":5800,"updated":"2026-01-07 12:18:14.000000000","message":"unnecessary change","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f16dbc44ebf4c0abd242b5c03d18bac3d5016aaa","unresolved":false,"context_lines":[{"line_number":5797,"context_line":"        self.assertEqual(\u0027a\u0027, parsed.account)"},{"line_number":5798,"context_line":"        self.assertEqual(\u0027root-has-some-hyphens\u0027, parsed.root_container)"},{"line_number":5799,"context_line":"        self.assertEqual(\u0027hash\u0027, parsed.parent_container_hash)"},{"line_number":5800,"context_line":"        self.assertEqual(utils.Timestamp(\u00271234\u0027), parsed.timestamp)"},{"line_number":5801,"context_line":"        self.assertEqual(99, parsed.index)"},{"line_number":5802,"context_line":""},{"line_number":5803,"context_line":"    def test_realistic_shard_range_names(self):"}],"source_content_type":"text/x-python","patch_set":38,"id":"3a552ba5_1166d96d","line":5800,"in_reply_to":"7e0eef0d_6a615b2d","updated":"2026-05-14 14:00:23.000000000","message":"Done","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"}],"test/unit/common/utils/test_timestamp.py":[{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"b8f2ca46ae6e9e9e0fdc0499b506354281ae1910","unresolved":true,"context_lines":[{"line_number":28,"context_line":"def mock_randint(value):"},{"line_number":29,"context_line":"    with mock.patch(\u0027swift.common.utils.timestamp.random.randint\u0027,"},{"line_number":30,"context_line":"                    return_value\u003dvalue):"},{"line_number":31,"context_line":"        yield"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":""},{"line_number":34,"context_line":"class TestTimestamp(unittest.TestCase):"}],"source_content_type":"text/x-python","patch_set":3,"id":"95da129f_788581f3","line":31,"updated":"2025-11-24 06:22:02.000000000","message":"Because we use timestamps everywhere, we might need to have something more availalbe for all the tests.\n\nI kind of cheated, and allowed you to turn off the proxy feature with a `proxy_number \u003d 0` for testing.. But this version, which is better in my opinion, is built into the timestamp.\n\nso we either need to be able to activate version 1, and slowly migrate tests. so have a way of locking in to some expected offsets to help with tests?","commit_id":"5d6eb8e12465b140461dc45af612c78c82a384d3"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"1f4a7e54a87065a56da9b18340e9b3e442211075","unresolved":false,"context_lines":[{"line_number":28,"context_line":"def mock_randint(value):"},{"line_number":29,"context_line":"    with mock.patch(\u0027swift.common.utils.timestamp.random.randint\u0027,"},{"line_number":30,"context_line":"                    return_value\u003dvalue):"},{"line_number":31,"context_line":"        yield"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":""},{"line_number":34,"context_line":"class TestTimestamp(unittest.TestCase):"}],"source_content_type":"text/x-python","patch_set":3,"id":"9bf73e19_de95fc5b","line":31,"in_reply_to":"95da129f_788581f3","updated":"2025-11-26 23:22:04.000000000","message":"Acknowledged","commit_id":"5d6eb8e12465b140461dc45af612c78c82a384d3"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"b501820341b9e1d27250f21113e189a5ba7eb88c","unresolved":true,"context_lines":[{"line_number":218,"context_line":"            self.assertGreaterEqual(hex_part, \u0027200000000000fade\u0027)"},{"line_number":219,"context_line":"            self.assertLess(hex_part, \u0027300000000000fade\u0027)"},{"line_number":220,"context_line":"            actual.append(hex_part)"},{"line_number":221,"context_line":"        # if this fails then the same jitter was chosen more than once :("},{"line_number":222,"context_line":"        self.assertEqual(1000, len(set(actual)))"},{"line_number":223,"context_line":""},{"line_number":224,"context_line":"    def test_init_from_timestamp(self):"}],"source_content_type":"text/x-python","patch_set":63,"id":"1e44ff28_65996841","line":221,"updated":"2026-02-27 00:14:29.000000000","message":"With 9 hex digits (over 68 billion possibilities), you only need about 314,000 random picks before you have a 50% chance of a collision.\nThis is much less than 68 billion—just as in the birthday paradox, collisions happen much sooner than you might expect!\n\n```\nvagrant@saio:~$ while true; do time python3 -c \u0027from swift.common.utils.timestamp import Timestamp; from unittest import mock; import time;^Mwith mock.patch(\"time.time\", return_value\u003dtime.time()): print(len({Timestamp.now().internal for i in range(300000)}))\u0027; done\n300000\n\nreal\t0m3.444s\nuser\t0m3.070s\nsys\t0m0.207s\n300000\n\nreal\t0m3.339s\nuser\t0m2.962s\nsys\t0m0.195s\n299998\n\nreal\t0m3.322s\nuser\t0m2.972s\nsys\t0m0.203s\n299999\n\nreal\t0m3.318s\nuser\t0m2.937s\nsys\t0m0.230s\n\n```\n\nN.B. if you don\u0027t artificially pin the clock the behavior on this branch is reliably perfect, where as master obviously sucked:\n\n```\nvagrant@saio:~$ python3 -c \u0027from swift.common.utils.timestamp import Timestamp; print(len({Timestamp.now().internal for i in range(300000)}))\u0027\n300000\nvagrant@saio:~$ python3 -c \u0027from swift.common.utils.timestamp import Timestamp; print(len({Timestamp.now().internal for i in range(300000)}))\u0027\n53939\n```\n\n... so even comparing worst case apples (jitter w/ mock\u0027d) to idealized oranges (master just using time) we\u0027re talking order of magnitude improvement on something that was already in the \"one in a billion\" sort of range.\n\nIf you want push the theoretical limits you have to admin master is aggregiously flawed:\n\n```\nvagrant@saio:~$ python3 -c \u0027from swift.common.utils.timestamp import Timestamp; from unittest import mock; import time;^Mwith mock.patch(\"time.time\", return_value\u003dtime.time()): print(len({Timestamp.now().internal for i in range(300000)}))\u0027\n1\n```","commit_id":"bce3e42d3f31422218ab8b7a2130832870a3bf07"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"281a9af18fde7ff1be1bf191c2b503fae0f527f5","unresolved":false,"context_lines":[{"line_number":218,"context_line":"            self.assertGreaterEqual(hex_part, \u0027200000000000fade\u0027)"},{"line_number":219,"context_line":"            self.assertLess(hex_part, \u0027300000000000fade\u0027)"},{"line_number":220,"context_line":"            actual.append(hex_part)"},{"line_number":221,"context_line":"        # if this fails then the same jitter was chosen more than once :("},{"line_number":222,"context_line":"        self.assertEqual(1000, len(set(actual)))"},{"line_number":223,"context_line":""},{"line_number":224,"context_line":"    def test_init_from_timestamp(self):"}],"source_content_type":"text/x-python","patch_set":63,"id":"d456bd7c_b547be8c","line":221,"in_reply_to":"1e44ff28_65996841","updated":"2026-02-27 14:53:55.000000000","message":"Acknowledged","commit_id":"bce3e42d3f31422218ab8b7a2130832870a3bf07"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"281a9af18fde7ff1be1bf191c2b503fae0f527f5","unresolved":true,"context_lines":[{"line_number":1418,"context_line":"        do_test(self.ts, self.ts_no_jitter)"},{"line_number":1419,"context_line":""},{"line_number":1420,"context_line":""},{"line_number":1421,"context_line":"@mock_timestamp_randint(0xa)"},{"line_number":1422,"context_line":"class TestTimestampEncoding(unittest.TestCase):"},{"line_number":1423,"context_line":""},{"line_number":1424,"context_line":"    def setUp(self):"}],"source_content_type":"text/x-python","patch_set":63,"id":"f7117217_18e0e0ff","line":1421,"updated":"2026-02-27 14:53:55.000000000","message":"yikes! this causes the TestCase to not be found by pytest!!!","commit_id":"bce3e42d3f31422218ab8b7a2130832870a3bf07"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"4763c4c88cfe0788f22fc72702f4c8818aa47166","unresolved":false,"context_lines":[{"line_number":1418,"context_line":"        do_test(self.ts, self.ts_no_jitter)"},{"line_number":1419,"context_line":""},{"line_number":1420,"context_line":""},{"line_number":1421,"context_line":"@mock_timestamp_randint(0xa)"},{"line_number":1422,"context_line":"class TestTimestampEncoding(unittest.TestCase):"},{"line_number":1423,"context_line":""},{"line_number":1424,"context_line":"    def setUp(self):"}],"source_content_type":"text/x-python","patch_set":63,"id":"ea14fbcc_28fa06c9","line":1421,"in_reply_to":"f7117217_18e0e0ff","updated":"2026-03-02 16:19:00.000000000","message":"Done","commit_id":"bce3e42d3f31422218ab8b7a2130832870a3bf07"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"06f0d04f715a4e8f1ba0923471fb6ee27d9d031e","unresolved":true,"context_lines":[{"line_number":198,"context_line":"                         str(cm.exception))"},{"line_number":199,"context_line":"        with self.assertRaises(TypeError) as cm:"},{"line_number":200,"context_line":"            ts_orig \u003d timestamp.Timestamp(1234567890.12345, version\u003d2)"},{"line_number":201,"context_line":"            timestamp.Timestamp(ts_orig, version\u003d2)"},{"line_number":202,"context_line":"        self.assertEqual(\u0027version not allowed when parsing timestamp\u0027,"},{"line_number":203,"context_line":"                         str(cm.exception))"},{"line_number":204,"context_line":""}],"source_content_type":"text/x-python","patch_set":87,"id":"da7d855f_3e0a81c8","line":201,"updated":"2026-05-12 23:16:57.000000000","message":"rather that feeling like an error, I find `version\u003d2` sort of gives me a warm fuzzy feeling that `ts_orig` WAS a v2-timestamp\n\nCompare this to the uuid module:\n\n```\n\u003e\u003e\u003e u4 \u003d uuid.uuid4()\n\u003e\u003e\u003e u4\nUUID(\u00279cd2f2a6-4291-4834-83bb-ecb7c6b797ad\u0027)\n\u003e\u003e\u003e uuid.UUID(str(u4), version\u003d4)\nUUID(\u00279cd2f2a6-4291-4834-83bb-ecb7c6b797ad\u0027)\n```\n\n^ here, where as \"str.version \u003d\u003d version kwarg\" is just identiy\n\nN.B. the \"implicit upgrade\" behavior:\n\n```\n\u003e\u003e\u003e u \u003d uuid.uuid1()\n\u003e\u003e\u003e u\nUUID(\u0027042e1814-4e4c-11f1-bcb8-7d6108c6fb38\u0027)\n\u003e\u003e\u003e uuid.UUID(str(u), version\u003d4)\nUUID(\u0027042e1814-4e4c-41f1-bcb8-7d6108c6fb38\u0027)\n```\n\n^ maybe not always what you want but it does mean there\u0027s a useful difference between:\n\n`UUID(str)` vs `UUID(str, version\u003d4)`\n\n... instead of just \"one of those is a TypeError\"\n\nand if you DON\u0027T want the \"implicit upgrade\" behavior you always have `UUID(str)` or even:\n\n```\nts !\u003d Timestamp(ts.internal, version\u003d2)  # i don\u0027t want to upgrade!\n```","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"06f0d04f715a4e8f1ba0923471fb6ee27d9d031e","unresolved":true,"context_lines":[{"line_number":273,"context_line":"                        return_value\u003dnow):"},{"line_number":274,"context_line":"            ts \u003d timestamp.Timestamp.now(offset\u003d0xfade)"},{"line_number":275,"context_line":"        self.assertEqual(0xfade, ts.offset)"},{"line_number":276,"context_line":"        self.assertEqual(\u0027%10.5f\u0027 % now, ts.normal)"},{"line_number":277,"context_line":"        exp \u003d round(float((int(round(now / 1e-5)) * 1e-5)), 5)"},{"line_number":278,"context_line":"        self.assertEqual(exp, float(ts))"},{"line_number":279,"context_line":"        float_part, hex_part \u003d ts.internal.split(\u0027_\u0027)"}],"source_content_type":"text/x-python","patch_set":87,"id":"13b2640b_0e0347ad","line":276,"updated":"2026-05-12 23:16:57.000000000","message":"this is interesting:\n\n```\n    def test_now_v1(self):\n        now \u003d time.time()\n        with mock.patch(\u0027swift.common.utils.timestamp.time.time\u0027,\n                        return_value\u003dnow):\n            ts \u003d timestamp.Timestamp.now(offset\u003d0xfade)\n        self.assertEqual(0xfade, ts.offset)\n\u003e       self.assertEqual(\u0027%10.5f\u0027 % now, ts.normal)\nE       AssertionError: \u00271778625590.81167\u0027 !\u003d \u00271778625590.81166\u0027\nE       - 1778625590.81167\nE       ?                ^\nE       + 1778625590.81166\nE       ?                ^\n```\n\nI wonder what our code does that\u0027s not `%10.5f`","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"06f0d04f715a4e8f1ba0923471fb6ee27d9d031e","unresolved":true,"context_lines":[{"line_number":299,"context_line":"            self.assertLess(hex_part, \u0027300000000000fade\u0027)"},{"line_number":300,"context_line":"            actual.append(hex_part)"},{"line_number":301,"context_line":"        # if this fails then the same jitter was chosen more than once :("},{"line_number":302,"context_line":"        self.assertEqual(1000, len(set(actual)))"},{"line_number":303,"context_line":""},{"line_number":304,"context_line":"    def test_init_from_timestamp_v1(self):"},{"line_number":305,"context_line":"        ts_orig \u003d timestamp.Timestamp(1234567890.12345)"}],"source_content_type":"text/x-python","patch_set":87,"id":"6c48e1bb_513ab63e","line":302,"updated":"2026-05-12 23:16:57.000000000","message":"```\n  Pairs:\n\n  1000 * 999 / 2 \u003d 499500\n\n  Approximate collision probability per test run:\n\n  from math import exp\n\n  p \u003d 1 - exp(-499500 / (16**9))\n  # about 0.00000727\n\n  Expected test runs until one collision:\n\n  1 / p ~\u003d 137,000 runs\n\n```\n\nI wonder how unlucky we\u0027ll be?  Maybe python\u0027s psudeo random seed means we won\u0027t ever see it, or we\u0027ll see it in a couple years or just a once-a-decade class flake!?","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"06f0d04f715a4e8f1ba0923471fb6ee27d9d031e","unresolved":true,"context_lines":[{"line_number":361,"context_line":"    def test_init_from_str_v2(self):"},{"line_number":362,"context_line":"        ts \u003d timestamp.Timestamp(\u00271234567890.12345\u0027)"},{"line_number":363,"context_line":"        self.assertEqual(0, ts.offset)"},{"line_number":364,"context_line":"        self.assertEqual(\u00271234567890.12345\u0027, ts.internal)"},{"line_number":365,"context_line":""},{"line_number":366,"context_line":"        ts \u003d timestamp.Timestamp(\u00271234567890.12345_2fffffffff000000\u0027)"},{"line_number":367,"context_line":"        self.assertEqual(0, ts.offset)"}],"source_content_type":"text/x-python","patch_set":87,"id":"18d1d1be_6e5fdf62","line":364,"updated":"2026-05-12 23:16:57.000000000","message":"what is this assert doing?  I don\u0027t think this is a v2 timestamp str or otherwise - I think it\u0027s repeating `test_init_legacy_from_str_v1` and adding the offset asserts?","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"06f0d04f715a4e8f1ba0923471fb6ee27d9d031e","unresolved":true,"context_lines":[{"line_number":486,"context_line":"    @mock_timestamp_randint(0xa)"},{"line_number":487,"context_line":"    def test_offset_limit_v2(self):"},{"line_number":488,"context_line":"        t \u003d 1417462430.78693"},{"line_number":489,"context_line":"        # can\u0027t have a offset above MAX_OFFSET"},{"line_number":490,"context_line":"        with self.assertRaises(ValueError):"},{"line_number":491,"context_line":"            timestamp.Timestamp(t, offset\u003d16 ** 6, version\u003d2)"},{"line_number":492,"context_line":"        # exactly max offset is fine"}],"source_content_type":"text/x-python","patch_set":87,"id":"e20714bd_21f5a739","line":489,"updated":"2026-05-12 23:16:57.000000000","message":"this doesn\u0027t make sense anymore in the comments\n\n... maybe it could/should if we just redefine `MAX_OFFSET \u003d 0xffffff`","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f16dbc44ebf4c0abd242b5c03d18bac3d5016aaa","unresolved":false,"context_lines":[{"line_number":486,"context_line":"    @mock_timestamp_randint(0xa)"},{"line_number":487,"context_line":"    def test_offset_limit_v2(self):"},{"line_number":488,"context_line":"        t \u003d 1417462430.78693"},{"line_number":489,"context_line":"        # can\u0027t have a offset above MAX_OFFSET"},{"line_number":490,"context_line":"        with self.assertRaises(ValueError):"},{"line_number":491,"context_line":"            timestamp.Timestamp(t, offset\u003d16 ** 6, version\u003d2)"},{"line_number":492,"context_line":"        # exactly max offset is fine"}],"source_content_type":"text/x-python","patch_set":87,"id":"41cd9734_cda4010e","line":489,"in_reply_to":"e20714bd_21f5a739","updated":"2026-05-14 14:00:23.000000000","message":"Done","commit_id":"5fcc81d447adf1afd164197182ed7431de474a88"},{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"fbaea1c724f7e5aee4079655602b0b5174c26079","unresolved":false,"context_lines":[{"line_number":478,"context_line":"        self.assertEqual(1, ts.offset)"},{"line_number":479,"context_line":"        self.assertEqual(\u00271417462430.78693_0000000000000001\u0027, ts.internal)"},{"line_number":480,"context_line":""},{"line_number":481,"context_line":"    def test_init_from_float_with_delta(self):"},{"line_number":482,"context_line":"        ts \u003d timestamp.Timestamp(1234567890.12345, delta\u003d1)"},{"line_number":483,"context_line":"        self.assertEqual(0, ts.offset)"},{"line_number":484,"context_line":"        self.assertEqual(\u00271234567890.12346\u0027, ts.internal)"}],"source_content_type":"text/x-python","patch_set":93,"id":"1571576e_f25e5b3b","line":481,"updated":"2026-06-26 09:00:31.000000000","message":"pep8: F811 redefinition of unused \u0027test_init_from_float_with_delta\u0027 from line 253","commit_id":"f9a2b30eed13bafec12fd14971a5fc2db4818af3"},{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"fbaea1c724f7e5aee4079655602b0b5174c26079","unresolved":false,"context_lines":[{"line_number":513,"context_line":"        self.assertEqual(float(ts_orig), float(ts))"},{"line_number":514,"context_line":"        self.assertEqual(ts_orig.internal[:-1], ts.internal[:-1])"},{"line_number":515,"context_line":""},{"line_number":516,"context_line":"    def test_init_from_normal_timestamp(self):"},{"line_number":517,"context_line":"        ts_orig \u003d timestamp.NormalTimestamp(1234567890.12345)"},{"line_number":518,"context_line":"        ts \u003d timestamp.Timestamp(ts_orig)"},{"line_number":519,"context_line":"        self.assertEqual(\u00271234567890.12345\u0027, ts.internal)"}],"source_content_type":"text/x-python","patch_set":93,"id":"03e63e7e_d0b49479","line":516,"updated":"2026-06-26 09:00:31.000000000","message":"pep8: F811 redefinition of unused \u0027test_init_from_normal_timestamp\u0027 from line 338","commit_id":"f9a2b30eed13bafec12fd14971a5fc2db4818af3"},{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"5ba0f1172097b0d4b522211281a714629bbd259d","unresolved":false,"context_lines":[{"line_number":478,"context_line":"        self.assertEqual(1, ts.offset)"},{"line_number":479,"context_line":"        self.assertEqual(\u00271417462430.78693_0000000000000001\u0027, ts.internal)"},{"line_number":480,"context_line":""},{"line_number":481,"context_line":"    def test_init_from_float_with_delta(self):"},{"line_number":482,"context_line":"        ts \u003d timestamp.Timestamp(1234567890.12345, delta\u003d1)"},{"line_number":483,"context_line":"        self.assertEqual(0, ts.offset)"},{"line_number":484,"context_line":"        self.assertEqual(\u00271234567890.12346\u0027, ts.internal)"}],"source_content_type":"text/x-python","patch_set":94,"id":"bda27b9f_1ccefd43","line":481,"updated":"2026-06-29 21:43:12.000000000","message":"pep8: F811 redefinition of unused \u0027test_init_from_float_with_delta\u0027 from line 253","commit_id":"b7a94f07f4efb6f7ba3eac470c0cd9e90bfe6de1"},{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"5ba0f1172097b0d4b522211281a714629bbd259d","unresolved":false,"context_lines":[{"line_number":513,"context_line":"        self.assertEqual(float(ts_orig), float(ts))"},{"line_number":514,"context_line":"        self.assertEqual(ts_orig.internal[:-1], ts.internal[:-1])"},{"line_number":515,"context_line":""},{"line_number":516,"context_line":"    def test_init_from_normal_timestamp(self):"},{"line_number":517,"context_line":"        ts_orig \u003d timestamp.NormalTimestamp(1234567890.12345)"},{"line_number":518,"context_line":"        ts \u003d timestamp.Timestamp(ts_orig)"},{"line_number":519,"context_line":"        self.assertEqual(\u00271234567890.12345\u0027, ts.internal)"}],"source_content_type":"text/x-python","patch_set":94,"id":"a058de18_f4255be8","line":516,"updated":"2026-06-29 21:43:12.000000000","message":"pep8: F811 redefinition of unused \u0027test_init_from_normal_timestamp\u0027 from line 338","commit_id":"b7a94f07f4efb6f7ba3eac470c0cd9e90bfe6de1"}],"test/unit/container/test_sync.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"efc12f94fab26cecc7d58b4c39dbf44093af418c","unresolved":true,"context_lines":[{"line_number":971,"context_line":"            sync.uuid \u003d FakeUUID"},{"line_number":972,"context_line":"            ts_data \u003d Timestamp(1.1)"},{"line_number":973,"context_line":"            timestamp \u003d Timestamp(1.2)"},{"line_number":974,"context_line":"            timestamp2 \u003d Timestamp(1.3)"},{"line_number":975,"context_line":"            put_object_calls \u003d []"},{"line_number":976,"context_line":""},{"line_number":977,"context_line":"            def fake_put_object(*args, **kwargs):"}],"source_content_type":"text/x-python","patch_set":38,"id":"9b481294_41d83346","line":974,"updated":"2026-01-07 12:18:14.000000000","message":"some of these changes could be in a pre-patch","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"e1624bc086ff6b11ac39c89fe5aab7869a785e6f","unresolved":false,"context_lines":[{"line_number":971,"context_line":"            sync.uuid \u003d FakeUUID"},{"line_number":972,"context_line":"            ts_data \u003d Timestamp(1.1)"},{"line_number":973,"context_line":"            timestamp \u003d Timestamp(1.2)"},{"line_number":974,"context_line":"            timestamp2 \u003d Timestamp(1.3)"},{"line_number":975,"context_line":"            put_object_calls \u003d []"},{"line_number":976,"context_line":""},{"line_number":977,"context_line":"            def fake_put_object(*args, **kwargs):"}],"source_content_type":"text/x-python","patch_set":38,"id":"0c72f78d_bbe7261d","line":974,"in_reply_to":"9b481294_41d83346","updated":"2026-01-07 12:20:05.000000000","message":"Done","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"}],"test/unit/obj/test_diskfile.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"b501820341b9e1d27250f21113e189a5ba7eb88c","unresolved":true,"context_lines":[{"line_number":3380,"context_line":"            t_meta \u003d next(self._ts_iter)"},{"line_number":3381,"context_line":"            if delta:"},{"line_number":3382,"context_line":"                sign \u003d \u0027-\u0027"},{"line_number":3383,"context_line":"                t_type \u003d Timestamp(t_meta, delta\u003d-1 * delta)"},{"line_number":3384,"context_line":"                # XXX we\u0027ve lost precision for t_type"},{"line_number":3385,"context_line":"                exp_t_type \u003d t_type.normalized()"},{"line_number":3386,"context_line":"            else:"}],"source_content_type":"text/x-python","patch_set":61,"id":"7fa36e9f_ae63174e","line":3383,"updated":"2026-02-27 00:14:29.000000000","message":"in the context of this change I too strongly associate \"t_type\" with the type of the Timestamp instead of \"timestamp_of_content_type\" or t_ctype","commit_id":"3b4cdeed1c1f9456e04bed2f54e644d342128b85"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"281a9af18fde7ff1be1bf191c2b503fae0f527f5","unresolved":false,"context_lines":[{"line_number":3380,"context_line":"            t_meta \u003d next(self._ts_iter)"},{"line_number":3381,"context_line":"            if delta:"},{"line_number":3382,"context_line":"                sign \u003d \u0027-\u0027"},{"line_number":3383,"context_line":"                t_type \u003d Timestamp(t_meta, delta\u003d-1 * delta)"},{"line_number":3384,"context_line":"                # XXX we\u0027ve lost precision for t_type"},{"line_number":3385,"context_line":"                exp_t_type \u003d t_type.normalized()"},{"line_number":3386,"context_line":"            else:"}],"source_content_type":"text/x-python","patch_set":61,"id":"ee00b356_a6958e75","line":3383,"in_reply_to":"7fa36e9f_ae63174e","updated":"2026-02-27 14:53:55.000000000","message":"Done","commit_id":"3b4cdeed1c1f9456e04bed2f54e644d342128b85"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"b501820341b9e1d27250f21113e189a5ba7eb88c","unresolved":true,"context_lines":[{"line_number":3387,"context_line":"                sign \u003d \u0027+\u0027"},{"line_number":3388,"context_line":"                t_type \u003d t_meta"},{"line_number":3389,"context_line":"                exp_t_type \u003d t_type"},{"line_number":3390,"context_line":"            expected \u003d \u0027%s%s%x.meta\u0027 % (t_meta.short, sign, delta)"},{"line_number":3391,"context_line":"            actual \u003d mgr.make_on_disk_filename("},{"line_number":3392,"context_line":"                t_meta, \u0027.meta\u0027, ctype_timestamp\u003dt_type)"},{"line_number":3393,"context_line":"            self.assertEqual(expected, actual)"}],"source_content_type":"text/x-python","patch_set":61,"id":"cf569332_e3cc1f50","line":3390,"updated":"2026-02-27 00:14:29.000000000","message":"I found that this expectation did not give me a super clear prediction of what these strings actually look like.","commit_id":"3b4cdeed1c1f9456e04bed2f54e644d342128b85"}],"test/unit/obj/test_reconstructor.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"efc12f94fab26cecc7d58b4c39dbf44093af418c","unresolved":true,"context_lines":[{"line_number":225,"context_line":"        # object fragments in place, not just part dirs, so we\u0027ll create them"},{"line_number":226,"context_line":"        # all here...."},{"line_number":227,"context_line":"        t \u003d 1421181937.70054  # time.time()"},{"line_number":228,"context_line":"        with mock_timestamp_randint(0xa):"},{"line_number":229,"context_line":"            ts \u003d utils.Timestamp(t)"},{"line_number":230,"context_line":"        with mock.patch(\u0027swift.obj.diskfile.time\u0027) as mock_time:"},{"line_number":231,"context_line":"            # since (a) we are using a fixed time here to create"}],"source_content_type":"text/x-python","patch_set":38,"id":"f425c139_d5e3fff2","line":228,"updated":"2026-01-07 12:18:14.000000000","message":"why was this necessary?","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"e1624bc086ff6b11ac39c89fe5aab7869a785e6f","unresolved":false,"context_lines":[{"line_number":225,"context_line":"        # object fragments in place, not just part dirs, so we\u0027ll create them"},{"line_number":226,"context_line":"        # all here...."},{"line_number":227,"context_line":"        t \u003d 1421181937.70054  # time.time()"},{"line_number":228,"context_line":"        with mock_timestamp_randint(0xa):"},{"line_number":229,"context_line":"            ts \u003d utils.Timestamp(t)"},{"line_number":230,"context_line":"        with mock.patch(\u0027swift.obj.diskfile.time\u0027) as mock_time:"},{"line_number":231,"context_line":"            # since (a) we are using a fixed time here to create"}],"source_content_type":"text/x-python","patch_set":38,"id":"8a040a48_6ce11ab5","line":228,"in_reply_to":"f425c139_d5e3fff2","updated":"2026-01-07 12:20:05.000000000","message":"Done","commit_id":"3690ca3f77179340acd80225bdb739d5cf6017c9"}],"test/unit/obj/test_ssync_receiver.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"ecd4361570c9020a1bf68d54304355011064f5cc","unresolved":true,"context_lines":[{"line_number":2682,"context_line":"        object_hash \u003d \u00279d41d8cd98f00b204e9800998ecf0abc\u0027"},{"line_number":2683,"context_line":"        ts_iter \u003d make_timestamp_iter()"},{"line_number":2684,"context_line":"        # first simple v1 timestamps"},{"line_number":2685,"context_line":"        t_data \u003d Timestamp(next(ts_iter).normal)"},{"line_number":2686,"context_line":"        t_meta \u003d Timestamp(next(ts_iter).normal)"},{"line_number":2687,"context_line":"        t_ctype \u003d Timestamp(next(ts_iter).normal)"},{"line_number":2688,"context_line":"        d_meta_data \u003d t_meta.raw - t_data.raw"}],"source_content_type":"text/x-python","patch_set":50,"id":"8f4582cd_f219fe5b","line":2685,"updated":"2026-02-11 20:14:53.000000000","message":"how are these not blowing up - it should be utils.Timestamp","commit_id":"57d83462328c7d6a9cde6288fcaccad14e0a3711"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"281a9af18fde7ff1be1bf191c2b503fae0f527f5","unresolved":false,"context_lines":[{"line_number":2682,"context_line":"        object_hash \u003d \u00279d41d8cd98f00b204e9800998ecf0abc\u0027"},{"line_number":2683,"context_line":"        ts_iter \u003d make_timestamp_iter()"},{"line_number":2684,"context_line":"        # first simple v1 timestamps"},{"line_number":2685,"context_line":"        t_data \u003d Timestamp(next(ts_iter).normal)"},{"line_number":2686,"context_line":"        t_meta \u003d Timestamp(next(ts_iter).normal)"},{"line_number":2687,"context_line":"        t_ctype \u003d Timestamp(next(ts_iter).normal)"},{"line_number":2688,"context_line":"        d_meta_data \u003d t_meta.raw - t_data.raw"}],"source_content_type":"text/x-python","patch_set":50,"id":"43a6ff92_cb1642f3","line":2685,"in_reply_to":"48001d8a_b525f467","updated":"2026-02-27 14:53:55.000000000","message":"Done","commit_id":"57d83462328c7d6a9cde6288fcaccad14e0a3711"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"5130e4b40bda460ee7b138d1dd93026264d9d16d","unresolved":true,"context_lines":[{"line_number":2682,"context_line":"        object_hash \u003d \u00279d41d8cd98f00b204e9800998ecf0abc\u0027"},{"line_number":2683,"context_line":"        ts_iter \u003d make_timestamp_iter()"},{"line_number":2684,"context_line":"        # first simple v1 timestamps"},{"line_number":2685,"context_line":"        t_data \u003d Timestamp(next(ts_iter).normal)"},{"line_number":2686,"context_line":"        t_meta \u003d Timestamp(next(ts_iter).normal)"},{"line_number":2687,"context_line":"        t_ctype \u003d Timestamp(next(ts_iter).normal)"},{"line_number":2688,"context_line":"        d_meta_data \u003d t_meta.raw - t_data.raw"}],"source_content_type":"text/x-python","patch_set":50,"id":"48001d8a_b525f467","line":2685,"in_reply_to":"8f4582cd_f219fe5b","updated":"2026-02-12 19:09:25.000000000","message":"grrr! I must have broken this with my rebase yesterday. It was good in patchset 49\nhttps://review.opendev.org/c/openstack/swift/+/967738/49..51/test/unit/obj/test_ssync_receiver.py\n\nbut I chose to move my recent test cleanup *after* these patches to remove impediments from us carrying this, and I guess that rebase somehow lost the import 😭","commit_id":"57d83462328c7d6a9cde6288fcaccad14e0a3711"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"5130e4b40bda460ee7b138d1dd93026264d9d16d","unresolved":true,"context_lines":[{"line_number":2783,"context_line":"                        ts_ctype\u003dt_data,"},{"line_number":2784,"context_line":"                        durable\u003dTrue)"},{"line_number":2785,"context_line":"        self.assertEqual("},{"line_number":2786,"context_line":"            expected, ssync_receiver.decode_missing(msg.encode(\u0027ascii\u0027)))"},{"line_number":2787,"context_line":""},{"line_number":2788,"context_line":"        # unexpected zero delta is tolerated"},{"line_number":2789,"context_line":"        msg \u003d \u0027%s %s m:0\u0027 % (object_hash, t_data.internal)"}],"source_content_type":"text/x-python","patch_set":51,"id":"6d486a20_f8b49657","line":2786,"updated":"2026-02-12 19:09:25.000000000","message":"this v2 timestamp testing should be beefed up into separate test cases","commit_id":"25bdbf9676953b49ce5f3d7ff8056f87bac9f739"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"e3f331fce7da982ef54ee283e8389e2c4dfad87e","unresolved":false,"context_lines":[{"line_number":2783,"context_line":"                        ts_ctype\u003dt_data,"},{"line_number":2784,"context_line":"                        durable\u003dTrue)"},{"line_number":2785,"context_line":"        self.assertEqual("},{"line_number":2786,"context_line":"            expected, ssync_receiver.decode_missing(msg.encode(\u0027ascii\u0027)))"},{"line_number":2787,"context_line":""},{"line_number":2788,"context_line":"        # unexpected zero delta is tolerated"},{"line_number":2789,"context_line":"        msg \u003d \u0027%s %s m:0\u0027 % (object_hash, t_data.internal)"}],"source_content_type":"text/x-python","patch_set":51,"id":"2ed5c377_abf1bbca","line":2786,"in_reply_to":"6d486a20_f8b49657","updated":"2026-02-18 17:45:54.000000000","message":"Done","commit_id":"25bdbf9676953b49ce5f3d7ff8056f87bac9f739"}]}
