2021-01-15 15:45:01 +03:00
import * as preact from 'preact' ;
/** @jsx preact.h */
2021-12-29 01:53:23 +03:00
import { cluster _afr , cluster _afr _bruteforce } from './afr.js' ;
2021-01-15 15:45:01 +03:00
class Calc extends preact . Component
{
state = {
hosts : 10 ,
drives : 10 ,
afr _drive : 3 ,
2021-12-29 01:53:23 +03:00
afr _host : 0 ,
2021-01-15 15:45:01 +03:00
capacity : 8 ,
speed : 20 ,
2021-12-29 01:53:23 +03:00
pg _per _osd : 10 ,
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 ,
2022-12-15 17:33:30 +03:00
error : null ,
2021-12-07 22:56:46 +03:00
use _speed : true ,
2021-12-29 01:53:23 +03:00
sim : true ,
2021-01-15 15:45:01 +03:00
}
2021-12-29 01:53:23 +03:00
_timer = null
calc ( st , force )
2021-01-15 15:45:01 +03:00
{
2021-12-29 01:53:23 +03:00
const empty _st = ! st ;
st = { ... this . state , ... ( st || { } ) } ;
if ( st . sim && ! force )
{
if ( this . _timer )
{
clearTimeout ( this . _timer ) ;
}
this . setState ( st ) ;
this . _timer = setTimeout ( ( ) => this . calc ( null , true ) , 200 ) ;
return ;
}
2022-12-15 17:33:30 +03:00
// !( >= ) not the same as < because of NaN
if ( ! ( parseInt ( st . hosts ) >= ( st . ec ? parseInt ( st . ec _data ) + parseInt ( st . ec _parity ) : parseInt ( st . replicas ) ) ) )
{
st . error = 'Число серверов (доменов отказа) меньше числа дисков в PG, расчёт невозможен' ;
st . result = 0 ;
}
else
{
const fn = st . sim ? cluster _afr _bruteforce : cluster _afr ;
st . error = null ;
st . result = 100 * fn ( {
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 . use _speed ? st . speed / 1000 : null ,
disk _heal _hours : st . use _speed ? null : st . disk _heal _hours ,
ec : st . ec ,
ec _data : st . ec _data ,
ec _parity : st . ec _parity ,
replicas : st . replicas ,
pgs : st . pg _per _osd ,
osd _rm : ! st . same _host ,
degraded _replacement : st . eager ,
down _out _interval : 600 ,
} ) ;
}
this . setState ( empty _st ? { error : st . error , result : st . result } : st ) ;
2021-01-15 15:45:01 +03:00
}
setter ( field )
{
if ( ! this . setter [ field ] )
{
this . setter [ field ] = ( event ) =>
{
this . calc ( { [ field ] : event . target . value } ) ;
} ;
}
return this . setter [ field ] ;
}
2021-12-29 01:53:23 +03:00
setSim = ( ) =>
{
if ( ! this . state . sim )
{
// Слишком много PG замедляет расчёт
this . calc ( { sim : true , pgs : this . state . pgs > 10 ? 10 : this . state . pgs } ) ;
}
}
setThe = ( ) =>
{
this . calc ( { sim : false } ) ;
}
2021-01-15 15:45:01 +03:00
setRepl = ( ) =>
{
this . calc ( { ec : false } ) ;
}
setEC = ( ) =>
{
this . calc ( { ec : true } ) ;
}
setEager = ( event ) =>
{
this . calc ( { eager : event . target . checked } ) ;
}
2021-12-07 22:56:46 +03:00
useSpeed = ( ) =>
{
this . calc ( { use _speed : true , speed : this . state . speed || 20 } ) ;
}
useTime = ( ) =>
{
this . calc ( { use _speed : false , disk _heal _hours : 12 } ) ;
}
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 ++ ;
}
2022-12-15 17:33:30 +03:00
return s [ s . length - 1 ] == '.' ? '0' : 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 >
2021-12-07 22:56:46 +03:00
Вероятность потери данных в кластере зависит от числа серверов и дисков
2021-01-15 15:45:01 +03:00
( чем их больше , тем вероятность больше ) , от схемы избыточности , скорости ребаланса ( восстановления ) ,
и , конечно , непосредственно вероятности выхода из строя самих дисков и серверов .
< / p >
< p >
2021-12-29 01:53:23 +03:00
Расчёт ведётся в простом предположении , что отказы распределены равномерно во времени .
2021-01-15 15:45:01 +03:00
< / p >
2021-12-29 01:53:23 +03:00
< div style = { { textAlign : 'center' } } >
< div style = { { display : 'inline-block' } } >
< label class = { "switch l" + ( state . sim ? "" : " sel" ) } >
< input type = "radio" name = "sim" checked = { ! state . sim } onclick = { this . setThe } / > Теоретический расчёт ( оценка сверху )
< / l a b e l >
< label class = { "switch r" + ( state . sim ? " sel" : "" ) } >
< input type = "radio" name = "sim" checked = { state . sim } onclick = { this . setSim } / > Переборная модель ( точно , но медленно )
< / l a b e l >
< / d i v >
< / d i v >
2021-01-15 15:45:01 +03:00
< table >
< tr >
2022-12-15 17:33:30 +03:00
< th > Число < abbr title = "Серверов либо других доменов отказа, если у вас домен отказа другой" > серверов < / a b b r > < / t h >
2021-01-15 15:45:01 +03:00
< 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 >
2021-12-07 22:56:46 +03:00
< th >
{ state . use _speed ? 'Оценочная' : 'Оценочное' } & nbsp ;
< span className = "icombo" >
{ state . use _speed ? 'скорость' : 'время' } < span className = "icon-arw-down" > < / s p a n >
< span className = "options" >
< span className = "option" onClick = { this . useSpeed } > скорость < / s p a n >
< span className = "option" onClick = { this . useTime } > время < / s p a n >
< / s p a n >
< / s p a n >
< br / > восстановления на 1 OSD
< / t h >
{ state . use _speed
? < td > < input type = "text" value = { state . speed } onchange = { this . setter ( 'speed' ) } / > МБ / с < / t d >
: < td > < input type = "text" value = { state . disk _heal _hours } onchange = { this . setter ( 'disk_heal_hours' ) } / > час ( ов ) < / t d > }
2021-01-15 15:45:01 +03:00
< / t r >
2021-07-20 16:25:41 +03:00
< tr >
2021-12-07 22:56:46 +03:00
< th > < abbr title = "Среднее число уникальных групп чётности (пар/троек и т.п.), включающих каждый отдельный диск. В Ceph нормой считается 100 PG на OSD" > PG на OSD < / a b b r > < / t h >
2021-07-20 16:25:41 +03:00
< 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 >
2021-11-26 02:36:21 +03:00
< th > < abbr title = "Вероятность отказа сервера сразу с о всеми дисками, без возвращения их в строй" > AFR сервера < / a b b r > < / t h >
2021-12-29 01:53:23 +03:00
< td > < input disabled = { state . sim } title = { state . sim ? 'Н е поддерживается в режиме симуляции' : '' }
type = "text" value = { state . afr _host } onchange = { this . setter ( 'afr_host' ) } / > % < / t d >
2021-01-15 15:45:01 +03:00
< / 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 >
2022-12-15 17:33:30 +03:00
{ state . error
? < div style = "text-align: center; color: red; margin: 20px 0" >
{ state . error }
< / d i v >
: < div style = "text-align: center; font-size: 200%; margin: 20px 0; font-weight: bold" >
{ this . format4 ( state . result ) } %
< / 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 ) ;