diff --git a/mon/lp-optimizer.js b/mon/lp-optimizer.js index 98ea1f69c..2cb653ea6 100644 --- a/mon/lp-optimizer.js +++ b/mon/lp-optimizer.js @@ -50,7 +50,7 @@ async function lp_solve(text) return { score, vars }; } -async function optimize_initial({ osd_tree, pg_count, pg_size = 3, pg_minsize = 2, max_combinations = 10000, parity_space = 1, round_robin = false }) +async function optimize_initial({ osd_tree, pg_count, pg_size = 3, pg_minsize = 2, max_combinations = 10000, parity_space = 1, ordered = false }) { if (!pg_count || !osd_tree) { @@ -92,7 +92,7 @@ async function optimize_initial({ osd_tree, pg_count, pg_size = 3, pg_minsize = console.log(lp); throw new Error('Problem is infeasible or unbounded - is it a bug?'); } - const int_pgs = make_int_pgs(lp_result.vars, pg_count, round_robin); + const int_pgs = make_int_pgs(lp_result.vars, pg_count, ordered); const eff = pg_list_space_efficiency(int_pgs, all_weights, pg_minsize, parity_space); const res = { score: lp_result.score, @@ -382,11 +382,35 @@ async function optimize_change({ prev_pgs: prev_int_pgs, osd_tree, pg_size = 3, { differs++; } - for (let j = 0; j < pg_size; j++) + } + if (ordered) + { + for (let i = 0; i < pg_count; i++) { - if (new_pgs[i][j] != prev_int_pgs[i][j]) + for (let j = 0; j < pg_size; j++) { - osd_differs++; + if (new_pgs[i][j] != prev_int_pgs[i][j]) + { + osd_differs++; + } + } + } + } + else + { + for (let i = 0; i < pg_count; i++) + { + const old_map = prev_int_pgs[i].reduce((a, c) => { a[c] = (a[c]|0) + 1; return a; }, {}); + for (let j = 0; j < pg_size; j++) + { + if ((0|old_map[new_pgs[i][j]]) > 0) + { + old_map[new_pgs[i][j]]--; + } + else + { + osd_differs++; + } } } } diff --git a/mon/mon.js b/mon/mon.js index a1ef25868..3e198ad24 100644 --- a/mon/mon.js +++ b/mon/mon.js @@ -1098,7 +1098,7 @@ class Mon pg_size: pool_cfg.pg_size, pg_minsize: pool_cfg.pg_minsize, max_combinations: pool_cfg.max_osd_combinations, - round_robin: pool_cfg.scheme != 'replicated', + ordered: pool_cfg.scheme != 'replicated', }; let optimize_result; if (old_pg_count > 0) @@ -1121,10 +1121,6 @@ class Mon { pg.push(0); } - while (pg.length > pool_cfg.pg_size) - { - pg.pop(); - } } if (!this.state.config.pgs.hash) { diff --git a/mon/test-optimize-16_2.js b/mon/test-optimize-16_2.js deleted file mode 100644 index 86987d74b..000000000 --- a/mon/test-optimize-16_2.js +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Vitaliy Filippov, 2019+ -// License: VNPL-1.1 (see README.md for details) - -const LPOptimizer = require('./lp-optimizer.js'); - -const osd_tree = { - 100: { 1: 1 }, - 200: { 2: 1 }, - 300: { 3: 1 }, -}; - -async function run() -{ - let res; - console.log('16 PGs, size=3'); - res = await LPOptimizer.optimize_initial({ osd_tree, pg_size: 3, pg_count: 16 }); - LPOptimizer.print_change_stats(res, false); - console.log('\nChanging size to 2'); - res = await LPOptimizer.optimize_change({ prev_pgs: res.int_pgs, osd_tree, pg_size: 2 }); - LPOptimizer.print_change_stats(res, false); - if (res.space < 3*14/16) - { - throw new Error('Redistribution failed'); - } -} - -run().catch(console.error); diff --git a/mon/test-optimize-simple.js b/mon/test-optimize-simple.js index 97daa6580..70f89047b 100644 --- a/mon/test-optimize-simple.js +++ b/mon/test-optimize-simple.js @@ -5,21 +5,45 @@ const LPOptimizer = require('./lp-optimizer.js'); async function run() { - const osd_tree = { a: { 1: 1 }, b: { 2: 1 }, c: { 3: 1 } }; + const osd_tree = { + 100: { 1: 1 }, + 200: { 2: 1 }, + 300: { 3: 1 }, + }; + let res; console.log('16 PGs, size=3'); - res = await LPOptimizer.optimize_initial({ osd_tree, pg_size: 3, pg_count: 16 }); + res = await LPOptimizer.optimize_initial({ osd_tree, pg_size: 3, pg_count: 16, ordered: false }); LPOptimizer.print_change_stats(res, false); - - console.log('\nReduce PG size to 2'); - res = await LPOptimizer.optimize_change({ prev_pgs: res.int_pgs.map(pg => pg.slice(0, 2)), osd_tree, pg_size: 2 }); + assert(res.space == 3, 'Initial distribution'); + console.log('\nChange size to 2'); + res = await LPOptimizer.optimize_change({ prev_pgs: res.int_pgs, osd_tree, pg_size: 2, ordered: false }); LPOptimizer.print_change_stats(res, false); - + assert(res.space >= 3*14/16 && res.osd_differs == 0, 'Redistribution'); console.log('\nRemove OSD 3'); - delete osd_tree['c']; - res = await LPOptimizer.optimize_change({ prev_pgs: res.int_pgs, osd_tree, pg_size: 2 }); + const no3_tree = { ...osd_tree }; + delete no3_tree['300']; + res = await LPOptimizer.optimize_change({ prev_pgs: res.int_pgs, osd_tree: no3_tree, pg_size: 2, ordered: false }); LPOptimizer.print_change_stats(res, false); + assert(res.space == 2, 'Redistribution after OSD removal'); + + console.log('\n16 PGs, size=3, ordered'); + res = await LPOptimizer.optimize_initial({ osd_tree, pg_size: 3, pg_count: 16, ordered: true }); + LPOptimizer.print_change_stats(res, false); + assert(res.space == 3, 'Initial distribution'); + console.log('\nChange size to 2, ordered'); + res = await LPOptimizer.optimize_change({ prev_pgs: res.int_pgs, osd_tree, pg_size: 2, ordered: true }); + LPOptimizer.print_change_stats(res, false); + assert(res.space >= 3*14/16 && res.osd_differs < 8, 'Redistribution'); +} + +function assert(cond, txt) +{ + if (!cond) + { + throw new Error((txt||'test')+' failed'); + } } run().catch(console.error); diff --git a/mon/test-optimize-undersized.js b/mon/test-optimize-undersized.js index 306a5e3da..d1a766e6a 100644 --- a/mon/test-optimize-undersized.js +++ b/mon/test-optimize-undersized.js @@ -45,30 +45,45 @@ async function run() console.log('Empty tree:'); let res = await LPOptimizer.optimize_initial({ osd_tree: cur_tree, pg_size: 3, pg_count: 256 }); LPOptimizer.print_change_stats(res, false); + assert(res.space == 0); console.log('\nAdding 1st failure domain:'); cur_tree['dom1'] = osd_tree['dom1']; res = await LPOptimizer.optimize_change({ prev_pgs: res.int_pgs, osd_tree: cur_tree, pg_size: 3 }); LPOptimizer.print_change_stats(res, false); + assert(res.space == 12 && res.total_space == 12); console.log('\nAdding 2nd failure domain:'); cur_tree['dom2'] = osd_tree['dom2']; res = await LPOptimizer.optimize_change({ prev_pgs: res.int_pgs, osd_tree: cur_tree, pg_size: 3 }); LPOptimizer.print_change_stats(res, false); + assert(res.space == 24 && res.total_space == 24); console.log('\nAdding 3rd failure domain:'); cur_tree['dom3'] = osd_tree['dom3']; res = await LPOptimizer.optimize_change({ prev_pgs: res.int_pgs, osd_tree: cur_tree, pg_size: 3 }); LPOptimizer.print_change_stats(res, false); + assert(res.space == 36 && res.total_space == 36); console.log('\nRemoving 3rd failure domain:'); delete cur_tree['dom3']; res = await LPOptimizer.optimize_change({ prev_pgs: res.int_pgs, osd_tree: cur_tree, pg_size: 3 }); LPOptimizer.print_change_stats(res, false); + assert(res.space == 24 && res.total_space == 24); console.log('\nRemoving 2nd failure domain:'); delete cur_tree['dom2']; res = await LPOptimizer.optimize_change({ prev_pgs: res.int_pgs, osd_tree: cur_tree, pg_size: 3 }); LPOptimizer.print_change_stats(res, false); + assert(res.space == 12 && res.total_space == 12); console.log('\nRemoving 1st failure domain:'); delete cur_tree['dom1']; res = await LPOptimizer.optimize_change({ prev_pgs: res.int_pgs, osd_tree: cur_tree, pg_size: 3 }); LPOptimizer.print_change_stats(res, false); + assert(res.space == 0); +} + +function assert(cond, txt) +{ + if (!cond) + { + throw new Error((txt||'test')+' failed'); + } } run().catch(console.error);