)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1c92860e8cba068a41069ed9e449d20c33870efd","unresolved":true,"context_lines":[{"line_number":12,"context_line":"both the .builder and the .ring."},{"line_number":13,"context_line":""},{"line_number":14,"context_line":"RingData.save promotes v1 to v2 only when the history table carries a"},{"line_number":15,"context_line":"live cell, so an operator who never opts in to v2 keeps the history"},{"line_number":16,"context_line":"section. The rebalance and write_ring commands print one stderr notice"},{"line_number":17,"context_line":"when save() will auto-promote, including the explicit"},{"line_number":18,"context_line":"--format-version\u003d1 path."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":3,"id":"2798a4a4_0fa9fda6","line":15,"updated":"2026-08-28 21:11:34.000000000","message":"this sounds like a bad idea - the operator can opt-in or out - we shouldn\u0027t structure the change such that they are forced one way or another\n\nif we can do the ~equivilent job of min-part-hours using the builder history table we can continue letting the operator write v1 rings until the fleet is upgraded.","commit_id":"a58ae65bd7b4010af7eeedc1ac479ea1a4ca048a"}],"/PATCHSET_LEVEL":[{"author":{"_account_id":38496,"name":"Andressa Cabistani","display_name":"Andressa","email":"acabistani@gmail.com","username":"andressadotpy","status":"I\u0027m a Software Engineer at Red Hat and I love Open Source and connect with people! Feel free to DM through IRC, I\u0027ll be delighted to chat"},"change_message_id":"f16d5e6c06d7581b8cb48045c383df6f8a587838","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"a67d1509_8e1e31eb","updated":"2026-07-28 14:42:51.000000000","message":"Looks good to me! A summary of what I analyzed:\n\n- Great test coverage\n - Tested _history_changed dirty bit: set by first rebalance, reset between passes, set by aging-only pass\n- Tested wall-clock \u003c\u003d boundary: age\u003d1 with max_history_cycles\u003d1 still blocks (the key fix)\n- Tested pass-based \u003c boundary: strict \u003c preserved for min_part_hours\u003d0\n- Tested auto-promote: inert history stays v1, live history promotes to v2, history section present\n- Tested 254 cap: 254 accepted, 255 rejected","commit_id":"a58ae65bd7b4010af7eeedc1ac479ea1a4ca048a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1c92860e8cba068a41069ed9e449d20c33870efd","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"aeeae535_705f857b","updated":"2026-08-28 21:11:34.000000000","message":"i\u0027m very skeptical that making the ring tool automatically blow up proxies is the best we can do; i\u0027d rather force operators to acknowledge ringv2 or downgrade their builder swift version if we can\u0027t maintain compatibility writing format v1 rings \n\nmaybe in 1 or 2 more releases after we default to v2 (but still support --format-version\u003d1) we could start shipping code that assumes v2 rings as opposed to merely \"can optionally take advantage of them when operator acknowledges they are available\".\n\nOr maybe even start now with changing the default and adding warning/deprecating for ring v1?  I think ring v2 is great!  I just don\u0027t like the ergonomics of \"auto promotion\" and ignoring explicit operator instruction.","commit_id":"a58ae65bd7b4010af7eeedc1ac479ea1a4ca048a"}],"swift/cli/ringbuilder.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1c92860e8cba068a41069ed9e449d20c33870efd","unresolved":true,"context_lines":[{"line_number":63,"context_line":"    # defaulted and stays v1; the two cases are mutually exclusive."},{"line_number":64,"context_line":"    if (requested_format_version \u003d\u003d 1 and ring_data is not None"},{"line_number":65,"context_line":"            and ring_data.has_live_history()):"},{"line_number":66,"context_line":"        print(\"Auto-promoting to --format-version\u003d2 because this ring\\n\""},{"line_number":67,"context_line":"              \"carries history; older Swift versions will skip the\\n\""},{"line_number":68,"context_line":"              \"swift/ring/history section but still read the rest.\\n\","},{"line_number":69,"context_line":"              file\u003dsys.stderr)"}],"source_content_type":"text/x-python","patch_set":3,"id":"4c821d72_e1fb3055","line":66,"updated":"2026-08-28 21:11:34.000000000","message":"i\u0027m not sure about that; i don\u0027t know WHEN swift is going to \"default\" (or auto-promote) to ring-v2\n\nat a minimum it requires all your nodes are running new-enough swift","commit_id":"a58ae65bd7b4010af7eeedc1ac479ea1a4ca048a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1c92860e8cba068a41069ed9e449d20c33870efd","unresolved":true,"context_lines":[{"line_number":64,"context_line":"    if (requested_format_version \u003d\u003d 1 and ring_data is not None"},{"line_number":65,"context_line":"            and ring_data.has_live_history()):"},{"line_number":66,"context_line":"        print(\"Auto-promoting to --format-version\u003d2 because this ring\\n\""},{"line_number":67,"context_line":"              \"carries history; older Swift versions will skip the\\n\""},{"line_number":68,"context_line":"              \"swift/ring/history section but still read the rest.\\n\","},{"line_number":69,"context_line":"              file\u003dsys.stderr)"},{"line_number":70,"context_line":"    elif defaulted:"}],"source_content_type":"text/x-python","patch_set":3,"id":"c5e9c9e8_bdf559bb","line":67,"updated":"2026-08-28 21:11:34.000000000","message":"\u003e older Swift versions will skip\n\nthat\u0027s true, but also under states things a bit - if your swift isn\u0027t newenough to read v2 rings your proxies won\u0027t start","commit_id":"a58ae65bd7b4010af7eeedc1ac479ea1a4ca048a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1c92860e8cba068a41069ed9e449d20c33870efd","unresolved":true,"context_lines":[{"line_number":1173,"context_line":"            # We must save if a device changed; this could be something like"},{"line_number":1174,"context_line":"            # a changed IP address."},{"line_number":1175,"context_line":"            be_cowardly \u003d False"},{"line_number":1176,"context_line":"        elif history_changed and not parts and not removed_devs:"},{"line_number":1177,"context_line":"            # A history-only pass shows no balance/dispersion delta, but the"},{"line_number":1178,"context_line":"            # advanced age table and history rewrites must still reach disk."},{"line_number":1179,"context_line":"            be_cowardly \u003d False"}],"source_content_type":"text/x-python","patch_set":3,"id":"d888b3f0_21748e58","line":1176,"updated":"2026-08-28 21:11:34.000000000","message":"why is `history_changed` not independently sufficient","commit_id":"a58ae65bd7b4010af7eeedc1ac479ea1a4ca048a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1c92860e8cba068a41069ed9e449d20c33870efd","unresolved":true,"context_lines":[{"line_number":1229,"context_line":"            print(\u0027-\u0027 * 79)"},{"line_number":1230,"context_line":"            status \u003d EXIT_WARNING"},{"line_number":1231,"context_line":"        _emit_format_version_notice("},{"line_number":1232,"context_line":"            builder.get_ring(), options.format_version,"},{"line_number":1233,"context_line":"            defaulted_format_version)"},{"line_number":1234,"context_line":"        ts \u003d time()"},{"line_number":1235,"context_line":"        builder.get_ring().save("}],"source_content_type":"text/x-python","patch_set":3,"id":"7caada32_8e4f6531","line":1232,"updated":"2026-08-28 21:11:34.000000000","message":"is this saying that operator could explicitly request a format_version\u003d1 and we write a format_version\u003d2 with a *warning*?","commit_id":"a58ae65bd7b4010af7eeedc1ac479ea1a4ca048a"}],"swift/common/ring/builder.py":[{"author":{"_account_id":38496,"name":"Andressa Cabistani","display_name":"Andressa","email":"acabistani@gmail.com","username":"andressadotpy","status":"I\u0027m a Software Engineer at Red Hat and I love Open Source and connect with people! Feel free to DM through IRC, I\u0027ll be delighted to chat"},"change_message_id":"f16d5e6c06d7581b8cb48045c383df6f8a587838","unresolved":true,"context_lines":[{"line_number":227,"context_line":"        byte, bit \u003d divmod(part, 8)"},{"line_number":228,"context_line":"        return bool(self._part_moved_bitmap[byte] \u0026 (128 \u003e\u003e bit))"},{"line_number":229,"context_line":""},{"line_number":230,"context_line":"    def _history_age_is_recent(self, age, threshold):"},{"line_number":231,"context_line":"        # With a wall-clock cycle, a newly-reset cell may inherit almost a full"},{"line_number":232,"context_line":"        # cycle of the shared epoch\u0027s remainder. Keep the threshold boundary"},{"line_number":233,"context_line":"        # itself live so history is never released before a complete retention"}],"source_content_type":"text/x-python","patch_set":3,"id":"2f4a70fb_dd034958","line":230,"updated":"2026-07-28 14:42:51.000000000","message":"Suggestion: rename the method to something like `_history_age_within_retention()`","commit_id":"a58ae65bd7b4010af7eeedc1ac479ea1a4ca048a"}],"swift/common/ring/composite_builder.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1c92860e8cba068a41069ed9e449d20c33870efd","unresolved":true,"context_lines":[{"line_number":221,"context_line":"                 for p2d in builder._replica2part2dev]"},{"line_number":222,"context_line":"        if has_history:"},{"line_number":223,"context_line":"            builder_history \u003d getattr("},{"line_number":224,"context_line":"                builder, \u0027_history_replica2part2dev\u0027, None)"},{"line_number":225,"context_line":"            # A component without history contributes inert rows, allowing"},{"line_number":226,"context_line":"            # other components\u0027 live history to remain usable."},{"line_number":227,"context_line":"            builder_history \u003d builder_history or builder._replica2part2dev"}],"source_content_type":"text/x-python","patch_set":3,"id":"48c922f5_2c2c2ddc","line":224,"updated":"2026-08-28 21:11:34.000000000","message":"why bother with `has_history` if you STILL have to do the getattr dance?","commit_id":"a58ae65bd7b4010af7eeedc1ac479ea1a4ca048a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1c92860e8cba068a41069ed9e449d20c33870efd","unresolved":true,"context_lines":[{"line_number":226,"context_line":"            # other components\u0027 live history to remain usable."},{"line_number":227,"context_line":"            builder_history \u003d builder_history or builder._replica2part2dev"},{"line_number":228,"context_line":"            history \u003d [resize_array(p2d, dev_id_bytes)"},{"line_number":229,"context_line":"                       for p2d in builder_history]"},{"line_number":230,"context_line":"        else:"},{"line_number":231,"context_line":"            history \u003d []"},{"line_number":232,"context_line":"        for part2dev in r2p2d + history:"}],"source_content_type":"text/x-python","patch_set":3,"id":"1ebcc80f_3910bf4b","line":229,"updated":"2026-08-28 21:11:34.000000000","message":"N.B. this is so that you can fit +\u003d device_offset and/or composite_none_dev in the *copy* of the component builder tables before you extend/combine them","commit_id":"a58ae65bd7b4010af7eeedc1ac479ea1a4ca048a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1c92860e8cba068a41069ed9e449d20c33870efd","unresolved":true,"context_lines":[{"line_number":232,"context_line":"        for part2dev in r2p2d + history:"},{"line_number":233,"context_line":"            for part, dev in enumerate(part2dev):"},{"line_number":234,"context_line":"                if dev !\u003d composite_none_dev:"},{"line_number":235,"context_line":"                    part2dev[part] +\u003d device_offset"},{"line_number":236,"context_line":"        for dev in [d for d in devs if d]:"},{"line_number":237,"context_line":"            # note that some devs may not be referenced in r2p2d but update"},{"line_number":238,"context_line":"            # their dev id nonetheless"}],"source_content_type":"text/x-python","patch_set":3,"id":"9b648562_2d79f6a2","line":235,"updated":"2026-08-28 21:11:34.000000000","message":"a) this test is wrong - we should be looking for the *components* NONE_DEV devices\nb) there should be an else - we can\u0027t \"blindly\" increment component NONE_DEV - we need to replace it with composite NONE_DEV\n\nalternatively we could consider doing the r2p2d and history table iteration in two loops - only the history table will ever have NONE_DEV","commit_id":"a58ae65bd7b4010af7eeedc1ac479ea1a4ca048a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1c92860e8cba068a41069ed9e449d20c33870efd","unresolved":true,"context_lines":[{"line_number":241,"context_line":"        if has_history:"},{"line_number":242,"context_line":"            composite_history.extend(history)"},{"line_number":243,"context_line":"        composite_devs.extend(devs)"},{"line_number":244,"context_line":"        device_offset +\u003d len(builder.devs)"},{"line_number":245,"context_line":""},{"line_number":246,"context_line":"    return RingData("},{"line_number":247,"context_line":"        composite_r2p2d, composite_devs, builders[0].part_shift,"}],"source_content_type":"text/x-python","patch_set":3,"id":"1ff87581_927f62bd","line":244,"updated":"2026-08-28 21:11:34.000000000","message":"oic, the first trip through the loop on ring0 the device_offset is 0 - then each following ring has it\u0027s device_ids incremented by the len of the previous rings device list\n\nthe same math has to happen in the history tables","commit_id":"a58ae65bd7b4010af7eeedc1ac479ea1a4ca048a"}],"swift/common/ring/ring.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1c92860e8cba068a41069ed9e449d20c33870efd","unresolved":true,"context_lines":[{"line_number":353,"context_line":"        # that never opted in to v2 keeps the history section. Notification"},{"line_number":354,"context_line":"        # lives in the CLI: save() runs twice per command and must stay silent."},{"line_number":355,"context_line":"        if format_version \u003d\u003d 1 and self.has_live_history():"},{"line_number":356,"context_line":"            format_version \u003d 2"},{"line_number":357,"context_line":"        # Override the timestamp so that the same ring data creates"},{"line_number":358,"context_line":"        # the same bytes on disk. This makes a checksum comparison a"},{"line_number":359,"context_line":"        # good way to see if two rings are identical."}],"source_content_type":"text/x-python","patch_set":3,"id":"1b6204c5_0a4194b5","line":356,"updated":"2026-08-28 21:11:34.000000000","message":"WTF?  I don\u0027t like this interface.  I would prefer we either:\n\n1) raise an error (demand history be saved with explicit format\u003d2)\n2) discard history and save the ring a format\u003d1 as requested","commit_id":"a58ae65bd7b4010af7eeedc1ac479ea1a4ca048a"}]}
