| | 110 | |
| | 111 | == Примери |
| | 112 | |
| | 113 | 1. Partial Transaction Corruption во Transfermarkt систем |
| | 114 | |
| | 115 | Во рамки на Transfermarkt базата, постојат повеќе меѓусебно поврзани табели кои ја моделираат логиката на трансфер на играчи: players, clubs и transfers на пример. Процесот на трансфер на играч вклучува повеќе чекори кои треба да се извршат како една атомска операција: |
| | 116 | |
| | 117 | -Се ажурира club_id во табелата players |
| | 118 | -Се намалува budget кај новиот клуб |
| | 119 | -Се додава запис во табелата transfers |
| | 120 | |
| | 121 | Овие операции логички претставуваат една трансакција. |
| | 122 | |
| | 123 | При извршување на трансакцијата, се случил пад на апликацијата и player_club_id е успешно ажуриран, но запис во transfers табелата нема и budget не е ажуриран. Поради ова, базата влегува во неконзистентна состојба, каде играчот е доделен на нов клуб но нема евиденција за трансферот. Наша цел е да ја вратиме базата во првобитната состојба, без да ги изгубиме другите валидни податоци. |
| | 124 | |
| | 125 | -Најпрво, преку логови, ќе го утврдиме точниот момент кога се случил проблемот. |
| | 126 | -Потоа ќе креираме нова база (recovery): |
| | 127 | {{{ |
| | 128 | createdb transfermarkt_debug |
| | 129 | }}} |
| | 130 | -PITR: конфигурираме враќање на состојбата непосредно пред грешката: |
| | 131 | {{{ |
| | 132 | recovery_target_time = '2026-04-29 16:22:09' |
| | 133 | }}} |
| | 134 | Со ова PostgreSQL ќе ги репродуцира WAL записите се до моментот пред неуспешната трансакција. |
| | 135 | -Потоа ги споредуваме податоците и ја бараме разликата: |
| | 136 | {{{ |
| | 137 | -- Неконзистентна состојба (production) |
| | 138 | SELECT * FROM players WHERE id = 101; |
| | 139 | |
| | 140 | -- Конзистентна состојба (recovery) |
| | 141 | SELECT * FROM players WHERE id = 101; |
| | 142 | }}} |
| | 143 | -Преку рачна поправка, наместо целосно враќање на базата, ја враќаме во првобитната состојба со едно query: |
| | 144 | {{{ |
| | 145 | UPDATE players |
| | 146 | SET club_id = <старa вредност> |
| | 147 | WHERE id = 101; |
| | 148 | }}} |
| | 149 | |
| | 150 | Со овој едноставен процес ја добивме старата конзистентност на базата и не изгубивме други валидни податоци. Овој пример покажува дека не е секогаш добра опција да се врати цела база, туку понекогаш во комплексни системи е подобро да се користи селективен пристап. Ваков тип на опоравување е важен во системи со чести трансакции и голем број на корисници. |
| | 151 | |
| | 152 | 2. Silent Data Corruption (Trigger Bug) |
| | 153 | |
| | 154 | Во оваа база се користат тригери кои вршат автоматизација на одредени полиња по извршување на трансфери. Еден ваков пример е тригер кој треба да ја зголеми вредноста на играчот за 10% (x1.1) по секој трансфер: |
| | 155 | |
| | 156 | {{{ |
| | 157 | CREATE TRIGGER update_value |
| | 158 | AFTER UPDATE ON transfers |
| | 159 | }}} |
| | 160 | |
| | 161 | Сепак, се случила грешка и системот наместо *1,1 го множи со *2. Овој bug не се приметува одма, но грешките кои произлегуваат од него се штетни на долгорочен период. За да ја решиме оваа грешка, најпрво ќе се вратиме пред започнување на trigger bug. |
| | 162 | |
| | 163 | -Кога ќе го откриеме моментот кога triggerот почнал да работи погрешно, креираме recovery база: |
| | 164 | {{{ |
| | 165 | createdb transfermarkt_recovery |
| | 166 | }}} |
| | 167 | |
| | 168 | {{{ |
| | 169 | recovery_target_time = '2026-04-29 09:00:00' |
| | 170 | }}} |
| | 171 | |
| | 172 | -Дури тогаш почнува recovery процесот. По овој процес market_value е вратен во реални вредности и нема последователни cascade грешки. Ова е пример за една од најопасните категории на грешки во базите на податоци - Silent data corruption, поради тоа што немаат crash, немаат error message и системот си работи нормално. Во системот на Transfermarkt една ваква грешка може да доведе до уништување на статистиката за цела сезона. |
| | 173 | |
| | 174 | 3. Huge Data Growth |
| | 175 | |
| | 176 | Transfermarkt системот претставува голема и брзорастечка база на податоци која содржи players, matches, transfers - табели кои од ден во ден се повеќе и повеќе растат. |
| | 177 | |
| | 178 | Ако користиме класичен full logical backup: |
| | 179 | {{{ |
| | 180 | pg_dump transfermarkt > full.sql |
| | 181 | }}} |
| | 182 | се појавуваат следниве проблеми: |
| | 183 | -backup трае 4 часа |
| | 184 | -се користи голем storage простор |
| | 185 | -процесот не е скалабилен за дневно извршување |
| | 186 | |
| | 187 | Затоа ќе користиме комбинирана backup стратегија од full physical backup + incremental backup (WAL) |
| | 188 | |
| | 189 | -Weekly Full Backup: Се прави целосна копија од базата еднаш неделно: |
| | 190 | {{{ |
| | 191 | pg_basebackup -U replication_user \ |
| | 192 | -D /backup/base_weekly \ |
| | 193 | -Fp -Xs -P |
| | 194 | }}} |
| | 195 | Овој backup претставува starting point за recovery. |
| | 196 | |
| | 197 | -WAL Archiving (Incremental backup): Се активира continuous logging на сите промени во базата: |
| | 198 | {{{ |
| | 199 | archive_mode = on |
| | 200 | archive_command = 'cp %p /wal_archive/%f' |
| | 201 | }}} |
| | 202 | Ова овозможува секоја промена во базата да биде зачувана во WAL логови. |
| | 203 | |
| | 204 | Сега да видиме едно recovery сценарио ако во четврток се случи crash на системот. Процесот на recovery е: |
| | 205 | -Се враќа последниот full backup (неделниот snapshot) |
| | 206 | -Се replay-ираат WAL логовите од понеделник до четврток |
| | 207 | -Базата се враќа во последната конзистентна состојба |
| | 208 | |
| | 209 | Овој пример покажува дека Full backup сам по себе не е доволен за големи системи, Incremental (WAL-based) backup е неопходен за production средини и дека комбинацијата од двата пристапи овозможува баланс помеѓу перформанси и сигурност. |
| | 210 | |
| | 211 | |