)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"8f897ce652813444bcc42d266da2087c323713bd","unresolved":true,"context_lines":[{"line_number":58,"context_line":"pruned out more than 300k candidates."},{"line_number":59,"context_line":""},{"line_number":60,"context_line":"After this patch the above pruning logic is not turned on automatically"},{"line_number":61,"context_line":"but can be enable via the config option:"},{"line_number":62,"context_line":""},{"line_number":63,"context_line":"  [workarounds]"},{"line_number":64,"context_line":"  optimize_for_wide_provider_trees \u003d true"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":18,"id":"3a3ebc9d_56a9e3b5","line":61,"range":{"start_line":61,"start_character":11,"end_line":61,"end_character":17},"updated":"2025-10-14 17:31:10.000000000","message":"enabled","commit_id":"98e56a39bd581b20dc80a60c1ef9d51980257893"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4f040a180e06e401f329460783aaeffd6d5c1ddd","unresolved":false,"context_lines":[{"line_number":58,"context_line":"pruned out more than 300k candidates."},{"line_number":59,"context_line":""},{"line_number":60,"context_line":"After this patch the above pruning logic is not turned on automatically"},{"line_number":61,"context_line":"but can be enable via the config option:"},{"line_number":62,"context_line":""},{"line_number":63,"context_line":"  [workarounds]"},{"line_number":64,"context_line":"  optimize_for_wide_provider_trees \u003d true"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":18,"id":"fda92b28_a923d704","line":61,"range":{"start_line":61,"start_character":11,"end_line":61,"end_character":17},"in_reply_to":"3a3ebc9d_56a9e3b5","updated":"2025-10-15 07:57:34.000000000","message":"Done","commit_id":"98e56a39bd581b20dc80a60c1ef9d51980257893"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"8f897ce652813444bcc42d266da2087c323713bd","unresolved":true,"context_lines":[{"line_number":88,"context_line":"        if rw_ctx.exceeds_capacity(areq)"},{"line_number":89,"context_line":"            continue"},{"line_number":90,"context_line":""},{"line_number":91,"context_line":"on all product after it was generated. The"},{"line_number":92,"context_line":"_consolidate_allocation_requests folds the individual"},{"line_number":93,"context_line":"AllocationRequestResource object in the product into a single"},{"line_number":94,"context_line":"allocation. This has a side effect on some of the ARRs so the logic does"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":18,"id":"fc0f3457_4ce28425","line":91,"range":{"start_line":91,"start_character":7,"end_line":91,"end_character":14},"updated":"2025-10-14 17:31:10.000000000","message":"products","commit_id":"98e56a39bd581b20dc80a60c1ef9d51980257893"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4f040a180e06e401f329460783aaeffd6d5c1ddd","unresolved":false,"context_lines":[{"line_number":88,"context_line":"        if rw_ctx.exceeds_capacity(areq)"},{"line_number":89,"context_line":"            continue"},{"line_number":90,"context_line":""},{"line_number":91,"context_line":"on all product after it was generated. The"},{"line_number":92,"context_line":"_consolidate_allocation_requests folds the individual"},{"line_number":93,"context_line":"AllocationRequestResource object in the product into a single"},{"line_number":94,"context_line":"allocation. This has a side effect on some of the ARRs so the logic does"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":18,"id":"6a289c5f_c43d4a18","line":91,"range":{"start_line":91,"start_character":7,"end_line":91,"end_character":14},"in_reply_to":"fc0f3457_4ce28425","updated":"2025-10-15 07:57:34.000000000","message":"Done","commit_id":"98e56a39bd581b20dc80a60c1ef9d51980257893"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"8f897ce652813444bcc42d266da2087c323713bd","unresolved":true,"context_lines":[{"line_number":111,"context_line":"full product and the algo yields. The default python recursion limit is"},{"line_number":112,"context_line":"1000 so we are not really limited by that as that means we could handle"},{"line_number":113,"context_line":"~ 990 iterables, meaning an allocation candidate query with 990 request"},{"line_number":114,"context_line":"groups. The limiting factor of this algorithm is not recursion dept but"},{"line_number":115,"context_line":"execution time."},{"line_number":116,"context_line":""},{"line_number":117,"context_line":"Gemini 2.5 pro was used to put together the generic Cartesian product"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":18,"id":"4ce12ae4_00316608","line":114,"range":{"start_line":114,"start_character":63,"end_line":114,"end_character":67},"updated":"2025-10-14 17:31:10.000000000","message":"depth","commit_id":"98e56a39bd581b20dc80a60c1ef9d51980257893"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4f040a180e06e401f329460783aaeffd6d5c1ddd","unresolved":false,"context_lines":[{"line_number":111,"context_line":"full product and the algo yields. The default python recursion limit is"},{"line_number":112,"context_line":"1000 so we are not really limited by that as that means we could handle"},{"line_number":113,"context_line":"~ 990 iterables, meaning an allocation candidate query with 990 request"},{"line_number":114,"context_line":"groups. The limiting factor of this algorithm is not recursion dept but"},{"line_number":115,"context_line":"execution time."},{"line_number":116,"context_line":""},{"line_number":117,"context_line":"Gemini 2.5 pro was used to put together the generic Cartesian product"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":18,"id":"4217cb64_b3054ce8","line":114,"range":{"start_line":114,"start_character":63,"end_line":114,"end_character":67},"in_reply_to":"4ce12ae4_00316608","updated":"2025-10-15 07:57:34.000000000","message":"Done","commit_id":"98e56a39bd581b20dc80a60c1ef9d51980257893"}],"/PATCHSET_LEVEL":[{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"b9939f4eae21750d8f06683ef0a10c8febda334e","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"ddd40f9d_bb2fdf6a","updated":"2025-10-03 10:07:46.000000000","message":"I need to clean this up. Move the new logic behind a WA flag so that we don\u0027t risk breaking general users with the new algo","commit_id":"22ea28e62df21b76ca094d3139a9fb30b6087530"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"bfceb1f543de08314de32fd62a69a567586038e0","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":5,"id":"9f85c20c_d49c884f","updated":"2025-10-03 15:05:35.000000000","message":"I still want:\n* tests for the odometer logic\n* reno","commit_id":"601b6075257c2d98ee24d5068012e54ce1bc0281"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"42ead22d557af1f55c28bef7e13d7ebd79e8ee27","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":5,"id":"b53ab066_6bd5b490","updated":"2025-10-03 15:29:50.000000000","message":"still not a fully review i just took a few notes on our irc converstaion and where im thinking we may want to go long term.\n\nnotting supper actionalble yet. ill try and look at this more closely early next week.","commit_id":"601b6075257c2d98ee24d5068012e54ce1bc0281"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"9cc1909462af7becc031026d39d6e456283d3dfd","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":5,"id":"7143ad96_0db63cd3","in_reply_to":"9f85c20c_d49c884f","updated":"2025-10-03 15:05:52.000000000","message":".","commit_id":"601b6075257c2d98ee24d5068012e54ce1bc0281"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"e11e23607fe657a5ceaf51078b1f535a4cbe817b","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":16,"id":"71ce3ec0_b9edd892","updated":"2025-10-09 21:16:43.000000000","message":"over all this looks good to me but im a littel tired so ill review this again tomorrow","commit_id":"d73e760ec1c4fa762632b9c3ca1c2edb724ff6b7"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"8731dab87d088203ff46df27d992f99af79b2a51","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":17,"id":"93db0872_8445d242","updated":"2025-10-13 18:47:47.000000000","message":"I was able to convince myself that the previous approach was correct. I\u0027m not able to do that for this yet, other than just seeing that the tests pass and assert what I can understand for small scenarios and assuming it\u0027s right. That makes me uncomfortable and also makes me think that grokking this in a few years without the context of the full problem is going to be harder, even though it\u0027s obviously more compact. That might just be me being stupid.","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"804804f1ea64a9900263234e7805d4a3742786c9","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":17,"id":"cea9ce54_c86fa416","updated":"2025-10-14 08:33:55.000000000","message":"Thanks for the reviews. I will respin the patch shortly.","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"e1955a066b0a3af8fb0e0399d1cbb1c0d1a4d06b","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":17,"id":"26213dbf_440ca021","updated":"2025-10-13 07:08:58.000000000","message":"This shows that nova\u0027s functional test suite also passing with the new config option enabled https://review.opendev.org/c/openstack/nova/+/963036/4","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"e9805793bb8c9eb9122e927e4ea684bc15e069ec","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":17,"id":"de55b570_ff5e8ab4","updated":"2025-10-13 16:17:01.000000000","message":"i don\u0027t really have any other actionable feed back on this change.\n\ni think this is a good solution to the scalability problem as it reduces the\nperformance form N^N to N*N! which is a significant performance optimisation.","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"863a027a1d0a1dbc226681a5b75e343153df60a6","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":17,"id":"87460d1c_96e5c86a","in_reply_to":"60ccba78_08d7e5cc","updated":"2025-10-14 17:18:33.000000000","message":"Yeah, I still need to do the stepping with a debugger. I was able to grok the odometer approach without, but this one is less obvious to me.","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"804804f1ea64a9900263234e7805d4a3742786c9","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":17,"id":"60ccba78_08d7e5cc","in_reply_to":"93db0872_8445d242","updated":"2025-10-14 08:33:55.000000000","message":"I added more explanation about recursion depth. I hope it helps. I do suggest to make a paper drawing or a debugger step through of one of the unit tests. That greatly helps seeing the steps. For me the odometer based approach actually need a lot more debugging / tracing to understand than the recursion one. So I have to think that it is a matter of taste what is simpler to model in our heads.","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"8f897ce652813444bcc42d266da2087c323713bd","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":18,"id":"747b3320_7ba52775","updated":"2025-10-14 17:31:10.000000000","message":"Was reading the new recursion depth explanation and noticed a typo (and a couple other things that make it easier to read). Not critical, but commit message typos can never be fixed later....","commit_id":"98e56a39bd581b20dc80a60c1ef9d51980257893"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4f040a180e06e401f329460783aaeffd6d5c1ddd","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":18,"id":"5112ff68_b0d1bd88","updated":"2025-10-15 07:57:34.000000000","message":"fixed up the commit message","commit_id":"98e56a39bd581b20dc80a60c1ef9d51980257893"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"21729e0f75b2ee0e62093969a14e4f6eec998c2d","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":19,"id":"ca1ead84_bc200466","updated":"2025-10-15 13:25:15.000000000","message":"Proxying Sean\u0027s +2 as only english (prose and the name) changes have been made along with added asserts in the test since +2ing.","commit_id":"5b73b980d0f022de2855480f57c9d5f04a7a4712"}],"placement/conf/workarounds.py":[{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"42ead22d557af1f55c28bef7e13d7ebd79e8ee27","unresolved":true,"context_lines":[{"line_number":23,"context_line":"certain conditions. These should only be enabled in exceptional circumstances."},{"line_number":24,"context_line":"All options are linked against bug IDs, where more information on the issue can"},{"line_number":25,"context_line":"be found."},{"line_number":26,"context_line":"\"\"\")"},{"line_number":27,"context_line":""},{"line_number":28,"context_line":"workaround_opts \u003d ["},{"line_number":29,"context_line":"    cfg.BoolOpt("}],"source_content_type":"text/x-python","patch_set":5,"id":"d02ab3d4_355cddb1","line":26,"updated":"2025-10-03 15:29:50.000000000","message":"nit: mind fixing the whitespace issues when you respin","commit_id":"601b6075257c2d98ee24d5068012e54ce1bc0281"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"8492040ea2bfdd0ecc763e6e350dcb150f40e60f","unresolved":false,"context_lines":[{"line_number":23,"context_line":"certain conditions. These should only be enabled in exceptional circumstances."},{"line_number":24,"context_line":"All options are linked against bug IDs, where more information on the issue can"},{"line_number":25,"context_line":"be found."},{"line_number":26,"context_line":"\"\"\")"},{"line_number":27,"context_line":""},{"line_number":28,"context_line":"workaround_opts \u003d ["},{"line_number":29,"context_line":"    cfg.BoolOpt("}],"source_content_type":"text/x-python","patch_set":5,"id":"a43d0e08_5558259b","line":26,"in_reply_to":"d02ab3d4_355cddb1","updated":"2025-10-07 08:51:42.000000000","message":"Done","commit_id":"601b6075257c2d98ee24d5068012e54ce1bc0281"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"0f1951e1aa7ab7827ceccf9623f95867e08f9abc","unresolved":false,"context_lines":[{"line_number":27,"context_line":""},{"line_number":28,"context_line":"workaround_opts \u003d ["},{"line_number":29,"context_line":"    cfg.BoolOpt("},{"line_number":30,"context_line":"        \"optimize_for_wide_provider_trees\","},{"line_number":31,"context_line":"        default\u003dFalse,"},{"line_number":32,"context_line":"        help\u003d\"\"\""},{"line_number":33,"context_line":"Enable optimization of allocation candidate generation for wide provider trees."}],"source_content_type":"text/x-python","patch_set":11,"id":"dce77c0f_0872d7ac","line":30,"range":{"start_line":30,"start_character":22,"end_line":30,"end_character":27},"updated":"2025-10-07 14:01:20.000000000","message":"I meant to say before, but I think \"wide\" is a good way to describe these problematic topologies.","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"0f1951e1aa7ab7827ceccf9623f95867e08f9abc","unresolved":true,"context_lines":[{"line_number":35,"context_line":"As reported in `bug #2126751`_ in the situation where many similar child"},{"line_number":36,"context_line":"provider is defined under the same root provider, placement\u0027s allocation"},{"line_number":37,"context_line":"candidate generation algorithm scales poorly. This config option enables"},{"line_number":38,"context_line":"certain optimization that helps decreasing the time it takes to generate the"},{"line_number":39,"context_line":"GET /allocation_candidates response for queries requesting multiple resources"},{"line_number":40,"context_line":"from those child providers."},{"line_number":41,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"9e7fa162_9c3a90a8","line":38,"range":{"start_line":38,"start_character":32,"end_line":38,"end_character":42},"updated":"2025-10-07 14:01:20.000000000","message":"\"decrease\" in either case.","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"0f1951e1aa7ab7827ceccf9623f95867e08f9abc","unresolved":true,"context_lines":[{"line_number":35,"context_line":"As reported in `bug #2126751`_ in the situation where many similar child"},{"line_number":36,"context_line":"provider is defined under the same root provider, placement\u0027s allocation"},{"line_number":37,"context_line":"candidate generation algorithm scales poorly. This config option enables"},{"line_number":38,"context_line":"certain optimization that helps decreasing the time it takes to generate the"},{"line_number":39,"context_line":"GET /allocation_candidates response for queries requesting multiple resources"},{"line_number":40,"context_line":"from those child providers."},{"line_number":41,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"8e1b5b0d_b6eeeead","line":38,"range":{"start_line":38,"start_character":0,"end_line":38,"end_character":20},"updated":"2025-10-07 14:01:20.000000000","message":"either \"a certain optimization\" or \"certain optimizations\" (and then depluralize \"helps\" if the latter)","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c885651bbcca919d70089e29d2ab1935159d0ea","unresolved":false,"context_lines":[{"line_number":35,"context_line":"As reported in `bug #2126751`_ in the situation where many similar child"},{"line_number":36,"context_line":"provider is defined under the same root provider, placement\u0027s allocation"},{"line_number":37,"context_line":"candidate generation algorithm scales poorly. This config option enables"},{"line_number":38,"context_line":"certain optimization that helps decreasing the time it takes to generate the"},{"line_number":39,"context_line":"GET /allocation_candidates response for queries requesting multiple resources"},{"line_number":40,"context_line":"from those child providers."},{"line_number":41,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"da4a8a0b_93d41c50","line":38,"range":{"start_line":38,"start_character":0,"end_line":38,"end_character":20},"in_reply_to":"8e1b5b0d_b6eeeead","updated":"2025-10-08 12:29:50.000000000","message":"Done","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c885651bbcca919d70089e29d2ab1935159d0ea","unresolved":false,"context_lines":[{"line_number":35,"context_line":"As reported in `bug #2126751`_ in the situation where many similar child"},{"line_number":36,"context_line":"provider is defined under the same root provider, placement\u0027s allocation"},{"line_number":37,"context_line":"candidate generation algorithm scales poorly. This config option enables"},{"line_number":38,"context_line":"certain optimization that helps decreasing the time it takes to generate the"},{"line_number":39,"context_line":"GET /allocation_candidates response for queries requesting multiple resources"},{"line_number":40,"context_line":"from those child providers."},{"line_number":41,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"b789efa1_3316cff1","line":38,"range":{"start_line":38,"start_character":32,"end_line":38,"end_character":42},"in_reply_to":"9e7fa162_9c3a90a8","updated":"2025-10-08 12:29:50.000000000","message":"Done","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"0f1951e1aa7ab7827ceccf9623f95867e08f9abc","unresolved":true,"context_lines":[{"line_number":55,"context_line":"to support VMs with more than 4 such resources."},{"line_number":56,"context_line":"E.g.:"},{"line_number":57,"context_line":""},{"line_number":58,"context_line":"* Nova\u0027s PCI in Placement feautre is enabled and you have at least 8 PCI"},{"line_number":59,"context_line":"  device with the same product_id in a single compute and you are using flavors"},{"line_number":60,"context_line":"  requesting more than 4 such devices."},{"line_number":61,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"5150fb7b_65eda822","line":58,"range":{"start_line":58,"start_character":26,"end_line":58,"end_character":33},"updated":"2025-10-07 14:01:20.000000000","message":"\"feature\"","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c885651bbcca919d70089e29d2ab1935159d0ea","unresolved":false,"context_lines":[{"line_number":55,"context_line":"to support VMs with more than 4 such resources."},{"line_number":56,"context_line":"E.g.:"},{"line_number":57,"context_line":""},{"line_number":58,"context_line":"* Nova\u0027s PCI in Placement feautre is enabled and you have at least 8 PCI"},{"line_number":59,"context_line":"  device with the same product_id in a single compute and you are using flavors"},{"line_number":60,"context_line":"  requesting more than 4 such devices."},{"line_number":61,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"28638912_9fcf6a06","line":58,"range":{"start_line":58,"start_character":26,"end_line":58,"end_character":33},"in_reply_to":"5150fb7b_65eda822","updated":"2025-10-08 12:29:50.000000000","message":"Done","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"0f1951e1aa7ab7827ceccf9623f95867e08f9abc","unresolved":true,"context_lines":[{"line_number":56,"context_line":"E.g.:"},{"line_number":57,"context_line":""},{"line_number":58,"context_line":"* Nova\u0027s PCI in Placement feautre is enabled and you have at least 8 PCI"},{"line_number":59,"context_line":"  device with the same product_id in a single compute and you are using flavors"},{"line_number":60,"context_line":"  requesting more than 4 such devices."},{"line_number":61,"context_line":""},{"line_number":62,"context_line":"* Nova GPU support is enabled and you have at least 8 GPUs per compute node"}],"source_content_type":"text/x-python","patch_set":11,"id":"8841b0a9_16a99f9b","line":59,"range":{"start_line":59,"start_character":2,"end_line":59,"end_character":8},"updated":"2025-10-07 14:01:20.000000000","message":"\"devices\"","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c885651bbcca919d70089e29d2ab1935159d0ea","unresolved":false,"context_lines":[{"line_number":56,"context_line":"E.g.:"},{"line_number":57,"context_line":""},{"line_number":58,"context_line":"* Nova\u0027s PCI in Placement feautre is enabled and you have at least 8 PCI"},{"line_number":59,"context_line":"  device with the same product_id in a single compute and you are using flavors"},{"line_number":60,"context_line":"  requesting more than 4 such devices."},{"line_number":61,"context_line":""},{"line_number":62,"context_line":"* Nova GPU support is enabled and you have at least 8 GPUs per compute node"}],"source_content_type":"text/x-python","patch_set":11,"id":"647f66c8_6cb9b08f","line":59,"range":{"start_line":59,"start_character":2,"end_line":59,"end_character":8},"in_reply_to":"8841b0a9_16a99f9b","updated":"2025-10-08 12:29:50.000000000","message":"Done","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"0f1951e1aa7ab7827ceccf9623f95867e08f9abc","unresolved":true,"context_lines":[{"line_number":59,"context_line":"  device with the same product_id in a single compute and you are using flavors"},{"line_number":60,"context_line":"  requesting more than 4 such devices."},{"line_number":61,"context_line":""},{"line_number":62,"context_line":"* Nova GPU support is enabled and you have at least 8 GPUs per compute node"},{"line_number":63,"context_line":"  while requesting more than 4 per VM."},{"line_number":64,"context_line":""},{"line_number":65,"context_line":"**When not to enable:** If you have a flat resource provider tree, i.e. all"}],"source_content_type":"text/x-python","patch_set":11,"id":"924f5adb_bf368e9e","line":62,"range":{"start_line":62,"start_character":2,"end_line":62,"end_character":6},"updated":"2025-10-07 14:01:20.000000000","message":"\"Nova\u0027s\"","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c885651bbcca919d70089e29d2ab1935159d0ea","unresolved":false,"context_lines":[{"line_number":59,"context_line":"  device with the same product_id in a single compute and you are using flavors"},{"line_number":60,"context_line":"  requesting more than 4 such devices."},{"line_number":61,"context_line":""},{"line_number":62,"context_line":"* Nova GPU support is enabled and you have at least 8 GPUs per compute node"},{"line_number":63,"context_line":"  while requesting more than 4 per VM."},{"line_number":64,"context_line":""},{"line_number":65,"context_line":"**When not to enable:** If you have a flat resource provider tree, i.e. all"}],"source_content_type":"text/x-python","patch_set":11,"id":"c4ff0110_e15e3922","line":62,"range":{"start_line":62,"start_character":2,"end_line":62,"end_character":6},"in_reply_to":"924f5adb_bf368e9e","updated":"2025-10-08 12:29:50.000000000","message":"Done","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"0f1951e1aa7ab7827ceccf9623f95867e08f9abc","unresolved":true,"context_lines":[{"line_number":63,"context_line":"  while requesting more than 4 per VM."},{"line_number":64,"context_line":""},{"line_number":65,"context_line":"**When not to enable:** If you have a flat resource provider tree, i.e. all"},{"line_number":66,"context_line":"resources reported on the root provider. Or if your flavors not requesting"},{"line_number":67,"context_line":"more than 4 PCI or GPU resources of the same type."},{"line_number":68,"context_line":""},{"line_number":69,"context_line":"Related options:"}],"source_content_type":"text/x-python","patch_set":11,"id":"74793f50_20ca0298","line":66,"range":{"start_line":66,"start_character":60,"end_line":66,"end_character":63},"updated":"2025-10-07 14:01:20.000000000","message":"\"are not\"","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c885651bbcca919d70089e29d2ab1935159d0ea","unresolved":false,"context_lines":[{"line_number":63,"context_line":"  while requesting more than 4 per VM."},{"line_number":64,"context_line":""},{"line_number":65,"context_line":"**When not to enable:** If you have a flat resource provider tree, i.e. all"},{"line_number":66,"context_line":"resources reported on the root provider. Or if your flavors not requesting"},{"line_number":67,"context_line":"more than 4 PCI or GPU resources of the same type."},{"line_number":68,"context_line":""},{"line_number":69,"context_line":"Related options:"}],"source_content_type":"text/x-python","patch_set":11,"id":"1284b104_7213545b","line":66,"range":{"start_line":66,"start_character":60,"end_line":66,"end_character":63},"in_reply_to":"74793f50_20ca0298","updated":"2025-10-08 12:29:50.000000000","message":"Done","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"c6c26cef04ebe008aebbec07d3e65e1742c8fb0f","unresolved":true,"context_lines":[{"line_number":70,"context_line":""},{"line_number":71,"context_line":"* ``[placement]max_allocation_candidates``: If you need to enable the"},{"line_number":72,"context_line":"  this optimization then you are also in a situation where you want to set"},{"line_number":73,"context_line":"  ``max_allocation_candidates`` to number not more than 1000."},{"line_number":74,"context_line":"* ``[placement]allocation_candidates_generation_strategy``: If you use"},{"line_number":75,"context_line":"  ``max_allocation_candidates`` then it is suggested to configure"},{"line_number":76,"context_line":"  ``allocation_candidates_generation_strategy`` to ``breadth-first`` balanced"}],"source_content_type":"text/x-python","patch_set":11,"id":"c9465d9d_80186e2e","line":73,"range":{"start_line":73,"start_character":32,"end_line":73,"end_character":41},"updated":"2025-10-08 13:41:48.000000000","message":"```suggestion\n  ``max_allocation_candidates`` to a number not more than 1000.\n```","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"d496f952929dffe8e89752aa84bec8205b8f6bda","unresolved":false,"context_lines":[{"line_number":70,"context_line":""},{"line_number":71,"context_line":"* ``[placement]max_allocation_candidates``: If you need to enable the"},{"line_number":72,"context_line":"  this optimization then you are also in a situation where you want to set"},{"line_number":73,"context_line":"  ``max_allocation_candidates`` to number not more than 1000."},{"line_number":74,"context_line":"* ``[placement]allocation_candidates_generation_strategy``: If you use"},{"line_number":75,"context_line":"  ``max_allocation_candidates`` then it is suggested to configure"},{"line_number":76,"context_line":"  ``allocation_candidates_generation_strategy`` to ``breadth-first`` balanced"}],"source_content_type":"text/x-python","patch_set":11,"id":"59167835_9f1fd54b","line":73,"range":{"start_line":73,"start_character":32,"end_line":73,"end_character":41},"in_reply_to":"c9465d9d_80186e2e","updated":"2025-10-08 14:10:14.000000000","message":"Done","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"c6c26cef04ebe008aebbec07d3e65e1742c8fb0f","unresolved":true,"context_lines":[{"line_number":73,"context_line":"  ``max_allocation_candidates`` to number not more than 1000."},{"line_number":74,"context_line":"* ``[placement]allocation_candidates_generation_strategy``: If you use"},{"line_number":75,"context_line":"  ``max_allocation_candidates`` then it is suggested to configure"},{"line_number":76,"context_line":"  ``allocation_candidates_generation_strategy`` to ``breadth-first`` balanced"},{"line_number":77,"context_line":"  candidates across available compute nodes."},{"line_number":78,"context_line":""},{"line_number":79,"context_line":".. _bug #2126751: https://bugs.launchpad.net/placement/+bug/2126751"},{"line_number":80,"context_line":"\"\"\"),"}],"source_content_type":"text/x-python","patch_set":11,"id":"5bca6398_956c4111","line":77,"range":{"start_line":76,"start_character":69,"end_line":77,"end_character":44},"updated":"2025-10-08 13:41:48.000000000","message":"```suggestion\n  ``allocation_candidates_generation_strategy`` to ``breadth-first`` which will return candidates balanced\n  across available compute nodes.\n```","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"d496f952929dffe8e89752aa84bec8205b8f6bda","unresolved":false,"context_lines":[{"line_number":73,"context_line":"  ``max_allocation_candidates`` to number not more than 1000."},{"line_number":74,"context_line":"* ``[placement]allocation_candidates_generation_strategy``: If you use"},{"line_number":75,"context_line":"  ``max_allocation_candidates`` then it is suggested to configure"},{"line_number":76,"context_line":"  ``allocation_candidates_generation_strategy`` to ``breadth-first`` balanced"},{"line_number":77,"context_line":"  candidates across available compute nodes."},{"line_number":78,"context_line":""},{"line_number":79,"context_line":".. _bug #2126751: https://bugs.launchpad.net/placement/+bug/2126751"},{"line_number":80,"context_line":"\"\"\"),"}],"source_content_type":"text/x-python","patch_set":11,"id":"843a2014_3a6299eb","line":77,"range":{"start_line":76,"start_character":69,"end_line":77,"end_character":44},"in_reply_to":"5bca6398_956c4111","updated":"2025-10-08 14:10:14.000000000","message":"Done","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"0f1951e1aa7ab7827ceccf9623f95867e08f9abc","unresolved":true,"context_lines":[{"line_number":75,"context_line":"  ``max_allocation_candidates`` then it is suggested to configure"},{"line_number":76,"context_line":"  ``allocation_candidates_generation_strategy`` to ``breadth-first`` balanced"},{"line_number":77,"context_line":"  candidates across available compute nodes."},{"line_number":78,"context_line":""},{"line_number":79,"context_line":".. _bug #2126751: https://bugs.launchpad.net/placement/+bug/2126751"},{"line_number":80,"context_line":"\"\"\"),"},{"line_number":81,"context_line":"]"}],"source_content_type":"text/x-python","patch_set":11,"id":"e957d363_07c9994b","line":78,"updated":"2025-10-07 14:01:20.000000000","message":"These changes don\u0027t sound better to me than the original text.. Was this intentional?","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"d496f952929dffe8e89752aa84bec8205b8f6bda","unresolved":false,"context_lines":[{"line_number":75,"context_line":"  ``max_allocation_candidates`` then it is suggested to configure"},{"line_number":76,"context_line":"  ``allocation_candidates_generation_strategy`` to ``breadth-first`` balanced"},{"line_number":77,"context_line":"  candidates across available compute nodes."},{"line_number":78,"context_line":""},{"line_number":79,"context_line":".. _bug #2126751: https://bugs.launchpad.net/placement/+bug/2126751"},{"line_number":80,"context_line":"\"\"\"),"},{"line_number":81,"context_line":"]"}],"source_content_type":"text/x-python","patch_set":11,"id":"09a67a7e_6fb08e85","line":78,"in_reply_to":"2614463f_7339c4ab","updated":"2025-10-08 14:10:14.000000000","message":"Done","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"c6c26cef04ebe008aebbec07d3e65e1742c8fb0f","unresolved":true,"context_lines":[{"line_number":75,"context_line":"  ``max_allocation_candidates`` then it is suggested to configure"},{"line_number":76,"context_line":"  ``allocation_candidates_generation_strategy`` to ``breadth-first`` balanced"},{"line_number":77,"context_line":"  candidates across available compute nodes."},{"line_number":78,"context_line":""},{"line_number":79,"context_line":".. _bug #2126751: https://bugs.launchpad.net/placement/+bug/2126751"},{"line_number":80,"context_line":"\"\"\"),"},{"line_number":81,"context_line":"]"}],"source_content_type":"text/x-python","patch_set":11,"id":"2614463f_7339c4ab","line":78,"in_reply_to":"c814cc42_f996887a","updated":"2025-10-08 13:41:48.000000000","message":"Yeah the PS7 version of this sentence was proper english and this one is not. But I think the above suggested change is closest to what you\u0027re going for in the later ones. The edit to the first sentence is fine, but I noticed one error while re-reading it.","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c885651bbcca919d70089e29d2ab1935159d0ea","unresolved":true,"context_lines":[{"line_number":75,"context_line":"  ``max_allocation_candidates`` then it is suggested to configure"},{"line_number":76,"context_line":"  ``allocation_candidates_generation_strategy`` to ``breadth-first`` balanced"},{"line_number":77,"context_line":"  candidates across available compute nodes."},{"line_number":78,"context_line":""},{"line_number":79,"context_line":".. _bug #2126751: https://bugs.launchpad.net/placement/+bug/2126751"},{"line_number":80,"context_line":"\"\"\"),"},{"line_number":81,"context_line":"]"}],"source_content_type":"text/x-python","patch_set":11,"id":"c814cc42_f996887a","line":78,"in_reply_to":"e957d363_07c9994b","updated":"2025-10-08 12:29:50.000000000","message":"Which changes do you refer to? The whole config description change between PS 7 and 11. Those are inspired by https://review.opendev.org/c/openstack/placement/+/963006/2/placement/conf/workarounds.py\n\nIf you just refer to the Related options section. I can revert that back to PS7 version easily.","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"e11e23607fe657a5ceaf51078b1f535a4cbe817b","unresolved":false,"context_lines":[{"line_number":23,"context_line":"certain conditions. These should only be enabled in exceptional circumstances."},{"line_number":24,"context_line":"All options are linked against bug IDs, where more information on the issue can"},{"line_number":25,"context_line":"be found."},{"line_number":26,"context_line":"\"\"\")"},{"line_number":27,"context_line":""},{"line_number":28,"context_line":"workaround_opts \u003d ["},{"line_number":29,"context_line":"    cfg.BoolOpt("}],"source_content_type":"text/x-python","patch_set":16,"id":"df891a4d_aeb0c716","line":26,"updated":"2025-10-09 21:16:43.000000000","message":"+1 this is similar but slightly better worded then the nova equivalent since its higher level.\n\nhttps://github.com/openstack/nova/blob/076498ed95958a5d6ccb784f3d336657584bc63a/nova/conf/workarounds.py#L39-L42","commit_id":"d73e760ec1c4fa762632b9c3ca1c2edb724ff6b7"}],"placement/objects/allocation_candidate.py":[{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"bfceb1f543de08314de32fd62a69a567586038e0","unresolved":true,"context_lines":[{"line_number":269,"context_line":""},{"line_number":270,"context_line":"    def __repr__(self):"},{"line_number":271,"context_line":"        return str("},{"line_number":272,"context_line":"            (self.resource_provider.uuid, self.resource_class, self.amount))"},{"line_number":273,"context_line":""},{"line_number":274,"context_line":""},{"line_number":275,"context_line":"class ProviderSummary(object):"}],"source_content_type":"text/x-python","patch_set":5,"id":"cdf9cf02_6ac57cdb","line":272,"updated":"2025-10-03 15:05:35.000000000","message":"This seems random but it is extremely useful if you want to look and step through the algo in a debugger","commit_id":"601b6075257c2d98ee24d5068012e54ce1bc0281"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"8492040ea2bfdd0ecc763e6e350dcb150f40e60f","unresolved":false,"context_lines":[{"line_number":269,"context_line":""},{"line_number":270,"context_line":"    def __repr__(self):"},{"line_number":271,"context_line":"        return str("},{"line_number":272,"context_line":"            (self.resource_provider.uuid, self.resource_class, self.amount))"},{"line_number":273,"context_line":""},{"line_number":274,"context_line":""},{"line_number":275,"context_line":"class ProviderSummary(object):"}],"source_content_type":"text/x-python","patch_set":5,"id":"d3619232_3d9c0d73","line":272,"in_reply_to":"b5ce04f6_1d88e270","updated":"2025-10-07 08:51:42.000000000","message":"Acknowledged","commit_id":"601b6075257c2d98ee24d5068012e54ce1bc0281"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"f50eb7635a7656cba9737fe2b527344b62652b0e","unresolved":true,"context_lines":[{"line_number":269,"context_line":""},{"line_number":270,"context_line":"    def __repr__(self):"},{"line_number":271,"context_line":"        return str("},{"line_number":272,"context_line":"            (self.resource_provider.uuid, self.resource_class, self.amount))"},{"line_number":273,"context_line":""},{"line_number":274,"context_line":""},{"line_number":275,"context_line":"class ProviderSummary(object):"}],"source_content_type":"text/x-python","patch_set":5,"id":"b5ce04f6_1d88e270","line":272,"in_reply_to":"cdf9cf02_6ac57cdb","updated":"2025-10-06 12:34:39.000000000","message":"not at all\nhaving good debuging support helps a lot when diagnosing theses issues so i think its ok to improve the repr output. when we find it difficutl to debug code like this i think its good to imporve that DX as part of fixign bugs. it could be its own commit or in the repoducer but i think its ok in the fix patch as well.\n\nif this had been written a few years later we may have used dataclases which woudl have provided some of the default magic methods liek this and the eq/hash function","commit_id":"601b6075257c2d98ee24d5068012e54ce1bc0281"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"bfceb1f543de08314de32fd62a69a567586038e0","unresolved":true,"context_lines":[{"line_number":821,"context_line":"        #   ...,"},{"line_number":822,"context_line":"        # ]"},{"line_number":823,"context_line":"        _get_product_generator(rw_ctx)(*list(areq_lists_by_suffix.values()))"},{"line_number":824,"context_line":"        # itertools.product(*list(areq_lists_by_suffix.values()))"},{"line_number":825,"context_line":"        for areq_lists_by_suffix in areq_lists_by_anchor.values()"},{"line_number":826,"context_line":"        # Filter out any entries that don\u0027t have allocation requests for"},{"line_number":827,"context_line":"        # *all* suffixes (i.e. all RequestGroups)"}],"source_content_type":"text/x-python","patch_set":5,"id":"5a6d3260_6695a021","line":824,"updated":"2025-10-03 15:05:35.000000000","message":"delete this","commit_id":"601b6075257c2d98ee24d5068012e54ce1bc0281"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"a9acd61d22685e3bdc73a572634ad9381b5bb885","unresolved":false,"context_lines":[{"line_number":821,"context_line":"        #   ...,"},{"line_number":822,"context_line":"        # ]"},{"line_number":823,"context_line":"        _get_product_generator(rw_ctx)(*list(areq_lists_by_suffix.values()))"},{"line_number":824,"context_line":"        # itertools.product(*list(areq_lists_by_suffix.values()))"},{"line_number":825,"context_line":"        for areq_lists_by_suffix in areq_lists_by_anchor.values()"},{"line_number":826,"context_line":"        # Filter out any entries that don\u0027t have allocation requests for"},{"line_number":827,"context_line":"        # *all* suffixes (i.e. all RequestGroups)"}],"source_content_type":"text/x-python","patch_set":5,"id":"072f3c3d_f08ffb79","line":824,"in_reply_to":"5a6d3260_6695a021","updated":"2025-10-07 08:50:45.000000000","message":"Done","commit_id":"601b6075257c2d98ee24d5068012e54ce1bc0281"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"bfceb1f543de08314de32fd62a69a567586038e0","unresolved":true,"context_lines":[{"line_number":847,"context_line":"    raise ValueError(\"Strategy \u0027%s\u0027 not recognized\" % strategy)"},{"line_number":848,"context_line":""},{"line_number":849,"context_line":""},{"line_number":850,"context_line":"def _combine_and_check_capacity(rw_ctx, areq_list):"},{"line_number":851,"context_line":"    \"\"\"Returns the consolidated allocation request (allocation candidate) if"},{"line_number":852,"context_line":"    it passed the capacity check after the individual group allocations from"},{"line_number":853,"context_line":"    the areq_list are combined. Returns None otherwise."}],"source_content_type":"text/x-python","patch_set":5,"id":"317245b0_7275a206","line":850,"updated":"2025-10-03 15:05:35.000000000","message":"this is an ugly combination of two steps, but this is the way I can meaningfully deduplicate the old and new codepath using the same logic and having the same explanation for the logic","commit_id":"601b6075257c2d98ee24d5068012e54ce1bc0281"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"7b522defb06680577fb0da245534113548e44932","unresolved":false,"context_lines":[{"line_number":847,"context_line":"    raise ValueError(\"Strategy \u0027%s\u0027 not recognized\" % strategy)"},{"line_number":848,"context_line":""},{"line_number":849,"context_line":""},{"line_number":850,"context_line":"def _combine_and_check_capacity(rw_ctx, areq_list):"},{"line_number":851,"context_line":"    \"\"\"Returns the consolidated allocation request (allocation candidate) if"},{"line_number":852,"context_line":"    it passed the capacity check after the individual group allocations from"},{"line_number":853,"context_line":"    the areq_list are combined. Returns None otherwise."}],"source_content_type":"text/x-python","patch_set":5,"id":"42b6c6e7_cd860478","line":850,"in_reply_to":"317245b0_7275a206","updated":"2025-10-07 12:16:04.000000000","message":"Acknowledged","commit_id":"601b6075257c2d98ee24d5068012e54ce1bc0281"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"bfceb1f543de08314de32fd62a69a567586038e0","unresolved":true,"context_lines":[{"line_number":934,"context_line":"        if optimize:"},{"line_number":935,"context_line":"            areq_list, areq \u003d product"},{"line_number":936,"context_line":"        else:"},{"line_number":937,"context_line":"            areq_list \u003d product"},{"line_number":938,"context_line":""},{"line_number":939,"context_line":"        # At this point, each AllocationRequest in areq_list is still"},{"line_number":940,"context_line":"        # marked as use_same_provider. This is necessary to filter by group"}],"source_content_type":"text/x-python","patch_set":5,"id":"cfdd8e0c_9e1bdae8","line":937,"updated":"2025-10-03 15:05:35.000000000","message":"As mentioned in the commit message this ugliness is the result of an optimization to avoid calling  _consolidate_allocation_requests for the same candidate twice in the new algo case. That call is expensive and has unfortunate side effect","commit_id":"601b6075257c2d98ee24d5068012e54ce1bc0281"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"7b522defb06680577fb0da245534113548e44932","unresolved":false,"context_lines":[{"line_number":934,"context_line":"        if optimize:"},{"line_number":935,"context_line":"            areq_list, areq \u003d product"},{"line_number":936,"context_line":"        else:"},{"line_number":937,"context_line":"            areq_list \u003d product"},{"line_number":938,"context_line":""},{"line_number":939,"context_line":"        # At this point, each AllocationRequest in areq_list is still"},{"line_number":940,"context_line":"        # marked as use_same_provider. This is necessary to filter by group"}],"source_content_type":"text/x-python","patch_set":5,"id":"2342ca3b_c903520b","line":937,"in_reply_to":"cfdd8e0c_9e1bdae8","updated":"2025-10-07 12:16:04.000000000","message":"Acknowledged","commit_id":"601b6075257c2d98ee24d5068012e54ce1bc0281"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"1619b02507dad1f2ac67b631f3fb14ebd77770be","unresolved":true,"context_lines":[{"line_number":702,"context_line":"    allowing to prune the search space based on a test result of a partial"},{"line_number":703,"context_line":"    product with a known invalid index prefix."},{"line_number":704,"context_line":""},{"line_number":705,"context_line":"    In our specific case each input iterable is a list of possible allocation"},{"line_number":706,"context_line":"    for a request group independently form the other request groups"},{"line_number":707,"context_line":"    represented by the other input iterators. This function generates each"},{"line_number":708,"context_line":"    product from the iterables and checks them that there is no overcapacity"}],"source_content_type":"text/x-python","patch_set":11,"id":"f43b5b08_b7b6c13a","line":705,"range":{"start_line":705,"start_character":67,"end_line":705,"end_character":77},"updated":"2025-10-07 14:05:12.000000000","message":"\"allocations\"","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c885651bbcca919d70089e29d2ab1935159d0ea","unresolved":false,"context_lines":[{"line_number":702,"context_line":"    allowing to prune the search space based on a test result of a partial"},{"line_number":703,"context_line":"    product with a known invalid index prefix."},{"line_number":704,"context_line":""},{"line_number":705,"context_line":"    In our specific case each input iterable is a list of possible allocation"},{"line_number":706,"context_line":"    for a request group independently form the other request groups"},{"line_number":707,"context_line":"    represented by the other input iterators. This function generates each"},{"line_number":708,"context_line":"    product from the iterables and checks them that there is no overcapacity"}],"source_content_type":"text/x-python","patch_set":11,"id":"a67e6d69_9a13e017","line":705,"range":{"start_line":705,"start_character":67,"end_line":705,"end_character":77},"in_reply_to":"f43b5b08_b7b6c13a","updated":"2025-10-08 12:29:50.000000000","message":"Done","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"1619b02507dad1f2ac67b631f3fb14ebd77770be","unresolved":true,"context_lines":[{"line_number":703,"context_line":"    product with a known invalid index prefix."},{"line_number":704,"context_line":""},{"line_number":705,"context_line":"    In our specific case each input iterable is a list of possible allocation"},{"line_number":706,"context_line":"    for a request group independently form the other request groups"},{"line_number":707,"context_line":"    represented by the other input iterators. This function generates each"},{"line_number":708,"context_line":"    product from the iterables and checks them that there is no overcapacity"},{"line_number":709,"context_line":"    due to different groups trying to grab the same resource. If"}],"source_content_type":"text/x-python","patch_set":11,"id":"422387f8_3db8dba9","line":706,"range":{"start_line":706,"start_character":38,"end_line":706,"end_character":42},"updated":"2025-10-07 14:05:12.000000000","message":"\"from\"","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c885651bbcca919d70089e29d2ab1935159d0ea","unresolved":false,"context_lines":[{"line_number":703,"context_line":"    product with a known invalid index prefix."},{"line_number":704,"context_line":""},{"line_number":705,"context_line":"    In our specific case each input iterable is a list of possible allocation"},{"line_number":706,"context_line":"    for a request group independently form the other request groups"},{"line_number":707,"context_line":"    represented by the other input iterators. This function generates each"},{"line_number":708,"context_line":"    product from the iterables and checks them that there is no overcapacity"},{"line_number":709,"context_line":"    due to different groups trying to grab the same resource. If"}],"source_content_type":"text/x-python","patch_set":11,"id":"5794df83_90492193","line":706,"range":{"start_line":706,"start_character":38,"end_line":706,"end_character":42},"in_reply_to":"422387f8_3db8dba9","updated":"2025-10-08 12:29:50.000000000","message":"Done","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"1619b02507dad1f2ac67b631f3fb14ebd77770be","unresolved":true,"context_lines":[{"line_number":705,"context_line":"    In our specific case each input iterable is a list of possible allocation"},{"line_number":706,"context_line":"    for a request group independently form the other request groups"},{"line_number":707,"context_line":"    represented by the other input iterators. This function generates each"},{"line_number":708,"context_line":"    product from the iterables and checks them that there is no overcapacity"},{"line_number":709,"context_line":"    due to different groups trying to grab the same resource. If"},{"line_number":710,"context_line":"    an invalid product found it is known that which is the leftmost group"},{"line_number":711,"context_line":"    that caused the overallocation and all possible product with this prefix"}],"source_content_type":"text/x-python","patch_set":11,"id":"c44caaed_4170356e","line":708,"range":{"start_line":708,"start_character":42,"end_line":708,"end_character":46},"updated":"2025-10-07 14:05:12.000000000","message":"s/them//","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c885651bbcca919d70089e29d2ab1935159d0ea","unresolved":false,"context_lines":[{"line_number":705,"context_line":"    In our specific case each input iterable is a list of possible allocation"},{"line_number":706,"context_line":"    for a request group independently form the other request groups"},{"line_number":707,"context_line":"    represented by the other input iterators. This function generates each"},{"line_number":708,"context_line":"    product from the iterables and checks them that there is no overcapacity"},{"line_number":709,"context_line":"    due to different groups trying to grab the same resource. If"},{"line_number":710,"context_line":"    an invalid product found it is known that which is the leftmost group"},{"line_number":711,"context_line":"    that caused the overallocation and all possible product with this prefix"}],"source_content_type":"text/x-python","patch_set":11,"id":"0ab56e2e_c0857574","line":708,"range":{"start_line":708,"start_character":42,"end_line":708,"end_character":46},"in_reply_to":"c44caaed_4170356e","updated":"2025-10-08 12:29:50.000000000","message":"Done","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"1619b02507dad1f2ac67b631f3fb14ebd77770be","unresolved":true,"context_lines":[{"line_number":707,"context_line":"    represented by the other input iterators. This function generates each"},{"line_number":708,"context_line":"    product from the iterables and checks them that there is no overcapacity"},{"line_number":709,"context_line":"    due to different groups trying to grab the same resource. If"},{"line_number":710,"context_line":"    an invalid product found it is known that which is the leftmost group"},{"line_number":711,"context_line":"    that caused the overallocation and all possible product with this prefix"},{"line_number":712,"context_line":"    is skipped by the generator to save time."},{"line_number":713,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"2bb5202f_3c004478","line":710,"range":{"start_line":710,"start_character":41,"end_line":710,"end_character":45},"updated":"2025-10-07 14:05:12.000000000","message":"s/that//","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c885651bbcca919d70089e29d2ab1935159d0ea","unresolved":false,"context_lines":[{"line_number":707,"context_line":"    represented by the other input iterators. This function generates each"},{"line_number":708,"context_line":"    product from the iterables and checks them that there is no overcapacity"},{"line_number":709,"context_line":"    due to different groups trying to grab the same resource. If"},{"line_number":710,"context_line":"    an invalid product found it is known that which is the leftmost group"},{"line_number":711,"context_line":"    that caused the overallocation and all possible product with this prefix"},{"line_number":712,"context_line":"    is skipped by the generator to save time."},{"line_number":713,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"6ed834f8_6706b300","line":710,"range":{"start_line":710,"start_character":41,"end_line":710,"end_character":45},"in_reply_to":"2bb5202f_3c004478","updated":"2025-10-08 12:29:50.000000000","message":"Done","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"1619b02507dad1f2ac67b631f3fb14ebd77770be","unresolved":true,"context_lines":[{"line_number":708,"context_line":"    product from the iterables and checks them that there is no overcapacity"},{"line_number":709,"context_line":"    due to different groups trying to grab the same resource. If"},{"line_number":710,"context_line":"    an invalid product found it is known that which is the leftmost group"},{"line_number":711,"context_line":"    that caused the overallocation and all possible product with this prefix"},{"line_number":712,"context_line":"    is skipped by the generator to save time."},{"line_number":713,"context_line":""},{"line_number":714,"context_line":"    :param args: A variable number of iterable objects."}],"source_content_type":"text/x-python","patch_set":11,"id":"6941243e_b07f8409","line":711,"range":{"start_line":711,"start_character":52,"end_line":711,"end_character":59},"updated":"2025-10-07 14:05:12.000000000","message":"\"products\"","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c885651bbcca919d70089e29d2ab1935159d0ea","unresolved":false,"context_lines":[{"line_number":708,"context_line":"    product from the iterables and checks them that there is no overcapacity"},{"line_number":709,"context_line":"    due to different groups trying to grab the same resource. If"},{"line_number":710,"context_line":"    an invalid product found it is known that which is the leftmost group"},{"line_number":711,"context_line":"    that caused the overallocation and all possible product with this prefix"},{"line_number":712,"context_line":"    is skipped by the generator to save time."},{"line_number":713,"context_line":""},{"line_number":714,"context_line":"    :param args: A variable number of iterable objects."}],"source_content_type":"text/x-python","patch_set":11,"id":"4019cb29_605bb09a","line":711,"range":{"start_line":711,"start_character":52,"end_line":711,"end_character":59},"in_reply_to":"6941243e_b07f8409","updated":"2025-10-08 12:29:50.000000000","message":"Done","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"1619b02507dad1f2ac67b631f3fb14ebd77770be","unresolved":true,"context_lines":[{"line_number":709,"context_line":"    due to different groups trying to grab the same resource. If"},{"line_number":710,"context_line":"    an invalid product found it is known that which is the leftmost group"},{"line_number":711,"context_line":"    that caused the overallocation and all possible product with this prefix"},{"line_number":712,"context_line":"    is skipped by the generator to save time."},{"line_number":713,"context_line":""},{"line_number":714,"context_line":"    :param args: A variable number of iterable objects."},{"line_number":715,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"45accdd7_b5a433fc","line":712,"range":{"start_line":712,"start_character":4,"end_line":712,"end_character":6},"updated":"2025-10-07 14:05:12.000000000","message":"\"are\"","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c885651bbcca919d70089e29d2ab1935159d0ea","unresolved":false,"context_lines":[{"line_number":709,"context_line":"    due to different groups trying to grab the same resource. If"},{"line_number":710,"context_line":"    an invalid product found it is known that which is the leftmost group"},{"line_number":711,"context_line":"    that caused the overallocation and all possible product with this prefix"},{"line_number":712,"context_line":"    is skipped by the generator to save time."},{"line_number":713,"context_line":""},{"line_number":714,"context_line":"    :param args: A variable number of iterable objects."},{"line_number":715,"context_line":""}],"source_content_type":"text/x-python","patch_set":11,"id":"f108ab97_958704dd","line":712,"range":{"start_line":712,"start_character":4,"end_line":712,"end_character":6},"in_reply_to":"45accdd7_b5a433fc","updated":"2025-10-08 12:29:50.000000000","message":"Done","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"e11e23607fe657a5ceaf51078b1f535a4cbe817b","unresolved":true,"context_lines":[{"line_number":749,"context_line":"    candidates (areq_lists) for the given anchor"},{"line_number":750,"context_line":"    \"\"\""},{"line_number":751,"context_line":"    return ["},{"line_number":752,"context_line":"        # We\u0027re using itertools.product to go from this:"},{"line_number":753,"context_line":"        # areq_lists_by_suffix \u003d {"},{"line_number":754,"context_line":"        #     \u0027\u0027:   [areq__A,   areq__B,   ...],"},{"line_number":755,"context_line":"        #     \u00271\u0027:  [areq_1_A,  areq_1_B,  ...],"}],"source_content_type":"text/x-python","patch_set":16,"id":"2a545a61_460ad7c0","line":752,"range":{"start_line":752,"start_character":22,"end_line":752,"end_character":39},"updated":"2025-10-09 21:16:43.000000000","message":"nit: comment are always wrong but we could update this","commit_id":"d73e760ec1c4fa762632b9c3ca1c2edb724ff6b7"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"ffe1d6af38b025f6946e37ce45de3d2686fb702a","unresolved":false,"context_lines":[{"line_number":749,"context_line":"    candidates (areq_lists) for the given anchor"},{"line_number":750,"context_line":"    \"\"\""},{"line_number":751,"context_line":"    return ["},{"line_number":752,"context_line":"        # We\u0027re using itertools.product to go from this:"},{"line_number":753,"context_line":"        # areq_lists_by_suffix \u003d {"},{"line_number":754,"context_line":"        #     \u0027\u0027:   [areq__A,   areq__B,   ...],"},{"line_number":755,"context_line":"        #     \u00271\u0027:  [areq_1_A,  areq_1_B,  ...],"}],"source_content_type":"text/x-python","patch_set":16,"id":"e5657670_b43391b6","line":752,"range":{"start_line":752,"start_character":22,"end_line":752,"end_character":39},"in_reply_to":"2a545a61_460ad7c0","updated":"2025-10-10 10:03:52.000000000","message":"Done","commit_id":"d73e760ec1c4fa762632b9c3ca1c2edb724ff6b7"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"8731dab87d088203ff46df27d992f99af79b2a51","unresolved":true,"context_lines":[{"line_number":708,"context_line":"    needed after the call just the result of the exceeds_capacity check. In"},{"line_number":709,"context_line":"    return this is a lot more performant than the former."},{"line_number":710,"context_line":""},{"line_number":711,"context_line":"    :return: True if the consolidated allocation request more resources that"},{"line_number":712,"context_line":"             available, False otherwise."},{"line_number":713,"context_line":"    \"\"\""},{"line_number":714,"context_line":"    amount_by_rp_rc \u003d collections.defaultdict(int)"}],"source_content_type":"text/x-python","patch_set":17,"id":"11d2551a_b5a18a76","line":711,"range":{"start_line":711,"start_character":49,"end_line":711,"end_character":56},"updated":"2025-10-13 18:47:47.000000000","message":"\"requests\"","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"8731dab87d088203ff46df27d992f99af79b2a51","unresolved":true,"context_lines":[{"line_number":708,"context_line":"    needed after the call just the result of the exceeds_capacity check. In"},{"line_number":709,"context_line":"    return this is a lot more performant than the former."},{"line_number":710,"context_line":""},{"line_number":711,"context_line":"    :return: True if the consolidated allocation request more resources that"},{"line_number":712,"context_line":"             available, False otherwise."},{"line_number":713,"context_line":"    \"\"\""},{"line_number":714,"context_line":"    amount_by_rp_rc \u003d collections.defaultdict(int)"}],"source_content_type":"text/x-python","patch_set":17,"id":"3a3d1797_f16031f0","line":711,"range":{"start_line":711,"start_character":72,"end_line":711,"end_character":76},"updated":"2025-10-13 18:47:47.000000000","message":"\"than\"","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"ae22977ad19d9a7cbb3d49fd22bd66398572f706","unresolved":false,"context_lines":[{"line_number":708,"context_line":"    needed after the call just the result of the exceeds_capacity check. In"},{"line_number":709,"context_line":"    return this is a lot more performant than the former."},{"line_number":710,"context_line":""},{"line_number":711,"context_line":"    :return: True if the consolidated allocation request more resources that"},{"line_number":712,"context_line":"             available, False otherwise."},{"line_number":713,"context_line":"    \"\"\""},{"line_number":714,"context_line":"    amount_by_rp_rc \u003d collections.defaultdict(int)"}],"source_content_type":"text/x-python","patch_set":17,"id":"b605777a_854fb0a6","line":711,"range":{"start_line":711,"start_character":49,"end_line":711,"end_character":56},"in_reply_to":"11d2551a_b5a18a76","updated":"2025-10-14 13:40:13.000000000","message":"Done","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"ae22977ad19d9a7cbb3d49fd22bd66398572f706","unresolved":false,"context_lines":[{"line_number":708,"context_line":"    needed after the call just the result of the exceeds_capacity check. In"},{"line_number":709,"context_line":"    return this is a lot more performant than the former."},{"line_number":710,"context_line":""},{"line_number":711,"context_line":"    :return: True if the consolidated allocation request more resources that"},{"line_number":712,"context_line":"             available, False otherwise."},{"line_number":713,"context_line":"    \"\"\""},{"line_number":714,"context_line":"    amount_by_rp_rc \u003d collections.defaultdict(int)"}],"source_content_type":"text/x-python","patch_set":17,"id":"c25b99a9_ada634cc","line":711,"range":{"start_line":711,"start_character":72,"end_line":711,"end_character":76},"in_reply_to":"3a3d1797_f16031f0","updated":"2025-10-14 13:40:13.000000000","message":"Done","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"8731dab87d088203ff46df27d992f99af79b2a51","unresolved":true,"context_lines":[{"line_number":796,"context_line":""},{"line_number":797,"context_line":"    raise ValueError(\"Strategy \u0027%s\u0027 not recognized\" % strategy)"},{"line_number":798,"context_line":""},{"line_number":799,"context_line":""},{"line_number":800,"context_line":"# TODO(efried): Move _merge_candidates to rw_ctx?"},{"line_number":801,"context_line":""},{"line_number":802,"context_line":""}],"source_content_type":"text/x-python","patch_set":17,"id":"6f1198c5_09625050","line":799,"updated":"2025-10-13 18:47:47.000000000","message":"Random whitespace damage?","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"ae22977ad19d9a7cbb3d49fd22bd66398572f706","unresolved":false,"context_lines":[{"line_number":796,"context_line":""},{"line_number":797,"context_line":"    raise ValueError(\"Strategy \u0027%s\u0027 not recognized\" % strategy)"},{"line_number":798,"context_line":""},{"line_number":799,"context_line":""},{"line_number":800,"context_line":"# TODO(efried): Move _merge_candidates to rw_ctx?"},{"line_number":801,"context_line":""},{"line_number":802,"context_line":""}],"source_content_type":"text/x-python","patch_set":17,"id":"e659f08c_b38c9a59","line":799,"in_reply_to":"6f1198c5_09625050","updated":"2025-10-14 13:40:13.000000000","message":"Done","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"}],"placement/objects/research_context.py":[{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"34d1d8b4cf217f9b5a206a14413be7f65cc0fcf1","unresolved":true,"context_lines":[{"line_number":337,"context_line":"        :return: arr or a copy thereof."},{"line_number":338,"context_line":"        \"\"\""},{"line_number":339,"context_line":"        if self.group_policy !\u003d \u0027none\u0027:"},{"line_number":340,"context_line":"            return arr"},{"line_number":341,"context_line":"        if arr.resource_class in self.multi_group_rcs:"},{"line_number":342,"context_line":"            return copy.copy(arr)"},{"line_number":343,"context_line":"        return arr"}],"source_content_type":"text/x-python","patch_set":5,"id":"398e0c96_1176e5a4","side":"PARENT","line":340,"updated":"2025-10-06 14:49:03.000000000","message":"I didn\u0027t see anything in the commit message about this, nor accounting for it anywhere else. Are you removing this because it\u0027s never hit or because the optimization is wrong?","commit_id":"0b783c7e1695f50b812cbae725f7c7913efd41a1"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"7b522defb06680577fb0da245534113548e44932","unresolved":true,"context_lines":[{"line_number":337,"context_line":"        :return: arr or a copy thereof."},{"line_number":338,"context_line":"        \"\"\""},{"line_number":339,"context_line":"        if self.group_policy !\u003d \u0027none\u0027:"},{"line_number":340,"context_line":"            return arr"},{"line_number":341,"context_line":"        if arr.resource_class in self.multi_group_rcs:"},{"line_number":342,"context_line":"            return copy.copy(arr)"},{"line_number":343,"context_line":"        return arr"}],"source_content_type":"text/x-python","patch_set":5,"id":"fa03b5da_fa2dbb87","side":"PARENT","line":340,"in_reply_to":"18386515_66a504d1","updated":"2025-10-07 12:16:04.000000000","message":"Done the extra conditional based on the optimization flag. I also added more text about this to the commit message.","commit_id":"0b783c7e1695f50b812cbae725f7c7913efd41a1"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"ceb279668b178a378a2ca1ab086ab2176d9ee17d","unresolved":true,"context_lines":[{"line_number":337,"context_line":"        :return: arr or a copy thereof."},{"line_number":338,"context_line":"        \"\"\""},{"line_number":339,"context_line":"        if self.group_policy !\u003d \u0027none\u0027:"},{"line_number":340,"context_line":"            return arr"},{"line_number":341,"context_line":"        if arr.resource_class in self.multi_group_rcs:"},{"line_number":342,"context_line":"            return copy.copy(arr)"},{"line_number":343,"context_line":"        return arr"}],"source_content_type":"text/x-python","patch_set":5,"id":"511429c3_15d7d857","side":"PARENT","line":340,"in_reply_to":"398e0c96_1176e5a4","updated":"2025-10-06 15:36:39.000000000","message":"This is the related piece from the commit message:\n```\nAlso the his _consolidate_allocation_requests call has side effect on the\nindividual Group - RP mappings therefore the copy logic need to be\nadjusted a bit to copy more.\n```\n\nI need to get back to this change to properly understood it. There was failing tests where the missing copy caused sideffect to spread accross individual checks. So I needed to copy more.","commit_id":"0b783c7e1695f50b812cbae725f7c7913efd41a1"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"9637cc26b928bdde8d20752a75acc7a5d4b8790c","unresolved":true,"context_lines":[{"line_number":337,"context_line":"        :return: arr or a copy thereof."},{"line_number":338,"context_line":"        \"\"\""},{"line_number":339,"context_line":"        if self.group_policy !\u003d \u0027none\u0027:"},{"line_number":340,"context_line":"            return arr"},{"line_number":341,"context_line":"        if arr.resource_class in self.multi_group_rcs:"},{"line_number":342,"context_line":"            return copy.copy(arr)"},{"line_number":343,"context_line":"        return arr"}],"source_content_type":"text/x-python","patch_set":5,"id":"18386515_66a504d1","side":"PARENT","line":340,"in_reply_to":"511429c3_15d7d857","updated":"2025-10-07 11:32:22.000000000","message":"There is a very complex dependency structure in the original code itself. \n\nThe _consolidate_allocation_requests (that calls copy_arr_if_needed) is only called after _satisfies_group_policy removed any candidates where two groups allocate from the same RP (regardless of RC) and the group_policy is isolate. So _consolidate_allocation_requests never see an overlap with isolate. So it is correct that it never need to copy an ARR as it never try to fold two ARRs into one by reusing the first. \n\nNow in the optimized code path this is not true any more as _satisfies_group_policy is not moved to the heart of the product generation but _consolidate_allocation_requests is moved there. So the order of filtering changed. This means we cannot assume no-folding in group_policy isolate case any more if the optimization is enabled. \n\nThe current code removes this condition in both code paths. But that is not optimal. I will add conditional code to use the config flag here too.","commit_id":"0b783c7e1695f50b812cbae725f7c7913efd41a1"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"5585b62a199aba0ea983e7277f80b0b3f2dc17b1","unresolved":false,"context_lines":[{"line_number":337,"context_line":"        :return: arr or a copy thereof."},{"line_number":338,"context_line":"        \"\"\""},{"line_number":339,"context_line":"        if self.group_policy !\u003d \u0027none\u0027:"},{"line_number":340,"context_line":"            return arr"},{"line_number":341,"context_line":"        if arr.resource_class in self.multi_group_rcs:"},{"line_number":342,"context_line":"            return copy.copy(arr)"},{"line_number":343,"context_line":"        return arr"}],"source_content_type":"text/x-python","patch_set":5,"id":"f56fda6b_2ccd49e6","side":"PARENT","line":340,"in_reply_to":"fa03b5da_fa2dbb87","updated":"2025-10-07 12:23:56.000000000","message":"Done","commit_id":"0b783c7e1695f50b812cbae725f7c7913efd41a1"}],"placement/tests/functional/test_allocation_candidates.py":[{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"42ead22d557af1f55c28bef7e13d7ebd79e8ee27","unresolved":true,"context_lines":[{"line_number":288,"context_line":"            req_limit\u003d1000,"},{"line_number":289,"context_line":"            expected_candidates\u003d1000, expected_computes_with_candidates\u003d2)"},{"line_number":290,"context_line":""},{"line_number":291,"context_line":"    def test_many_non_viable_candidates_21_21_two_computes(self):"},{"line_number":292,"context_line":"        # This is runs in 6.9 seconds"},{"line_number":293,"context_line":"        self.conf_fixture.conf.set_override("},{"line_number":294,"context_line":"            \"optimize_for_wide_provider_trees\", True, group\u003d\"workarounds\")"}],"source_content_type":"text/x-python","patch_set":5,"id":"34bf5f43_fb670ee5","line":291,"updated":"2025-10-03 15:29:50.000000000","message":"we chatted a littel about this on irc\n\n+1 for usign the value in the bug as a basilien i.e. 21 device\n\ni woudl be intereste to see if we can add some larger values like 32/32\n\nand as you suggested perhaps limit the candiate to 100 if we see that takes too long so we can demonstarte or provdie guitence on how to tune these parmeter if you need too for some reason.\n\nill try and look at thsi again properly on monday but this seems very promising indeed.\n\nby hte way im more wonderign where this approch stop been effeive more so then askign for lots of addtional coverage\n\nas i mentiond on irc a typeic 2U server can only phsyciall fit 24 u.2/u.3 drive bays in the front\n\nwhich woudl need 96 pci lanes to have full speed\n\nwhile a typeical 2 chase can have 8-12 adtional pci device in the back and sometime up to 2 more m.2 slots wee are unlikely ot pass more then 32-48 pie device total in a 2U server because we are also going to start hittign the max pcie lane cout on cpus.\n\n\ni belive some amd eypc cpu can have up to 160-196 pci lane depending on how many cpu you have but we shoud not expect to have 100s of pci resouce resouce provider each with a single compute node.. mid to low 10s is resonable\n\nso if we can say something liek \"if you have more then 32 resouce providers each with a single inventoy of the same resouce class on a host, then you shoudl limit max_allocation_candiats to \u003c500 or \u003c100 to have a respocne time less then 10s\" then i think that gudiance woudl be more then enough for now.\n\nseperately we may want to consider if we shoudl revisit exposeing inventoies of total\u003d1 in this case.\n\ni.e. can we group based on resocue class and numa node for exmaple and create RP for the numa node and merge all device rp for each ssd on that numa node into one RP with inventory of N\n\ni think that would help in this specifc case but we may be able to mege the RP in a diffent way if we think about it or change the placment modeling\n\nfor exmaple if the RP invetory had say 10 unit of a PF resouce class in a single resocue provdier.\nperhasp we coudl have a second table that had each device pci adrsss. so we could seperate out the counting of the capatiy form the device assignement.\n\nwe coudl jsut move the assignment of the device back to nova too and only track the aggrated count. at the end fo the day the pci tracker and filter is still actully invovled in doign the actual selection fo the device so perhaps we do not need to have an RP per devvice in the PF case and we can mange the grouping in nova and placment differntly.\n\ni have not formed an opion on this at this point but those are some of the idea i think we shoudl dicsuss in the ptg session for medium to long term plans. tempered by how scalable this patch is too. if it expodes at 22 that might be a problem\nif it keep workign fine to 100 rps we are probaly good to take a step back and design a better way of modelign things with more time.","commit_id":"601b6075257c2d98ee24d5068012e54ce1bc0281"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c4a114655e2a53c41223942b0ff3463de02e674","unresolved":false,"context_lines":[{"line_number":288,"context_line":"            req_limit\u003d1000,"},{"line_number":289,"context_line":"            expected_candidates\u003d1000, expected_computes_with_candidates\u003d2)"},{"line_number":290,"context_line":""},{"line_number":291,"context_line":"    def test_many_non_viable_candidates_21_21_two_computes(self):"},{"line_number":292,"context_line":"        # This is runs in 6.9 seconds"},{"line_number":293,"context_line":"        self.conf_fixture.conf.set_override("},{"line_number":294,"context_line":"            \"optimize_for_wide_provider_trees\", True, group\u003d\"workarounds\")"}],"source_content_type":"text/x-python","patch_set":5,"id":"9431f6e3_a0bddcab","line":291,"in_reply_to":"34bf5f43_fb670ee5","updated":"2025-10-07 09:59:08.000000000","message":"Done","commit_id":"601b6075257c2d98ee24d5068012e54ce1bc0281"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"062d1e8e0763386f10dae52c97903eccebe9cee7","unresolved":true,"context_lines":[{"line_number":289,"context_line":"            expected_candidates\u003d1000, expected_computes_with_candidates\u003d2)"},{"line_number":290,"context_line":""},{"line_number":291,"context_line":"    def test_many_non_viable_candidates_21_21_two_computes(self):"},{"line_number":292,"context_line":"        # This is runs in 6.9 seconds"},{"line_number":293,"context_line":"        self.conf_fixture.conf.set_override("},{"line_number":294,"context_line":"            \"optimize_for_wide_provider_trees\", True, group\u003d\"workarounds\")"},{"line_number":295,"context_line":"        self.conf_fixture.conf.set_override("}],"source_content_type":"text/x-python","patch_set":5,"id":"f68ed8e5_0803b616","line":292,"updated":"2025-10-03 16:12:44.000000000","message":"so this takes 12.403856s on my laptop\n\nand\n```\n    def test_many_non_viable_candidates_32_32_two_computes(self):\n        # This is runs in 31.85 seconds\n        self.conf_fixture.conf.set_override(\n            \"optimize_for_wide_provider_trees\", True, group\u003d\"workarounds\")\n        self.conf_fixture.conf.set_override(\n            \"max_allocation_candidates\", 1000, group\u003d\"placement\")\n        self.conf_fixture.conf.set_override(\n            \"allocation_candidates_generation_strategy\", \"breadth-first\",\n            group\u003d\"placement\")\n        self._test_num_candidates_and_computes(\n            computes\u003d2, pfs\u003d32, vfs_per_pf\u003d1, req_groups\u003d32,\n            req_res_per_group\u003d1,\n            req_limit\u003d1000,\n            expected_candidates\u003d1000, expected_computes_with_candidates\u003d2)\n``` \n\ntakes basically 32s for me so i expect it to be faster on your system\ni bleve tox has an inbuilt  limit on the execution fo each test case which i think is in the 120-300s range but i dont think we want to get close to that for stablity reasons.\n\ni could be wrong but i think you have enough headroom to test with 32/32so it will be interstign to see how this test perfromce in the ci env.\n\npushing ti to 64/64 even with just 10 candiates  did not finish after 2 mintues so i think your approch has definlty help but we will evenually hit a limit\n\n\n```\n    def test_many_non_viable_candidates_64_64_two_computes(self):\n        self.conf_fixture.conf.set_override(\n            \"optimize_for_wide_provider_trees\", True, group\u003d\"workarounds\")\n        self.conf_fixture.conf.set_override(\n            \"max_allocation_candidates\", 1000, group\u003d\"placement\")\n        self.conf_fixture.conf.set_override(\n            \"allocation_candidates_generation_strategy\", \"breadth-first\",\n            group\u003d\"placement\")\n        self._test_num_candidates_and_computes(\n            computes\u003d2, pfs\u003d64, vfs_per_pf\u003d1, req_groups\u003d64,\n            req_res_per_group\u003d1,\n            req_limit\u003d10,\n            expected_candidates\u003d10, expected_computes_with_candidates\u003d2)\n```\n48/48 wiht 10 can diate failed with only 1 compute in the respocne after 107s\n\nso first i think that is at or above  where i think the limti is and we shoudl proably dicussage configuration with more then 32 to be safe. but second this might mean that the test could be unstable if the limit is too low\n\ni think what happend is there are more then 10 valid candidate for the firt host\nand even with breath first seach we are nitting niting invlaid candate form the secon hsot such that we bias to the first before we hit the overall limit\n\n\n```\ndef test_many_non_viable_candidates_48_48_two_computes(self):\n        # This is runs in 3 seconds\n        self.conf_fixture.conf.set_override(\n            \"optimize_for_wide_provider_trees\", True, group\u003d\"workarounds\")\n        self.conf_fixture.conf.set_override(\n            \"max_allocation_candidates\", 1000, group\u003d\"placement\")\n        self.conf_fixture.conf.set_override(\n            \"allocation_candidates_generation_strategy\", \"breadth-first\",\n            group\u003d\"placement\")\n        self._test_num_candidates_and_computes(\n            computes\u003d2, pfs\u003d48, vfs_per_pf\u003d1, req_groups\u003d48,\n            req_res_per_group\u003d1,\n            req_limit\u003d1000,\n            expected_candidates\u003d1000, expected_computes_with_candidates\u003d2)\n```\nthis passed in 102.5 second so about the same time as the 10 candiate case but this time it pases.\n\nbased on this i dont think that reduting the reqeust_limit helps so i guess your currently only factoring the config option\n\n```\n    def test_many_non_viable_candidates_64_64_two_computes(self):\n        # This is runs in 3 seconds\n        self.conf_fixture.conf.set_override(\n            \"optimize_for_wide_provider_trees\", True, group\u003d\"workarounds\")\n        self.conf_fixture.conf.set_override(\n            \"max_allocation_candidates\", 10, group\u003d\"placement\")\n        self.conf_fixture.conf.set_override(\n            \"allocation_candidates_generation_strategy\", \"breadth-first\",\n            group\u003d\"placement\")\n        self._test_num_candidates_and_computes(\n            computes\u003d2, pfs\u003d64, vfs_per_pf\u003d1, req_groups\u003d64,\n            req_res_per_group\u003d1,\n            req_limit\u003d10,\n            expected_candidates\u003d10, expected_computes_with_candidates\u003d2)\n```\nfinsihes in 6.28 seconds\n\nso i guess my question form this is are we missing a min(conf.max_allocation_candidates, requst_limit) somewhere?\n\n\ni woudl expect either to be able to limit the generation and supprot large request but only the config option apprease to be effective.","commit_id":"601b6075257c2d98ee24d5068012e54ce1bc0281"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"f50eb7635a7656cba9737fe2b527344b62652b0e","unresolved":false,"context_lines":[{"line_number":289,"context_line":"            expected_candidates\u003d1000, expected_computes_with_candidates\u003d2)"},{"line_number":290,"context_line":""},{"line_number":291,"context_line":"    def test_many_non_viable_candidates_21_21_two_computes(self):"},{"line_number":292,"context_line":"        # This is runs in 6.9 seconds"},{"line_number":293,"context_line":"        self.conf_fixture.conf.set_override("},{"line_number":294,"context_line":"            \"optimize_for_wide_provider_trees\", True, group\u003d\"workarounds\")"},{"line_number":295,"context_line":"        self.conf_fixture.conf.set_override("}],"source_content_type":"text/x-python","patch_set":5,"id":"d700d5d5_83925d92","line":292,"in_reply_to":"f68ed8e5_0803b616","updated":"2025-10-06 12:34:39.000000000","message":"we chatted about this on irc and while i think we shoudl revisit what requst_limit does perhaps in a new microverion if rquried and the meaning of randomize allcation candiates that is out of scope fo this patchand this is workign as documeted so ill resolve this thread.","commit_id":"601b6075257c2d98ee24d5068012e54ce1bc0281"}],"placement/tests/unit/objects/test_allocation_candidate.py":[{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"5b860683ea6ee098389a3dd8b4b0ecd06d6e29ec","unresolved":true,"context_lines":[{"line_number":196,"context_line":"        rw_ctx.psum_res_by_rp_rc.update(_rp(2, capacity\u003d1))"},{"line_number":197,"context_line":"        rw_ctx.psum_res_by_rp_rc.update(_rp(3, capacity\u003d1))"},{"line_number":198,"context_line":"        rw_ctx.multi_group_rcs \u003d {\"SRIOV_VF\"}"},{"line_number":199,"context_line":""},{"line_number":200,"context_line":"        # The independent ARs that can fulfill each group in the request:"},{"line_number":201,"context_line":"        # Here each Group can be fulfilled from each RP1..3 when checked"},{"line_number":202,"context_line":"        # independently."}],"source_content_type":"text/x-python","patch_set":6,"id":"c67bf6cc_b2827bb8","line":199,"updated":"2025-10-06 15:19:26.000000000","message":"Just because my brain isn\u0027t primed for this.. I think a summary comment here could say:\n\u003e We have a root provider with three PF\u003d1,VF\u003d1 child providers. We\u0027re going to ask for allocation candidates with three groups (Group 1\u003dG1, etc), each of one of those resources.","commit_id":"e9b0a915ae0b196b9273e5d71544f97ab4d008b0"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"e79305569a0f6f813cc40b10fc71d101d225bc26","unresolved":false,"context_lines":[{"line_number":196,"context_line":"        rw_ctx.psum_res_by_rp_rc.update(_rp(2, capacity\u003d1))"},{"line_number":197,"context_line":"        rw_ctx.psum_res_by_rp_rc.update(_rp(3, capacity\u003d1))"},{"line_number":198,"context_line":"        rw_ctx.multi_group_rcs \u003d {\"SRIOV_VF\"}"},{"line_number":199,"context_line":""},{"line_number":200,"context_line":"        # The independent ARs that can fulfill each group in the request:"},{"line_number":201,"context_line":"        # Here each Group can be fulfilled from each RP1..3 when checked"},{"line_number":202,"context_line":"        # independently."}],"source_content_type":"text/x-python","patch_set":6,"id":"fc99f396_eb1ae667","line":199,"in_reply_to":"6d0e0e64_8212b249","updated":"2025-10-06 16:27:14.000000000","message":"Acknowledged","commit_id":"e9b0a915ae0b196b9273e5d71544f97ab4d008b0"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"be7a3a984d62406b0b13ae55099b12b8b50a78ff","unresolved":true,"context_lines":[{"line_number":196,"context_line":"        rw_ctx.psum_res_by_rp_rc.update(_rp(2, capacity\u003d1))"},{"line_number":197,"context_line":"        rw_ctx.psum_res_by_rp_rc.update(_rp(3, capacity\u003d1))"},{"line_number":198,"context_line":"        rw_ctx.multi_group_rcs \u003d {\"SRIOV_VF\"}"},{"line_number":199,"context_line":""},{"line_number":200,"context_line":"        # The independent ARs that can fulfill each group in the request:"},{"line_number":201,"context_line":"        # Here each Group can be fulfilled from each RP1..3 when checked"},{"line_number":202,"context_line":"        # independently."}],"source_content_type":"text/x-python","patch_set":6,"id":"6d0e0e64_8212b249","line":199,"in_reply_to":"c67bf6cc_b2827bb8","updated":"2025-10-06 15:32:57.000000000","message":"Yes. One root provider with 3 PF\u003d1 children. The childrens are RP id\u003d1, id\u003d2, id\u003d3\nAnd we ask for candidates where G1, G2, G3 each asks for PF\u003d1","commit_id":"e9b0a915ae0b196b9273e5d71544f97ab4d008b0"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"5b860683ea6ee098389a3dd8b4b0ecd06d6e29ec","unresolved":true,"context_lines":[{"line_number":200,"context_line":"        # The independent ARs that can fulfill each group in the request:"},{"line_number":201,"context_line":"        # Here each Group can be fulfilled from each RP1..3 when checked"},{"line_number":202,"context_line":"        # independently."},{"line_number":203,"context_line":"        G1_1 \u003d _alloc_req(\"G1\", rp_id\u003d1, amount\u003d1)"},{"line_number":204,"context_line":"        G1_2 \u003d _alloc_req(\"G1\", rp_id\u003d2, amount\u003d1)"},{"line_number":205,"context_line":"        G1_3 \u003d _alloc_req(\"G1\", rp_id\u003d3, amount\u003d1)"},{"line_number":206,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"7add3ae1_e7129f15","line":203,"updated":"2025-10-06 15:19:26.000000000","message":"I think it would  be easier for me to reason about this if the variable name was `G1_RP1`, `G1_RP2`, etc. It\u0027s a minor thing, but `1_1` is hard for me to intuitively know what the thing after the underscore means.","commit_id":"e9b0a915ae0b196b9273e5d71544f97ab4d008b0"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"be7a3a984d62406b0b13ae55099b12b8b50a78ff","unresolved":true,"context_lines":[{"line_number":200,"context_line":"        # The independent ARs that can fulfill each group in the request:"},{"line_number":201,"context_line":"        # Here each Group can be fulfilled from each RP1..3 when checked"},{"line_number":202,"context_line":"        # independently."},{"line_number":203,"context_line":"        G1_1 \u003d _alloc_req(\"G1\", rp_id\u003d1, amount\u003d1)"},{"line_number":204,"context_line":"        G1_2 \u003d _alloc_req(\"G1\", rp_id\u003d2, amount\u003d1)"},{"line_number":205,"context_line":"        G1_3 \u003d _alloc_req(\"G1\", rp_id\u003d3, amount\u003d1)"},{"line_number":206,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"823bb7e9_bc19d74a","line":203,"in_reply_to":"7add3ae1_e7129f15","updated":"2025-10-06 15:32:57.000000000","message":"sure, I can change it","commit_id":"e9b0a915ae0b196b9273e5d71544f97ab4d008b0"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"a9acd61d22685e3bdc73a572634ad9381b5bb885","unresolved":false,"context_lines":[{"line_number":200,"context_line":"        # The independent ARs that can fulfill each group in the request:"},{"line_number":201,"context_line":"        # Here each Group can be fulfilled from each RP1..3 when checked"},{"line_number":202,"context_line":"        # independently."},{"line_number":203,"context_line":"        G1_1 \u003d _alloc_req(\"G1\", rp_id\u003d1, amount\u003d1)"},{"line_number":204,"context_line":"        G1_2 \u003d _alloc_req(\"G1\", rp_id\u003d2, amount\u003d1)"},{"line_number":205,"context_line":"        G1_3 \u003d _alloc_req(\"G1\", rp_id\u003d3, amount\u003d1)"},{"line_number":206,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"c0c76486_1def4ffc","line":203,"in_reply_to":"823bb7e9_bc19d74a","updated":"2025-10-07 08:50:45.000000000","message":"Done","commit_id":"e9b0a915ae0b196b9273e5d71544f97ab4d008b0"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"5b860683ea6ee098389a3dd8b4b0ecd06d6e29ec","unresolved":true,"context_lines":[{"line_number":212,"context_line":"        G3_2 \u003d _alloc_req(\"G3\", rp_id\u003d2, amount\u003d1)"},{"line_number":213,"context_line":"        G3_3 \u003d _alloc_req(\"G3\", rp_id\u003d3, amount\u003d1)"},{"line_number":214,"context_line":""},{"line_number":215,"context_line":"        areq_lists_by_anchor \u003d {"},{"line_number":216,"context_line":"            \"root\": {"},{"line_number":217,"context_line":"                \"G1\": [G1_1, G1_2, G1_3],"},{"line_number":218,"context_line":"                \"G2\": [G2_1, G2_2, G2_3],"}],"source_content_type":"text/x-python","patch_set":6,"id":"6a6e3caa_c3144fa5","line":215,"updated":"2025-10-06 15:19:26.000000000","message":"And here:\n\u003e This represents the possible solutions. Group 1 wants one resource, which could be satisfied with any of the three available providers (provider IDs 1, 2, 3). Group 2 also wants one, which could be satisfied by any of the same three providers, and so on for Group 3.","commit_id":"e9b0a915ae0b196b9273e5d71544f97ab4d008b0"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"be7a3a984d62406b0b13ae55099b12b8b50a78ff","unresolved":true,"context_lines":[{"line_number":212,"context_line":"        G3_2 \u003d _alloc_req(\"G3\", rp_id\u003d2, amount\u003d1)"},{"line_number":213,"context_line":"        G3_3 \u003d _alloc_req(\"G3\", rp_id\u003d3, amount\u003d1)"},{"line_number":214,"context_line":""},{"line_number":215,"context_line":"        areq_lists_by_anchor \u003d {"},{"line_number":216,"context_line":"            \"root\": {"},{"line_number":217,"context_line":"                \"G1\": [G1_1, G1_2, G1_3],"},{"line_number":218,"context_line":"                \"G2\": [G2_1, G2_2, G2_3],"}],"source_content_type":"text/x-python","patch_set":6,"id":"e36be521_ee462855","line":215,"in_reply_to":"6a6e3caa_c3144fa5","updated":"2025-10-06 15:32:57.000000000","message":"correct","commit_id":"e9b0a915ae0b196b9273e5d71544f97ab4d008b0"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"e79305569a0f6f813cc40b10fc71d101d225bc26","unresolved":false,"context_lines":[{"line_number":212,"context_line":"        G3_2 \u003d _alloc_req(\"G3\", rp_id\u003d2, amount\u003d1)"},{"line_number":213,"context_line":"        G3_3 \u003d _alloc_req(\"G3\", rp_id\u003d3, amount\u003d1)"},{"line_number":214,"context_line":""},{"line_number":215,"context_line":"        areq_lists_by_anchor \u003d {"},{"line_number":216,"context_line":"            \"root\": {"},{"line_number":217,"context_line":"                \"G1\": [G1_1, G1_2, G1_3],"},{"line_number":218,"context_line":"                \"G2\": [G2_1, G2_2, G2_3],"}],"source_content_type":"text/x-python","patch_set":6,"id":"adea856c_b599fd48","line":215,"in_reply_to":"e36be521_ee462855","updated":"2025-10-06 16:27:14.000000000","message":"Acknowledged","commit_id":"e9b0a915ae0b196b9273e5d71544f97ab4d008b0"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"5b860683ea6ee098389a3dd8b4b0ecd06d6e29ec","unresolved":true,"context_lines":[{"line_number":241,"context_line":"            # This is the optimization as we did not checked G1_1, G2_1, G3_x"},{"line_number":242,"context_line":"            # as the algo knows that such products are invalid due to the"},{"line_number":243,"context_line":"            # G1_1, G2_1 prefix."},{"line_number":244,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),"},{"line_number":245,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),  # bug duplicate"},{"line_number":246,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),  # bug duplicate"},{"line_number":247,"context_line":"            mock.call([G1_1, G2_2, G3_2], rw_ctx),"}],"source_content_type":"text/x-python","patch_set":6,"id":"05b0ce6d_48710288","line":244,"updated":"2025-10-06 15:19:26.000000000","message":"Why is this valid? Isn\u0027t this promising RP1 to both group 1 and group 3? The comment makes it sound like it\u0027s expected, so cool, but I don\u0027t understand why. By the time we call `_consolidate_allocation_requests()` we expect to have filtered out invalid things, right?","commit_id":"e9b0a915ae0b196b9273e5d71544f97ab4d008b0"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"be7a3a984d62406b0b13ae55099b12b8b50a78ff","unresolved":true,"context_lines":[{"line_number":241,"context_line":"            # This is the optimization as we did not checked G1_1, G2_1, G3_x"},{"line_number":242,"context_line":"            # as the algo knows that such products are invalid due to the"},{"line_number":243,"context_line":"            # G1_1, G2_1 prefix."},{"line_number":244,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),"},{"line_number":245,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),  # bug duplicate"},{"line_number":246,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),  # bug duplicate"},{"line_number":247,"context_line":"            mock.call([G1_1, G2_2, G3_2], rw_ctx),"}],"source_content_type":"text/x-python","patch_set":6,"id":"5a92c069_80007854","line":244,"in_reply_to":"05b0ce6d_48710288","updated":"2025-10-06 15:32:57.000000000","message":"What is cool here that we did not continued with \n* G1_1, G2_1, G3_1\n* G1_1, G2_1, G3_2\n* G1_1, G2_1, G3_3\nand only then moved to \n* G1_1, G2_2 ...\n\nBut we actually skipped that 3 products and jumped ahead of G1_1, G2_2, \n\nOf course G1_1, G2_2, G3_1 is not a valid solution (and it will fail the capacity check), but it is a solution that needed to be generated as it does not have an already known out of capacity prefix.","commit_id":"e9b0a915ae0b196b9273e5d71544f97ab4d008b0"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"f216dc88dd364eb401bc188d4a74d99195a07b71","unresolved":true,"context_lines":[{"line_number":241,"context_line":"            # This is the optimization as we did not checked G1_1, G2_1, G3_x"},{"line_number":242,"context_line":"            # as the algo knows that such products are invalid due to the"},{"line_number":243,"context_line":"            # G1_1, G2_1 prefix."},{"line_number":244,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),"},{"line_number":245,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),  # bug duplicate"},{"line_number":246,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),  # bug duplicate"},{"line_number":247,"context_line":"            mock.call([G1_1, G2_2, G3_2], rw_ctx),"}],"source_content_type":"text/x-python","patch_set":6,"id":"b0b1c76c_aee613c2","line":244,"in_reply_to":"5a92c069_80007854","updated":"2025-10-06 15:42:55.000000000","message":"I see okay .. the \"this is correct\" comment is identifying that we didn\u0027t continue with 1,1,2 after 1,1,1 failed and is not saying \"this is a correct solution to the overall problem\". I got it.","commit_id":"e9b0a915ae0b196b9273e5d71544f97ab4d008b0"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"e79305569a0f6f813cc40b10fc71d101d225bc26","unresolved":false,"context_lines":[{"line_number":241,"context_line":"            # This is the optimization as we did not checked G1_1, G2_1, G3_x"},{"line_number":242,"context_line":"            # as the algo knows that such products are invalid due to the"},{"line_number":243,"context_line":"            # G1_1, G2_1 prefix."},{"line_number":244,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),"},{"line_number":245,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),  # bug duplicate"},{"line_number":246,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),  # bug duplicate"},{"line_number":247,"context_line":"            mock.call([G1_1, G2_2, G3_2], rw_ctx),"}],"source_content_type":"text/x-python","patch_set":6,"id":"8a2355c7_18305b13","line":244,"in_reply_to":"b0b1c76c_aee613c2","updated":"2025-10-06 16:27:14.000000000","message":"Done","commit_id":"e9b0a915ae0b196b9273e5d71544f97ab4d008b0"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"129f811311a4f1d2059527cf203ac0008f6651e3","unresolved":true,"context_lines":[{"line_number":242,"context_line":"            # as the algo knows that such products are invalid due to the"},{"line_number":243,"context_line":"            # G1_1, G2_1 prefix."},{"line_number":244,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),"},{"line_number":245,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),  # bug duplicate"},{"line_number":246,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),  # bug duplicate"},{"line_number":247,"context_line":"            mock.call([G1_1, G2_2, G3_2], rw_ctx),"},{"line_number":248,"context_line":"            mock.call([G1_1, G2_2, G3_2], rw_ctx),  # bug duplicate"}],"source_content_type":"text/x-python","patch_set":6,"id":"4c1f9463_b331ebad","line":245,"range":{"start_line":245,"start_character":51,"end_line":245,"end_character":67},"updated":"2025-10-06 15:55:47.000000000","message":"Some of this is an artifact of the test mocking _consolidate and the mock recording the actual parameters of the call, but those actual parameters are lists, that are mutated after the call, and a mock only record the reference so those records are mutated as well. I need to do a bit more involved mocking to get a non mutable record of the actual call args.","commit_id":"e9b0a915ae0b196b9273e5d71544f97ab4d008b0"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"a9acd61d22685e3bdc73a572634ad9381b5bb885","unresolved":false,"context_lines":[{"line_number":242,"context_line":"            # as the algo knows that such products are invalid due to the"},{"line_number":243,"context_line":"            # G1_1, G2_1 prefix."},{"line_number":244,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),"},{"line_number":245,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),  # bug duplicate"},{"line_number":246,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),  # bug duplicate"},{"line_number":247,"context_line":"            mock.call([G1_1, G2_2, G3_2], rw_ctx),"},{"line_number":248,"context_line":"            mock.call([G1_1, G2_2, G3_2], rw_ctx),  # bug duplicate"}],"source_content_type":"text/x-python","patch_set":6,"id":"21832227_103fc84a","line":245,"range":{"start_line":245,"start_character":51,"end_line":245,"end_character":67},"in_reply_to":"099766e3_8515fc4d","updated":"2025-10-07 08:50:45.000000000","message":"Done","commit_id":"e9b0a915ae0b196b9273e5d71544f97ab4d008b0"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"9907f9b6bc13900007f232c109f547f96287d6e2","unresolved":true,"context_lines":[{"line_number":242,"context_line":"            # as the algo knows that such products are invalid due to the"},{"line_number":243,"context_line":"            # G1_1, G2_1 prefix."},{"line_number":244,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),"},{"line_number":245,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),  # bug duplicate"},{"line_number":246,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),  # bug duplicate"},{"line_number":247,"context_line":"            mock.call([G1_1, G2_2, G3_2], rw_ctx),"},{"line_number":248,"context_line":"            mock.call([G1_1, G2_2, G3_2], rw_ctx),  # bug duplicate"}],"source_content_type":"text/x-python","patch_set":6,"id":"099766e3_8515fc4d","line":245,"range":{"start_line":245,"start_character":51,"end_line":245,"end_character":67},"in_reply_to":"4c1f9463_b331ebad","updated":"2025-10-06 16:13:07.000000000","message":"Fixed it with a mock that does a deep copy.","commit_id":"e9b0a915ae0b196b9273e5d71544f97ab4d008b0"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"5b860683ea6ee098389a3dd8b4b0ecd06d6e29ec","unresolved":true,"context_lines":[{"line_number":244,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),"},{"line_number":245,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),  # bug duplicate"},{"line_number":246,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),  # bug duplicate"},{"line_number":247,"context_line":"            mock.call([G1_1, G2_2, G3_2], rw_ctx),"},{"line_number":248,"context_line":"            mock.call([G1_1, G2_2, G3_2], rw_ctx),  # bug duplicate"},{"line_number":249,"context_line":"            mock.call([G1_1, G2_2, G3_2], rw_ctx),  # bug duplicate"},{"line_number":250,"context_line":"            mock.call([G1_1, G2_2, G3_3], rw_ctx),"}],"source_content_type":"text/x-python","patch_set":6,"id":"7fc8ae2f_395d4791","line":247,"updated":"2025-10-06 15:19:26.000000000","message":"Same here... I must be missing something.","commit_id":"e9b0a915ae0b196b9273e5d71544f97ab4d008b0"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"e79305569a0f6f813cc40b10fc71d101d225bc26","unresolved":false,"context_lines":[{"line_number":244,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),"},{"line_number":245,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),  # bug duplicate"},{"line_number":246,"context_line":"            mock.call([G1_1, G2_2, G3_1], rw_ctx),  # bug duplicate"},{"line_number":247,"context_line":"            mock.call([G1_1, G2_2, G3_2], rw_ctx),"},{"line_number":248,"context_line":"            mock.call([G1_1, G2_2, G3_2], rw_ctx),  # bug duplicate"},{"line_number":249,"context_line":"            mock.call([G1_1, G2_2, G3_2], rw_ctx),  # bug duplicate"},{"line_number":250,"context_line":"            mock.call([G1_1, G2_2, G3_3], rw_ctx),"}],"source_content_type":"text/x-python","patch_set":6,"id":"0dde071b_1563fe6e","line":247,"in_reply_to":"7fc8ae2f_395d4791","updated":"2025-10-06 16:27:14.000000000","message":"Done","commit_id":"e9b0a915ae0b196b9273e5d71544f97ab4d008b0"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"9907f9b6bc13900007f232c109f547f96287d6e2","unresolved":true,"context_lines":[{"line_number":262,"context_line":"            [G1_1],"},{"line_number":263,"context_line":"            [G1_1, G2_2],"},{"line_number":264,"context_line":"            [G1_1, G2_2, G3_1],"},{"line_number":265,"context_line":"            [G1_1],"},{"line_number":266,"context_line":"            [G1_1, G2_2],"},{"line_number":267,"context_line":"            [G1_1, G2_2, G3_2],"},{"line_number":268,"context_line":"            [G1_1],"},{"line_number":269,"context_line":"            [G1_1, G2_2],"},{"line_number":270,"context_line":"            [G1_1, G2_2, G3_3],"},{"line_number":271,"context_line":"            # ..."},{"line_number":272,"context_line":"        ]"}],"source_content_type":"text/x-python","patch_set":7,"id":"fc5f23e1_90dbf851","line":269,"range":{"start_line":265,"start_character":1,"end_line":269,"end_character":25},"updated":"2025-10-06 16:13:07.000000000","message":"we check G1_1,G2_2 twice. This is optimized in the next patch in the series I think :)","commit_id":"7ab10e2968b217db884c99d85d3a12389069d602"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"ff870915d38b39f98d801a8697b9abc3fe670be1","unresolved":false,"context_lines":[{"line_number":262,"context_line":"            [G1_1],"},{"line_number":263,"context_line":"            [G1_1, G2_2],"},{"line_number":264,"context_line":"            [G1_1, G2_2, G3_1],"},{"line_number":265,"context_line":"            [G1_1],"},{"line_number":266,"context_line":"            [G1_1, G2_2],"},{"line_number":267,"context_line":"            [G1_1, G2_2, G3_2],"},{"line_number":268,"context_line":"            [G1_1],"},{"line_number":269,"context_line":"            [G1_1, G2_2],"},{"line_number":270,"context_line":"            [G1_1, G2_2, G3_3],"},{"line_number":271,"context_line":"            # ..."},{"line_number":272,"context_line":"        ]"}],"source_content_type":"text/x-python","patch_set":7,"id":"89285a02_f5c16f0d","line":269,"range":{"start_line":265,"start_character":1,"end_line":269,"end_character":25},"in_reply_to":"fc5f23e1_90dbf851","updated":"2025-10-07 12:23:34.000000000","message":"some of it is optimized away in the next patch it just hard to show it as it is the call is inlined to reuse already calculated prefixes.\n\nOthers cannot be easily removed in the current structure due to the iterative nature of the algorithm and because consolidation means folding ARRs together. So when we jump the odomater ahead we might still have a common prefix but we need to re-calculate that prefix as otherwise we would need to subtract ARRs from the already consolidated view. Subtracting is complex and probably costly. Another option would be caching the caluclated prefixes that are passing the test and reusing them based on the same index prefix. But we have a lot of such prefixes which will blow up the size of the cache. Maybe we could only build the prefix cache for a certain length. But that is also pretty complex code and require another config about how deep the cache should be. \n\nA totally different approach would be to re-implement the product generation as a recursive algorithm where a failed prefix means backtracking from the recursion dept and the stack would serve as our prefix cache automatically. Maybe it is a viable approach but I haven\u0027t spent time on trying to implement a product generator with recursion.","commit_id":"7ab10e2968b217db884c99d85d3a12389069d602"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"3c9705859246810c8d152850b209dc9d43b916a2","unresolved":true,"context_lines":[{"line_number":257,"context_line":""},{"line_number":258,"context_line":"            areqs \u003d [areq for _, areq in generator]"},{"line_number":259,"context_line":""},{"line_number":260,"context_line":"            # We don\u0027t have 9 (3^3) valid solutions just 6 (3!) as if one of"},{"line_number":261,"context_line":"            # the Group is fulfilled from an RP then the other Groups cannot be"},{"line_number":262,"context_line":"            # fulfilled from the same RP as each RP has 1 resource only."},{"line_number":263,"context_line":"            # Without the optimize_for_wide_provider_trees \u003d true the"}],"source_content_type":"text/x-python","patch_set":10,"id":"8667c4b7_6c472284","line":260,"updated":"2025-10-07 12:03:18.000000000","message":"27 (3^3)","commit_id":"9e8a305100301820c2937f5a74b296df40d54946"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"d621aead883c7d15ae2038b79a3db01babdc40e8","unresolved":false,"context_lines":[{"line_number":257,"context_line":""},{"line_number":258,"context_line":"            areqs \u003d [areq for _, areq in generator]"},{"line_number":259,"context_line":""},{"line_number":260,"context_line":"            # We don\u0027t have 9 (3^3) valid solutions just 6 (3!) as if one of"},{"line_number":261,"context_line":"            # the Group is fulfilled from an RP then the other Groups cannot be"},{"line_number":262,"context_line":"            # fulfilled from the same RP as each RP has 1 resource only."},{"line_number":263,"context_line":"            # Without the optimize_for_wide_provider_trees \u003d true the"}],"source_content_type":"text/x-python","patch_set":10,"id":"0054262b_e62dc75e","line":260,"in_reply_to":"8667c4b7_6c472284","updated":"2025-10-07 12:14:17.000000000","message":"Done","commit_id":"9e8a305100301820c2937f5a74b296df40d54946"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"0f1951e1aa7ab7827ceccf9623f95867e08f9abc","unresolved":true,"context_lines":[{"line_number":213,"context_line":"        G3_RP2 \u003d _alloc_req(\"G3\", rp_id\u003d\"RP2\", amount\u003d1)"},{"line_number":214,"context_line":"        G3_RP3 \u003d _alloc_req(\"G3\", rp_id\u003d\"RP3\", amount\u003d1)"},{"line_number":215,"context_line":""},{"line_number":216,"context_line":"        # This algorithm stats with the possible solutions for each group"},{"line_number":217,"context_line":"        # independently of the other groups in the same request. So here G1"},{"line_number":218,"context_line":"        # can be fulfilled form RP1, RP2, or RP3. As well as G2, and G3."},{"line_number":219,"context_line":"        areq_lists_by_anchor \u003d {"}],"source_content_type":"text/x-python","patch_set":11,"id":"d0726e87_a303c744","line":216,"range":{"start_line":216,"start_character":28,"end_line":216,"end_character":30},"updated":"2025-10-07 14:01:20.000000000","message":"\"starts\"","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c885651bbcca919d70089e29d2ab1935159d0ea","unresolved":false,"context_lines":[{"line_number":213,"context_line":"        G3_RP2 \u003d _alloc_req(\"G3\", rp_id\u003d\"RP2\", amount\u003d1)"},{"line_number":214,"context_line":"        G3_RP3 \u003d _alloc_req(\"G3\", rp_id\u003d\"RP3\", amount\u003d1)"},{"line_number":215,"context_line":""},{"line_number":216,"context_line":"        # This algorithm stats with the possible solutions for each group"},{"line_number":217,"context_line":"        # independently of the other groups in the same request. So here G1"},{"line_number":218,"context_line":"        # can be fulfilled form RP1, RP2, or RP3. As well as G2, and G3."},{"line_number":219,"context_line":"        areq_lists_by_anchor \u003d {"}],"source_content_type":"text/x-python","patch_set":11,"id":"2246d415_a912a202","line":216,"range":{"start_line":216,"start_character":28,"end_line":216,"end_character":30},"in_reply_to":"d0726e87_a303c744","updated":"2025-10-08 12:29:50.000000000","message":"Done","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"0f1951e1aa7ab7827ceccf9623f95867e08f9abc","unresolved":true,"context_lines":[{"line_number":214,"context_line":"        G3_RP3 \u003d _alloc_req(\"G3\", rp_id\u003d\"RP3\", amount\u003d1)"},{"line_number":215,"context_line":""},{"line_number":216,"context_line":"        # This algorithm stats with the possible solutions for each group"},{"line_number":217,"context_line":"        # independently of the other groups in the same request. So here G1"},{"line_number":218,"context_line":"        # can be fulfilled form RP1, RP2, or RP3. As well as G2, and G3."},{"line_number":219,"context_line":"        areq_lists_by_anchor \u003d {"},{"line_number":220,"context_line":"            \"root\": {"}],"source_content_type":"text/x-python","patch_set":11,"id":"c4334713_db4af6c9","line":217,"range":{"start_line":217,"start_character":10,"end_line":217,"end_character":23},"updated":"2025-10-07 14:01:20.000000000","message":"\"independent\"","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c885651bbcca919d70089e29d2ab1935159d0ea","unresolved":false,"context_lines":[{"line_number":214,"context_line":"        G3_RP3 \u003d _alloc_req(\"G3\", rp_id\u003d\"RP3\", amount\u003d1)"},{"line_number":215,"context_line":""},{"line_number":216,"context_line":"        # This algorithm stats with the possible solutions for each group"},{"line_number":217,"context_line":"        # independently of the other groups in the same request. So here G1"},{"line_number":218,"context_line":"        # can be fulfilled form RP1, RP2, or RP3. As well as G2, and G3."},{"line_number":219,"context_line":"        areq_lists_by_anchor \u003d {"},{"line_number":220,"context_line":"            \"root\": {"}],"source_content_type":"text/x-python","patch_set":11,"id":"ba216c3e_0ec31c67","line":217,"range":{"start_line":217,"start_character":10,"end_line":217,"end_character":23},"in_reply_to":"c4334713_db4af6c9","updated":"2025-10-08 12:29:50.000000000","message":"Done","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"0f1951e1aa7ab7827ceccf9623f95867e08f9abc","unresolved":true,"context_lines":[{"line_number":215,"context_line":""},{"line_number":216,"context_line":"        # This algorithm stats with the possible solutions for each group"},{"line_number":217,"context_line":"        # independently of the other groups in the same request. So here G1"},{"line_number":218,"context_line":"        # can be fulfilled form RP1, RP2, or RP3. As well as G2, and G3."},{"line_number":219,"context_line":"        areq_lists_by_anchor \u003d {"},{"line_number":220,"context_line":"            \"root\": {"},{"line_number":221,"context_line":"                \"G1\": [G1_RP1, G1_RP2, G1_RP3],"}],"source_content_type":"text/x-python","patch_set":11,"id":"057ec0b4_5a99202c","line":218,"range":{"start_line":218,"start_character":27,"end_line":218,"end_character":31},"updated":"2025-10-07 14:01:20.000000000","message":"\"from\"\n\nAnd thanks, this comment will be helpful in the future I think.","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c885651bbcca919d70089e29d2ab1935159d0ea","unresolved":false,"context_lines":[{"line_number":215,"context_line":""},{"line_number":216,"context_line":"        # This algorithm stats with the possible solutions for each group"},{"line_number":217,"context_line":"        # independently of the other groups in the same request. So here G1"},{"line_number":218,"context_line":"        # can be fulfilled form RP1, RP2, or RP3. As well as G2, and G3."},{"line_number":219,"context_line":"        areq_lists_by_anchor \u003d {"},{"line_number":220,"context_line":"            \"root\": {"},{"line_number":221,"context_line":"                \"G1\": [G1_RP1, G1_RP2, G1_RP3],"}],"source_content_type":"text/x-python","patch_set":11,"id":"e5422a61_61dc4056","line":218,"range":{"start_line":218,"start_character":27,"end_line":218,"end_character":31},"in_reply_to":"057ec0b4_5a99202c","updated":"2025-10-08 12:29:50.000000000","message":"Done","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"0f1951e1aa7ab7827ceccf9623f95867e08f9abc","unresolved":true,"context_lines":[{"line_number":261,"context_line":"            # the Group is fulfilled from an RP then the other Groups cannot be"},{"line_number":262,"context_line":"            # fulfilled from the same RP as each RP has 1 resource only."},{"line_number":263,"context_line":"            # Without the optimize_for_wide_provider_trees \u003d true the"},{"line_number":264,"context_line":"            # _generate_areq_lists call would generate all the 9 possible"},{"line_number":265,"context_line":"            # product and then later processing would filter out the invalid,"},{"line_number":266,"context_line":"            # overlapping ones."},{"line_number":267,"context_line":"            self.assertEqual(6, len(areqs))"}],"source_content_type":"text/x-python","patch_set":11,"id":"c6efc873_116b7d52","line":264,"range":{"start_line":264,"start_character":63,"end_line":264,"end_character":64},"updated":"2025-10-07 14:01:20.000000000","message":"27?","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c885651bbcca919d70089e29d2ab1935159d0ea","unresolved":false,"context_lines":[{"line_number":261,"context_line":"            # the Group is fulfilled from an RP then the other Groups cannot be"},{"line_number":262,"context_line":"            # fulfilled from the same RP as each RP has 1 resource only."},{"line_number":263,"context_line":"            # Without the optimize_for_wide_provider_trees \u003d true the"},{"line_number":264,"context_line":"            # _generate_areq_lists call would generate all the 9 possible"},{"line_number":265,"context_line":"            # product and then later processing would filter out the invalid,"},{"line_number":266,"context_line":"            # overlapping ones."},{"line_number":267,"context_line":"            self.assertEqual(6, len(areqs))"}],"source_content_type":"text/x-python","patch_set":11,"id":"b2764836_5ded3dcd","line":264,"range":{"start_line":264,"start_character":63,"end_line":264,"end_character":64},"in_reply_to":"c6efc873_116b7d52","updated":"2025-10-08 12:29:50.000000000","message":"yes:) my basic math is baad","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"0f1951e1aa7ab7827ceccf9623f95867e08f9abc","unresolved":true,"context_lines":[{"line_number":305,"context_line":"            [G1_RP1],"},{"line_number":306,"context_line":"            [G1_RP1, G2_RP3],"},{"line_number":307,"context_line":"            [G1_RP1, G2_RP3, G3_RP2],"},{"line_number":308,"context_line":"            # ..."},{"line_number":309,"context_line":"        ]"},{"line_number":310,"context_line":""},{"line_number":311,"context_line":"        for i, p in enumerate("}],"source_content_type":"text/x-python","patch_set":11,"id":"4f441226_a8f3ffc5","line":308,"updated":"2025-10-07 14:01:20.000000000","message":"So this is just checking the first bunch of these calls to make sure we look like we\u0027re doing the right thing, correct?","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"d496f952929dffe8e89752aa84bec8205b8f6bda","unresolved":false,"context_lines":[{"line_number":305,"context_line":"            [G1_RP1],"},{"line_number":306,"context_line":"            [G1_RP1, G2_RP3],"},{"line_number":307,"context_line":"            [G1_RP1, G2_RP3, G3_RP2],"},{"line_number":308,"context_line":"            # ..."},{"line_number":309,"context_line":"        ]"},{"line_number":310,"context_line":""},{"line_number":311,"context_line":"        for i, p in enumerate("}],"source_content_type":"text/x-python","patch_set":11,"id":"3d210bd4_433dfe5e","line":308,"in_reply_to":"2bca4962_91562d96","updated":"2025-10-08 14:10:14.000000000","message":"Done","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c885651bbcca919d70089e29d2ab1935159d0ea","unresolved":true,"context_lines":[{"line_number":305,"context_line":"            [G1_RP1],"},{"line_number":306,"context_line":"            [G1_RP1, G2_RP3],"},{"line_number":307,"context_line":"            [G1_RP1, G2_RP3, G3_RP2],"},{"line_number":308,"context_line":"            # ..."},{"line_number":309,"context_line":"        ]"},{"line_number":310,"context_line":""},{"line_number":311,"context_line":"        for i, p in enumerate("}],"source_content_type":"text/x-python","patch_set":11,"id":"821a24a3_5ed14f95","line":308,"in_reply_to":"4f441226_a8f3ffc5","updated":"2025-10-08 12:29:50.000000000","message":"correct. I would not really try to list all of them. But I can.","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"c6c26cef04ebe008aebbec07d3e65e1742c8fb0f","unresolved":true,"context_lines":[{"line_number":305,"context_line":"            [G1_RP1],"},{"line_number":306,"context_line":"            [G1_RP1, G2_RP3],"},{"line_number":307,"context_line":"            [G1_RP1, G2_RP3, G3_RP2],"},{"line_number":308,"context_line":"            # ..."},{"line_number":309,"context_line":"        ]"},{"line_number":310,"context_line":""},{"line_number":311,"context_line":"        for i, p in enumerate("}],"source_content_type":"text/x-python","patch_set":11,"id":"2bca4962_91562d96","line":308,"in_reply_to":"821a24a3_5ed14f95","updated":"2025-10-08 13:41:48.000000000","message":"No, it\u0027s fine.. Normally we\u0027d assert them all, but understand why not in this case. It might be worth a comment (instead of just `# ...`) about this, but it\u0027s not that important.","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"0f1951e1aa7ab7827ceccf9623f95867e08f9abc","unresolved":true,"context_lines":[{"line_number":315,"context_line":"            self.assertEqual("},{"line_number":316,"context_line":"                expected, actual, \"Call index %d does not match\" % i)"},{"line_number":317,"context_line":""},{"line_number":318,"context_line":"        self.assertEqual(60, len(mock_consolidate.mock_calls))"},{"line_number":319,"context_line":""},{"line_number":320,"context_line":"        # pairs of consolidated allocation request mapping and the"},{"line_number":321,"context_line":"        # expected result of the exceeds_capacity call on that areq."}],"source_content_type":"text/x-python","patch_set":11,"id":"4134da0b_a3eb4b0a","line":318,"updated":"2025-10-07 14:01:20.000000000","message":"I don\u0027t think I know where \"60\" comes from","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4c885651bbcca919d70089e29d2ab1935159d0ea","unresolved":true,"context_lines":[{"line_number":315,"context_line":"            self.assertEqual("},{"line_number":316,"context_line":"                expected, actual, \"Call index %d does not match\" % i)"},{"line_number":317,"context_line":""},{"line_number":318,"context_line":"        self.assertEqual(60, len(mock_consolidate.mock_calls))"},{"line_number":319,"context_line":""},{"line_number":320,"context_line":"        # pairs of consolidated allocation request mapping and the"},{"line_number":321,"context_line":"        # expected result of the exceeds_capacity call on that areq."}],"source_content_type":"text/x-python","patch_set":11,"id":"535c4aa4_65283b98","line":318,"in_reply_to":"4134da0b_a3eb4b0a","updated":"2025-10-08 12:29:50.000000000","message":"Brutal honesty, me neither. I will figure out the math... or list all the 60 steps so we can validate that it make sense to take those steps","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"c6c26cef04ebe008aebbec07d3e65e1742c8fb0f","unresolved":false,"context_lines":[{"line_number":315,"context_line":"            self.assertEqual("},{"line_number":316,"context_line":"                expected, actual, \"Call index %d does not match\" % i)"},{"line_number":317,"context_line":""},{"line_number":318,"context_line":"        self.assertEqual(60, len(mock_consolidate.mock_calls))"},{"line_number":319,"context_line":""},{"line_number":320,"context_line":"        # pairs of consolidated allocation request mapping and the"},{"line_number":321,"context_line":"        # expected result of the exceeds_capacity call on that areq."}],"source_content_type":"text/x-python","patch_set":11,"id":"04e7b501_9f560b0b","line":318,"in_reply_to":"465886e7_c7449aea","updated":"2025-10-08 13:41:48.000000000","message":"Acknowledged","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"9bad9ea513b0f07e07af0b7d0c782f5bcf4b2070","unresolved":true,"context_lines":[{"line_number":315,"context_line":"            self.assertEqual("},{"line_number":316,"context_line":"                expected, actual, \"Call index %d does not match\" % i)"},{"line_number":317,"context_line":""},{"line_number":318,"context_line":"        self.assertEqual(60, len(mock_consolidate.mock_calls))"},{"line_number":319,"context_line":""},{"line_number":320,"context_line":"        # pairs of consolidated allocation request mapping and the"},{"line_number":321,"context_line":"        # expected result of the exceeds_capacity call on that areq."}],"source_content_type":"text/x-python","patch_set":11,"id":"465886e7_c7449aea","line":318,"in_reply_to":"535c4aa4_65283b98","updated":"2025-10-08 12:52:32.000000000","message":"Described the math","commit_id":"e3cebf6f2263513149bfc3e68e6d638ea7e7e54f"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"c6c26cef04ebe008aebbec07d3e65e1742c8fb0f","unresolved":true,"context_lines":[{"line_number":361,"context_line":"        #    build, so we pruned 9 steps."},{"line_number":362,"context_line":"        # 2. When we find an invalid prefix with length 3 we cannot prune"},{"line_number":363,"context_line":"        #   anything anymore just continue with the next product. So that is 0"},{"line_number":364,"context_line":"        #   pruned steps"},{"line_number":365,"context_line":"        # 3. There are two more invalid prefixes with length 2:"},{"line_number":366,"context_line":"        #    * G1-RP2, G2-RP2 \u003d\u003e 9 pruned steps"},{"line_number":367,"context_line":"        #    * G1-RP3, G2-RP3 \u003d\u003e 9 pruned steps"}],"source_content_type":"text/x-python","patch_set":13,"id":"aec9e59f_3561df5b","line":364,"updated":"2025-10-08 13:41:48.000000000","message":"Bad indenting here, if you\u0027re going to respin anyway.","commit_id":"d3da9c722565981628a86758218a9e46108125bc"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"d496f952929dffe8e89752aa84bec8205b8f6bda","unresolved":false,"context_lines":[{"line_number":361,"context_line":"        #    build, so we pruned 9 steps."},{"line_number":362,"context_line":"        # 2. When we find an invalid prefix with length 3 we cannot prune"},{"line_number":363,"context_line":"        #   anything anymore just continue with the next product. So that is 0"},{"line_number":364,"context_line":"        #   pruned steps"},{"line_number":365,"context_line":"        # 3. There are two more invalid prefixes with length 2:"},{"line_number":366,"context_line":"        #    * G1-RP2, G2-RP2 \u003d\u003e 9 pruned steps"},{"line_number":367,"context_line":"        #    * G1-RP3, G2-RP3 \u003d\u003e 9 pruned steps"}],"source_content_type":"text/x-python","patch_set":13,"id":"60b1ce3d_33ba8e55","line":364,"in_reply_to":"aec9e59f_3561df5b","updated":"2025-10-08 14:10:14.000000000","message":"Done","commit_id":"d3da9c722565981628a86758218a9e46108125bc"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"750f7904d303e20b00b74f9a97b6501d45f9b1c9","unresolved":true,"context_lines":[{"line_number":370,"context_line":"        #"},{"line_number":371,"context_line":"        # Also as a crosscheck, we pruned 3*3 \u003d 9 full candidates and that is"},{"line_number":372,"context_line":"        # matching our math above. We have 27 possible candidates due to the"},{"line_number":373,"context_line":"        # nature of the product (3^3) but only 6 (3!) are valid. And 27 - 6 \u003d 9"},{"line_number":374,"context_line":"        # so indeed the optimization pruned out checking invalid candidates."},{"line_number":375,"context_line":""},{"line_number":376,"context_line":"        # With this 3 by 3 example the number of checks are higher when"}],"source_content_type":"text/x-python","patch_set":13,"id":"496d8954_e815556e","line":373,"updated":"2025-10-08 13:25:50.000000000","message":"gibi please stop doing math! this is embarrassing.\n\nAnyhow the pruning filtered 9 candidates but other invalid candidates are filtered without pruning. Those are the ones that has a length of 3 when fail the test.","commit_id":"d3da9c722565981628a86758218a9e46108125bc"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"e0f528b1d6fac177354c188614509a6146f352d7","unresolved":false,"context_lines":[{"line_number":370,"context_line":"        #"},{"line_number":371,"context_line":"        # Also as a crosscheck, we pruned 3*3 \u003d 9 full candidates and that is"},{"line_number":372,"context_line":"        # matching our math above. We have 27 possible candidates due to the"},{"line_number":373,"context_line":"        # nature of the product (3^3) but only 6 (3!) are valid. And 27 - 6 \u003d 9"},{"line_number":374,"context_line":"        # so indeed the optimization pruned out checking invalid candidates."},{"line_number":375,"context_line":""},{"line_number":376,"context_line":"        # With this 3 by 3 example the number of checks are higher when"}],"source_content_type":"text/x-python","patch_set":13,"id":"35d72ddb_c1da1139","line":373,"in_reply_to":"496d8954_e815556e","updated":"2025-10-08 13:27:07.000000000","message":"Done","commit_id":"d3da9c722565981628a86758218a9e46108125bc"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"e11e23607fe657a5ceaf51078b1f535a4cbe817b","unresolved":false,"context_lines":[{"line_number":271,"context_line":"            # product and then later processing would filter out the invalid,"},{"line_number":272,"context_line":"            # overlapping ones."},{"line_number":273,"context_line":"            self.assertEqual(6, len(areq_lists))"},{"line_number":274,"context_line":"            self.assertEqual(["},{"line_number":275,"context_line":"                (G1_RP1, G2_RP2, G3_RP3),"},{"line_number":276,"context_line":"                (G1_RP1, G2_RP3, G3_RP2),"},{"line_number":277,"context_line":"                (G1_RP2, G2_RP1, G3_RP3),"},{"line_number":278,"context_line":"                (G1_RP2, G2_RP3, G3_RP1),"},{"line_number":279,"context_line":"                (G1_RP3, G2_RP1, G3_RP2),"},{"line_number":280,"context_line":"                (G1_RP3, G2_RP2, G3_RP1),],"},{"line_number":281,"context_line":"                areq_lists)"},{"line_number":282,"context_line":""},{"line_number":283,"context_line":"        # areq_list, result pairs where areq_list is a partial product"},{"line_number":284,"context_line":"        # and the result is the expected return value of _exceeds_capacity"},{"line_number":285,"context_line":"        expected_exceeds_capacity_calls \u003d ["}],"source_content_type":"text/x-python","patch_set":16,"id":"1aedd300_1b18a6e5","line":282,"range":{"start_line":274,"start_character":11,"end_line":282,"end_character":1},"updated":"2025-10-09 21:16:43.000000000","message":"ok i guess it makes senses that this would still have a deterministic order.\nsince we are explicitly generating the product leaveraging the prefix to discard the candidates.\n\nso while in relaity we cant rely on the db returing the candiates in any paricalar order in this case we have a fixed input so the new product generator shoudl be deterministic as a result.","commit_id":"d73e760ec1c4fa762632b9c3ca1c2edb724ff6b7"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"e11e23607fe657a5ceaf51078b1f535a4cbe817b","unresolved":false,"context_lines":[{"line_number":326,"context_line":"            ((G1_RP3, G2_RP3), True),  # suffixes are pruned"},{"line_number":327,"context_line":"            # backtrack all the way as we finished with the level 1"},{"line_number":328,"context_line":"            # possibilities as well"},{"line_number":329,"context_line":"        ]"},{"line_number":330,"context_line":""},{"line_number":331,"context_line":"        for i, p in enumerate("},{"line_number":332,"context_line":"            zip(expected_exceeds_capacity_calls, exceeds_capacity_calls)"}],"source_content_type":"text/x-python","patch_set":16,"id":"acd78402_7f3ad669","line":329,"updated":"2025-10-09 21:16:43.000000000","message":"+1 this perhaps more then anything else very clearly show the behavior of the algorthim","commit_id":"d73e760ec1c4fa762632b9c3ca1c2edb724ff6b7"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"e11e23607fe657a5ceaf51078b1f535a4cbe817b","unresolved":true,"context_lines":[{"line_number":336,"context_line":"                expected, actual, \"Call index %d does not match\" % i)"},{"line_number":337,"context_line":""},{"line_number":338,"context_line":"        # Why 30 is the correct answer?"},{"line_number":339,"context_line":"        # I don\u0027t have an easy way to prove that mathematically. It was"},{"line_number":340,"context_line":"        # easier to list them all out above so they can be reviewed"},{"line_number":341,"context_line":"        #"},{"line_number":342,"context_line":"        # With this 3 by 3 example the number of checks are higher when"}],"source_content_type":"text/x-python","patch_set":16,"id":"ea8fac38_a0309cf7","line":339,"range":{"start_line":339,"start_character":10,"end_line":339,"end_character":63},"updated":"2025-10-09 21:16:43.000000000","message":"i asks gemini\n\n```\nExcellent questions. Here’s how the number of checks scales and a breakdown of when this\nrecursive approach is better than a simpler alternative.\n\n## Scaling and the Predictive Formula\nThe number of checks performed by the recursive algorithm isn\u0027t a simple polynomial or\nexponential function, but it can be predicted precisely. It scales based on the number\nof permutations at each level of the search. Let N be the number of groups and resource\nproviders (in your example, N\u003d3). The total number of checks, C(N), is the sum of checks\nperformed at each level of recursion (from assigning the first group to assigning the\nNth group). The formula is:\n\nC(N)\u003dNk\u003d0∑N−1​P(N,k)\nwhere 1P(N,k)\u003d(N−k)!N!​ is the number of k-permutations of N (i.e., the number of ways\nto choose and arrange k items from a set of N).2\n\nLet’s re-verify this for your N\u003d3 example:\nP(3,0)\u003d1\nP(3,1)\u003d3\nP(3,2)\u003d3×2\u003d6\nC(3)\u003d3×(P(3,0)+P(3,1)+P(3,2))\u003d3×(1+3+6)\u003d3×10\u003d30\nThis matches your result. The complexity is dominated by the largest term, making it\nroughly proportional to O(N⋅N!).\n\n## Recursive Check vs. Simple Product: The Crossover Point\nThe main alternative is a \"brute-force\" method:\n\n1. Generate the Cartesian product of all possible choices (e.g., using Python\u0027s\nitertools.product).3 This creates every possible combination, including invalid ones.\nThe total number of combinations is NN.\n2. Filter this massive list by checking each of the NN combinations to see if it\u0027s valid\n(i.e., contains no duplicate resource providers).\n\nThe question then becomes: at what point is it better to check partial solutions\nrecursively instead of generating all NN solutions and checking them at the end? Let\u0027s\ncompare the number of checks for both methods as N grows.\n\nN (Groups)Recursive Checks ( C(N) )Simple Product Checks ( NN )More Efficient Method\n1                 1                           1               Tie\n2                 6                           4               Simple Product\n3                30                          27               Simple Product\n4               164                         256               Recursive\n5             1,030                       3,125               Recursive\n6             7,812                      46,656               Recursive\n8           870,920                  16,777,216               Recursive\n\nThe crossover point is at N\u003d4. For N \u003c 4, the overhead of checking partial solutions at\neach recursive step is higher than simply generating all combinations and filtering them\nonce. For N ≥ 4, the recursive approach becomes dramatically more efficient. The reason is\nthe difference in growth rates. The simple product\u0027s search space (NN) grows\nsuperexponentially. The recursive algorithm\u0027s \"pruning\" ability—where it discards an\nentire branch of possibilities as soon as it finds a single conflict—saves it from\nexploring an enormous number of dead ends. As the author of the code noted, this\noptimization is essential for solving the problem in a reasonable amount of time for\nlarger N, like N\u003d8.\n```","commit_id":"d73e760ec1c4fa762632b9c3ca1c2edb724ff6b7"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"56d58b6f8fa680f1467c0b4c2a833755f147330e","unresolved":true,"context_lines":[{"line_number":336,"context_line":"                expected, actual, \"Call index %d does not match\" % i)"},{"line_number":337,"context_line":""},{"line_number":338,"context_line":"        # Why 30 is the correct answer?"},{"line_number":339,"context_line":"        # I don\u0027t have an easy way to prove that mathematically. It was"},{"line_number":340,"context_line":"        # easier to list them all out above so they can be reviewed"},{"line_number":341,"context_line":"        #"},{"line_number":342,"context_line":"        # With this 3 by 3 example the number of checks are higher when"}],"source_content_type":"text/x-python","patch_set":16,"id":"c2b9f9c7_d85dd198","line":339,"range":{"start_line":339,"start_character":10,"end_line":339,"end_character":63},"in_reply_to":"09f5822f_4e6c1e95","updated":"2025-10-13 19:13:42.000000000","message":"Memory usage is higher, but by less than 1MiB. I don\u0027t really understand how that can be, but given the cpu time difference, I assume it must be just be far less deep than I\u0027m expecting.\n\nStill, `_exceeds_capacity()` gets run almost 5k times. I\u0027d really like to understand how recursion depth varies with N because that\u0027s 5x the stack limit. I assume it must be much wider than deep.","commit_id":"d73e760ec1c4fa762632b9c3ca1c2edb724ff6b7"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"ae22977ad19d9a7cbb3d49fd22bd66398572f706","unresolved":true,"context_lines":[{"line_number":336,"context_line":"                expected, actual, \"Call index %d does not match\" % i)"},{"line_number":337,"context_line":""},{"line_number":338,"context_line":"        # Why 30 is the correct answer?"},{"line_number":339,"context_line":"        # I don\u0027t have an easy way to prove that mathematically. It was"},{"line_number":340,"context_line":"        # easier to list them all out above so they can be reviewed"},{"line_number":341,"context_line":"        #"},{"line_number":342,"context_line":"        # With this 3 by 3 example the number of checks are higher when"}],"source_content_type":"text/x-python","patch_set":16,"id":"f94c96ca_e48bbb50","line":339,"range":{"start_line":339,"start_character":10,"end_line":339,"end_character":63},"in_reply_to":"6cec1196_516ba64e","updated":"2025-10-14 13:40:13.000000000","message":"Added the recursion length explanation to the commit message.","commit_id":"d73e760ec1c4fa762632b9c3ca1c2edb724ff6b7"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"4f040a180e06e401f329460783aaeffd6d5c1ddd","unresolved":false,"context_lines":[{"line_number":336,"context_line":"                expected, actual, \"Call index %d does not match\" % i)"},{"line_number":337,"context_line":""},{"line_number":338,"context_line":"        # Why 30 is the correct answer?"},{"line_number":339,"context_line":"        # I don\u0027t have an easy way to prove that mathematically. It was"},{"line_number":340,"context_line":"        # easier to list them all out above so they can be reviewed"},{"line_number":341,"context_line":"        #"},{"line_number":342,"context_line":"        # With this 3 by 3 example the number of checks are higher when"}],"source_content_type":"text/x-python","patch_set":16,"id":"17a149e2_a7be5d33","line":339,"range":{"start_line":339,"start_character":10,"end_line":339,"end_character":63},"in_reply_to":"7fb83518_ce3702f0","updated":"2025-10-15 07:57:34.000000000","message":"Acknowledged","commit_id":"d73e760ec1c4fa762632b9c3ca1c2edb724ff6b7"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"804804f1ea64a9900263234e7805d4a3742786c9","unresolved":true,"context_lines":[{"line_number":336,"context_line":"                expected, actual, \"Call index %d does not match\" % i)"},{"line_number":337,"context_line":""},{"line_number":338,"context_line":"        # Why 30 is the correct answer?"},{"line_number":339,"context_line":"        # I don\u0027t have an easy way to prove that mathematically. It was"},{"line_number":340,"context_line":"        # easier to list them all out above so they can be reviewed"},{"line_number":341,"context_line":"        #"},{"line_number":342,"context_line":"        # With this 3 by 3 example the number of checks are higher when"}],"source_content_type":"text/x-python","patch_set":16,"id":"6cec1196_516ba64e","line":339,"range":{"start_line":339,"start_character":10,"end_line":339,"end_character":63},"in_reply_to":"c2b9f9c7_d85dd198","updated":"2025-10-14 08:33:55.000000000","message":"To see the depth you need to check when we recuse downwards. \nThe code\n```\n    yield from _generate(index + 1, new_partial_product)\n```\ndoes a step down in the recursion. And each such call increases the ``index`` by 1. So the maximum recursion depth is the length of array ``index`` is indexing into.\n\n```\n    for item in frozen_iterables[index]:\n```\n\nSo the recursion depth is the number of iterables passed to product(). \nThe python default allowed recursion depth is 1000. So this product() function is good for ~ 990 request groups per GET allocation_candidate requests with the default python configuration. (and if needed python\u0027s limit can be bumped too)\n\n---\n\nI see where the memory usage increase is coming from. Each recursion step in the current tried path needs to hold a partial product list. That is a list of refs to AllocationRequest objects. So in a 64 group example we have a 1+2+3+...+64\u003d2016 total length kept in memory. That is more than the index array held in memory for the previous solution. But not extreme. If you feel this is too much we can improve on it further based on Sean\u0027s suggestion. The on stack partial candidates can be replaced with a single list maintained during the recursion and backtrack. This does not give us performance benefits (I measured) but it would limit the memory usage to a maximum list length equal to the number of iterables product() got (i.e. the number of groups in the a_c query).","commit_id":"d73e760ec1c4fa762632b9c3ca1c2edb724ff6b7"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"8731dab87d088203ff46df27d992f99af79b2a51","unresolved":true,"context_lines":[{"line_number":336,"context_line":"                expected, actual, \"Call index %d does not match\" % i)"},{"line_number":337,"context_line":""},{"line_number":338,"context_line":"        # Why 30 is the correct answer?"},{"line_number":339,"context_line":"        # I don\u0027t have an easy way to prove that mathematically. It was"},{"line_number":340,"context_line":"        # easier to list them all out above so they can be reviewed"},{"line_number":341,"context_line":"        #"},{"line_number":342,"context_line":"        # With this 3 by 3 example the number of checks are higher when"}],"source_content_type":"text/x-python","patch_set":16,"id":"09f5822f_4e6c1e95","line":339,"range":{"start_line":339,"start_character":10,"end_line":339,"end_character":63},"in_reply_to":"d3bb9c9c_175c9de0","updated":"2025-10-13 18:47:47.000000000","message":"870k checks in the recursive mode makes me worry about recursion depth. I\u0027ll be honest and say I\u0027m really not fully grokking the thing enough to understand how deep we\u0027ll go, but..what\u0027s the depth of the 64x test\u0027s call tree? What about the memory footprint there? I would think that the previous approach, even if it does more, would be constant in terms of memory usage where this scales non-linearly with N right?","commit_id":"d73e760ec1c4fa762632b9c3ca1c2edb724ff6b7"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"ffe1d6af38b025f6946e37ce45de3d2686fb702a","unresolved":false,"context_lines":[{"line_number":336,"context_line":"                expected, actual, \"Call index %d does not match\" % i)"},{"line_number":337,"context_line":""},{"line_number":338,"context_line":"        # Why 30 is the correct answer?"},{"line_number":339,"context_line":"        # I don\u0027t have an easy way to prove that mathematically. It was"},{"line_number":340,"context_line":"        # easier to list them all out above so they can be reviewed"},{"line_number":341,"context_line":"        #"},{"line_number":342,"context_line":"        # With this 3 by 3 example the number of checks are higher when"}],"source_content_type":"text/x-python","patch_set":16,"id":"d3bb9c9c_175c9de0","line":339,"range":{"start_line":339,"start_character":10,"end_line":339,"end_character":63},"in_reply_to":"ea8fac38_a0309cf7","updated":"2025-10-10 10:03:52.000000000","message":"Nice. I\u0027m not saying I fully able to follow it but at least it matches my observations.","commit_id":"d73e760ec1c4fa762632b9c3ca1c2edb724ff6b7"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"863a027a1d0a1dbc226681a5b75e343153df60a6","unresolved":true,"context_lines":[{"line_number":336,"context_line":"                expected, actual, \"Call index %d does not match\" % i)"},{"line_number":337,"context_line":""},{"line_number":338,"context_line":"        # Why 30 is the correct answer?"},{"line_number":339,"context_line":"        # I don\u0027t have an easy way to prove that mathematically. It was"},{"line_number":340,"context_line":"        # easier to list them all out above so they can be reviewed"},{"line_number":341,"context_line":"        #"},{"line_number":342,"context_line":"        # With this 3 by 3 example the number of checks are higher when"}],"source_content_type":"text/x-python","patch_set":16,"id":"7fb83518_ce3702f0","line":339,"range":{"start_line":339,"start_character":10,"end_line":339,"end_character":63},"in_reply_to":"f94c96ca_e48bbb50","updated":"2025-10-14 17:18:33.000000000","message":"\u003e So the recursion depth is the number of iterables passed to product().\n\nYeah I got here myself yesterday after some more study.\n\n\u003e But not extreme. If you feel this is too much \n\nNo, as noted after I tried -- I was expecting it to be much higher, because I was expecting the recursion to be much deeper than it is.","commit_id":"d73e760ec1c4fa762632b9c3ca1c2edb724ff6b7"}],"placement/tests/unit/test_util.py":[{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"8731dab87d088203ff46df27d992f99af79b2a51","unresolved":true,"context_lines":[{"line_number":1505,"context_line":"    def test_product_with_pruning(self):"},{"line_number":1506,"context_line":"        mock_filter \u003d mock.Mock(return_value\u003dFalse)"},{"line_number":1507,"context_line":"        product \u003d list("},{"line_number":1508,"context_line":"            util.product(mock_filter, *([range(3)] * 3)))"},{"line_number":1509,"context_line":"        self.assertEqual(27, len(product))"},{"line_number":1510,"context_line":""},{"line_number":1511,"context_line":"        skip_repeat_calls \u003d []"}],"source_content_type":"text/x-python","patch_set":17,"id":"ab40cfc3_02e1e26c","line":1508,"updated":"2025-10-13 18:47:47.000000000","message":"Is there some reason this doesn\u0027t just use `self._no_skip()` like the two above?","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"804804f1ea64a9900263234e7805d4a3742786c9","unresolved":true,"context_lines":[{"line_number":1505,"context_line":"    def test_product_with_pruning(self):"},{"line_number":1506,"context_line":"        mock_filter \u003d mock.Mock(return_value\u003dFalse)"},{"line_number":1507,"context_line":"        product \u003d list("},{"line_number":1508,"context_line":"            util.product(mock_filter, *([range(3)] * 3)))"},{"line_number":1509,"context_line":"        self.assertEqual(27, len(product))"},{"line_number":1510,"context_line":""},{"line_number":1511,"context_line":"        skip_repeat_calls \u003d []"}],"source_content_type":"text/x-python","patch_set":17,"id":"fa5e97d1_7e2dba42","line":1508,"in_reply_to":"ab40cfc3_02e1e26c","updated":"2025-10-14 08:33:55.000000000","message":"I assert the number of calls to mock_filter below","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"21729e0f75b2ee0e62093969a14e4f6eec998c2d","unresolved":false,"context_lines":[{"line_number":1505,"context_line":"    def test_product_with_pruning(self):"},{"line_number":1506,"context_line":"        mock_filter \u003d mock.Mock(return_value\u003dFalse)"},{"line_number":1507,"context_line":"        product \u003d list("},{"line_number":1508,"context_line":"            util.product(mock_filter, *([range(3)] * 3)))"},{"line_number":1509,"context_line":"        self.assertEqual(27, len(product))"},{"line_number":1510,"context_line":""},{"line_number":1511,"context_line":"        skip_repeat_calls \u003d []"}],"source_content_type":"text/x-python","patch_set":17,"id":"a092744d_acc0718e","line":1508,"in_reply_to":"fa5e97d1_7e2dba42","updated":"2025-10-15 13:25:15.000000000","message":"Acknowledged","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"8731dab87d088203ff46df27d992f99af79b2a51","unresolved":true,"context_lines":[{"line_number":1506,"context_line":"        mock_filter \u003d mock.Mock(return_value\u003dFalse)"},{"line_number":1507,"context_line":"        product \u003d list("},{"line_number":1508,"context_line":"            util.product(mock_filter, *([range(3)] * 3)))"},{"line_number":1509,"context_line":"        self.assertEqual(27, len(product))"},{"line_number":1510,"context_line":""},{"line_number":1511,"context_line":"        skip_repeat_calls \u003d []"},{"line_number":1512,"context_line":""}],"source_content_type":"text/x-python","patch_set":17,"id":"f01e4c30_96e1bcc0","line":1509,"updated":"2025-10-13 18:47:47.000000000","message":"This is just testing that if we never filter anything we get the same thing as `itertools.product()` right? This seems sort of duplicative of the above test, and it doesn\u0027t really tell me that it\u0027s generating the same thing.\n\nIn either this or the above case, why not compare this to the result of `itertools.product()` which would assert both things and probably make it obvious without a comment?","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"ae22977ad19d9a7cbb3d49fd22bd66398572f706","unresolved":false,"context_lines":[{"line_number":1506,"context_line":"        mock_filter \u003d mock.Mock(return_value\u003dFalse)"},{"line_number":1507,"context_line":"        product \u003d list("},{"line_number":1508,"context_line":"            util.product(mock_filter, *([range(3)] * 3)))"},{"line_number":1509,"context_line":"        self.assertEqual(27, len(product))"},{"line_number":1510,"context_line":""},{"line_number":1511,"context_line":"        skip_repeat_calls \u003d []"},{"line_number":1512,"context_line":""}],"source_content_type":"text/x-python","patch_set":17,"id":"8d84384e_e5b3feb4","line":1509,"in_reply_to":"18004b52_d892b892","updated":"2025-10-14 13:40:13.000000000","message":"\u003e Good point for doing the comparing to itertools.product().\n\u003e \n\nDone.\n\n\u003e Also I can remove the duplication and just assume the number of calls to mock_filter is 27 below at L1530. But I felt it is more appropriate not to assume that but measure it.\n\u003e \n\u003e I have test_product_with_filtering and test_full_product_no_pruning as two separate test as it actually test two distinct behavior of the product() call. \n\u003e \n\u003e The former shows that you can remove final product via the filter function.\n\u003e The latter shows that the filter function can remove whole subspaces (subtrees) of the search space and hence doing less steps than the itertools.product()\n\nI added more text to the test cases to point out the difference.","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"804804f1ea64a9900263234e7805d4a3742786c9","unresolved":true,"context_lines":[{"line_number":1506,"context_line":"        mock_filter \u003d mock.Mock(return_value\u003dFalse)"},{"line_number":1507,"context_line":"        product \u003d list("},{"line_number":1508,"context_line":"            util.product(mock_filter, *([range(3)] * 3)))"},{"line_number":1509,"context_line":"        self.assertEqual(27, len(product))"},{"line_number":1510,"context_line":""},{"line_number":1511,"context_line":"        skip_repeat_calls \u003d []"},{"line_number":1512,"context_line":""}],"source_content_type":"text/x-python","patch_set":17,"id":"18004b52_d892b892","line":1509,"in_reply_to":"f01e4c30_96e1bcc0","updated":"2025-10-14 08:33:55.000000000","message":"Good point for doing the comparing to itertools.product().\n\nAlso I can remove the duplication and just assume the number of calls to mock_filter is 27 below at L1530. But I felt it is more appropriate not to assume that but measure it.\n\nI have test_product_with_filtering and test_full_product_no_pruning as two separate test as it actually test two distinct behavior of the product() call. \n\nThe former shows that you can remove final product via the filter function.\nThe latter shows that the filter function can remove whole subspaces (subtrees) of the search space and hence doing less steps than the itertools.product()","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"8731dab87d088203ff46df27d992f99af79b2a51","unresolved":true,"context_lines":[{"line_number":1512,"context_line":""},{"line_number":1513,"context_line":"        def skip_repeat(partial_product):"},{"line_number":1514,"context_line":"            skip_repeat_calls.append(partial_product)"},{"line_number":1515,"context_line":"            return len(set(partial_product)) !\u003d len(partial_product)"},{"line_number":1516,"context_line":""},{"line_number":1517,"context_line":"        product \u003d list(util.product(skip_repeat, *([range(3)] * 3)))"},{"line_number":1518,"context_line":"        self.assertEqual(6, len(product))"}],"source_content_type":"text/x-python","patch_set":17,"id":"8c01c1f2_606eeb9b","line":1515,"updated":"2025-10-13 18:47:47.000000000","message":"I don\u0027t understand what this test filter is doing. Its name implies to me that it\u0027s going to record all calls (which it is doing) and skip duplicates. But that must not be right because you\u0027re only actually checking the length(s). So what is `skip_repeat()` actually doing?\n\n(later)\n\nI guess it\u0027s resulting in skipping items that have the same number in any other \"column\" and that\u0027s the \"repeat\"? But I don\u0027t understand why that\u0027s the result of comparing the lengths of ...\n\n(later still)\n\nOkay I see, I guess you\u0027re returning True (skip) if there are dupes filtered out by the `set()`. Maybe I\u0027m just stupid, but I think a comment here could have saved me a lot of wondering. I think the confusion also came from the fact that you\u0027re recording all the `partial_product` values, so I thought you were going to be filtering based on something else (and the name). We could just increment a call counter in the test function scope to make it clearer (to me at least) that we\u0027re not actually caring to save the partial products? I read this before the `skip_repeat()` above, which probably would have made it more obvious, but that one not recording the calls is more straightforward.","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"ae22977ad19d9a7cbb3d49fd22bd66398572f706","unresolved":false,"context_lines":[{"line_number":1512,"context_line":""},{"line_number":1513,"context_line":"        def skip_repeat(partial_product):"},{"line_number":1514,"context_line":"            skip_repeat_calls.append(partial_product)"},{"line_number":1515,"context_line":"            return len(set(partial_product)) !\u003d len(partial_product)"},{"line_number":1516,"context_line":""},{"line_number":1517,"context_line":"        product \u003d list(util.product(skip_repeat, *([range(3)] * 3)))"},{"line_number":1518,"context_line":"        self.assertEqual(6, len(product))"}],"source_content_type":"text/x-python","patch_set":17,"id":"d1a96510_915e9297","line":1515,"in_reply_to":"8afd1512_c31e5043","updated":"2025-10-14 13:40:13.000000000","message":"Done","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"804804f1ea64a9900263234e7805d4a3742786c9","unresolved":true,"context_lines":[{"line_number":1512,"context_line":""},{"line_number":1513,"context_line":"        def skip_repeat(partial_product):"},{"line_number":1514,"context_line":"            skip_repeat_calls.append(partial_product)"},{"line_number":1515,"context_line":"            return len(set(partial_product)) !\u003d len(partial_product)"},{"line_number":1516,"context_line":""},{"line_number":1517,"context_line":"        product \u003d list(util.product(skip_repeat, *([range(3)] * 3)))"},{"line_number":1518,"context_line":"        self.assertEqual(6, len(product))"}],"source_content_type":"text/x-python","patch_set":17,"id":"8afd1512_c31e5043","line":1515,"in_reply_to":"8c01c1f2_606eeb9b","updated":"2025-10-14 08:33:55.000000000","message":"It filters any candidates that has a repeated value in it. E.g. (0, 1, 2) is OK, but (0,1,0,...) is not OK.\n\nI will ad comment and I will switch from recording calls to just counting them.","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"}],"placement/util.py":[{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"e11e23607fe657a5ceaf51078b1f535a4cbe817b","unresolved":true,"context_lines":[{"line_number":628,"context_line":"        yield from map(next, iterators)"},{"line_number":629,"context_line":""},{"line_number":630,"context_line":""},{"line_number":631,"context_line":"def product(should_skip, *iterables):"},{"line_number":632,"context_line":"    \"\"\"Recursively generates the Cartesian product of a list of iterables,"},{"line_number":633,"context_line":"    allowing for parts of the product space to be skipped."},{"line_number":634,"context_line":""}],"source_content_type":"text/x-python","patch_set":16,"id":"bfd65694_8192bbca","line":631,"updated":"2025-10-09 21:16:43.000000000","message":"gemini is bad a codign but claude sonet 4.5 is not\n\nthis is easyer to read then the prever version but it had some intersting insight we coudl try in a follow up patch\n```\nKey Optimizations\n1. Eliminate tuple concatenation\nThe current implementation uses current_product + (item,) which creates a new tuple on every recursion level. This is O(n) for each concatenation. Using a list and converting to tuple only when yielding is much faster.\n2. Avoid redundant tuple conversion at the start\nConverting all iterables to tuples upfront can be wasteful if pruning eliminates most branches early.\n3. Reduce function call overhead\nDirect list manipulation is faster than building tuples recursively.\n```\nit suggests\n\n```\ndef product(should_skip, *iterables):\n    \"\"\"Optimized version with reduced allocations and faster path building.\"\"\"\n    frozen_iterables \u003d tuple(map(tuple, iterables))\n    num_iterables \u003d len(frozen_iterables)\n    \n    if not frozen_iterables:\n        yield ()\n        return\n    \n    # Use a list to build the path, convert to tuple only when yielding\n    path \u003d []\n    \n    def _generate(index: int):\n        if index \u003d\u003d num_iterables:\n            yield tuple(path)  # Convert to tuple only at leaf nodes\n            return\n        \n        for item in frozen_iterables[index]:\n            path.append(item)\n            \n            # Check pruning with tuple view (only allocates when needed)\n            if not should_skip(tuple(path)):\n                yield from _generate(index + 1)\n            \n            path.pop()\n    \n    yield from _generate(0)\n```\n\nbecause \n```\nPerformance Impact\n\nTuple concatenation elimination: Reduces time complexity from O(n²) per path to O(n)\nIn-place list operations: append/pop are O(1) operations\nFewer allocations: Only creates tuples for non-pruned complete products and pruning checks\n```\n\nyour version has much better commments.","commit_id":"d73e760ec1c4fa762632b9c3ca1c2edb724ff6b7"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"e9805793bb8c9eb9122e927e4ea684bc15e069ec","unresolved":false,"context_lines":[{"line_number":628,"context_line":"        yield from map(next, iterators)"},{"line_number":629,"context_line":""},{"line_number":630,"context_line":""},{"line_number":631,"context_line":"def product(should_skip, *iterables):"},{"line_number":632,"context_line":"    \"\"\"Recursively generates the Cartesian product of a list of iterables,"},{"line_number":633,"context_line":"    allowing for parts of the product space to be skipped."},{"line_number":634,"context_line":""}],"source_content_type":"text/x-python","patch_set":16,"id":"531f0167_ae2d90e4","line":631,"in_reply_to":"658d0222_8bf643f6","updated":"2025-10-13 16:17:01.000000000","message":"Acknowledged","commit_id":"d73e760ec1c4fa762632b9c3ca1c2edb724ff6b7"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"ffe1d6af38b025f6946e37ce45de3d2686fb702a","unresolved":true,"context_lines":[{"line_number":628,"context_line":"        yield from map(next, iterators)"},{"line_number":629,"context_line":""},{"line_number":630,"context_line":""},{"line_number":631,"context_line":"def product(should_skip, *iterables):"},{"line_number":632,"context_line":"    \"\"\"Recursively generates the Cartesian product of a list of iterables,"},{"line_number":633,"context_line":"    allowing for parts of the product space to be skipped."},{"line_number":634,"context_line":""}],"source_content_type":"text/x-python","patch_set":16,"id":"658d0222_8bf643f6","line":631,"in_reply_to":"bfd65694_8192bbca","updated":"2025-10-10 10:03:52.000000000","message":"\u003e 1. Eliminate tuple concatenation\nThe current implementation uses current_product + (item,) which creates a new tuple on every recursion level. This is O(n) for each concatenation. Using a list and converting to tuple only when yielding is much faster.\n\nI think I disagree. new_partial_product needs to be an independent list from current_product. The current_product list is coming from the previous level of recursion by reference. So if we append to the current_product on this level that will have a side-effect on the previous level. So when we backtrack to the previous level the current_product there will be wrong.\n\nBut then I noticed that path.pop() on the backtrack codepath. That corrects for the side-effect spreading through the \"global\" list. So with the pop included it is a valid optimization.\n\n\u003e 2. Avoid redundant tuple conversion at the start\nConverting all iterables to tuples upfront can be wasteful if pruning eliminates most branches early.\n\nStrangely the suggested solution does not contain this change. The input iterables are generators so maybe this confused the LLM to not do it. \nAnyhow we need to index only into the outer tuple that holding a list of iterables. But that is a python variadric args and python stores them as a tuple already. So we need to convert nothing at the start. So I dropped that.\n\n\u003e 3. Reduce function call overhead\nDirect list manipulation is faster than building tuples recursively.\n\nthat is basically 1. in other words. So Done as well.\n\n---\n\nThen I run the functional tests expecting performance gains. But I see basically no difference. Then I realized that Suggestion 1 is moot as the proposed code converts the appended list to a tuple to pass it to should_skip. So we pay the same tuple conversion price. But we can remove that conversion if we state that should_skip should not ever modify the list passed in. And our current exceed_capacity() is good in this regard.\n\nStill no dice. The performance is the same. \n\nMaybe creating a new tuple from a previous tuples has some optimization in the background as python can assume both pieces of the concatenation is immutable. Or simply small tuples are cheaper than small lists. Anyhow I have some level of synthetic proof top of the functional test timings:\n\n```\n\u003e\u003e\u003e timeit.timeit(\"for i in range(32): a.append(i); a.pop()\", \"a \u003d []\", number\u003d10000000)\n5.0623831570010225\n\u003e\u003e\u003e timeit.timeit(\"for i in range(32): b \u003d a + (i,)\", \"a \u003d ()\", number\u003d10000000)\n4.098525649998919\n\u003e\u003e\u003e \n```\n\n---\n\nbottom line: \n* 1 does not actually optimize the performance in our case due to python internals\n* 2 is valid but insignificant due to done only once the start\n* 3 is 1\n\n---\nI published the code change as a separate patch to look at: https://review.opendev.org/c/openstack/placement/+/963673","commit_id":"d73e760ec1c4fa762632b9c3ca1c2edb724ff6b7"},{"author":{"_account_id":4393,"name":"Dan Smith","email":"dms@danplanet.com","username":"danms"},"change_message_id":"8731dab87d088203ff46df27d992f99af79b2a51","unresolved":true,"context_lines":[{"line_number":628,"context_line":"        yield from map(next, iterators)"},{"line_number":629,"context_line":""},{"line_number":630,"context_line":""},{"line_number":631,"context_line":"def product(should_skip, *iterables):"},{"line_number":632,"context_line":"    \"\"\"Recursively generates the Cartesian product of a list of iterables,"},{"line_number":633,"context_line":"    allowing for parts of the product space to be skipped."},{"line_number":634,"context_line":""}],"source_content_type":"text/x-python","patch_set":17,"id":"13aebf6e_2cbd67fa","line":631,"range":{"start_line":631,"start_character":4,"end_line":631,"end_character":11},"updated":"2025-10-13 18:47:47.000000000","message":"I think this should have a name indicative of its actual behavior. Unless I\u0027m missing something, this does not generate a product, but rather a filtered/pruned product according to our specific needs here. Something like `filtered_product()` or `product_by_capacity()` maybe? The latter seems the most appropriate to me, although since you\u0027re allowing the skip function to be passed in, perhaps it needs to be more generic (but more specific than just \"product\" IMHO).","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"804804f1ea64a9900263234e7805d4a3742786c9","unresolved":true,"context_lines":[{"line_number":628,"context_line":"        yield from map(next, iterators)"},{"line_number":629,"context_line":""},{"line_number":630,"context_line":""},{"line_number":631,"context_line":"def product(should_skip, *iterables):"},{"line_number":632,"context_line":"    \"\"\"Recursively generates the Cartesian product of a list of iterables,"},{"line_number":633,"context_line":"    allowing for parts of the product space to be skipped."},{"line_number":634,"context_line":""}],"source_content_type":"text/x-python","patch_set":17,"id":"aa70024c_56cbc92a","line":631,"range":{"start_line":631,"start_character":4,"end_line":631,"end_character":11},"in_reply_to":"13aebf6e_2cbd67fa","updated":"2025-10-14 08:33:55.000000000","message":"I can name it filtered_product for sure.","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"},{"author":{"_account_id":9708,"name":"Balazs Gibizer","display_name":"gibi","email":"gibizer@gmail.com","username":"gibi"},"change_message_id":"ae22977ad19d9a7cbb3d49fd22bd66398572f706","unresolved":false,"context_lines":[{"line_number":628,"context_line":"        yield from map(next, iterators)"},{"line_number":629,"context_line":""},{"line_number":630,"context_line":""},{"line_number":631,"context_line":"def product(should_skip, *iterables):"},{"line_number":632,"context_line":"    \"\"\"Recursively generates the Cartesian product of a list of iterables,"},{"line_number":633,"context_line":"    allowing for parts of the product space to be skipped."},{"line_number":634,"context_line":""}],"source_content_type":"text/x-python","patch_set":17,"id":"682ac486_1f5599e4","line":631,"range":{"start_line":631,"start_character":4,"end_line":631,"end_character":11},"in_reply_to":"aa70024c_56cbc92a","updated":"2025-10-14 13:40:13.000000000","message":"Done","commit_id":"2e66df14aad5f72de2a436195de385d21fdb2163"}]}
