)]}'
{"/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"}],"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"}],"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"}],"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"}],"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"}]}
