| | 223 | |
| | 224 | == Примери |
| | 225 | |
| | 226 | 1. 2 клуба што истовремено се обидуваат да купат ист играч |
| | 227 | |
| | 228 | Услови: играчот може да оди само во 1 клуб, клубот мора да има буџет, вредноста на играчот се зема „најнова пред трансфер“, се бројат статистики пред трансфер, системот мора да избегне lost update, write skew, deadlock. |
| | 229 | |
| | 230 | Без добра контрола двата клуба го купуваат истиот играч. |
| | 231 | {{{ |
| | 232 | SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; |
| | 233 | |
| | 234 | BEGIN; |
| | 235 | |
| | 236 | -- 1. LOCK PLAYER |
| | 237 | SELECT current_club_id |
| | 238 | FROM players |
| | 239 | WHERE player_id = 65 |
| | 240 | FOR UPDATE; |
| | 241 | |
| | 242 | -- 2. LOCK BUYING CLUB |
| | 243 | SELECT budget |
| | 244 | FROM clubs |
| | 245 | WHERE club_id = 10 |
| | 246 | FOR UPDATE; |
| | 247 | |
| | 248 | -- 3. CHECK: дали веќе е трансфериран? |
| | 249 | -- ако current_club_id = 10 → ROLLBACK |
| | 250 | |
| | 251 | -- 4. CHECK: доволен буџет |
| | 252 | -- ако budget < 5000000 → ROLLBACK |
| | 253 | |
| | 254 | -- 5. MVCC READ (не блокира) |
| | 255 | -- најнова вредност пред трансфер |
| | 256 | SELECT market_value_in_eur |
| | 257 | FROM player_valuations |
| | 258 | WHERE player_id = 65 |
| | 259 | AND date <= CURRENT_DATE |
| | 260 | ORDER BY date DESC |
| | 261 | LIMIT 1; |
| | 262 | |
| | 263 | -- 6. COUNT активности (MVCC snapshot) |
| | 264 | SELECT COUNT(*) |
| | 265 | FROM appearances |
| | 266 | WHERE player_id = 65 |
| | 267 | AND date < CURRENT_DATE; |
| | 268 | |
| | 269 | SELECT COUNT(*) |
| | 270 | FROM game_events |
| | 271 | WHERE player_id = 65 |
| | 272 | AND date < CURRENT_DATE; |
| | 273 | |
| | 274 | -- 7. UPDATE буџет |
| | 275 | UPDATE clubs |
| | 276 | SET budget = budget - 5000000 |
| | 277 | WHERE club_id = 10; |
| | 278 | |
| | 279 | -- 8. TRANSFER |
| | 280 | UPDATE players |
| | 281 | SET current_club_id = 10 |
| | 282 | WHERE player_id = 65; |
| | 283 | |
| | 284 | -- 9. LOG transfer |
| | 285 | INSERT INTO transfers(player_id, from_club, to_club, transfer_fee, transfer_date) |
| | 286 | VALUES (65, 5, 10, 5000000, CURRENT_DATE); |
| | 287 | |
| | 288 | COMMIT; |
| | 289 | }}} |
| | 290 | |
| | 291 | MVCC не блокираат туку читаат snapshots од податоци. Row-level locking го заклучува само дадениот ред, а seriaziable спречува write skew. Овој пример демонстрира реален конкурентен систем каде повеќе трансакции се обидуваат да модифицираат исти ресурси. |
| | 292 | |
| | 293 | 2. Трансфер со специфични барања |
| | 294 | |
| | 295 | Клуб сака да купи играч но не смее да има повеќе од 25 играчи, не смее да има повеќе од 5 играчи со > 20 goals, се зема form (последни настапи) пред трансфер, повеќе трансакции можат истовремено да купуваат играчи. |
| | 296 | |
| | 297 | Проблемот тука е што ако имаме 2 трансакции, и двете мислат дека има место и ако не контролираме и двете ќе додадат играч и ќе се наруши условот. |
| | 298 | |
| | 299 | {{{ |
| | 300 | SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; |
| | 301 | |
| | 302 | BEGIN; |
| | 303 | |
| | 304 | -- 1. LOCK PLAYER |
| | 305 | SELECT current_club_id |
| | 306 | FROM players |
| | 307 | WHERE player_id = 80 |
| | 308 | FOR UPDATE; |
| | 309 | |
| | 310 | -- 2. LOCK CLUB |
| | 311 | SELECT club_id |
| | 312 | FROM clubs |
| | 313 | WHERE club_id = 10 |
| | 314 | FOR UPDATE; |
| | 315 | |
| | 316 | -- 3. CHECK TOTAL SQUAD SIZE (MVCC snapshot) |
| | 317 | SELECT COUNT(*) |
| | 318 | FROM players |
| | 319 | WHERE current_club_id = 10; |
| | 320 | |
| | 321 | -- ако >= 25 → ROLLBACK |
| | 322 | |
| | 323 | -- 4. CHECK HIGH-SCORING PLAYERS |
| | 324 | SELECT COUNT(*) |
| | 325 | FROM appearances |
| | 326 | WHERE player_current_club_id = 10 |
| | 327 | AND goals > 20; |
| | 328 | |
| | 329 | -- ако >= 5 → ROLLBACK |
| | 330 | |
| | 331 | -- 5. GET PLAYER FORM (MVCC read) |
| | 332 | SELECT AVG(goals) |
| | 333 | FROM appearances |
| | 334 | WHERE player_id = 80 |
| | 335 | AND date > CURRENT_DATE - INTERVAL '6 months'; |
| | 336 | |
| | 337 | -- 6. OPTIONAL BUSINESS RULE |
| | 338 | -- ако form < 2 → можеби не купуваме |
| | 339 | |
| | 340 | -- 7. UPDATE PLAYER |
| | 341 | UPDATE players |
| | 342 | SET current_club_id = 10 |
| | 343 | WHERE player_id = 80; |
| | 344 | |
| | 345 | -- 8. INSERT TRANSFER |
| | 346 | INSERT INTO transfers(player_id, from_club, to_club, transfer_fee, transfer_date) |
| | 347 | VALUES (80, 3, 10, 8000000, CURRENT_DATE); |
| | 348 | |
| | 349 | COMMIT; |
| | 350 | }}} |
| | 351 | |
| | 352 | Овој пример демонстрира write skew проблем кој не може да се реши со класично заклучување, бидејќи агрегатните проверки не заклучуваат конкретни редови. Затоа се користи SERIALIZABLE изолација која гарантира дека паралелните трансакции нема да доведат до неконзистентна состојба. MVCC овозможува аналитичките операции да се извршуваат без блокирање, додека row-level locking спречува директни конфликти врз исти записи. |