2021-01-15 15:45:01 +03:00
import * as preact from 'preact' ;
/** @jsx preact.h */
import { cluster _afr } from './afr.js' ;
class Calc extends preact . Component
{
state = {
hosts : 10 ,
drives : 10 ,
afr _drive : 3 ,
afr _host : 5 ,
capacity : 8 ,
speed : 20 ,
2021-07-20 16:25:41 +03:00
pg _per _osd : 50 ,
2021-01-15 15:45:01 +03:00
ec : false ,
replicas : 2 ,
ec _data : 2 ,
ec _parity : 1 ,
eager : false ,
2021-01-16 22:21:06 +03:00
same _host : true ,
2021-01-15 15:45:01 +03:00
result : 0 ,
}
calc ( st )
{
st = { ... this . state , ... st } ;
st . result = 100 * cluster _afr ( {
n _hosts : st . hosts ,
n _drives : st . drives ,
afr _drive : st . afr _drive / 100 ,
afr _host : st . afr _host / 100 ,
capacity : st . capacity * 1000 ,
speed : st . speed / 1000 ,
2021-01-16 22:15:42 +03:00
ec : st . ec ,
ec _data : st . ec _data ,
ec _parity : st . ec _parity ,
2021-01-15 15:45:01 +03:00
replicas : st . replicas ,
2021-07-20 16:25:41 +03:00
pgs : st . pg _per _osd ,
2021-01-16 22:21:06 +03:00
osd _rm : ! st . same _host ,
2021-01-15 15:45:01 +03:00
degraded _replacement : st . eager ,
} ) ;
this . setState ( st ) ;
}
setter ( field )
{
if ( ! this . setter [ field ] )
{
this . setter [ field ] = ( event ) =>
{
this . calc ( { [ field ] : event . target . value } ) ;
} ;
}
return this . setter [ field ] ;
}
setRepl = ( ) =>
{
this . calc ( { ec : false } ) ;
}
setEC = ( ) =>
{
this . calc ( { ec : true } ) ;
}
setEager = ( event ) =>
{
this . calc ( { eager : event . target . checked } ) ;
}
2021-01-16 22:21:06 +03:00
setSameHost = ( event ) =>
{
this . calc ( { same _host : event . target . checked } ) ;
}
2021-01-15 16:17:16 +03:00
format4 = ( n ) =>
{
2021-01-16 22:15:42 +03:00
if ( n >= 1 || n <= - 1 )
return '' + ( Math . round ( n * 10000 ) / 10000 ) ;
if ( n == 0 )
return '0' ;
let s = '0.' , i = 0 , c = 0 ;
if ( n < 0 )
2021-01-15 16:17:16 +03:00
{
2021-01-16 22:15:42 +03:00
s = '-0.' ;
n = - n ;
2021-01-15 16:17:16 +03:00
}
2021-01-16 22:15:42 +03:00
while ( n && i < 4 )
{
n = n * 10 ;
s += ( n | 0 ) ;
c = c || ( n | 0 ) ;
n = n - ( n | 0 ) ;
if ( c )
i ++ ;
}
return s ;
2021-01-15 16:17:16 +03:00
}
2021-01-15 15:45:01 +03:00
componentDidMount ( )
{
this . calc ( { } ) ;
}
render ( props , state )
{
return ( < div style = "width: 750px; margin: 20px; padding: 20px; box-shadow: 0 19px 60px rgba(0, 0, 0, 0.3), 0 15px 20px rgba(0, 0, 0, 0.22);" >
< h2 style = "text-align: center; font-size: 150%; margin: 10px 0 20px 0; font-weight: bold" >
Калькулятор вероятности отказа кластера Ceph / Vitastor
< / h 2 >
< p >
Вероятность полного отказа кластера зависит от числа серверов и дисков
( чем их больше , тем вероятность больше ) , от схемы избыточности , скорости ребаланса ( восстановления ) ,
и , конечно , непосредственно вероятности выхода из строя самих дисков и серверов .
< / p >
< p >
Расчёт ведётся в простом предположении , что отказы распределены равномерно во времени .
< / p >
< table >
< tr >
< th > Число серверов < / t h >
< td > < input type = "text" value = { state . hosts } onchange = { this . setter ( 'hosts' ) } / > < / t d >
< / t r >
< tr >
< th > Число дисков в сервере < / t h >
< td > < input type = "text" value = { state . drives } onchange = { this . setter ( 'drives' ) } / > < / t d >
< / t r >
< tr >
< th > Ёмкость дисков < / t h >
< td > < input type = "text" value = { state . capacity } onchange = { this . setter ( 'capacity' ) } / > ТБ < / t d >
< / t r >
< tr >
< th > Схема избыточности < / t h >
< td >
< label class = { "switch l" + ( state . ec ? "" : " sel" ) } >
< input type = "radio" name = "scheme" checked = { ! state . ec } onclick = { this . setRepl } / > Репликация
< / l a b e l >
< label class = { "switch r" + ( state . ec ? " sel" : "" ) } >
< input type = "radio" name = "scheme" checked = { state . ec } onclick = { this . setEC } / > EC ( коды коррекции ошибок )
< / l a b e l >
< / t d >
< / t r >
{ state . ec ? null : < tr >
< th > Число реплик < / t h >
< td > < input type = "text" value = { state . replicas } onchange = { this . setter ( 'replicas' ) } / > < / t d >
< / t r > }
{ state . ec ? < tr >
< th > Число дисков данных < / t h >
< td > < input type = "text" value = { state . ec _data } onchange = { this . setter ( 'ec_data' ) } / > < / t d >
< / t r > : n u l l }
{ state . ec ? < tr >
< th > Число дисков чётности < / t h >
< td > < input type = "text" value = { state . ec _parity } onchange = { this . setter ( 'ec_parity' ) } / > < / t d >
< / t r > : n u l l }
< tr >
< th > Оценочная скорость < br / > восстановления на 1 OSD < / t h >
< td > < input type = "text" value = { state . speed } onchange = { this . setter ( 'speed' ) } / > МБ / с < / t d >
< / t r >
2021-07-20 16:25:41 +03:00
< tr >
< th > < abbr title = "Среднее число уникальных групп чётности (пар/троек и т.п.), включающих каждый отдельный диск. В Ceph нормой считается 100 PG на OSD, из которых, допустим, половина дублируется" > PG на OSD < / a b b r > < / t h >
< td > < input type = "text" value = { state . pg _per _osd } onchange = { this . setter ( 'pg_per_osd' ) } / > < / t d >
< / t r >
2021-01-15 15:45:01 +03:00
< tr >
< th > < abbr title = "Annualized Failure Rate, вероятность отказа в течение года в %" > AFR < / a b b r > д и с к а < / t h >
< td > < input type = "text" value = { state . afr _drive } onchange = { this . setter ( 'afr_drive' ) } / > % < / t d >
< / t r >
< tr >
< th > AFR сервера < / t h >
< td > < input type = "text" value = { state . afr _host } onchange = { this . setter ( 'afr_host' ) } / > % < / t d >
< / t r >
< / t a b l e >
2021-01-16 22:21:06 +03:00
< p >
< label > < input type = "checkbox" checked = { state . same _host } onchange = { this . setSameHost } / >
При отказе диска данные распределяются только по другим дискам того же сервера ,
как в Ceph
< / l a b e l >
< / p >
2021-01-15 15:45:01 +03:00
< p >
< label > < input type = "checkbox" checked = { state . eager } onchange = { this . setEager } / >
Я нетерпеливый и заменяю отказавший диск сразу , не давая данным уехать на остальные диски
( либо данным уезжать некуда , например , сервера всего 3 при 3 репликах )
< / l a b e l >
< / p >
< div style = "text-align: center; font-size: 150%; margin: 20px 0; font-weight: bold" >
Вероятность потери данных в течение года :
< / d i v >
2021-01-15 15:59:26 +03:00
< div style = "text-align: center; font-size: 200%; margin: 20px 0; font-weight: bold" >
2021-01-15 16:17:16 +03:00
{ this . format4 ( state . result ) } %
2021-01-15 15:45:01 +03:00
< / d i v >
2021-01-15 15:59:26 +03:00
< div style = "text-align: center; color: #aaa; margin: 10px 0" >
& copy ; Виталий Филиппов 2020 + < a style = "color: inherit" href = "https://yourcmc.ru/git/vitalif/ceph-afr-calc" > ( исходники ) < / a >
< / d i v >
2021-01-15 15:45:01 +03:00
< / d i v > ) ;
}
}
preact . render ( < Calc / > , document . body ) ;