ЁЯдЭ рдзрдбрд╛ 11 тАФ Services рдУрд▓рд╛рдВрдбреВрди transactions: рд╢рд╛рд│реЗрдЪреНрдпрд╛ рд╕рд╣рд▓реАрдЪреЗ booking
ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 18 рдкреИрдХреА рдзрдбрд╛ 11 ┬╖ рдкреБрдвреАрд▓: lesson-12-rate-limiting
ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ
рдзрдбреЗ 01тАУ10, рдЖрдгрд┐ рдХрд╛рдо рдЕрдиреЗрдХ services рдордзреНрдпреЗ рдкрд╕рд░рд▓реЗрд▓реЗ рдЕрд╕рддрд╛рдирд╛ "рд╕рдЧрд│реЗ рдХрд┐рдВрд╡рд╛ рдХрд╛рд╣реАрдЪ рдирд╛рд╣реА", рдЬрд┐рдереЗ рдкреНрд░рддреНрдпреЗрдХреАрдЪрд╛ рд╕реНрд╡рддрдГрдЪрд╛ database рдЕрд╕рддреЛ. рддреБрдореНрд╣реА рд╢рд┐рдХрддрд╛ рдХреА local transaction рдкреБрд░реЗрд╕рд╛ рдХрд╛ рдирд╛рд╣реА, two-phase commit (2PC) рдЖрдгрд┐ рддреЛ рдХрд╛ рдЕрдбрдХрддреЛ, sagas (orchestration рдЖрдгрд┐ choreography), compensations, idempotency, рдЖрдгрд┐ рдкреБрдиреНрд╣рд╛ рдзрдбрд╛ 08 рдордзреАрд▓ outbox.
- design/blocks.py тАФ
two_phase_commit(votes, coordinator_alive)рдЖрдгрд┐saga(steps, fail_at) - design/demo.py тАФ
python3 design/demo.py transactions - design/test_design.py тАФ check L11
ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ
рдХрддрд░рд┐рдирд╛ рддрд┐рдЪреНрдпрд╛ рдореБрд▓реАрд╕рд╛рдареА рд╢рд╛рд│реЗрдЪреА рд╕рд╣рд▓ book рдХрд░рддреЗ. рддреАрди рдХрд╛рд░реНрдпрд╛рд▓рдпрд╛рдВрдЪреЗ рдПрдХрдордд рд╣рд╡реЗ: seats рдХрд╛рд░реНрдпрд╛рд▓рдп, fees рдХрд╛рд░реНрдпрд╛рд▓рдп рдЖрдгрд┐ bus рдХрд╛рд░реНрдпрд╛рд▓рдп. рдкреНрд░рддреНрдпреЗрдХ рдХрд╛рд░реНрдпрд╛рд▓рдпрд╛рдЪреА рд╕реНрд╡рддрдГрдЪреА рдиреЛрдВрджрд╡рд╣реА рдЖрд╣реЗ.
рдпреЛрдЬрдирд╛ A тАФ рдлреЛрди рдХреЙрд▓ (2PC). рджреАрдкрд┐рдХрд╛ рддрд┐рдШрд╛рдВрдирд╛рд╣реА рдлреЛрди рдХрд░рддреЗ: "рддреБрдореНрд╣реА рд╣реЗ рдХрд░реВ рд╢рдХрддрд╛ рдХрд╛? рд╣реЛ рдЕрд╕реЗрд▓ рддрд░ рддреЗ рд░рд╛рдЦреВрди рдареЗрд╡рд╛ рдЖрдгрд┐ рдорд╛рдЭреНрдпрд╛ рд╢рдмреНрджрд╛рдЪреА рд╡рд╛рдЯ рдкрд╛рд╣рд╛." рддрд┐рдШреЗрд╣реА рд╣реЛ рдореНрд╣рдгрддрд╛рдд рдЖрдгрд┐ рд░рд╛рдЦреВрди рдареЗрд╡рддрд╛рдд: seat рд░рд╛рдЦрд▓реА, рдкреИрд╕реЗ рд░рд╛рдЦрд▓реЗ, bus рдордзрд▓реА рдЬрд╛рдЧрд╛ рд░рд╛рдЦрд▓реА. рдордЧ рджреАрдкрд┐рдХрд╛ рдореНрд╣рдгрддреЗ "рдЪрд▓рд╛!" рдЖрдгрд┐ рддрд┐рдШреЗрд╣реА рддреЗ рд▓рд┐рд╣реВрди рдШреЗрддрд╛рдд. рдкрдг "рд░рд╛рдЦреВрди рдареЗрд╡рд╛" рдирдВрддрд░ рджреАрдкрд┐рдХрд╛рдЪрд╛ рдлреЛрди рдмрдВрдж рдкрдбрд▓рд╛, рддрд░ рддрд┐рдиреНрд╣реА рдХрд╛рд░реНрдпрд╛рд▓рдпреЗ рд╡рд╛рдЯ рдкрд╛рд╣рдд рд░рд╛рд╣рддрд╛рдд. Seat рд░рд╛рдЦрд▓реЗрд▓реАрдЪ рд░рд╛рд╣рддреЗ. рдкреИрд╕реЗ рд░рд╛рдЦрд▓реЗрд▓реЗрдЪ рд░рд╛рд╣рддрд╛рдд. рджреБрд╕рд░реЗ рдХреЛрдгреАрдЪ рддреЗ рд╡рд╛рдкрд░реВ рд╢рдХрдд рдирд╛рд╣реА. рджреАрдкрд┐рдХрд╛ рдкрд░рдд рдлреЛрди рдХрд░реЗрдкрд░реНрдпрдВрдд рддреЗ рдерд╛рдВрдмрддрд╛рдд.
рдпреЛрдЬрдирд╛ B тАФ undo рдпрд╛рджреА (saga). рдкреНрд░рддреНрдпреЗрдХ рдХрд╛рд░реНрдпрд╛рд▓рдп рдЖрдкрд▓рд╛ рднрд╛рдЧ рд▓рдЧреЗрдЪ рдХрд░рддреЗ рдЖрдгрд┐ рд╕реЛрдмрдд рддреЛ undo рдХрд╕рд╛ рдХрд░рд╛рдпрдЪрд╛ рд╣реЗ рд▓рд┐рд╣реВрди рдареЗрд╡рддреЗ. Seats: рдЭрд╛рд▓реЗ (undo: seat рдореЛрдХрд│реА рдХрд░рд╛). Fees: рдЭрд╛рд▓реЗ (undo: рдкреИрд╕реЗ рдкрд░рдд). Bus: "рдорд╛рдл рдХрд░рд╛, bus рднрд░рд▓реА рдЖрд╣реЗ." рдореНрд╣рдгреВрди рдХрд╛рд░реНрдпрд╛рд▓рдп рдЙрд▓рдЯ рдХреНрд░рдорд╛рдиреЗ рдорд╛рдЧреЗ рдЬрд╛рддреЗ: рдкреИрд╕реЗ рдкрд░рдд рдХрд░рд╛, seat рдореЛрдХрд│реА рдХрд░рд╛. рдХреЛрдгреАрдЪ рдерд╛рдВрдмрд▓реЗ рдирд╛рд╣реА. рдереЛрдбреНрдпрд╛ рд╡реЗрд│рд╛рд╕рд╛рдареА seat рдШреЗрддрд▓реЗрд▓реА рд╣реЛрддреА рдЖрдгрд┐ рдкреИрд╕реЗ рдХрд╛рдкрд▓реЗрд▓реЗ рд╣реЛрддреЗ тАФ рдЖрдгрд┐ рдордЧ рджреЛрдиреНрд╣реА undo рдЭрд╛рд▓реЗ.
ЁЯЧ║я╕П рдЖрдХреГрддреА
flowchart LR
subgraph tpc["ЁЯУЮ 2PC: one coordinator"]
co["coordinator"] -->|"1 prepare"| s1["seats"]
co -->|"1 prepare"| p1["fees"]
co -->|"1 prepare"| b1["bus"]
co -.->|"2 commit or abort"| s1
dead["ЁЯТА coordinator dies after prepare<br/>тЖТ BLOCKED, locks held"]
end
subgraph sg["ЁЯУЛ saga: local steps + undo"]
a["тЬУ reserve seat"] --> b["тЬУ charge тВ╣500"] --> c["тЬЧ book the bus"]
c -->|"compensate"| rb["тЖй refund тВ╣500"] --> rs["тЖй release seat"]
end
ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрд╡реГрддреНрддреА + рдПрдХ lab: https://school-edh.pages.dev/system-design/lesson-diagrams.html#l11
тЭУ рдХрд╛рдп
- Local transaction тАФ рдПрдХрд╛рдЪ database рдЪреНрдпрд╛ рдЖрдд:
BEGIN тАж COMMIT, рд╕рдЧрд│реЗ рдХрд┐рдВрд╡рд╛ рдХрд╛рд╣реАрдЪ рдирд╛рд╣реА. рддреЛ рджреБрд╕рд▒реНрдпрд╛ service рдЪреНрдпрд╛ database рдкрд░реНрдпрдВрдд рдкреЛрд╣реЛрдЪреВ рд╢рдХрдд рдирд╛рд╣реА. Seats, fees рдЖрдгрд┐ bus рдпрд╛ рддреАрди services рдЭрд╛рд▓реНрдпрд╛ (рдзрдбрд╛ 09) рдХреА рдХреЛрдгрддрд╛рд╣реА рдПрдХCOMMITрддрд┐рдШрд╛рдВрдирд╛ рд╡реНрдпрд╛рдкрдд рдирд╛рд╣реА. - Two-phase commit (2PC) тАФ рдПрдХ coordinator рджреЛрди рдЯрдкреНрдкреЗ рдЪрд╛рд▓рд╡рддреЛ:
- prepare тАФ рдкреНрд░рддреНрдпреЗрдХ participant рдХрд╛рдо рдХрд░рддреЛ, рдЬреНрдпрд╛рд▓рд╛ рд╕реНрдкрд░реНрд╢ рдХреЗрд▓рд╛ рддреНрдпрд╛рд▓рд╛ lock рд▓рд╛рд╡рддреЛ, disk рд╡рд░ "ready" рд▓рд┐рд╣рд┐рддреЛ, рдЖрдгрд┐ рд╣реЛ рдХрд┐рдВрд╡рд╛ рдирд╛рд╣реА рдЕрд╕реЗ рдордд рджреЗрддреЛ;
- commit рдХрд┐рдВрд╡рд╛ abort тАФ рд╕рдЧрд│реЗ рд╣реЛ тЖТ рд╕рдЧрд│реАрдХрдбреЗ commit; рдХреЛрдгреА рдПрдХ рдирд╛рд╣реА тЖТ рд╕рдЧрд│реАрдХрдбреЗ abort.
- 2PC рдХрд╛ рдЕрдбрдХрддреЛ тАФ рд╣реЛ рдЕрд╕реЗ рдордд рджрд┐рд▓реЗрд▓реНрдпрд╛ participant рдиреЗ coordinator рдЪреЗ рдРрдХрдгреНрдпрд╛рдЪреЗ рд╡рдЪрди рджрд┐рд▓реЗрд▓реЗ рдЕрд╕рддреЗ. рддреЛ рд╕реНрд╡рддрдГрд╣реВрди commit рдХрд┐рдВрд╡рд╛ abort рдХрд░реВ рд╢рдХрдд рдирд╛рд╣реА. рджреЛрди рдЯрдкреНрдкреНрдпрд╛рдВрдЪреНрдпрд╛ рдордзреНрдпреЗ coordinator рдЧреЗрд▓рд╛, рддрд░ рд╣реЛ рдореНрд╣рдгрдгрд╛рд░реЗ рддреЛ рдкрд░рдд рдпреЗрдИрдкрд░реНрдпрдВрдд рдЖрдкрд▓реЗ locks рдзрд░реВрди рдареЗрд╡рддрд╛рдд. (рдирд╛рд╣реА рдЕрд╕реЗ рдордд рджрд┐рд▓реЗрд▓рд╛ participant рд▓рдЧреЗрдЪ abort рдХрд░реВ рд╢рдХрддреЛ тАФ рдлрдХреНрдд рд╣реЛ рдореНрд╣рдгрдгрд╛рд░реЗ рдЕрдбрдХрддрд╛рдд.) 2PC рдореБрд│реЗ рдкреНрд░рддреНрдпреЗрдХ participant рд╕рд░реНрд╡рд╛рдд рд╣рд│реВ participant рдЗрддрдХрд╛ рд╣рд│реВ рд╣реЛрддреЛ, рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ service рд▓рд╛ рдПрдХрдЪ protocol рдмреЛрд▓рд╛рд╡рд╛ рд▓рд╛рдЧрддреЛ (рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде XA).
- Saga тАФ local transactions рдЪреА рдорд╛рд▓рд┐рдХрд╛. рдкреНрд░рддреНрдпреЗрдХ рдкрд╛рдпрд░реА рддрд┐рдЪреНрдпрд╛ рд╕реНрд╡рддрдГрдЪреНрдпрд╛ service рдордзреНрдпреЗ рд▓рдЧреЗрдЪ commit рд╣реЛрддреЗ. рдкреНрд░рддреНрдпреЗрдХ рдкрд╛рдпрд░реАрдЪреЗ рдПрдХ compensation рдЕрд╕рддреЗ: рддрд┐рдЪрд╛ рдкрд░рд┐рдгрд╛рдо undo рдХрд░рдгрд╛рд░реА рдПрдХ рдирд╡реА рдХреГрддреА. рдкрд╛рдпрд░реА k рдЕрдкрдпрд╢реА рдЭрд╛рд▓реА, рддрд░ рдкрд╛рдпрд▒реНрдпрд╛ kтИТ1 тАж 1 рдЪреА compensations рдЙрд▓рдЯ рдХреНрд░рдорд╛рдиреЗ рдЪрд╛рд▓рд╡рд╛.
- Compensation рдореНрд╣рдгрдЬреЗ delete рдХрд░рдгреЗ рдирд╡реНрд╣реЗ тАФ refund рд╣реА fees рдиреЛрдВрджрд╡рд╣реАрддреАрд▓ рдПрдХ рдирд╡реА рдиреЛрдВрдж рдЖрд╣реЗ; charge рдЗрддрд┐рд╣рд╛рд╕рд╛рдд рддрд╕рд╛рдЪ рд░рд╛рд╣рддреЛ. рдХрд╛рд╣реА рдкрд╛рдпрд▒реНрдпрд╛ undo рдХрд░рддрд╛ рдпреЗрдд рдирд╛рд╣реАрдд (рдЖрдзреАрдЪ рдкрд╛рдард╡рд▓реЗрд▓рд╛ SMS). рддреНрдпрд╛ рд╢реЗрд╡рдЯреА рдареЗрд╡рд╛, рдЕрдЬреВрдирд╣реА рдЕрдкрдпрд╢реА рд╣реЛрдК рд╢рдХрдгрд╛рд▒реНрдпрд╛ рдкрд╛рдпрд▒реНрдпрд╛рдВрдирдВрддрд░.
- Orchestration тАФ рдПрдХ orchestrator (workflow service) рдкрд╛рдпрд▒реНрдпрд╛ рдЬрд╛рдгрддреЛ рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ service рд▓рд╛ рдХреНрд░рдорд╛рдиреЗ call рдХрд░рддреЛ. рдПрдХрд╛рдЪ рдард┐рдХрд╛рдгреА рд╡рд╛рдЪрд╛рдпрд▓рд╛ рдЖрдгрд┐ monitor рдХрд░рд╛рдпрд▓рд╛ рд╕реЛрдкреЗ.
- Choreography тАФ рдХреЗрдВрджреНрд░ рдирд╛рд╣реА: рдкреНрд░рддреНрдпреЗрдХ service рдПрдХрд╛ event рдЪреА рд╡рд╛рдЯ рдРрдХрддреЗ рдЖрдгрд┐
рдкреБрдврдЪрд╛ publish рдХрд░рддреЗ (
SeatReservedтЖТ fees charge рдХрд░рддреЗ тЖТFeeChargedтЖТ bus book рдХрд░рддреЗ тАж). 2тАУ3 рдкрд╛рдпрд▒реНрдпрд╛рдВрд╕рд╛рдареА рдХрдореА рд╣рд▓рдгрд╛рд░реЗ рднрд╛рдЧ; рдЕрдиреЗрдХ рдкрд╛рдпрд▒реНрдпрд╛рдВрд╕рд╛рдареА рдорд╛рдЧреЛрд╡рд╛ рдШреЗрдгреЗ рдХрдареАрдг. - Idempotency тАФ рдПрдЦрд╛рджреА рдкрд╛рдпрд░реА рдХрд┐рдВрд╡рд╛ compensation рджреЛрдирджрд╛ рдЪрд╛рд▓реВ рд╢рдХрддреЗ (retry, at-least-once
queue тАФ рдзрдбрд╛ 07). рдкреНрд░рддреНрдпреЗрдХ call рд╕реЛрдмрдд рдПрдХ key рдЕрд╕рддреЗ (
trip-42-charge), рдЖрдгрд┐ service рдкреБрдирд░рд╛рд╡реГрддреНрддреАрд╕рд╛рдареА рдкрд╣рд┐рд▓рд╛рдЪ рдирд┐рдХрд╛рд▓ рдкрд░рдд рдХрд░рддреЗ. рджреЛрдирджрд╛ рдЪрд╛рд▓рд▓реЗрд▓рд╛ refund рдПрдХрджрд╛рдЪ refund рдХрд░рд╛рдпрд▓рд╛ рд╣рд╡рд╛. - рдкреБрдиреНрд╣рд╛ outbox (рдзрдбрд╛ 08) тАФ рдкреНрд░рддреНрдпреЗрдХ saga рдкрд╛рдпрд░реА рд╕реНрд╡рддрдГрдЪреЗ table рдмрджрд▓рддреЗ рдЖрдгрд┐ рдкреБрдврдЪрд╛ event рддреНрдпрд╛рдЪ local transaction рдордзреНрдпреЗ рд▓рд┐рд╣рд┐рддреЗ. "charged" рдЖрдгрд┐ "рдкреБрдврдЪреНрдпрд╛ рдкрд╛рдпрд░реАрд▓рд╛ рд╕рд╛рдВрдЧрд╛" рдпрд╛рдВрдЪреНрдпрд╛ рдордзреНрдпреЗ crash рдЭрд╛рд▓рд╛ рддрд░реА рдХрд╛рд╣реАрдЪ рд╣рд░рд╡рдд рдирд╛рд╣реА.
ЁЯдФ рдХрд╛
рдХрд╛рд░рдг microservices data рд╡рд┐рднрд╛рдЧрддрд╛рдд. рдирд┐рдпреЛрдЬрди рдХрд╛рд░реНрдпрд╛рд▓рдпрд╛рд▓рд╛ рддрд░реАрд╣реА рд╡рдЪрди рджреНрдпрд╛рдпрдЪреЗ рдЕрд╕рддреЗ рдХреА "рд╕рд╣рд▓ рдПрдХрддрд░ рдкреВрд░реНрдг book рд╣реЛрддреЗ рдХрд┐рдВрд╡рд╛ рдЕрдЬрд┐рдмрд╛рдд рд╣реЛрдд рдирд╛рд╣реА". 2PC рд╣реЗ рд╡рдЪрди рджреЗрддреЛ, рдкрдг рддреЛ services рдУрд▓рд╛рдВрдбреВрди locks рдзрд░реВрди рдареЗрд╡рддреЛ рдЖрдгрд┐ coordinator рдерд╛рдВрдмрд▓рд╛ рдХреА рдерд╛рдВрдмрддреЛ. Saga рдХрдзреАрдЪ global lock рдзрд░рдд рдирд╛рд╣реА рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ service рд╕реНрд╡рддрдВрддреНрд░ рдареЗрд╡рддреЗ тАФ рддреНрдпрд╛рдЪреА рдХрд┐рдВрдордд рдореНрд╣рдгрдЬреЗ рдереЛрдбреНрдпрд╛ рд╡реЗрд│рд╛рдЪреА рдПрдХ рдордзрд▓реА рдЕрд╡рд╕реНрдерд╛ рдЬреА рдЗрддрд░ рд╡рд╛рдЪрдХрд╛рдВрдирд╛ рджрд┐рд╕реВ рд╢рдХрддреЗ (seat рдШреЗрддрд▓реА, рдордЧ рдореЛрдХрд│реА рдХреЗрд▓реА). рдореНрд╣рдгреВрди рдХрд╛рд░реНрдпрд╛рд▓рдп рдХрд╛рд╣реАрд╣реА рдмрд╛рдВрдзрдгреНрдпрд╛рдЖрдзреА рджреЛрдиреНрд╣реА рд▓рд┐рд╣рд┐рддреЗ: рдкрд╛рдпрд▒реНрдпрд╛ рдЖрдгрд┐ рддреНрдпрд╛рдВрдЪреЗ undo.
ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)
design/blocks.py рдордзреНрдпреЗ:
two_phase_commit(votes, coordinator_alive=True)рд╕рдЧрд│реА рдорддреЗ рд╣реЛ рдЕрд╕рддреАрд▓ рддрд░COMMIT, рдХреЛрдгрддреЗрд╣реА рдирд╛рд╣реА рдЕрд╕реЗрд▓ рддрд░ABORT, рдЖрдгрд┐ prepare рдирдВрддрд░ coordinator рдЧреЗрд▓рд╛ рддрд░BLOCKED тАжрдкрд░рдд рдХрд░рддреЛ. (рд╣реЗ model рдкреНрд░рддреНрдпреЗрдХ рдордд-рдпрд╛рджреАрд╕рд╛рдареАBLOCKEDрдкрд░рдд рдХрд░рддреЗ; рдЦрд▒реНрдпрд╛ system рдордзреНрдпреЗ рдлрдХреНрдд рд╣реЛ рдореНрд╣рдгрдгрд╛рд░реЗрдЪ рдерд╛рдВрдмрддрд╛рдд.)saga(steps, fail_at=None)(do, undo)рдЬреЛрдбреНрдпрд╛ рдХреНрд░рдорд╛рдиреЗ рдЪрд╛рд▓рд╡рддреЛ.fail_atрд▓рд╛ рддреЛтЬЧlog рдХрд░рддреЛ, рдкреВрд░реНрдг рдЭрд╛рд▓реЗрд▓реНрдпрд╛ рдкрд╛рдпрд▒реНрдпрд╛рдВрдЪреЗundoрдЙрд▓рдЯ рдХреНрд░рдорд╛рдиреЗ рдЪрд╛рд▓рд╡рддреЛ (тЖй), рдЖрдгрд┐COMPENSATEDрдкрд░рдд рдХрд░рддреЛ.
design/demo.py рдордзреАрд▓ transactions() рд╢рд╛рд│реЗрдЪреА рд╕рд╣рд▓ book рдХрд░рддреЛ.
ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛
python3 design/demo.py transactions
python3 - <<'EOF'
import sys; sys.path.insert(0, "design"); from blocks import two_phase_commit, saga
for votes in ([True] * 3, [True, False, True], [False] * 3):
print(votes, "тЖТ", two_phase_commit(votes), "| coordinator dead тЖТ", two_phase_commit(votes, coordinator_alive=False)[:7])
steps = [("reserve seat", "release seat"), ("charge Katrina тВ╣500", "refund Katrina тВ╣500"), ("book the bus", "cancel the bus")]
for f in (None, 0, 1, 2):
s, log = saga(steps, fail_at=f)
print(f"fail_at={f!s:<4} тЖТ {s:<11} {' ┬╖ '.join(log)}")
EOF
тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ
transactions рд╣реЗ рдЫрд╛рдкрддреЛ:
тФАтФА a school-trip booking touches 3 services: seats, payment, bus
2PC, all vote yes тЖТ COMMIT ┬╖ one votes no тЖТ ABORT
2PC, the coordinator dies after 'prepare' тЖТ BLOCKED тАФ participants hold locks until the coordinator returns
saga, the bus is full тЖТ COMPENSATED: тЬУ reserve seat ┬╖ тЬУ charge Katrina тВ╣500 ┬╖ тЬЧ book the bus ┬╖ тЖй refund Katrina тВ╣500 ┬╖ тЖй release seat
sagas trade 'all at once' for 'eventually consistent, with an undo for every step'
рддреБрдордЪрд╛ snippet рд╣реЗ рдЫрд╛рдкрддреЛ:
[True, True, True] тЖТ COMMIT | coordinator dead тЖТ BLOCKED
[True, False, True] тЖТ ABORT | coordinator dead тЖТ BLOCKED
[False, False, False] тЖТ ABORT | coordinator dead тЖТ BLOCKED
fail_at=None тЖТ DONE тЬУ reserve seat ┬╖ тЬУ charge Katrina тВ╣500 ┬╖ тЬУ book the bus
fail_at=0 тЖТ COMPENSATED тЬЧ reserve seat
fail_at=1 тЖТ COMPENSATED тЬУ reserve seat ┬╖ тЬЧ charge Katrina тВ╣500 ┬╖ тЖй release seat
fail_at=2 тЖТ COMPENSATED тЬУ reserve seat ┬╖ тЬУ charge Katrina тВ╣500 ┬╖ тЬЧ book the bus ┬╖ тЖй refund Katrina тВ╣500 ┬╖ тЖй release seat
Tests рдордзреНрдпреЗ тЬЕ L11 2PC blocks without its coordinator; a saga compensates in reverse рдЕрд╕рддреЗ.
ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ
Coordinator рдЬрд┐рд╡рдВрдд рдЕрд╕реЗрдкрд░реНрдпрдВрдд 2PC рд╕реЛрдкрд╛ рдЖрд╣реЗ: рд╕рдЧрд│реЗ рд╣реЛ тЖТ COMMIT, рдПрдХ рдирд╛рд╣реА тЖТ ABORT. Prepare рдирдВрддрд░
рддреЛ рдЧреЗрд▓рд╛, рддрд░ рдЙрддреНрддрд░ BLOCKED тАФ locks рдзрд░рд▓реЗрд▓реЗ, рдХрд╛рдо рдерд╛рдВрдмрд▓реЗрд▓реЗ. Saga рдХрдзреАрдЪ рдЕрдбрдХрдд рдирд╛рд╣реА: рдкрд╛рдпрд░реА 0 рд╡рд░
рдЕрдкрдпрд╢ рдЖрд▓реЗ рддрд░ рдХреЛрдгрддрд╛рд╣реА undo рд▓рд╛рдЧрдд рдирд╛рд╣реА, рдкрд╛рдпрд░реА 1 рд╡рд░ рдЕрдкрдпрд╢ рдПрдХ рдкрд╛рдпрд░реА undo рдХрд░рддреЗ, рдкрд╛рдпрд░реА 2 рд╡рд░
рдЕрдкрдпрд╢ рджреЛрди undo рдХрд░рддреЗ, рдиреЗрд╣рдореА рд╕рд░реНрд╡рд╛рдд рдирд╡реА рдЖрдзреА. Saga рдХрд┐рддреА рдкреБрдвреЗ рдЧреЗрд▓рд╛ рддреНрдпрд╛рдиреБрд╕рд╛рд░ compensations рдЪреА
рд╕рдВрдЦреНрдпрд╛ рд╡рд╛рдврддреЗ тАФ рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ compensation рджреЛрдирджрд╛ рдЪрд╛рд▓рд╡рдгреЗ рд╕реБрд░рдХреНрд╖рд┐рдд рдЕрд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ.
тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛
- рд╡реЗрдЧрд╡реЗрдЧрд│реНрдпрд╛ teams рдЪреНрдпрд╛ рдорд╛рд▓рдХреАрдЪреНрдпрд╛ services рдУрд▓рд╛рдВрдбреВрди 2PC тАФ рдПрдХ рд╣рд│реВ team рдЗрддрд░ рдкреНрд░рддреНрдпреЗрдХ team рд▓рд╛ рдЕрдбрд╡рддреЗ
- compensation рдирд╕рд▓реЗрд▓реА saga рдкрд╛рдпрд░реА, рдЬреА production рдордзреАрд▓ рдкрд╣рд┐рд▓реНрдпрд╛ рдЕрдкрдпрд╢рд╛рд╡рд░ рд▓рдХреНрд╖рд╛рдд рдпреЗрддреЗ
- idempotent рдирд╕рд▓реЗрд▓реА compensations тАФ рдкреБрдиреНрд╣рд╛ рдЪрд╛рд▓рд▓реЗрд▓рд╛ refund рдкрд╛рд▓рдХрд╛рд▓рд╛ рджреЛрдирджрд╛ рдкреИрд╕реЗ рджреЗрддреЛ
- undo рди рд╣реЛрдгрд╛рд░реА рдкрд╛рдпрд░реА (SMS рдкрд╛рдард╡рдгреЗ, ticket рдЫрд╛рдкрдгреЗ) рдЕрдкрдпрд╢реА рд╣реЛрдК рд╢рдХрдгрд╛рд▒реНрдпрд╛ рдкрд╛рдпрд░реАрдЪреНрдпрд╛ рдЖрдзреА рдареЗрд╡рдгреЗ
- рдкреБрдврдЪрд╛ saga event local transaction рдЪреНрдпрд╛ рдмрд╛рд╣реЗрд░ publish рдХрд░рдгреЗ тАФ рдзрдбрд╛ 08 рдордзреАрд▓ dual-write bug; outbox рд╡рд╛рдкрд░рд╛
- saga рдкреВрд░реНрдг рд╣реЛрдгреНрдпрд╛рдЖрдзреАрдЪ рдордзрд▓реА рдЕрд╡рд╕реНрдерд╛ рдЕрдВрддрд┐рдо рдореНрд╣рдгреВрди рджрд╛рдЦрд╡рдгреЗ ("рд╕рд╣рд▓ book рдЭрд╛рд▓реА!") тАФ рд╢реЗрд╡рдЯрдЪреНрдпрд╛ рдкрд╛рдпрд░реАрдкрд░реНрдпрдВрдд "bookingтАж" рджрд╛рдЦрд╡рд╛
- рджрд╣рд╛ рдкрд╛рдпрд▒реНрдпрд╛рдВрд╕рд╛рдареА choreography тАФ booking рдХреБрдареЗ рдЖрд╣реЗ рд╣реЗ рдХреЛрдгреАрдЪ рд╕рд╛рдВрдЧреВ рд╢рдХрдд рдирд╛рд╣реА; orchestrator рд╡рд╛рдкрд░рд╛
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд
рдЦрд▒реНрдпрд╛ account рд╡рд░ тАФ AWS Step Functions рдордзреНрдпреЗ рдПрдХ orchestrated saga. рдкреНрд░рддреНрдпреЗрдХ task рдордзреНрдпреЗ рдПрдХ
Catch рдЕрд╕рддреЛ рдЬреЛ compensations рдХрдбреЗ, рдЙрд▓рдЯ рдХреНрд░рдорд╛рдиреЗ, рдЙрдбреА рдорд╛рд░рддреЛ (Amazon States Language, рдЫреЛрдЯреЗ рдХреЗрд▓реЗрд▓реЗ):
{
"StartAt": "ReserveSeat",
"States": {
"ReserveSeat": { "Type": "Task", "Resource": "arn:aws:lambda:ap-south-1:111122223333:function:reserve-seat",
"Catch": [{ "ErrorEquals": ["States.ALL"], "Next": "Failed" }], "Next": "ChargeFee" },
"ChargeFee": { "Type": "Task", "Resource": "arn:aws:lambda:ap-south-1:111122223333:function:charge-fee",
"Catch": [{ "ErrorEquals": ["States.ALL"], "Next": "ReleaseSeat" }], "Next": "BookBus" },
"BookBus": { "Type": "Task", "Resource": "arn:aws:lambda:ap-south-1:111122223333:function:book-bus",
"Catch": [{ "ErrorEquals": ["States.ALL"], "Next": "RefundFee" }], "End": true },
"RefundFee": { "Type": "Task", "Resource": "arn:aws:lambda:ap-south-1:111122223333:function:refund-fee",
"Retry": [{ "ErrorEquals": ["States.ALL"], "MaxAttempts": 5, "BackoffRate": 2 }], "Next": "ReleaseSeat" },
"ReleaseSeat": { "Type": "Task", "Resource": "arn:aws:lambda:ap-south-1:111122223333:function:release-seat",
"Retry": [{ "ErrorEquals": ["States.ALL"], "MaxAttempts": 5, "BackoffRate": 2 }], "Next": "Failed" },
"Failed": { "Type": "Fail", "Error": "TripNotBooked" }
}
}
Compensations retry рдХрд░рддрд╛рдд (рддреА рдкреВрд░реНрдг рд╡реНрд╣рд╛рдпрд▓рд╛рдЪ рд╣рд╡реАрдд), рдореНрд╣рдгреВрди рддреА idempotent рд╣рд╡реАрдд. рдкреНрд░рддреНрдпреЗрдХ service рдордзреНрдпреЗ, рдЖрдзреАрдЪ рд╣рд╛рддрд╛рд│рд▓реЗрд▓реНрдпрд╛ keys рд▓рдХреНрд╖рд╛рдд рдареЗрд╡рд╛:
CREATE TABLE handled_requests (idempotency_key text PRIMARY KEY, result jsonb, created_at timestamptz DEFAULT now());
-- inside the same transaction as the refund:
INSERT INTO handled_requests (idempotency_key, result) VALUES ('trip-42-refund', '{"refunded": 500}')
ON CONFLICT (idempotency_key) DO NOTHING; -- 0 rows inserted тЖТ it was done before; return the saved result
2PC рдПрдХрд╛рдЪ database рдХреБрдЯреБрдВрдмрд╛рдЪреНрдпрд╛ рдЖрдд рдЦрд░реЛрдЦрд░ рдЕрд╕рддреЛ. PostgreSQL рдордзреНрдпреЗ prepared transactions рдЖрд╣реЗрдд
(default рдиреЗ рдмрдВрдж тАФ max_prepared_transactions = 0), рдЬреЗ transaction managers рд╡рд╛рдкрд░рддрд╛рдд:
BEGIN; UPDATE seats SET taken = true WHERE trip = 42 AND seat = 17;
PREPARE TRANSACTION 'trip-42'; -- phase 1: saved to disk, locks held
COMMIT PREPARED 'trip-42'; -- phase 2 (or ROLLBACK PREPARED 'trip-42')
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рд╡рд┐рд╕рд░рд▓реЗрд▓рд╛ prepared transaction locks рдзрд░реВрди рдареЗрд╡рддреЛ рдЖрдгрд┐ VACUUM рдерд╛рдВрдмрд╡рддреЛ.
SELECT * FROM pg_prepared_xacts;рддрдкрд╛рд╕рд╛. Sagas рд╕рд╛рдареА, compensation рдорд╛рд░реНрдЧрд╛рдиреЗ рд╕рдВрдкрдгрд╛рд▒реНрдпрд╛ executions рд╡рд░ alarm рд▓рд╛рд╡рд╛ тАФ рдкреНрд░рддреНрдпреЗрдХ рдореНрд╣рдгрдЬреЗ charge рдЖрдгрд┐ refund рджреЛрдиреНрд╣реА рдкрд╛рд╣рд┐рд▓реЗрд▓рд╛ рдПрдХ рдкрд╛рд▓рдХ.
тПня╕П рдкреБрдвреЗ
рдХрд╛рд░реНрдпрд╛рд▓рдп рдЖрддрд╛ рдПрдХрдордд рдХрд░реВ рд╢рдХрддреЗ рдЖрдгрд┐ undo рдХрд░реВ рд╢рдХрддреЗ. рдкрдг рдПрдХрд╛ рдкрд╛рд▓рдХрд╛рдЪреНрдпрд╛ app рдиреЗ рд╕реЗрдХрдВрджрд╛рд▓рд╛ 1,000 requests рдкрд╛рдард╡рд▓реНрдпрд╛ рддрд░? Rate limiting.
git checkout lesson-12-rate-limiting