Experimental: specify avg recovery time instead of speed

master
Vitaliy Filippov 2021-12-07 22:56:46 +03:00
parent fe06536dd0
commit 6f01329374
11 changed files with 148 additions and 10 deletions

View File

@ -45,7 +45,7 @@ https://yourcmc.ru/git/vitalif/vitastor/
AFR сервера эмпирически поделен на число дисков, чтобы "размазать" вероятность отказа сервера AFR сервера эмпирически поделен на число дисков, чтобы "размазать" вероятность отказа сервера
по его дискам. по его дискам.
Парадоксы дней рождений: ## Парадокс дней рождений
- PG почти гарантированно пересекаются, особенно в небольших кластерах. Степень их пересечения - PG почти гарантированно пересекаются, особенно в небольших кластерах. Степень их пересечения
очень полезно учитывать. очень полезно учитывать.

17
afr.js
View File

@ -55,7 +55,8 @@ function failure_rate_fullmesh(n, a, f)
// is that, with k=2, total failure rate doesn't depend on number of peers per OSD, // is that, with k=2, total failure rate doesn't depend on number of peers per OSD,
// because it gets increased linearly by increased number of peers to fail // because it gets increased linearly by increased number of peers to fail
// and decreased linearly by reduced rebalance time. // and decreased linearly by reduced rebalance time.
function cluster_afr({ n_hosts, n_drives, afr_drive, afr_host, capacity, speed, ec, ec_data, ec_parity, replicas, pgs = 1, osd_rm, degraded_replacement, down_out_interval = 600 }) function cluster_afr({ n_hosts, n_drives, afr_drive, afr_host, capacity, speed, disk_heal_hours, host_heal_hours,
ec, ec_data, ec_parity, replicas, pgs = 1, osd_rm, degraded_replacement, down_out_interval = 0 })
{ {
const pg_size = (ec ? ec_data+ec_parity : replicas); const pg_size = (ec ? ec_data+ec_parity : replicas);
// <peers> is a number of non-intersecting PGs that a single OSD/drive has on average // <peers> is a number of non-intersecting PGs that a single OSD/drive has on average
@ -66,8 +67,18 @@ function cluster_afr({ n_hosts, n_drives, afr_drive, afr_host, capacity, speed,
const resilver_peers = n_drives == 1 || osd_rm ? avg_distinct((n_hosts-1)*n_drives, pgs) : avg_distinct(n_drives-1, pgs); const resilver_peers = n_drives == 1 || osd_rm ? avg_distinct((n_hosts-1)*n_drives, pgs) : avg_distinct(n_drives-1, pgs);
// <host_resilver_peers> other drives participate in resilvering of a failed host // <host_resilver_peers> other drives participate in resilvering of a failed host
const host_resilver_peers = avg_distinct((n_hosts-1)*n_drives, n_drives*pgs); const host_resilver_peers = avg_distinct((n_hosts-1)*n_drives, n_drives*pgs);
const disk_heal_time = (down_out_interval + capacity/(degraded_replacement ? 1 : resilver_peers)/speed)/86400/365; let disk_heal_time, host_heal_time;
const host_heal_time = (down_out_interval + n_drives*capacity/host_resilver_peers/speed)/86400/365; if (speed)
disk_heal_time = (down_out_interval + capacity/(degraded_replacement ? 1 : resilver_peers)/speed)/86400/365;
else
{
disk_heal_time = disk_heal_hours/24/365;
speed = capacity / (degraded_replacement ? 1 : resilver_peers) / (disk_heal_hours*3600 - down_out_interval);
}
if (host_heal_hours)
host_heal_time = host_heal_hours/24/365;
else
host_heal_time = (down_out_interval + n_drives*capacity/host_resilver_peers/speed)/86400/365;
const disk_heal_fail = ((afr_drive+afr_host/n_drives)*disk_heal_time); const disk_heal_fail = ((afr_drive+afr_host/n_drives)*disk_heal_time);
const host_heal_fail = ((afr_drive+afr_host/n_drives)*host_heal_time); const host_heal_fail = ((afr_drive+afr_host/n_drives)*host_heal_time);
const disk_pg_fail = ec const disk_pg_fail = ec

24
fontello/config.json Normal file
View File

@ -0,0 +1,24 @@
{
"name": "fontello",
"css_prefix_text": "icon-",
"css_use_suffix": false,
"hinting": true,
"units_per_em": 1000,
"ascent": 850,
"glyphs": [
{
"uid": "0430f56230dd33f67f26be82b07a84fc",
"css": "arw-down",
"code": 59412,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M800 956C828.7 956 857.4 945.1 879.2 923.2L1567.2 235.2C1610.9 191.5 1610.9 120.5 1567.2 76.8 1523.4 33 1452.5 33 1408.7 76.8L800 685.5 191.3 76.8C147.5 33.1 76.6 33.1 32.8 76.8-10.9 120.6-10.9 191.5 32.8 235.3L720.8 923.2C742.7 945.1 771.3 956 800 956Z",
"width": 1600
},
"search": [
"arw-down"
]
}
]
}

57
fontello/css/fontello.css vendored Normal file
View File

@ -0,0 +1,57 @@
@font-face {
font-family: 'fontello';
src: url('../font/fontello.eot?162972');
src: url('../font/fontello.eot?162972#iefix') format('embedded-opentype'),
url('../font/fontello.woff2?162972') format('woff2'),
url('../font/fontello.woff?162972') format('woff'),
url('../font/fontello.ttf?162972') format('truetype'),
url('../font/fontello.svg?162972#fontello') format('svg');
font-weight: normal;
font-style: normal;
}
/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
/*
@media screen and (-webkit-min-device-pixel-ratio:0) {
@font-face {
font-family: 'fontello';
src: url('../font/fontello.svg?162972#fontello') format('svg');
}
}
*/
[class^="icon-"]:before, [class*=" icon-"]:before {
font-family: "fontello";
font-style: normal;
font-weight: normal;
speak: never;
display: inline-block;
text-decoration: inherit;
width: 1em;
margin-right: .2em;
text-align: center;
/* opacity: .8; */
/* For safety - reset parent styles, that can break glyph codes*/
font-variant: normal;
text-transform: none;
/* fix buttons height, for twitter bootstrap */
line-height: 1em;
/* Animation center compensation - margins should be symmetric */
/* remove if not needed */
margin-left: .2em;
/* you can be more comfortable with increased icons size */
/* font-size: 120%; */
/* Font smoothing. That was taken from TWBS */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* Uncomment for 3D effect */
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
}
.icon-arw-down:before { content: '\e814'; } /* '' */

BIN
fontello/font/fontello.eot Normal file

Binary file not shown.

View File

@ -0,0 +1,12 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Copyright (C) 2021 by original authors @ fontello.com</metadata>
<defs>
<font id="fontello" horiz-adv-x="1000" >
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
<missing-glyph horiz-adv-x="1000" />
<glyph glyph-name="arw-down" unicode="&#xe814;" d="M800-106c29 0 57 11 79 33l688 688c44 44 44 115 0 158-44 44-114 44-158 0l-609-608-609 608c-43 44-114 44-158 0-44-44-44-114 0-158l688-688c22-22 50-33 79-33z" horiz-adv-x="1600" />
</font>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 715 B

BIN
fontello/font/fontello.ttf Normal file

Binary file not shown.

BIN
fontello/font/fontello.woff Normal file

Binary file not shown.

Binary file not shown.

View File

@ -4,6 +4,7 @@
<title>Калькулятор вероятности отказа кластера Ceph/Vitastor</title> <title>Калькулятор вероятности отказа кластера Ceph/Vitastor</title>
<meta name="viewport" content="width=790px, initial-scale=1"> <meta name="viewport" content="width=790px, initial-scale=1">
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="stylesheet" href="fontello/css/fontello.css" />
<style> <style>
* { box-sizing: border-box; } * { box-sizing: border-box; }
body { margin: 0; font-size: 15px; font-family: Arial, Helvetica, sans-serif; } body { margin: 0; font-size: 15px; font-family: Arial, Helvetica, sans-serif; }
@ -18,6 +19,14 @@ input[type="text"] { border: 1px solid #aaa; padding: 4px; border-radius: 3px; }
.switch.r { border-left-width: 0; border-radius: 0 20px 20px 0; padding-right: 15px; } .switch.r { border-left-width: 0; border-radius: 0 20px 20px 0; padding-right: 15px; }
.switch.sel { border-color: #08f; background: #08f; color: white; } .switch.sel { border-color: #08f; background: #08f; color: white; }
.switch:hover { border-color: #4af; background: #4af; color: white; } .switch:hover { border-color: #4af; background: #4af; color: white; }
.icombo { display: inline-block; position: relative; border: 1px solid #aaa; border-radius: 3px; padding: 2px 8px 2px 5px; cursor: pointer; transition: 0.2s ease-out; }
.icombo .icon-arw-down { font-size: 7px; display: inline-block; vertical-align: middle; color: #aaa; transition: 0.2s ease-out; }
.icombo:hover { border-color: #4af; background: #4af; color: white; }
.icombo:hover .icon-arw-down { color: white; }
.icombo .options { opacity: 0; position: absolute; top: 100%; left: -1px; width: 150px; background: white; color: black; border-radius: 3px; overflow: hidden; cursor: pointer; transition: 0.2s ease-out; box-shadow: 0 0 10px 3px rgba(0, 0, 0, 0.2); }
.icombo:hover .options { opacity: 1; }
.icombo .option { display: block; padding: 4px 6px; }
.icombo .option:hover { background: #4af; color: white; }
</style> </style>
</head> </head>
<body> <body>

37
main.js
View File

@ -11,7 +11,7 @@ class Calc extends preact.Component
afr_host: 5, afr_host: 5,
capacity: 8, capacity: 8,
speed: 20, speed: 20,
pg_per_osd: 50, pg_per_osd: 100,
ec: false, ec: false,
replicas: 2, replicas: 2,
ec_data: 2, ec_data: 2,
@ -19,6 +19,7 @@ class Calc extends preact.Component
eager: false, eager: false,
same_host: true, same_host: true,
result: 0, result: 0,
use_speed: true,
} }
calc(st) calc(st)
@ -30,7 +31,8 @@ class Calc extends preact.Component
afr_drive: st.afr_drive/100, afr_drive: st.afr_drive/100,
afr_host: st.afr_host/100, afr_host: st.afr_host/100,
capacity: st.capacity*1000, capacity: st.capacity*1000,
speed: st.speed/1000, speed: st.use_speed ? st.speed/1000 : null,
disk_heal_hours: st.use_speed ? null : st.disk_heal_hours,
ec: st.ec, ec: st.ec,
ec_data: st.ec_data, ec_data: st.ec_data,
ec_parity: st.ec_parity, ec_parity: st.ec_parity,
@ -38,6 +40,7 @@ class Calc extends preact.Component
pgs: st.pg_per_osd, pgs: st.pg_per_osd,
osd_rm: !st.same_host, osd_rm: !st.same_host,
degraded_replacement: st.eager, degraded_replacement: st.eager,
down_out_interval: 600,
}); });
this.setState(st); this.setState(st);
} }
@ -69,6 +72,16 @@ class Calc extends preact.Component
this.calc({ eager: event.target.checked }); this.calc({ eager: event.target.checked });
} }
useSpeed = () =>
{
this.calc({ use_speed: true, speed: this.state.speed || 20 });
}
useTime = () =>
{
this.calc({ use_speed: false, disk_heal_hours: 12 });
}
setSameHost = (event) => setSameHost = (event) =>
{ {
this.calc({ same_host: event.target.checked }); this.calc({ same_host: event.target.checked });
@ -110,7 +123,7 @@ class Calc extends preact.Component
Калькулятор вероятности отказа кластера Ceph/Vitastor Калькулятор вероятности отказа кластера Ceph/Vitastor
</h2> </h2>
<p> <p>
Вероятность полного отказа кластера зависит от числа серверов и дисков Вероятность потери данных в кластере зависит от числа серверов и дисков
(чем их больше, тем вероятность больше), от схемы избыточности, скорости ребаланса (восстановления), (чем их больше, тем вероятность больше), от схемы избыточности, скорости ребаланса (восстановления),
и, конечно, непосредственно вероятности выхода из строя самих дисков и серверов. и, конечно, непосредственно вероятности выхода из строя самих дисков и серверов.
</p> </p>
@ -154,11 +167,23 @@ class Calc extends preact.Component
<td><input type="text" value={state.ec_parity} onchange={this.setter('ec_parity')} /></td> <td><input type="text" value={state.ec_parity} onchange={this.setter('ec_parity')} /></td>
</tr> : null} </tr> : null}
<tr> <tr>
<th>Оценочная скорость<br />восстановления на 1 OSD</th> <th>
<td><input type="text" value={state.speed} onchange={this.setter('speed')} /> МБ/с</td> {state.use_speed ? 'Оценочная' : 'Оценочное'}&nbsp;
<span className="icombo">
{state.use_speed ? 'скорость' : 'время'} <span className="icon-arw-down"></span>
<span className="options">
<span className="option" onClick={this.useSpeed}>скорость</span>
<span className="option" onClick={this.useTime}>время</span>
</span>
</span>
<br />восстановления на 1 OSD
</th>
{state.use_speed
? <td><input type="text" value={state.speed} onchange={this.setter('speed')} /> МБ/с</td>
: <td><input type="text" value={state.disk_heal_hours} onchange={this.setter('disk_heal_hours')} /> час(ов)</td>}
</tr> </tr>
<tr> <tr>
<th><abbr title="Среднее число уникальных групп чётности (пар/троек и т.п.), включающих каждый отдельный диск. В Ceph нормой считается 100 PG на OSD, из которых, допустим, половина дублируется">PG на OSD</abbr></th> <th><abbr title="Среднее число уникальных групп чётности (пар/троек и т.п.), включающих каждый отдельный диск. В Ceph нормой считается 100 PG на OSD">PG на OSD</abbr></th>
<td><input type="text" value={state.pg_per_osd} onchange={this.setter('pg_per_osd')} /></td> <td><input type="text" value={state.pg_per_osd} onchange={this.setter('pg_per_osd')} /></td>
</tr> </tr>
<tr> <tr>