)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"aa03528149098e7d152851a5de7b4e3301e43873","unresolved":true,"context_lines":[{"line_number":7,"context_line":"Prevent root osr epoch loss during object moves"},{"line_number":8,"context_line":""},{"line_number":9,"context_line":"A fresh root replica can return a synthetic ACTIVE own shard range"},{"line_number":10,"context_line":"with no epoch. The misplaced-object mover can persist this fallback"},{"line_number":11,"context_line":"over an existing root OSR, making a sharded database appear unsharded."},{"line_number":12,"context_line":""},{"line_number":13,"context_line":"Use synthetic root ranges only to route object updates. Also reject"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":1,"id":"c66842d3_127f100d","line":10,"updated":"2026-09-11 15:33:20.000000000","message":"\u003e with no epoch\n\nwhy?  how?  where?   can we just please NOT?","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"f27ecb6e1457dca7232ca60789b72313504486e3","unresolved":false,"context_lines":[{"line_number":7,"context_line":"Prevent root osr epoch loss during object moves"},{"line_number":8,"context_line":""},{"line_number":9,"context_line":"A fresh root replica can return a synthetic ACTIVE own shard range"},{"line_number":10,"context_line":"with no epoch. The misplaced-object mover can persist this fallback"},{"line_number":11,"context_line":"over an existing root OSR, making a sharded database appear unsharded."},{"line_number":12,"context_line":""},{"line_number":13,"context_line":"Use synthetic root ranges only to route object updates. Also reject"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":1,"id":"c3d54e0c_e754b80c","line":10,"in_reply_to":"c66842d3_127f100d","updated":"2026-09-16 04:52:35.000000000","message":"\u003e can we just please NOT?\n\nyou can *try* ... but it\u0027s harder than you might hope.  The hang-up was probably thinking \"of course shards have an *epoch* - we put it in the name!\" \n\nbut a child shard ranges generation_timestamp that gets embedded in the name is NOT the same Timestamp.now() you write into the root db\u0027s OSR during `find_and_replace --enable` (unless you\u0027re in a test that mocks now)\n\nfor any unsharded child shard range - the *correct* representation of their osr is epoch\u003dNone (until they themselves shard)\n\nIf you happen to want to try and infer \"approximately when\" was a shard\u0027s *parents* epoch - you could parse the shards generation_timestamp out of it\u0027s name; but it will only be \"close\"","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"aa03528149098e7d152851a5de7b4e3301e43873","unresolved":true,"context_lines":[{"line_number":8,"context_line":""},{"line_number":9,"context_line":"A fresh root replica can return a synthetic ACTIVE own shard range"},{"line_number":10,"context_line":"with no epoch. The misplaced-object mover can persist this fallback"},{"line_number":11,"context_line":"over an existing root OSR, making a sharded database appear unsharded."},{"line_number":12,"context_line":""},{"line_number":13,"context_line":"Use synthetic root ranges only to route object updates. Also reject"},{"line_number":14,"context_line":"incoming own shard ranges without an epoch when the persisted OSR"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":1,"id":"85c9c542_92953b97","line":11,"updated":"2026-09-11 15:33:20.000000000","message":"\u003e over an existing root OSR\n\nis any shard row w/o an epoch ever worth persisting?","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"f27ecb6e1457dca7232ca60789b72313504486e3","unresolved":false,"context_lines":[{"line_number":8,"context_line":""},{"line_number":9,"context_line":"A fresh root replica can return a synthetic ACTIVE own shard range"},{"line_number":10,"context_line":"with no epoch. The misplaced-object mover can persist this fallback"},{"line_number":11,"context_line":"over an existing root OSR, making a sharded database appear unsharded."},{"line_number":12,"context_line":""},{"line_number":13,"context_line":"Use synthetic root ranges only to route object updates. Also reject"},{"line_number":14,"context_line":"incoming own shard ranges without an epoch when the persisted OSR"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":1,"id":"b04788a3_257ddf1e","line":11,"in_reply_to":"85c9c542_92953b97","updated":"2026-09-16 04:52:35.000000000","message":"yes, MOST shard rows have epoch\u003dNone - epoch\u003dNone means *that row* represents a shard that has not itself sharded (or shrunk)\n\nso we encode a shards \"generation_timestamp\" into it\u0027s *name* - but it\u0027s not written down as it\u0027s epoch\n\nhell, we don\u0027t even encode the shard-ranges\u0027 \"generation_timestamp\" in the root OSRs epoch when we find_and_replace --enable - that\u0027s just *another* quick to follow `Timestamp.now()`\n\n... the \"generation_timestamp\" isn\u0027t really written *anywhere* (except in the shard-range-dbs-names, and I think we sometimes parse it out of them) and it will almost never be equal to their parents epoch - except when we mock Timestamp.now() - and I think that\u0027s kind of a seperate problem.","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"aa03528149098e7d152851a5de7b4e3301e43873","unresolved":true,"context_lines":[{"line_number":10,"context_line":"with no epoch. The misplaced-object mover can persist this fallback"},{"line_number":11,"context_line":"over an existing root OSR, making a sharded database appear unsharded."},{"line_number":12,"context_line":""},{"line_number":13,"context_line":"Use synthetic root ranges only to route object updates. Also reject"},{"line_number":14,"context_line":"incoming own shard ranges without an epoch when the persisted OSR"},{"line_number":15,"context_line":"already has one. Check under the database write lock and reject the"},{"line_number":16,"context_line":"whole row so its state and statistics cannot overwrite valid data."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":1,"id":"20e3cd68_e2a10db6","line":13,"updated":"2026-09-11 15:33:20.000000000","message":"\u003e only to route object updates\n\nto route them... to ourselves?","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"f27ecb6e1457dca7232ca60789b72313504486e3","unresolved":false,"context_lines":[{"line_number":10,"context_line":"with no epoch. The misplaced-object mover can persist this fallback"},{"line_number":11,"context_line":"over an existing root OSR, making a sharded database appear unsharded."},{"line_number":12,"context_line":""},{"line_number":13,"context_line":"Use synthetic root ranges only to route object updates. Also reject"},{"line_number":14,"context_line":"incoming own shard ranges without an epoch when the persisted OSR"},{"line_number":15,"context_line":"already has one. Check under the database write lock and reject the"},{"line_number":16,"context_line":"whole row so its state and statistics cannot overwrite valid data."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":1,"id":"ba877c00_1677659f","line":13,"in_reply_to":"20e3cd68_e2a10db6","updated":"2026-09-16 04:52:35.000000000","message":"Acknowledged","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"aa03528149098e7d152851a5de7b4e3301e43873","unresolved":true,"context_lines":[{"line_number":12,"context_line":""},{"line_number":13,"context_line":"Use synthetic root ranges only to route object updates. Also reject"},{"line_number":14,"context_line":"incoming own shard ranges without an epoch when the persisted OSR"},{"line_number":15,"context_line":"already has one. Check under the database write lock and reject the"},{"line_number":16,"context_line":"whole row so its state and statistics cannot overwrite valid data."},{"line_number":17,"context_line":"Ordinary child ranges and collapse into an epoched root remain valid."},{"line_number":18,"context_line":""}],"source_content_type":"text/x-gerrit-commit-message","patch_set":1,"id":"e75e14c5_9dc676e6","line":15,"updated":"2026-09-11 15:33:20.000000000","message":"\u003e when the persisted OSR\n\nhow about just \"Reject shard ranges without an epoch.\"","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"f27ecb6e1457dca7232ca60789b72313504486e3","unresolved":false,"context_lines":[{"line_number":12,"context_line":""},{"line_number":13,"context_line":"Use synthetic root ranges only to route object updates. Also reject"},{"line_number":14,"context_line":"incoming own shard ranges without an epoch when the persisted OSR"},{"line_number":15,"context_line":"already has one. Check under the database write lock and reject the"},{"line_number":16,"context_line":"whole row so its state and statistics cannot overwrite valid data."},{"line_number":17,"context_line":"Ordinary child ranges and collapse into an epoched root remain valid."},{"line_number":18,"context_line":""}],"source_content_type":"text/x-gerrit-commit-message","patch_set":1,"id":"d9062a54_1321a6bd","line":15,"in_reply_to":"e75e14c5_9dc676e6","updated":"2026-09-16 04:52:35.000000000","message":"you don\u0027t want to do that; a shard-range with epoch\u003dNone is just an un-sharded shard - they\u0027ll want that row in their local db until they themselves need to shard at which point they\u0027ll replace their ACTIVE osr epoch\u003dNone with a sharding row and an epoch timestamp","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"aa03528149098e7d152851a5de7b4e3301e43873","unresolved":true,"context_lines":[{"line_number":14,"context_line":"incoming own shard ranges without an epoch when the persisted OSR"},{"line_number":15,"context_line":"already has one. Check under the database write lock and reject the"},{"line_number":16,"context_line":"whole row so its state and statistics cannot overwrite valid data."},{"line_number":17,"context_line":"Ordinary child ranges and collapse into an epoched root remain valid."},{"line_number":18,"context_line":""},{"line_number":19,"context_line":"Convert the diagnostic probes into regression tests that verify epoch"},{"line_number":20,"context_line":"preservation and delivery of the delayed update to every leaf replica."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":1,"id":"a25b2535_96d0b847","line":17,"updated":"2026-09-11 15:33:20.000000000","message":"\u003e ranges and collapse into an epoched root\n\nwat?","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"f27ecb6e1457dca7232ca60789b72313504486e3","unresolved":false,"context_lines":[{"line_number":14,"context_line":"incoming own shard ranges without an epoch when the persisted OSR"},{"line_number":15,"context_line":"already has one. Check under the database write lock and reject the"},{"line_number":16,"context_line":"whole row so its state and statistics cannot overwrite valid data."},{"line_number":17,"context_line":"Ordinary child ranges and collapse into an epoched root remain valid."},{"line_number":18,"context_line":""},{"line_number":19,"context_line":"Convert the diagnostic probes into regression tests that verify epoch"},{"line_number":20,"context_line":"preservation and delivery of the delayed update to every leaf replica."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":1,"id":"aa012fe1_17471b54","line":17,"in_reply_to":"a25b2535_96d0b847","updated":"2026-09-16 04:52:35.000000000","message":"I think \"ordinary child ranges\" was trying to say why `epoch\u003dNone` has to be allowed - because that\u0027s how sharding works.  I think \"collapse into an epoched root\" was trying to say \"its ok to `merge_shard_ranges(fallback_epochless_osr)` b/c we\u0027ll make sure if we HAVE an epoch\u0027d OSR we won\u0027t let the fallback overwrite us\"\n\n... but I think both conditions are already handled if we just make fallback OSRs Timestamp.zero","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"}],"/PATCHSET_LEVEL":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"aa03528149098e7d152851a5de7b4e3301e43873","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"92fbd087_fed281c4","updated":"2026-09-11 15:33:20.000000000","message":"i\u0027m sure we have a bug; and I would like to see it fixed - this might be the best we can do but I don\u0027t understand the strategy.","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"f27ecb6e1457dca7232ca60789b72313504486e3","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"1279d5e4_5048f98a","updated":"2026-09-16 04:52:35.000000000","message":"I didn\u0027t mean to push over this - if the change seems confusing or worse than patchset 2 in some way - please revert and I\u0027ll move the parts of the diff I like to a different change-id as I originally intended\n\nHOWEVER - in the interest of optimistically making forward progress I *do* sort of think this chnage *might* be getting close to mergeable on it\u0027s own as a fix to the problem we\u0027re having in prod!?\n\nopen questions are probably mostly about \"what do we do about existing stuck shards\" - but I think that could reasonably be addressed as follow-on or even one-off-cleanup after we \"stop the bleeding\"","commit_id":"78366cbbc7ac258ca9d818ad1af0fd0a7c534c9c"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"7aa377b42196c431757544bdd4383d7ec44bc403","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"629cb379_656b0859","updated":"2026-09-16 05:12:53.000000000","message":"I think we could polish this a bit more; but AFAICT the idea is sound and fixes the bugs w/o breaking anything else.","commit_id":"78366cbbc7ac258ca9d818ad1af0fd0a7c534c9c"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"c950de10ba81837b2cfc27e2804bec35617257de","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":5,"id":"d426cc05_a5e9b074","updated":"2026-09-18 07:43:10.000000000","message":"I guess one issue in relation to upgrades is a nonzero primary timestamp wins before state or epoch is considered. A positive-timestamp fallback from an older node, or previously persisted metadata, can therefore supersede a genuine zero-timestamp OSR.\n\nBut I guess that\u0027s only a problem if we basically hit our bug","commit_id":"7b03770c4c4d0b0ee7fe876d79c54e4101ae71e6"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"c69cf285581bc344e1b14f959a0ef28dcfecc57f","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":5,"id":"789e9c1e_380f18a1","updated":"2026-09-21 07:01:59.000000000","message":"Just want to get some more confidence, so here are some more tests: https://review.opendev.org/c/openstack/swift/+/1006446 probably should just sqaush that into this change.","commit_id":"7b03770c4c4d0b0ee7fe876d79c54e4101ae71e6"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"2fa825e51dadaa0e3f8bc6b5fa514ba41c18754d","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":5,"id":"a9c98cc5_96a0f38d","updated":"2026-09-16 23:38:31.000000000","message":"So I guess the existing probe was saying \"if you get an osr.epoch\u003dNone it gets stuck\" - we KNEW that and we said \"but that\u0027s only TODO b/c we don\u0027t actually know how this could *happen*\"\n\nthe big win from the previous change was the discovery of \"find_local_handoff returns a primary\" - and in THAT case _get_shard_broker just be slamming fallback `osr.epoch\u003dNone` into roots\u0027 `merge_shard_ranges`!\n\n1005823: do not persist routing metadata while moving misplaced objects | https://review.opendev.org/c/openstack/swift/+/1005823\n\nBut, I\u0027m still not yet seeing a downsize of fallback `osr.timestamp\u003dzero`\n\nI\u0027ll keep polishing tests and waiting for someone to tell me the like/hate the idea:\n\n1005973: sq? make OSR replication tests behavioral | https://review.opendev.org/c/openstack/swift/+/1005973","commit_id":"7b03770c4c4d0b0ee7fe876d79c54e4101ae71e6"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"6791af9a63362a0f6c770961916c9fb8b29f9f9e","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":5,"id":"166ee426_2c21e869","updated":"2026-09-17 13:11:10.000000000","message":"Yeah I think I like it, much simpler and easier to grok!\n\nJust a +1 because I need to look at your test comments some more, they could be DRY\u0027ed some more, true. But also don\u0027t want to block. Will look closer in the morning. I spent more time on the next patch.","commit_id":"7b03770c4c4d0b0ee7fe876d79c54e4101ae71e6"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"c8b3c949fc2653f325ecf91d4027af1a60d10723","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":5,"id":"c309c7e9_b4a3401e","updated":"2026-09-18 07:32:40.000000000","message":"Yeah, OK I like this simplified approach, it doesn\u0027t fully fix the mistplaced routing issue, but we\u0027re working on that in the next patch. Better to land something then nothing I think.\n\nJust want to doublecheck shardranges now with a timestamp of zero() are OK throughout the state machine.","commit_id":"7b03770c4c4d0b0ee7fe876d79c54e4101ae71e6"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"72707298ef757de18f0f6747b48cb54ede3265e1","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":5,"id":"7e4afff3_da6cc2b9","updated":"2026-09-18 07:41:20.000000000","message":"enable_sharding() inherits the fallback’s zero timestamp, then sets a real state_timestamp and epoch. So genuine root OSRs can remain timestamp-zero. Normal state and statistics updates can still merge using their separate timestamps, so I guess that\u0027s ok.. but the main timestamp seems to only come into its own now when we need to collapse and go backwards in the state_machine. \n\nfunny because in sharding we started with 1 timestamp, then grew lots, now the orig is basically nothing until we force the state machine backwards.","commit_id":"7b03770c4c4d0b0ee7fe876d79c54e4101ae71e6"}],"swift/container/backend.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"aa03528149098e7d152851a5de7b4e3301e43873","unresolved":true,"context_lines":[{"line_number":274,"context_line":"        take precedence over the corresponding item in ``existing``"},{"line_number":275,"context_line":"    \"\"\""},{"line_number":276,"context_line":"    if not existing:"},{"line_number":277,"context_line":"        return True"},{"line_number":278,"context_line":"    if existing[\u0027timestamp\u0027] \u003c shard_data[\u0027timestamp\u0027]:"},{"line_number":279,"context_line":"        # note that currently we do not roll forward any meta or state from"},{"line_number":280,"context_line":"        # an item that was created at older time, newer created time trumps"}],"source_content_type":"text/x-python","patch_set":1,"id":"1dc424df_7e4c2f30","line":277,"updated":"2026-09-11 15:33:20.000000000","message":"what about\n\n```\nif not shard_data[\u0027epoch\u0027]:\n    return False\n```","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"caf2529897714e23abd65025823a864033978e4f","unresolved":true,"context_lines":[{"line_number":274,"context_line":"        take precedence over the corresponding item in ``existing``"},{"line_number":275,"context_line":"    \"\"\""},{"line_number":276,"context_line":"    if not existing:"},{"line_number":277,"context_line":"        return True"},{"line_number":278,"context_line":"    if existing[\u0027timestamp\u0027] \u003c shard_data[\u0027timestamp\u0027]:"},{"line_number":279,"context_line":"        # note that currently we do not roll forward any meta or state from"},{"line_number":280,"context_line":"        # an item that was created at older time, newer created time trumps"}],"source_content_type":"text/x-python","patch_set":1,"id":"6aa3fd15_b7e03bcf","line":277,"in_reply_to":"1dc424df_7e4c2f30","updated":"2026-09-14 03:46:15.000000000","message":"I think this may break normal shard creation. `make_shard_ranges()` creates legitimate FOUND child ranges with epoch\u003dNone, and _find_shard_ranges() persists them.\nAn epoch is assigned when they begin sharding or shrinking. So a missing epoch is not enough to identify a deafulted/synthetic range.","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"f27ecb6e1457dca7232ca60789b72313504486e3","unresolved":false,"context_lines":[{"line_number":274,"context_line":"        take precedence over the corresponding item in ``existing``"},{"line_number":275,"context_line":"    \"\"\""},{"line_number":276,"context_line":"    if not existing:"},{"line_number":277,"context_line":"        return True"},{"line_number":278,"context_line":"    if existing[\u0027timestamp\u0027] \u003c shard_data[\u0027timestamp\u0027]:"},{"line_number":279,"context_line":"        # note that currently we do not roll forward any meta or state from"},{"line_number":280,"context_line":"        # an item that was created at older time, newer created time trumps"}],"source_content_type":"text/x-python","patch_set":1,"id":"3fa27cf0_ed8c58ca","line":277,"in_reply_to":"6aa3fd15_b7e03bcf","updated":"2026-09-16 04:52:35.000000000","message":"\u003e I think this may break normal shard creation\n\n100% it does - I looked at making shards persist their \"generation timestamp\" as epoch in the row and it was mostly just confusing the issue\n\nright now epoch\u003dNone is required and expected to be persisted on an OSR for a child shard ranges that hasn\u0027t sharded.\n\nI don\u0027t even think the \"stuck epochless-osr\" we\u0027ve seen in prod were on roots - so the fallback osr is going to look exactly like a totally valid epoch-less brand new un-sharded child OSR when you\u0027re a sharded shard that\u0027s suddenly getting a NEWER OSR that says you\u0027re not sharded 😵‍💫\n\n\u003e a missing epoch is not enough to identify a deafulted/synthetic range\n\nagreed - but a zero timestamp would be!","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"aa03528149098e7d152851a5de7b4e3301e43873","unresolved":true,"context_lines":[{"line_number":1443,"context_line":""},{"line_number":1444,"context_line":"        def _really_merge_items(conn):"},{"line_number":1445,"context_line":"            curs \u003d conn.cursor()"},{"line_number":1446,"context_line":"            curs.execute(\u0027BEGIN IMMEDIATE\u0027)"},{"line_number":1447,"context_line":""},{"line_number":1448,"context_line":"            # Get rows for items that already exist."},{"line_number":1449,"context_line":"            # We must chunk it up to avoid sqlite\u0027s limit of 999 args."}],"source_content_type":"text/x-python","patch_set":1,"id":"5f966d91_c84769e0","line":1446,"updated":"2026-09-11 15:33:20.000000000","message":"we\u0027re in a transaction right now","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"caf2529897714e23abd65025823a864033978e4f","unresolved":false,"context_lines":[{"line_number":1443,"context_line":""},{"line_number":1444,"context_line":"        def _really_merge_items(conn):"},{"line_number":1445,"context_line":"            curs \u003d conn.cursor()"},{"line_number":1446,"context_line":"            curs.execute(\u0027BEGIN IMMEDIATE\u0027)"},{"line_number":1447,"context_line":""},{"line_number":1448,"context_line":"            # Get rows for items that already exist."},{"line_number":1449,"context_line":"            # We must chunk it up to avoid sqlite\u0027s limit of 999 args."}],"source_content_type":"text/x-python","patch_set":1,"id":"ccbd34bd_cb28589f","line":1446,"in_reply_to":"5f966d91_c84769e0","updated":"2026-09-14 03:46:15.000000000","message":"Acknowledged","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"aa03528149098e7d152851a5de7b4e3301e43873","unresolved":true,"context_lines":[{"line_number":1464,"context_line":"            # Reject the whole fallback, including its newer state and stats."},{"line_number":1465,"context_line":"            own_sr \u003d records.get(own_name)"},{"line_number":1466,"context_line":"            to_merge \u003d []"},{"line_number":1467,"context_line":"            for item in item_list:"},{"line_number":1468,"context_line":"                if (item[\u0027name\u0027] \u003d\u003d own_name and own_sr and"},{"line_number":1469,"context_line":"                        own_sr[\u0027epoch\u0027] is not None and item[\u0027epoch\u0027] is None):"},{"line_number":1470,"context_line":"                    candidate \u003d dict(item)"}],"source_content_type":"text/x-python","patch_set":1,"id":"c07dbea1_d3b5eacf","line":1467,"updated":"2026-09-11 15:33:20.000000000","message":"how many times does this method traverse this list?","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"caf2529897714e23abd65025823a864033978e4f","unresolved":false,"context_lines":[{"line_number":1464,"context_line":"            # Reject the whole fallback, including its newer state and stats."},{"line_number":1465,"context_line":"            own_sr \u003d records.get(own_name)"},{"line_number":1466,"context_line":"            to_merge \u003d []"},{"line_number":1467,"context_line":"            for item in item_list:"},{"line_number":1468,"context_line":"                if (item[\u0027name\u0027] \u003d\u003d own_name and own_sr and"},{"line_number":1469,"context_line":"                        own_sr[\u0027epoch\u0027] is not None and item[\u0027epoch\u0027] is None):"},{"line_number":1470,"context_line":"                    candidate \u003d dict(item)"}],"source_content_type":"text/x-python","patch_set":1,"id":"ad84150a_97826766","line":1467,"in_reply_to":"c07dbea1_d3b5eacf","updated":"2026-09-14 03:46:15.000000000","message":"Done","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"aa03528149098e7d152851a5de7b4e3301e43873","unresolved":true,"context_lines":[{"line_number":1475,"context_line":"                    continue"},{"line_number":1476,"context_line":"                to_merge.append(item)"},{"line_number":1477,"context_line":""},{"line_number":1478,"context_line":"            to_add, to_delete \u003d sift_shard_ranges(to_merge, records)"},{"line_number":1479,"context_line":""},{"line_number":1480,"context_line":"            if to_delete:"},{"line_number":1481,"context_line":"                curs.executemany("}],"source_content_type":"text/x-python","patch_set":1,"id":"9dd71d60_4435cfc4","line":1478,"updated":"2026-09-11 15:33:20.000000000","message":"this seems like an interesting abstraction - are some of the records in item_list already discarded by this method?","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"caf2529897714e23abd65025823a864033978e4f","unresolved":false,"context_lines":[{"line_number":1475,"context_line":"                    continue"},{"line_number":1476,"context_line":"                to_merge.append(item)"},{"line_number":1477,"context_line":""},{"line_number":1478,"context_line":"            to_add, to_delete \u003d sift_shard_ranges(to_merge, records)"},{"line_number":1479,"context_line":""},{"line_number":1480,"context_line":"            if to_delete:"},{"line_number":1481,"context_line":"                curs.executemany("}],"source_content_type":"text/x-python","patch_set":1,"id":"d3c59eb7_fad8e5ea","line":1478,"in_reply_to":"9dd71d60_4435cfc4","updated":"2026-09-14 03:46:15.000000000","message":"Done","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"f27ecb6e1457dca7232ca60789b72313504486e3","unresolved":true,"context_lines":[{"line_number":283,"context_line":"        existing_state \u003d (existing[\u0027timestamp\u0027], existing[\u0027state_timestamp\u0027],"},{"line_number":284,"context_line":"                          existing[\u0027state\u0027])"},{"line_number":285,"context_line":"        if incoming_state \u003e existing_state:"},{"line_number":286,"context_line":"            return False"},{"line_number":287,"context_line":"    if existing[\u0027timestamp\u0027] \u003c shard_data[\u0027timestamp\u0027]:"},{"line_number":288,"context_line":"        # note that currently we do not roll forward any meta or state from"},{"line_number":289,"context_line":"        # an item that was created at older time, newer created time trumps"}],"source_content_type":"text/x-python","patch_set":2,"id":"19c38735_f8e1bed8","line":286,"updated":"2026-09-16 04:52:35.000000000","message":"isn\u0027t this saying:\n\n```\nif incoming[\u0027timestamp\u0027] \u003e existing[\u0027timestamp\u0027]:\n    return False\n```\n\n^ that has to ONLY ever be right when `incoming[\u0027epoch\u0027] \u003d\u003d None` - which we should just fix by making the special epoch\u003dNone case of a fallback OSR be infinately old.","commit_id":"94eaba37362c72037b9a93aede7ec7943a8244d9"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"2fa825e51dadaa0e3f8bc6b5fa514ba41c18754d","unresolved":true,"context_lines":[{"line_number":2065,"context_line":"            own_shard_range \u003d None"},{"line_number":2066,"context_line":"        else:"},{"line_number":2067,"context_line":"            own_shard_range \u003d ShardRange("},{"line_number":2068,"context_line":"                self.path, Timestamp.zero(),"},{"line_number":2069,"context_line":"                ShardRange.MIN, ShardRange.MAX,"},{"line_number":2070,"context_line":"                state\u003dShardRange.ACTIVE)"},{"line_number":2071,"context_line":"        return own_shard_range"}],"source_content_type":"text/x-python","patch_set":5,"id":"ec266c55_b3da9988","line":2068,"updated":"2026-09-16 23:38:31.000000000","message":"i love the idea a one-line fix 😎","commit_id":"7b03770c4c4d0b0ee7fe876d79c54e4101ae71e6"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"6791af9a63362a0f6c770961916c9fb8b29f9f9e","unresolved":true,"context_lines":[{"line_number":2065,"context_line":"            own_shard_range \u003d None"},{"line_number":2066,"context_line":"        else:"},{"line_number":2067,"context_line":"            own_shard_range \u003d ShardRange("},{"line_number":2068,"context_line":"                self.path, Timestamp.zero(),"},{"line_number":2069,"context_line":"                ShardRange.MIN, ShardRange.MAX,"},{"line_number":2070,"context_line":"                state\u003dShardRange.ACTIVE)"},{"line_number":2071,"context_line":"        return own_shard_range"}],"source_content_type":"text/x-python","patch_set":5,"id":"1da6fcc5_334eb129","line":2068,"in_reply_to":"ec266c55_b3da9988","updated":"2026-09-17 13:11:10.000000000","message":"I do like a simple one line fix, to default to 0, so it always is older.. smart!\n\nThough it\u0027s not quite standalone, so the next patch is also required to land to really fix this bug in my opinion. The zero Timestamp also arrives into genuine root OSRs created by `enable_sharding()`. So with the filler misplaced object race can still happen.","commit_id":"7b03770c4c4d0b0ee7fe876d79c54e4101ae71e6"}],"swift/container/replicator.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"f27ecb6e1457dca7232ca60789b72313504486e3","unresolved":false,"context_lines":[{"line_number":58,"context_line":"            remote_state \u003d (shard[\u0027timestamp\u0027], shard[\u0027state_timestamp\u0027],"},{"line_number":59,"context_line":"                            shard[\u0027state\u0027])"},{"line_number":60,"context_line":"            local_state \u003d (own_sr.timestamp.internal,"},{"line_number":61,"context_line":"                           own_sr.state_timestamp.internal, own_sr.state)"},{"line_number":62,"context_line":"            if remote_state \u003e local_state:"},{"line_number":63,"context_line":"                logger.warning("},{"line_number":64,"context_line":"                    \u0027Ignoring remote osr w/o epoch, own_sr: %r, remote_sr: %r,\u0027"}],"source_content_type":"text/x-python","patch_set":2,"id":"24acfa80_d96c0cdc","line":61,"updated":"2026-09-16 04:52:35.000000000","message":"why are we changing this?  this was the only part that was working.","commit_id":"94eaba37362c72037b9a93aede7ec7943a8244d9"}],"swift/container/sharder.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"aa03528149098e7d152851a5de7b4e3301e43873","unresolved":true,"context_lines":[{"line_number":1256,"context_line":""},{"line_number":1257,"context_line":"        # Get the valid info into the broker.container, etc"},{"line_number":1258,"context_line":"        shard_broker.get_info()"},{"line_number":1259,"context_line":"        # An unsharded root may supply a synthetic range for routing updates."},{"line_number":1260,"context_line":"        # Persisting it would give the fallback authority over a real OSR."},{"line_number":1261,"context_line":"        if shard_range.name !\u003d root_path or shard_range.epoch is not None:"},{"line_number":1262,"context_line":"            shard_broker.merge_shard_ranges(shard_range)"}],"source_content_type":"text/x-python","patch_set":1,"id":"cca4226d_e13ccfea","line":1259,"updated":"2026-09-11 15:33:20.000000000","message":"can the \"synthetic range\" just be infinitely old?  and also patently *invalid* to persist?  so we don\u0027t have to litter every \"merge_shard_ranges\" call with \"unless we have a bad one\" guards?","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"caf2529897714e23abd65025823a864033978e4f","unresolved":true,"context_lines":[{"line_number":1256,"context_line":""},{"line_number":1257,"context_line":"        # Get the valid info into the broker.container, etc"},{"line_number":1258,"context_line":"        shard_broker.get_info()"},{"line_number":1259,"context_line":"        # An unsharded root may supply a synthetic range for routing updates."},{"line_number":1260,"context_line":"        # Persisting it would give the fallback authority over a real OSR."},{"line_number":1261,"context_line":"        if shard_range.name !\u003d root_path or shard_range.epoch is not None:"},{"line_number":1262,"context_line":"            shard_broker.merge_shard_ranges(shard_range)"}],"source_content_type":"text/x-python","patch_set":1,"id":"e8cc46ce_bb2d7181","line":1259,"in_reply_to":"cca4226d_e13ccfea","updated":"2026-09-14 03:46:15.000000000","message":"oh that\u0027s an interesting idea. Need the this on this some more.","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"f27ecb6e1457dca7232ca60789b72313504486e3","unresolved":false,"context_lines":[{"line_number":1256,"context_line":""},{"line_number":1257,"context_line":"        # Get the valid info into the broker.container, etc"},{"line_number":1258,"context_line":"        shard_broker.get_info()"},{"line_number":1259,"context_line":"        # An unsharded root may supply a synthetic range for routing updates."},{"line_number":1260,"context_line":"        # Persisting it would give the fallback authority over a real OSR."},{"line_number":1261,"context_line":"        if shard_range.name !\u003d root_path or shard_range.epoch is not None:"},{"line_number":1262,"context_line":"            shard_broker.merge_shard_ranges(shard_range)"}],"source_content_type":"text/x-python","patch_set":1,"id":"bae94221_d4977549","line":1259,"in_reply_to":"e8cc46ce_bb2d7181","updated":"2026-09-16 04:52:35.000000000","message":"yeah at first I thought it was \"just some crazy idea\" ... later it turned out it was basically exactly what we need and really easy with all the existing tests!","commit_id":"d1a38062acc4553c4f371396426591db55af1d77"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"f27ecb6e1457dca7232ca60789b72313504486e3","unresolved":false,"context_lines":[{"line_number":1259,"context_line":"        # An unsharded root may supply a synthetic range for routing updates."},{"line_number":1260,"context_line":"        # Persisting it would give the fallback authority over a real OSR."},{"line_number":1261,"context_line":"        if shard_range.name !\u003d root_path or shard_range.epoch is not None:"},{"line_number":1262,"context_line":"            shard_broker.merge_shard_ranges(shard_range)"},{"line_number":1263,"context_line":"        shard_broker.set_sharding_sysmeta(\u0027Quoted-Root\u0027, quote(root_path))"},{"line_number":1264,"context_line":"        # NB: we *used* to do"},{"line_number":1265,"context_line":"        #    shard_broker.set_sharding_sysmeta(\u0027Root\u0027, root_path)"}],"source_content_type":"text/x-python","patch_set":2,"id":"7bf25bd1_3c64a33c","line":1262,"updated":"2026-09-16 04:52:35.000000000","message":"I don\u0027t think merge should be conditional here - it already filters out the bad None state and if it didn\u0027t this probably isn\u0027t even the only place that would be wrong.\n\nUPDATE: i was wrong - I DO think that merge should be \"conditional\" here - just not the way it\u0027s spelled in the earlier patch set and also it\u0027s not needed once we fix merge_shard_ranges to handle fallback OSR correctly:\n\n1005823: do not persist routing metadata while moving misplaced objects | https://review.opendev.org/c/openstack/swift/+/1005823","commit_id":"94eaba37362c72037b9a93aede7ec7943a8244d9"}],"test/probe/test_sharder.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"f27ecb6e1457dca7232ca60789b72313504486e3","unresolved":true,"context_lines":[{"line_number":4407,"context_line":"                         healthy_broker.get_own_shard_range().epoch)"},{"line_number":4408,"context_line":"        self.assertEqual(SHARDED, healthy_broker.get_db_state())"},{"line_number":4409,"context_line":""},{"line_number":4410,"context_line":"    def test_misplaced_objects_create_root_osr_without_epoch(self):"},{"line_number":4411,"context_line":"        self._test_misplaced_objects_reset_root_epoch(existing_root\u003dFalse)"},{"line_number":4412,"context_line":""},{"line_number":4413,"context_line":"    def test_misplaced_objects_reset_sharded_root_epoch(self):"}],"source_content_type":"text/x-python","patch_set":2,"id":"1524acbd_1847fcfa","side":"PARENT","line":4410,"updated":"2026-09-16 04:52:35.000000000","message":"I thought this was some named behavior that we used to want to preserve and now we say we do NOT want that!?\n\nbut no... it was just a test that was \"assert the bug\" so obviously we want to replace that with \"assert not the bug anymore\"","commit_id":"89e0b0b0e67720eea2c3f18459b6332bb2471ec1"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"f27ecb6e1457dca7232ca60789b72313504486e3","unresolved":true,"context_lines":[{"line_number":4266,"context_line":"        handoff_osr \u003d handoff_broker.get_own_shard_range(no_default\u003dTrue)"},{"line_number":4267,"context_line":"        self.assertIsNotNone(handoff_osr.epoch)"},{"line_number":4268,"context_line":""},{"line_number":4269,"context_line":"    def _test_misplaced_objects_preserve_root_epoch(self, existing_root):"},{"line_number":4270,"context_line":"        obj_names \u003d self._make_object_names(8)"},{"line_number":4271,"context_line":"        self.put_objects(obj_names)"},{"line_number":4272,"context_line":"        client.post_container(self.url, self.admin_token, self.container_name,"}],"source_content_type":"text/x-python","patch_set":2,"id":"3a343fa8_fe30d0f6","line":4269,"updated":"2026-09-16 04:52:35.000000000","message":"I got SUPER confused by this change\u0027s diff\n\nbasically this diff mixes \"a bunch of new probe test I don\u0027t understand\" with \"a change in behavior that makes the bad go away\"\n\n... and that\u0027s not normally how I understand diffs with probe-tets\n\nit\u0027s FINE (useful even!) to kick up:\n\n\"DNM: this is broken\"\n\nwith a *passing* probe test that says \"XXX this is NOT correct\"\n\nthen a new commit:\n\n\"sq? idk, maybe fix it this way!\"\n\nand that can even *fail* at the XXX\n\nif we say \"yeah that fix looks right let\u0027s do that!\" THEN we can polish up the commit:\n\n\"fix the thing w/ a awesome probetest that gives you a solid assertion failure when you revert the fix\"","commit_id":"94eaba37362c72037b9a93aede7ec7943a8244d9"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"f27ecb6e1457dca7232ca60789b72313504486e3","unresolved":true,"context_lines":[{"line_number":4381,"context_line":""},{"line_number":4382,"context_line":"        target_broker \u003d self.get_broker(self.brain.part, target_node)"},{"line_number":4383,"context_line":"        persisted \u003d target_broker.get_own_shard_range(no_default\u003dTrue)"},{"line_number":4384,"context_line":"        if existing_root:"},{"line_number":4385,"context_line":"            # Replication may supply newer stats from another healthy root."},{"line_number":4386,"context_line":"            # Only its sharding identity must remain unchanged here."},{"line_number":4387,"context_line":"            for key in (\u0027name\u0027, \u0027lower\u0027, \u0027upper\u0027, \u0027timestamp\u0027, \u0027state\u0027,"}],"source_content_type":"text/x-python","patch_set":2,"id":"41bbb4ee_bce0f99c","line":4384,"updated":"2026-09-16 04:52:35.000000000","message":"I don\u0027t like test \"helpers\" that say:\n\n```\nif boolean:\n   do_setup_one_way\nelse:\n   do_setup_another_way\n# turn the crank!\n....\nif boolean:\n   do_assertion_one_way\nelse:\n   do_assertion_another_way\n```\n\nIMHO this is the definition of DRY not DAMP\n\nI LIKE test helpers that once you understand them can help you construct multiple interesting scenarios:\n\nhttps://review.opendev.org/c/openstack/swift/+/1005811/2/test/unit/obj/test_replicator.py#1831\n\n... the trick is to make them *not do the asserting* so that your tests can still read:\n\n```\ndef test_maintainable_behavior():\n    result \u003d generic_helper(**setup_kwargs)\n    self.assert(result, \u0027what you should expect\u0027)\n```","commit_id":"94eaba37362c72037b9a93aede7ec7943a8244d9"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"7aa377b42196c431757544bdd4383d7ec44bc403","unresolved":true,"context_lines":[{"line_number":4437,"context_line":"        self.assertIsNone(fallback.epoch)"},{"line_number":4438,"context_line":"        self.assertEqual(Timestamp.zero(), fallback.timestamp)"},{"line_number":4439,"context_line":"        self.assertIsNone("},{"line_number":4440,"context_line":"            recreated_root_broker.get_own_shard_range(no_default\u003dTrue))"},{"line_number":4441,"context_line":""},{"line_number":4442,"context_line":"        # The selected child is complete, so the newly accepted row in that"},{"line_number":4443,"context_line":"        # child is misplaced and ready for the sharder pass below."}],"source_content_type":"text/x-python","patch_set":3,"id":"b5c79bd6_335278b5","line":4440,"updated":"2026-09-16 05:12:53.000000000","message":"these two stanza:\n\n```\n# simulate the loss of a specific db\n...\n# verify the recreated root will provide the x-newest fallback osr\n...\n```\n\nare VERY similar between both tests - they could probably get DRYd out to:\n\n    self._verify_failed_root_is_newest_fallback_osr(recrated_root_node)\n\n... in this test, and \n\n    self._verify_failed_root_is_newest_fallback_osr(child_node)\n    \n... in the other, to demonsrate \"no matter WHERE you get the empty primary, a PUT will always create a fallback OSR that we\u0027ll explicitly prefer with x-newest\"\n\nI don\u0027t *mind* the tests the way they are written; but I could see wanting to DRY out at least that part specifically - just to maybe make the \"two different failure modes\" a little easier to grok as \"very similar\"","commit_id":"78366cbbc7ac258ca9d818ad1af0fd0a7c534c9c"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"7aa377b42196c431757544bdd4383d7ec44bc403","unresolved":true,"context_lines":[{"line_number":4558,"context_line":"                override_devices\u003d[child_node[\u0027device\u0027]], auto_shard\u003dFalse)"},{"line_number":4559,"context_line":""},{"line_number":4560,"context_line":"            # TODO: The synthetic routing fallback is persisted as the"},{"line_number":4561,"context_line":"            # recreated root\u0027s OSR along with sharding metadata."},{"line_number":4562,"context_line":"            recreated_root_broker \u003d self.get_broker("},{"line_number":4563,"context_line":"                self.brain.part, child_node)"},{"line_number":4564,"context_line":"            persisted_fallback \u003d recreated_root_broker.get_own_shard_range("}],"source_content_type":"text/x-python","patch_set":3,"id":"c588dfe7_5188e95e","line":4561,"updated":"2026-09-16 05:12:53.000000000","message":"this is why patch-set 2 had two diffs\n\none hunk fixed \"auto merge fallback into local\" doesn\u0027t persist the epoch\u003dNone when there was *already* an OSR - and the OTHER fixed it when the local *was* the the recreated root and it did NOT have an OSR\n\nFWIW I think we *could* fix this bug ONLY by fixing the \"auto merge fallback into local\" behavior:\n\n1005823: do not persist routing metadata while moving misplaced objects | https://review.opendev.org/c/openstack/swift/+/1005823\n\n^ but it my mind, doing that \"right\" opens up a bunch more cans of worms like \"how can `_get_shard_broker` do ANY of this as a side-effect for a misplaced destination!?\" and the `Timestamp.zero()` fix seemed more expedient even if \"stop punching ourselves in the face\" would be a good follow-on to consider after the fire-drill gets put out.","commit_id":"78366cbbc7ac258ca9d818ad1af0fd0a7c534c9c"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"2fa825e51dadaa0e3f8bc6b5fa514ba41c18754d","unresolved":true,"context_lines":[{"line_number":4329,"context_line":"        self.assertIsNone("},{"line_number":4330,"context_line":"            new_primary_broker.get_own_shard_range(no_default\u003dTrue))"},{"line_number":4331,"context_line":""},{"line_number":4332,"context_line":"        # Let\u0027s reset the osr by hand."},{"line_number":4333,"context_line":"        reset_osr \u003d new_primary_broker.get_own_shard_range()"},{"line_number":4334,"context_line":"        self.assertIsNone(reset_osr.epoch)"},{"line_number":4335,"context_line":"        self.assertEqual(reset_osr.state, ShardRange.ACTIVE)"}],"source_content_type":"text/x-python","patch_set":5,"id":"f639f844_03e1035a","side":"PARENT","line":4332,"updated":"2026-09-16 23:38:31.000000000","message":"whoa!  we just kicking ourselves in the teeth here!?","commit_id":"ef169bada576dbe7a6b87da4bf94c7431b448b16"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"2fa825e51dadaa0e3f8bc6b5fa514ba41c18754d","unresolved":true,"context_lines":[{"line_number":4351,"context_line":"        self.assertIsNotNone(old_osr.epoch)"},{"line_number":4352,"context_line":"        self.assertEqual(old_osr.state, ShardRange.SHARDED)"},{"line_number":4353,"context_line":""},{"line_number":4354,"context_line":"        self.assertIsNone(new_osr.epoch)"},{"line_number":4355,"context_line":"        self.assertGreater(new_osr.timestamp, old_osr.timestamp)"},{"line_number":4356,"context_line":""},{"line_number":4357,"context_line":"    def test_manage_shard_ranges_missing_epoch_no_false_positives(self):"}],"source_content_type":"text/x-python","patch_set":5,"id":"4a18db4a_46504070","side":"PARENT","line":4354,"updated":"2026-09-16 23:38:31.000000000","message":"wait - this was ALREADY the bug!?  Or at least half of it... local osr.epoch\u003dNone gets stuck","commit_id":"ef169bada576dbe7a6b87da4bf94c7431b448b16"}]}
