Changes between Version 52 and Version 53 of Transactions


Ignore:
Timestamp:
09/11/26 09:12:08 (33 hours ago)
Author:
222004
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Transactions

    v52 v53  
    33=== Вовед
    44
    5 Кај систем за авионски резервации повеќе корисници можат истовремено да резервираат, менуваат или откажуваат резервации за истиот лет. Без трансакции и locking, две операции можат паралелно да ја прочитаат истата состојба и да доведат до неконзистентни податоци.
    6 
    7 Во оваа фаза се проверуваат четири правила:
    8 
    9 1. Едно седиште не смее да биде резервирано двапати на ист лет. \\
    10 2. Бројот на резервации не смее да го надмине капацитетот на авионот. \\
    11 3. Промена или откажување на резервација мора да биде атомска операција. \\
    12 4. Промена на авион не смее да создаде состојба во која бројот на резервации е поголем од новиот капацитет.
    13 
    14 Имплементацијата и автоматизираната валидација се изведени во изолираната `airportdb_transactions_lab`, со три InnoDB табели: `airplane`, `flight` и `booking`.
     5Во рамките на оваа фаза, целта е да се прикаже како еден систем за авионски резервации управува со паралелни барања од повеќе корисници во исто време, без да се наруши конзистентноста на податоците.
     6
     7Фокусот е системот да гарантира конзистентна продажба и резервација на седишта:
     8* две различни резервации да не можат да завршат со исто седиште на ист лет;
     9* да нема overbooking, односно повеќе резервации од капацитетот на авионот;
     10* промена или откажување на резервација да биде атомска операција;
     11* промена на авионот да не доведе до состојба во која постоечките резервации го надминуваат новиот капацитет.
     12
     13Овие операции во пракса можат да се случуваат истовремено од различни корисници. Без трансакции и locking, две операции можат паралелно да ја прочитаат истата состојба и да донесат одлука врз основа на веќе застарени податоци.
     14
     15=== Core transactional domain во airportdb
     16
     17'''booking''' е централната табела за резервации. Тука најчесто се појавуваат concurrency проблемите, бидејќи повеќе корисници можат истовремено да резервираат или менуваат седишта за ист лет.
     18
     19'''flight''' го дефинира конкретниот лет и авионот кој го извршува. Оваа табела е важна затоа што сите резервации се врзуваат за flight, а flight редот може да се користи и како заедничка точка за locking на операции што работат над истиот лет.
     20
     21'''airplane''' го содржи `capacity`, кој се користи при проверка дали може да се направи нова резервација или дали летот смее да се префрли на друг авион.
     22
     23Дополнително, `passenger` и `passengerdetails` ги содржат податоците за патниците и се поврзани со резервациите.
     24
     25=== Дефинирани правила за конзистентност
     26
     271. Единственост на седиште по лет. \\
     282. Почитување на капацитетот на авионот. \\
     293. Атомичност на seat change и cancel. \\
     304. Промена на авион само ако постоечките резервации се вклопуваат во новиот капацитет. \\
    1531
    1632Во `booking` постои UNIQUE ограничување над:
     
    2036}}}
    2137
    22 со што на ниво на база се спречува две резервации да завршат со исто седиште на ист лет.
    23 
    24 === Реализација
    25 
    26 За операциите се користат четири stored procedures:
    27 
    28 || '''Процедура''' || '''Намена''' ||
    29 || `sp_reserve_seat(flight_id, seat, passenger_id, price)` || Резервира седиште и проверува дали седиштето е зафатено и дали летот има слободен капацитет. ||
    30 || `sp_change_seat(booking_id, new_seat)` || Атомски го менува седиштето и ја одбива промената ако новото седиште е веќе зафатено. ||
    31 || `sp_cancel_booking(booking_id)` || Безбедно ја откажува резервацијата. ||
    32 || `sp_change_flight_airplane(flight_id, new_airplane_id)` || Го менува авионот само ако постоечките резервации се вклопуваат во новиот капацитет. ||
    33 
    34 Процедурите користат `START TRANSACTION`, `FOR UPDATE`, `COMMIT` и `ROLLBACK`. Редот од `flight` се користи како заедничка точка за locking на операциите што работат над истиот лет.
    35 
    36 При SQL грешка се извршува:
    37 
    38 {{{
    39 ROLLBACK;
    40 RESIGNAL;
    41 }}}
    42 
    43 со што промените од неуспешната трансакција не остануваат запишани.
    44 
    45 `@txn_lab_pause` се користеше само при тестирање за намерно да се задржи една трансакција неколку секунди и полесно да се репродуцира паралелно извршување. Не претставува дел од бизнис логиката.
     38Ова ограничување претставува дополнителна заштита на ниво на база: две резервации не можат да имаат ист `flight_id` и `seat`.
     39
     40Сепак, UNIQUE ограничувањето само по себе не е доволно за проверка на вкупниот капацитет. Две паралелни резервации можат да изберат различни седишта, а сепак заедно да го надминат капацитетот. Затоа е потребна и трансакциска логика.
     41
     42=== Реални сценарија
     43
     44Во системот можат да се појават неколку типични конкурентни ситуации.
     45
     461. Два корисници во ист момент се обидуваат да го резервираат истото седиште на ист лет. \\
     47Ако двете операции ја прочитаат состојбата пред било која да запише промена, двете можат да сметаат дека седиштето е слободно.
     48
     492. Два корисници резервираат различни седишта на лет кој има само едно преостанато место. \\
     50UNIQUE ограничувањето нема да помогне бидејќи седиштата се различни, па мора посебно да се контролира capacity.
     51
     523. Корисник сака да го промени своето седиште, а новото седиште во меѓувреме го резервира друг корисник. \\
     53Промената мора да биде атомска: или целосно успева, или старата резервација останува непроменета.
     54
     554. Една операција менува седиште, а друга истовремено ја откажува резервацијата. \\
     56Операциите мора да се серијализираат за да не работат врз неконзистентна состојба.
     57
     585. Оператор сака да го промени авионот на летот со авион со помал капацитет. \\
     59Промената мора да се одбие ако веќе постојат повеќе резервации од капацитетот на новиот авион.
     60
     61Затоа процедурите користат:
     62* `START TRANSACTION`
     63* `SELECT ... FOR UPDATE`
     64* `COMMIT`
     65* `ROLLBACK`
     66* `RESIGNAL`
     67
     68Редот од `flight` се користи како заедничка точка за locking на операциите што работат над истиот лет. Со тоа проверките и промените не се извршуваат истовремено врз различна верзија на состојбата.
     69
     70===== Procedure Reserve Seat - резервација и контрола на капацитет
     71
     72Кај резервирањето имаме типична "check-then-write" логика: прво се проверува дали седиштето е слободно и дали летот има капацитет, а потоа се прави INSERT.
     73
     74Ако две сесии ја направат проверката паралелно без locking, и двете можат да видат стара состојба и да продолжат со внесување.
     75
     76{{{
     77DELIMITER $$
     78
     79CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_reserve_seat`(
     80    IN p_flight_id INT,
     81    IN p_seat VARCHAR(10),
     82    IN p_passenger_id INT,
     83    IN p_price DECIMAL(10,2)
     84)
     85BEGIN
     86    DECLARE v_airplane_id INT DEFAULT NULL;
     87    DECLARE v_capacity INT DEFAULT NULL;
     88    DECLARE v_booked INT DEFAULT 0;
     89    DECLARE v_occupied INT DEFAULT 0;
     90    DECLARE v_booking_id INT DEFAULT NULL;
     91
     92    DECLARE EXIT HANDLER FOR SQLEXCEPTION
     93    BEGIN
     94        ROLLBACK;
     95        RESIGNAL;
     96    END;
     97
     98    START TRANSACTION;
     99
     100    IF p_flight_id IS NULL OR p_flight_id <= 0 THEN
     101        SIGNAL SQLSTATE '45000'
     102            SET MYSQL_ERRNO = 30001, MESSAGE_TEXT = 'invalid flight_id';
     103    END IF;
     104
     105    IF p_passenger_id IS NULL OR p_passenger_id <= 0 THEN
     106        SIGNAL SQLSTATE '45000'
     107            SET MYSQL_ERRNO = 30002, MESSAGE_TEXT = 'invalid passenger_id';
     108    END IF;
     109
     110    IF p_price IS NULL OR p_price < 0 THEN
     111        SIGNAL SQLSTATE '45000'
     112            SET MYSQL_ERRNO = 30009, MESSAGE_TEXT = 'invalid price';
     113    END IF;
     114
     115    IF p_seat IS NULL
     116       OR CHAR_LENGTH(TRIM(p_seat)) = 0
     117       OR CHAR_LENGTH(TRIM(p_seat)) > 10 THEN
     118        SIGNAL SQLSTATE '45000'
     119            SET MYSQL_ERRNO = 30003, MESSAGE_TEXT = 'invalid seat';
     120    END IF;
     121
     122    SET p_seat = TRIM(p_seat);
     123
     124    SELECT airplane_id
     125      INTO v_airplane_id
     126      FROM flight
     127     WHERE flight_id = p_flight_id
     128     FOR UPDATE;
     129
     130    IF v_airplane_id IS NULL THEN
     131        SIGNAL SQLSTATE '45000'
     132            SET MYSQL_ERRNO = 30004, MESSAGE_TEXT = 'flight not found';
     133    END IF;
     134
     135    IF COALESCE(@txn_lab_pause, 0) > 0 THEN
     136        DO SLEEP(@txn_lab_pause);
     137    END IF;
     138
     139    SELECT capacity
     140      INTO v_capacity
     141      FROM airplane
     142     WHERE airplane_id = v_airplane_id;
     143
     144    IF v_capacity IS NULL THEN
     145        SIGNAL SQLSTATE '45000'
     146            SET MYSQL_ERRNO = 30005, MESSAGE_TEXT = 'airplane not found';
     147    END IF;
     148
     149    SELECT COUNT(*)
     150      INTO v_occupied
     151      FROM booking
     152     WHERE flight_id = p_flight_id
     153       AND seat = p_seat;
     154
     155    IF v_occupied > 0 THEN
     156        SIGNAL SQLSTATE '45000'
     157            SET MYSQL_ERRNO = 30006, MESSAGE_TEXT = 'seat already occupied';
     158    END IF;
     159
     160    SELECT COUNT(*)
     161      INTO v_booked
     162      FROM booking
     163     WHERE flight_id = p_flight_id;
     164
     165    IF v_booked >= v_capacity THEN
     166        SIGNAL SQLSTATE '45000'
     167            SET MYSQL_ERRNO = 30007, MESSAGE_TEXT = 'flight is full';
     168    END IF;
     169
     170    INSERT INTO booking (flight_id, passenger_id, seat, price)
     171    VALUES (p_flight_id, p_passenger_id, p_seat, p_price);
     172
     173    SET v_booking_id = LAST_INSERT_ID();
     174
     175    COMMIT;
     176
     177    SELECT 'RESERVED' AS status,
     178           v_booking_id AS booking_id,
     179           p_flight_id AS flight_id,
     180           p_passenger_id AS passenger_id,
     181           p_seat AS seat,
     182           p_price AS price;
     183END$$
     184
     185DELIMITER ;
     186}}}
     187
     188'''Што прави процедурата?'''
     189
     190`sp_reserve_seat` прво ги валидира влезните параметри, а потоа го заклучува соодветниот `flight` ред со `FOR UPDATE`.
     191
     192Додека тој lock е активен, друга процедура што работи над истиот flight не може паралелно да ја помине критичната проверка.
     193
     194Потоа:
     1951. се чита капацитетот на авионот;
     1962. се проверува дали избраното седиште е веќе зафатено;
     1973. се брои бројот на постоечки резервации;
     1984. ако има капацитет, се прави INSERT;
     1995. при успех се прави `COMMIT`.
     200
     201Ако седиштето е зафатено се враќа `30006`, а ако летот е полн се враќа `30007`.
     202
     203`EXIT HANDLER` прави `ROLLBACK` и `RESIGNAL`, така што при грешка не останува делумно запишана трансакција.
     204
     205`@txn_lab_pause` не е дел од бизнис логиката. Се користи само за тестирање, за намерно да се задржи lock-от неколку секунди и полесно да се репродуцира конкурентно извршување.
     206
     207===== Procedure Change Seat - атомична промена на седиште
     208
     209При промена на седиште е важно старата позиција да не се изгуби ако новото седиште е веќе зафатено.
     210
     211{{{
     212DELIMITER $$
     213
     214CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_change_seat`(
     215    IN p_booking_id INT,
     216    IN p_new_seat VARCHAR(10)
     217)
     218BEGIN
     219    DECLARE v_flight_id INT DEFAULT NULL;
     220    DECLARE v_airplane_id INT DEFAULT NULL;
     221    DECLARE v_capacity INT DEFAULT NULL;
     222    DECLARE v_old_seat VARCHAR(10) DEFAULT NULL;
     223    DECLARE v_occupied INT DEFAULT 0;
     224
     225    DECLARE EXIT HANDLER FOR SQLEXCEPTION
     226    BEGIN
     227        ROLLBACK;
     228        RESIGNAL;
     229    END;
     230
     231    START TRANSACTION;
     232
     233    IF p_booking_id IS NULL OR p_booking_id <= 0 THEN
     234        SIGNAL SQLSTATE '45000'
     235            SET MYSQL_ERRNO = 30011, MESSAGE_TEXT = 'invalid booking_id';
     236    END IF;
     237
     238    IF p_new_seat IS NULL
     239       OR CHAR_LENGTH(TRIM(p_new_seat)) = 0
     240       OR CHAR_LENGTH(TRIM(p_new_seat)) > 10 THEN
     241        SIGNAL SQLSTATE '45000'
     242            SET MYSQL_ERRNO = 30012, MESSAGE_TEXT = 'invalid seat';
     243    END IF;
     244
     245    SET p_new_seat = TRIM(p_new_seat);
     246
     247    SELECT flight_id
     248      INTO v_flight_id
     249      FROM booking
     250     WHERE booking_id = p_booking_id;
     251
     252    IF v_flight_id IS NULL THEN
     253        SIGNAL SQLSTATE '45000'
     254            SET MYSQL_ERRNO = 30013, MESSAGE_TEXT = 'booking not found';
     255    END IF;
     256
     257    SELECT airplane_id
     258      INTO v_airplane_id
     259      FROM flight
     260     WHERE flight_id = v_flight_id
     261     FOR UPDATE;
     262
     263    IF v_airplane_id IS NULL THEN
     264        SIGNAL SQLSTATE '45000'
     265            SET MYSQL_ERRNO = 30014, MESSAGE_TEXT = 'flight not found';
     266    END IF;
     267
     268    IF COALESCE(@txn_lab_pause, 0) > 0 THEN
     269        DO SLEEP(@txn_lab_pause);
     270    END IF;
     271
     272    SET v_old_seat = NULL;
     273
     274    SELECT seat
     275      INTO v_old_seat
     276      FROM booking
     277     WHERE booking_id = p_booking_id
     278       AND flight_id = v_flight_id
     279     FOR UPDATE;
     280
     281    IF v_old_seat IS NULL THEN
     282        SIGNAL SQLSTATE '45000'
     283            SET MYSQL_ERRNO = 30013, MESSAGE_TEXT = 'booking not found';
     284    END IF;
     285
     286    SELECT capacity
     287      INTO v_capacity
     288      FROM airplane
     289     WHERE airplane_id = v_airplane_id;
     290
     291    IF v_capacity IS NULL THEN
     292        SIGNAL SQLSTATE '45000'
     293            SET MYSQL_ERRNO = 30015, MESSAGE_TEXT = 'airplane not found';
     294    END IF;
     295
     296    IF p_new_seat = v_old_seat THEN
     297        COMMIT;
     298
     299        SELECT 'UNCHANGED' AS status,
     300               p_booking_id AS booking_id,
     301               v_flight_id AS flight_id,
     302               v_old_seat AS old_seat,
     303               v_old_seat AS new_seat;
     304    ELSE
     305        SELECT COUNT(*)
     306          INTO v_occupied
     307          FROM booking
     308         WHERE flight_id = v_flight_id
     309           AND seat = p_new_seat
     310           AND booking_id <> p_booking_id;
     311
     312        IF v_occupied > 0 THEN
     313            SIGNAL SQLSTATE '45000'
     314                SET MYSQL_ERRNO = 30016,
     315                    MESSAGE_TEXT = 'target seat already occupied';
     316        END IF;
     317
     318        UPDATE booking
     319           SET seat = p_new_seat
     320         WHERE booking_id = p_booking_id;
     321
     322        COMMIT;
     323
     324        SELECT 'CHANGED' AS status,
     325               p_booking_id AS booking_id,
     326               v_flight_id AS flight_id,
     327               v_old_seat AS old_seat,
     328               p_new_seat AS new_seat;
     329    END IF;
     330END$$
     331
     332DELIMITER ;
     333}}}
     334
     335'''Што прави процедурата?'''
     336
     337Најпрво се пронаоѓа flight-от на резервацијата, а потоа неговиот ред се заклучува со `FOR UPDATE`.
     338
     339Потоа повторно се чита и се заклучува конкретниот booking ред. Ова е важно затоа што booking-от може да биде променет или избришан додека се чека flight lock-от.
     340
     341Ако новото седиште е исто со старото, нема потреба од UPDATE.
     342
     343Во спротивно се проверува дали друго booking веќе го користи новото седиште. Ако е зафатено, се враќа:
     344
     345{{{
     34630016: target seat already occupied
     347}}}
     348
     349и целиот transaction се враќа назад.
     350
     351Со ова старата позиција на патникот останува непроменета ако промената не може успешно да се заврши.
     352
     353===== Procedure Cancel Booking - безбедно откажување
     354
     355Cancel може да се судри со seat change или друга операција врз истата резервација. Затоа и оваа процедура го користи истиот locking редослед.
     356
     357{{{
     358DELIMITER $$
     359
     360CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_cancel_booking`(
     361    IN p_booking_id INT
     362)
     363BEGIN
     364    DECLARE v_flight_id INT DEFAULT NULL;
     365    DECLARE v_airplane_id INT DEFAULT NULL;
     366    DECLARE v_old_seat VARCHAR(10) DEFAULT NULL;
     367
     368    DECLARE EXIT HANDLER FOR SQLEXCEPTION
     369    BEGIN
     370        ROLLBACK;
     371        RESIGNAL;
     372    END;
     373
     374    START TRANSACTION;
     375
     376    IF p_booking_id IS NULL OR p_booking_id <= 0 THEN
     377        SIGNAL SQLSTATE '45000'
     378            SET MYSQL_ERRNO = 30021, MESSAGE_TEXT = 'invalid booking_id';
     379    END IF;
     380
     381    SELECT flight_id
     382      INTO v_flight_id
     383      FROM booking
     384     WHERE booking_id = p_booking_id;
     385
     386    IF v_flight_id IS NULL THEN
     387        SIGNAL SQLSTATE '45000'
     388            SET MYSQL_ERRNO = 30022, MESSAGE_TEXT = 'booking not found';
     389    END IF;
     390
     391    SELECT airplane_id
     392      INTO v_airplane_id
     393      FROM flight
     394     WHERE flight_id = v_flight_id
     395     FOR UPDATE;
     396
     397    IF v_airplane_id IS NULL THEN
     398        SIGNAL SQLSTATE '45000'
     399            SET MYSQL_ERRNO = 30023, MESSAGE_TEXT = 'flight not found';
     400    END IF;
     401
     402    IF COALESCE(@txn_lab_pause, 0) > 0 THEN
     403        DO SLEEP(@txn_lab_pause);
     404    END IF;
     405
     406    SET v_old_seat = NULL;
     407
     408    SELECT seat
     409      INTO v_old_seat
     410      FROM booking
     411     WHERE booking_id = p_booking_id
     412       AND flight_id = v_flight_id
     413     FOR UPDATE;
     414
     415    IF v_old_seat IS NULL THEN
     416        SIGNAL SQLSTATE '45000'
     417            SET MYSQL_ERRNO = 30022, MESSAGE_TEXT = 'booking not found';
     418    END IF;
     419
     420    DELETE FROM booking
     421     WHERE booking_id = p_booking_id;
     422
     423    COMMIT;
     424
     425    SELECT 'CANCELLED' AS status,
     426           p_booking_id AS booking_id,
     427           v_flight_id AS flight_id,
     428           v_old_seat AS seat;
     429END$$
     430
     431DELIMITER ;
     432}}}
     433
     434'''Што прави процедурата?'''
     435
     436`sp_cancel_booking` прво го наоѓа flight-от на booking-от, потоа го заклучува истиот flight ред кој го користат и другите процедури.
     437
     438После тоа booking-от повторно се чита со `FOR UPDATE`. Ова спречува да се избрише запис кој во меѓувреме бил променет или веќе откажан.
     439
     440Ако booking-от постои, се извршува `DELETE` и `COMMIT`.
     441
     442Ако друга трансакција истовремено врши seat change или друга промена на истиот лет, едната операција ќе почека додека другата го ослободи lock-от.
     443
     444===== Procedure Change Flight Airplane - промена на авион
     445
     446Ова е административна операција каде flight се префрла на друг airplane.
     447
     448Најважната проверка е дали новиот авион има доволен капацитет за сите веќе постоечки резервации.
     449
     450{{{
     451DELIMITER $$
     452
     453CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_change_flight_airplane`(
     454    IN p_flight_id INT,
     455    IN p_new_airplane_id INT
     456)
     457BEGIN
     458    DECLARE v_old_airplane_id INT DEFAULT NULL;
     459    DECLARE v_new_capacity INT DEFAULT NULL;
     460    DECLARE v_booked INT DEFAULT 0;
     461
     462    DECLARE EXIT HANDLER FOR SQLEXCEPTION
     463    BEGIN
     464        ROLLBACK;
     465        RESIGNAL;
     466    END;
     467
     468    START TRANSACTION;
     469
     470    IF p_flight_id IS NULL OR p_flight_id <= 0 THEN
     471        SIGNAL SQLSTATE '45000'
     472            SET MYSQL_ERRNO = 30031, MESSAGE_TEXT = 'invalid flight_id';
     473    END IF;
     474
     475    IF p_new_airplane_id IS NULL OR p_new_airplane_id <= 0 THEN
     476        SIGNAL SQLSTATE '45000'
     477            SET MYSQL_ERRNO = 30032,
     478                MESSAGE_TEXT = 'invalid new_airplane_id';
     479    END IF;
     480
     481    SELECT airplane_id
     482      INTO v_old_airplane_id
     483      FROM flight
     484     WHERE flight_id = p_flight_id
     485     FOR UPDATE;
     486
     487    IF v_old_airplane_id IS NULL THEN
     488        SIGNAL SQLSTATE '45000'
     489            SET MYSQL_ERRNO = 30033, MESSAGE_TEXT = 'flight not found';
     490    END IF;
     491
     492    IF COALESCE(@txn_lab_pause, 0) > 0 THEN
     493        DO SLEEP(@txn_lab_pause);
     494    END IF;
     495
     496    SELECT capacity
     497      INTO v_new_capacity
     498      FROM airplane
     499     WHERE airplane_id = p_new_airplane_id;
     500
     501    IF v_new_capacity IS NULL THEN
     502        SIGNAL SQLSTATE '45000'
     503            SET MYSQL_ERRNO = 30034, MESSAGE_TEXT = 'airplane not found';
     504    END IF;
     505
     506    SELECT COUNT(*)
     507      INTO v_booked
     508      FROM booking
     509     WHERE flight_id = p_flight_id;
     510
     511    IF v_booked > v_new_capacity THEN
     512        SIGNAL SQLSTATE '45000'
     513            SET MYSQL_ERRNO = 30035,
     514                MESSAGE_TEXT = 'new airplane capacity is too small';
     515    END IF;
     516
     517    UPDATE flight
     518       SET airplane_id = p_new_airplane_id
     519     WHERE flight_id = p_flight_id;
     520
     521    COMMIT;
     522
     523    SELECT 'AIRPLANE_CHANGED' AS status,
     524           p_flight_id AS flight_id,
     525           v_old_airplane_id AS old_airplane_id,
     526           p_new_airplane_id AS new_airplane_id,
     527           v_booked AS booking_count,
     528           v_new_capacity AS new_capacity;
     529END$$
     530
     531DELIMITER ;
     532}}}
     533
     534'''Што прави процедурата?'''
     535
     536Процедурата прво го заклучува flight редот со `FOR UPDATE`.
     537
     538Додека flight-от е заклучен:
     5391. се чита капацитетот на новиот авион;
     5402. се брои бројот на постоечки booking записи;
     5413. се проверува дали сите можат да се сместат во новиот авион.
     542
     543Ако:
     544
     545{{{
     546bookings > new_capacity
     547}}}
     548
     549процедурата враќа:
     550
     551{{{
     55230035: new airplane capacity is too small
     553}}}
     554
     555и transaction-от се враќа назад.
     556
     557Ако капацитетот е доволен, `flight.airplane_id` се ажурира и се прави `COMMIT`.
     558
     559Со тоа не може преку оваа процедура да се создаде flight кој веќе има повеќе резервации од капацитетот на новиот авион.
    46560
    47561=== Тестирање