From 431f780347f3bda4b101f13e361558b15a4c785d Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Thu, 22 Dec 2022 01:47:52 +0300 Subject: [PATCH] Implement a PG generator for hierarchical failure domains --- mon/lp-optimizer.js | 101 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/mon/lp-optimizer.js b/mon/lp-optimizer.js index 8505c252..780b4c8d 100644 --- a/mon/lp-optimizer.js +++ b/mon/lp-optimizer.js @@ -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, };