Support and describe simulation-based model
parent
28a26f4867
commit
e53288339f
65
README.md
65
README.md
|
@ -10,7 +10,7 @@ https://yourcmc.ru/afr-calc/
|
|||
|
||||
https://yourcmc.ru/git/vitalif/vitastor/
|
||||
|
||||
## Логика расчёта
|
||||
## Теоретическая модель
|
||||
|
||||
- Вероятность потери данных равна вероятности того, что в течение года выйдет из строя любой 1 диск
|
||||
и при этом в течение времени, которое восстанавливается недостающая копия данных, выйдут из строя
|
||||
|
@ -45,7 +45,7 @@ https://yourcmc.ru/git/vitalif/vitastor/
|
|||
AFR сервера эмпирически поделен на число дисков, чтобы "размазать" вероятность отказа сервера
|
||||
по его дискам.
|
||||
|
||||
## Парадокс дней рождений
|
||||
### Парадокс дней рождений
|
||||
|
||||
- PG почти гарантированно пересекаются, особенно в небольших кластерах. Степень их пересечения
|
||||
очень полезно учитывать.
|
||||
|
@ -69,3 +69,64 @@ https://yourcmc.ru/git/vitalif/vitastor/
|
|||
- Зная число участвующих в восстановлении дисков, среднюю скорость восстановления в пересчёте на 1 диск,
|
||||
оцениваемую с учётом пропускной способности сети, а также объём дисков, мы можем рассчитать
|
||||
ожидаемое время восстановления данных одного диска или одного хоста.
|
||||
|
||||
## Симуляция (переборная модель)
|
||||
|
||||
К сожалению, при теоретическом расчёте по вышеприведённой модели корректно учесть степень
|
||||
пересечения вероятностей выхода из строя разных PG всё равно не получается, из-за чего вероятность
|
||||
оказывается завышенной.
|
||||
|
||||
Чтобы попробовать оценить вероятность более реально, придумана вторая модель - переборная.
|
||||
Идея в том, чтобы сначала сгенерировать заданное количество случайных PG с учётом распределения
|
||||
данных по хостам, а потом перебрать все варианты комбинаций событий их выхода из строя, по
|
||||
принципу:
|
||||
- PG 1 вышла из строя в течение года
|
||||
- PG 1 не вышла из строя в течение года, но вышла из строя PG 2
|
||||
- PG 1 и 2 не вышли из строя в течение года, но вышла из строя PG 3
|
||||
- И так далее...
|
||||
|
||||
Как же подсчитать вероятности выхода из строя PG? Начнём с простого - N-кратной репликации:
|
||||
- Берём очередную PG. Допустим, она включает диски 1, 2, ..., N.
|
||||
- Поделим все варианты событий следующим образом:
|
||||
- Диск №1 умирает в течение года
|
||||
- Вероятность этого события равна AFR диска 1 = AFR1
|
||||
- Диск №2 умирает в диапазоне +- времени восстановления от диска №1 (либо до диска №1, либо после)
|
||||
- Вероятность этого события = AFR1 * AFR2 * 2 * время_восстановления / год
|
||||
- Диск №3 умирает в диапазоне +- времени восстановления от дисков №1 и №2
|
||||
- Вероятность этого события `AFR2 * коэффициент(2) * время_восстановления/год`
|
||||
- Коэффициент(N+1) - это среднее пересечение N+1-ого отрезка с предыдущими N,
|
||||
при условии, что центр каждого равномерно распределён в интервале от -1 до 1
|
||||
и длина равна 2.
|
||||
- Путём несложных умозаключений можно понять, что это 2 * (0.5 + объём_N-мерной_пирамиды/объём_N-мерного_куба)
|
||||
- Объём N-мерной пирамиды = 1/N * Площадь_основания * Высота = 1/N * 2^(N-1)
|
||||
- Так что коэффициент(N+1) равен просто (1 + 1/N)
|
||||
- И так далее для всех последующих дисков PG
|
||||
- Диск №4 не умирает в этом диапазоне => PG умереть уже не может (одна копия данных точно жива)
|
||||
- Диск №2 не умирает в этом диапазоне => PG умереть не может
|
||||
- Диск №1 не умирает в течение года => PG умереть не может
|
||||
- После каждого шага мы знаем, что учли всю вероятность выхода из строя диска №1
|
||||
- А также часть `(AFR1)` вероятности выхода из строка диска №2
|
||||
- А также часть `(AFR1 * AFR2)` вероятности выхода из строка диска №3
|
||||
- И так далее...
|
||||
- Поэтому для последующих шагов вероятность выхода из строя диска №1 приравнивается к 0,
|
||||
а дисков №2 - №N умножается на `(1 - AFR1 * ... * AFRi-1)`
|
||||
|
||||
Эту же схему легко расширить до EC N+K - кстати, N реплик, по сути, то же самое, что "EC" 1+(N+1).
|
||||
Нужно только в рамках каждой PG перебирать комбинации отказов дисков:
|
||||
- Начать с `PREV=1` и `PGFAIL=0`
|
||||
- Диск №1 умирает в течение года: `AFR1`
|
||||
- Число умерших дисков: `M=1`
|
||||
- Вероятность отказа текущей комбинации: `CUR=PREV*AFR1`
|
||||
- Для каждого последующего диска:
|
||||
- Диск №i умирает в диапазоне времени восстановления предыдущих: `X = Коэффициент(M+1)*Время*AFRi`
|
||||
- Если `M+1 > K`, добавить CUR к вероятности отказа PG и остановить ветку перебора
|
||||
- Иначе повторить перебор для остальных дисков с `M=M+1` и `CUR=CUR*X`
|
||||
- Диск №i не умирает в этом диапазоне
|
||||
- Уменьшить AFRi: `AFRi = AFRi * (1-CUR)`
|
||||
- Повторить перебор для остальных дисков с `M=M` и `CUR=CUR*(1-X)`
|
||||
- Диск №1 не умирает в течение года: `1-AFR1`
|
||||
- Умножить `PREV = PREV*(1-AFR1)`
|
||||
- Приравнять оставшуюся (неучтённую) вероятность отказа диска 1 к 0: `AFR1 = 0`
|
||||
- Повторить перебор, начиная с последующих дисков, кроме последних K
|
||||
- Общую вероятность отказа умножить на `(1-PGFAIL)`
|
||||
- Перебрать таким же образом все последующие PG
|
||||
|
|
3
afr.js
3
afr.js
|
@ -291,7 +291,8 @@ function pg_death_combinations(maydie, pg, ec_parity, heal_time, cur, i, deadn)
|
|||
return 0;
|
||||
}
|
||||
let drive_death = cur * pyramid(deadn) * heal_time * maydie[pg[i]];
|
||||
maydie[pg[i]] -= drive_death;
|
||||
// intersecting deaths are accounted for and non-intersecting deaths are accounted for too
|
||||
maydie[pg[i]] *= (1 - cur);
|
||||
let is_dead = 0, not_dead = 0;
|
||||
if (deadn > ec_parity)
|
||||
{
|
||||
|
|
58
main.js
58
main.js
|
@ -1,6 +1,6 @@
|
|||
import * as preact from 'preact';
|
||||
/** @jsx preact.h */
|
||||
import { cluster_afr } from './afr.js';
|
||||
import { cluster_afr, cluster_afr_bruteforce } from './afr.js';
|
||||
|
||||
class Calc extends preact.Component
|
||||
{
|
||||
|
@ -8,10 +8,10 @@ class Calc extends preact.Component
|
|||
hosts: 10,
|
||||
drives: 10,
|
||||
afr_drive: 3,
|
||||
afr_host: 5,
|
||||
afr_host: 0,
|
||||
capacity: 8,
|
||||
speed: 20,
|
||||
pg_per_osd: 100,
|
||||
pg_per_osd: 10,
|
||||
ec: false,
|
||||
replicas: 2,
|
||||
ec_data: 2,
|
||||
|
@ -20,12 +20,27 @@ class Calc extends preact.Component
|
|||
same_host: true,
|
||||
result: 0,
|
||||
use_speed: true,
|
||||
sim: true,
|
||||
}
|
||||
|
||||
calc(st)
|
||||
_timer = null
|
||||
|
||||
calc(st, force)
|
||||
{
|
||||
st = { ...this.state, ...st };
|
||||
st.result = 100*cluster_afr({
|
||||
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;
|
||||
}
|
||||
const fn = st.sim ? cluster_afr_bruteforce : cluster_afr;
|
||||
st.result = 100*fn({
|
||||
n_hosts: st.hosts,
|
||||
n_drives: st.drives,
|
||||
afr_drive: st.afr_drive/100,
|
||||
|
@ -42,7 +57,7 @@ class Calc extends preact.Component
|
|||
degraded_replacement: st.eager,
|
||||
down_out_interval: 600,
|
||||
});
|
||||
this.setState(st);
|
||||
this.setState(empty_st ? { result: st.result } : st);
|
||||
}
|
||||
|
||||
setter(field)
|
||||
|
@ -57,6 +72,20 @@ class Calc extends preact.Component
|
|||
return this.setter[field];
|
||||
}
|
||||
|
||||
setSim = () =>
|
||||
{
|
||||
if (!this.state.sim)
|
||||
{
|
||||
// Слишком много PG замедляет расчёт
|
||||
this.calc({ sim: true, pgs: this.state.pgs > 10 ? 10 : this.state.pgs });
|
||||
}
|
||||
}
|
||||
|
||||
setThe = () =>
|
||||
{
|
||||
this.calc({ sim: false });
|
||||
}
|
||||
|
||||
setRepl = () =>
|
||||
{
|
||||
this.calc({ ec: false });
|
||||
|
@ -128,8 +157,18 @@ class Calc extends preact.Component
|
|||
и, конечно, непосредственно вероятности выхода из строя самих дисков и серверов.
|
||||
</p>
|
||||
<p>
|
||||
Рассчитывается оценка сверху. Расчёт ведётся в простом предположении, что отказы распределены равномерно во времени.
|
||||
Расчёт ведётся в простом предположении, что отказы распределены равномерно во времени.
|
||||
</p>
|
||||
<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} /> Теоретический расчёт (оценка сверху)
|
||||
</label>
|
||||
<label class={"switch r"+(state.sim ? " sel" : "")}>
|
||||
<input type="radio" name="sim" checked={state.sim} onclick={this.setSim} /> Переборная модель (точно, но медленно)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Число серверов</th>
|
||||
|
@ -192,7 +231,8 @@ class Calc extends preact.Component
|
|||
</tr>
|
||||
<tr>
|
||||
<th><abbr title="Вероятность отказа сервера сразу со всеми дисками, без возвращения их в строй">AFR сервера</abbr></th>
|
||||
<td><input type="text" value={state.afr_host} onchange={this.setter('afr_host')} /> %</td>
|
||||
<td><input disabled={state.sim} title={state.sim ? 'Не поддерживается в режиме симуляции' : ''}
|
||||
type="text" value={state.afr_host} onchange={this.setter('afr_host')} /> %</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
|
|
Loading…
Reference in New Issue