Implement a PG generator for hierarchical failure domains

hier-failure-domains
Vitaliy Filippov 2022-12-22 01:47:52 +03:00
parent 98077a1712
commit 431f780347
1 changed files with 101 additions and 0 deletions

View File

@ -539,6 +539,106 @@ function extract_osds(osd_tree, levels, osd_level, osds = {})
return osds;
}
// generate random PGs with hierarchical failure domains, i.e. for example 3 DC each with 2 HOSTS
// osd_tree = { level3_id: { level2_id: { level1_id: scalar_value } }, ... }
// osd_tree may contain arbitrary number of levels, but level count must be the same across the whole tree
// size_per_level = number of items to select on each level, for example [3, 2, 1].
// must have the same number of items as the osd_tree level count.
// count = PG count to generate
// ordered = don't treat (x,y) and (y,x) as equal
// seq_layout = true for the [DC1,DC1,DC2,DC2,DC3,DC3] layout, false for [DC1,DC2,DC3,DC1,DC2,DC3] layout
function random_hier_combinations(osd_tree, size_per_level, count, ordered, seq_layout)
{
let seed = 0x5f020e43;
const rng = () =>
{
seed ^= seed << 13;
seed ^= seed >> 17;
seed ^= seed << 5;
return seed + 2147483648;
};
const get_max_level = (o) =>
{
let lvl = 0;
while (o instanceof Object)
{
for (const k in o)
{
lvl++;
o = o[k];
break;
}
}
return lvl;
};
const max_level = get_max_level(osd_tree);
const gen_pg = (select) =>
{
let pg = [ osd_tree ];
for (let level = 0; level < max_level; level++)
{
let npg = [];
for (let i = 0; i < pg.length; i++)
{
const keys = pg[i] instanceof Object ? Object.keys(pg[i]) : [];
const max_keys = keys.length < size_per_level[level] ? keys.length : size_per_level[level];
for (let j = 0; j < max_keys; j++)
{
const r = select(level, i, j, (ordered ? keys.length : (keys.length - (max_keys - j - 1))));
const el = pg[i][keys[r]] instanceof Object ? pg[i][keys[r]] : keys[r];
npg[seq_layout ? i*size_per_level[level]+j : j*pg.length+i] = el;
keys.splice(ordered ? r : 0, ordered ? 1 : (r+1));
}
for (let j = max_keys; j < size_per_level[level]; j++)
npg[seq_layout ? i*size_per_level[level]+j : j*pg.length+i] = NO_OSD;
}
pg = npg;
}
return pg;
};
const r = {};
// Generate random combinations including each OSD at least once
let has_next = true;
let ctr = [];
while (has_next)
{
let pg = gen_pg((level, i, j, n) =>
{
if (i == 0 && j == 0)
{
// Select a pre-determined OSD in the first position on each level
const r = ctr[level] == null || ctr[level][1] != n ? 0 : ctr[level][0];
ctr[level] = [ r, n ];
return r;
}
return rng() % n;
});
for (let i = ctr.length-1; i >= 0; i--)
{
ctr[i][0]++;
if (ctr[i][0] < ctr[i][1])
break;
else
ctr[i] = null;
}
has_next = ctr[0] != null;
const cyclic_pgs = [ pg ];
if (ordered)
for (let i = 1; i < pg.size; i++)
cyclic_pgs.push([ ...pg.slice(i), ...pg.slice(0, i) ]);
for (const pg of cyclic_pgs)
r['pg_'+pg.join('_')] = pg;
}
// Generate purely random combinations
while (count > 0)
{
let pg = gen_pg((l, i, j, n) => rng() % n);
r['pg_'+pg.join('_')] = pg;
count--;
}
return r;
}
// ordered = don't treat (x,y) and (y,x) as equal
function random_combinations(osd_tree, pg_size, count, ordered)
{
@ -758,5 +858,6 @@ module.exports = {
make_int_pgs,
align_pgs,
random_combinations,
random_hier_combinations,
all_combinations,
};