8.1 Mga Transaction ID

Ito ay itinalaga bilang XID o TxID (kung may pagkakaiba, sabihin sa akin). Maaaring gamitin ang mga timestamp bilang TxID, na maaaring maglaro sa mga kamay kung gusto nating ibalik ang lahat ng pagkilos sa ilang oras. Ang problema ay maaaring lumitaw kung ang timestamp ay hindi sapat na butil - kung gayon ang mga transaksyon ay maaaring makakuha ng parehong ID.

Samakatuwid, ang pinaka-maaasahang opsyon ay ang bumuo ng mga natatanging UUID prod ID. Sa Python ito ay napakadali:

>>> import uuid 
>>> str(uuid.uuid4()) 
'f50ec0b7-f960-400d-91f0-c42a6d44e3d0' 
>>> str(uuid.uuid4()) 
'd15bed89-c0a5-4a72-98d9-5507ea7bc0ba' 

Mayroon ding opsyon na i-hash ang isang set ng data na tumutukoy sa transaksyon at gamitin ang hash na ito bilang TxID.

8.2 Pagsubok muli

Kung alam natin na ang isang partikular na function o programa ay idempotent, nangangahulugan ito na maaari at dapat nating subukang ulitin ang tawag nito kung sakaling magkaroon ng error. At kailangan lang nating maging handa para sa katotohanan na ang ilang operasyon ay magbibigay ng isang error - ibinigay na ang mga modernong aplikasyon ay ipinamamahagi sa network at hardware, ang error ay dapat isaalang-alang hindi bilang isang pagbubukod, ngunit bilang pamantayan. Maaaring mangyari ang error dahil sa pag-crash ng server, error sa network, pagsisikip ng malayuang application. Paano dapat kumilos ang aming aplikasyon? Tama, subukang ulitin ang operasyon.

Dahil ang isang piraso ng code ay maaaring magsabi ng higit pa sa isang buong pahina ng mga salita, gamitin natin ang isang halimbawa upang maunawaan kung paano dapat gumana ang walang muwang na mekanismo ng muling pagsubok. Ipapakita ko ito gamit ang Tenacity library (ito ay napakahusay na idinisenyo na kahit na hindi mo planong gamitin ito, ang halimbawa ay dapat magpakita sa iyo kung paano mo maaaring idisenyo ang mekanismo ng pag-ulit):

import logging
import random
import sys
from tenacity import retry, stop_after_attempt, stop_after_delay, wait_exponential, retry_if_exception_type, before_log

logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
logger = logging.getLogger(__name__)

@retry(
	stop=(stop_after_delay(10) | stop_after_attempt(5)),
	wait=wait_exponential(multiplier=1, min=4, max=10),
	retry=retry_if_exception_type(IOError),
	before=before_log(logger, logging.DEBUG)
)
def do_something_unreliable():
	if random.randint(0, 10) > 1:
    	raise IOError("Broken sauce, everything is hosed!!!111one")
	else:
    	return "Awesome sauce!"

print(do_something_unreliable.retry.statistics)

> Kung sakali, sasabihin ko: Ang \@retry(...) ay isang espesyal na Python syntax na tinatawag na "decorator". Isa lang itong retry(...) function na bumabalot ng isa pang function at gumagawa ng isang bagay bago o pagkatapos itong isagawa.

Tulad ng nakikita natin, ang mga muling pagsubok ay maaaring idisenyo nang malikhain:

  • Maaari mong limitahan ang mga pagtatangka ayon sa oras (10 segundo) o bilang ng mga pagtatangka (5).
  • Maaaring exponential (iyon ay, 2 ** ilang tumataas na bilang n ). o sa anumang paraan (halimbawa, naayos) upang madagdagan ang oras sa pagitan ng magkahiwalay na mga pagtatangka. Ang exponential variant ay tinatawag na "congestion collapse".
  • Maaari mo lamang subukang muli para sa ilang uri ng mga error (IOError).
  • Ang mga pagsubok na muli ay maaaring mauna o makumpleto ng ilang mga espesyal na entry sa log.

Ngayon na nakumpleto na natin ang kursong batang manlalaban at alam ang mga pangunahing bloke ng gusali na kailangan nating magtrabaho kasama ang mga transaksyon sa panig ng aplikasyon, kilalanin natin ang dalawang pamamaraan na nagpapahintulot sa atin na ipatupad ang mga transaksyon sa mga distributed system.

8.3 Mga advanced na tool para sa mga mahilig sa transaksyon

Magbibigay lamang ako ng medyo pangkalahatang mga kahulugan, dahil ang paksang ito ay karapat-dapat sa isang hiwalay na malaking artikulo.

Two-phase commit (2pc) . Ang 2pc ay may dalawang yugto: isang yugto ng paghahanda at isang yugto ng pangako. Sa yugto ng paghahanda, hihilingin sa lahat ng microservice na maghanda para sa ilang pagbabago sa data na maaaring gawin nang atomically. Kapag handa na silang lahat, gagawin ng commit phase ang mga aktwal na pagbabago. Upang i-coordinate ang proseso, kailangan ang isang global coordinator, na nagla-lock ng mga kinakailangang bagay - iyon ay, nagiging hindi naa-access ang mga ito para sa mga pagbabago hanggang sa ma-unlock sila ng coordinator. Kung ang isang partikular na microservice ay hindi handa para sa mga pagbabago (halimbawa, hindi tumugon), iaabort ng coordinator ang transaksyon at sisimulan ang proseso ng rollback.

Bakit maganda ang protocol na ito? Nagbibigay ito ng atomicity. Bilang karagdagan, ginagarantiyahan nito ang paghihiwalay kapag nagsusulat at nagbabasa. Nangangahulugan ito na ang mga pagbabago sa isang transaksyon ay hindi makikita ng iba hanggang sa gawin ng coordinator ang mga pagbabago. Ngunit ang mga pag-aari na ito ay mayroon ding kawalan: dahil ang protocol na ito ay sabay-sabay (pag-block), pinapabagal nito ang system (sa kabila ng katotohanan na ang tawag mismo ng RPC ay medyo mabagal). At muli, may panganib ng pagharang sa isa't isa.

Saga . Sa pattern na ito, ang isang distributed na transaksyon ay isinasagawa ng mga asynchronous na lokal na transaksyon sa lahat ng nauugnay na microservice. Ang mga microservice ay nakikipag-usap sa isa't isa sa pamamagitan ng isang event bus. Kung ang anumang microservice ay nabigo upang makumpleto ang lokal na transaksyon nito, ang ibang mga microservice ay magsasagawa ng mga transaksyon sa pagbabayad upang ibalik ang mga pagbabago.

Ang bentahe ng Saga ay walang mga bagay na naharang. Ngunit may mga, siyempre, downsides.

Mahirap i-debug ang Saga, lalo na kapag maraming microservice ang kasangkot. Ang isa pang kawalan ng pattern ng Saga ay kulang ito sa read isolation. Iyon ay, kung ang mga katangian na ipinahiwatig sa ACID ay mahalaga sa amin, kung gayon ang Saga ay hindi masyadong angkop para sa amin.

Ano ang nakikita natin sa paglalarawan ng dalawang pamamaraang ito? Ang katotohanan na sa mga distributed system, ang responsibilidad para sa atomicity at paghihiwalay ay nakasalalay sa aplikasyon. Ang parehong bagay ay nangyayari kapag gumagamit ng mga database na hindi nagbibigay ng mga garantiya ng ACID. Ibig sabihin, ang mga bagay tulad ng paglutas ng salungatan, pag-rollback, pag-commit, at pagpapalaya ng espasyo ay nasa balikat ng developer.

8.4 Paano ko malalaman kung kailangan ko ng mga garantiya ng ACID?

Kapag may mataas na posibilidad na ang isang tiyak na hanay ng mga user o proseso ay sabay na gagana sa parehong data .

Paumanhin para sa pagiging banal, ngunit isang tipikal na halimbawa ay mga transaksyon sa pananalapi.

Kapag mahalaga ang pagkakasunud-sunod kung saan isinasagawa ang mga transaksyon.

Isipin na ang iyong kumpanya ay malapit nang lumipat mula sa FunnyYellowChat messenger patungo sa FunnyRedChat messenger, dahil pinapayagan ka ng FunnyRedChat na magpadala ng mga gif, ngunit hindi magagawa ng FunnyYellowChat. Ngunit hindi mo lang pinapalitan ang messenger - inililipat mo ang sulat ng iyong kumpanya mula sa isang messenger patungo sa isa pa. Ginagawa mo ito dahil masyadong tamad ang iyong mga programmer na magdokumento ng mga programa at proseso sa isang lugar, at sa halip ay inilathala nila ang lahat sa iba't ibang channel sa messenger. Oo, at inilathala ng iyong mga salespeople ang mga detalye ng mga negosasyon at kasunduan sa parehong lugar. Sa madaling salita, ang buong buhay ng iyong kumpanya ay nariyan, at dahil walang sinuman ang may oras upang ilipat ang kabuuan ng bagay sa isang serbisyo para sa dokumentasyon, at ang paghahanap para sa mga instant messenger ay gumagana nang maayos, napagpasyahan mo sa halip na linisin ang mga durog na bato upang kopyahin lamang ang lahat ng mga mensahe sa isang bagong lokasyon. Ang pagkakasunud-sunod ng mga mensahe ay mahalaga

Sa pamamagitan ng paraan, para sa pagsusulatan sa isang messenger, ang pagkakasunud-sunod sa pangkalahatan ay mahalaga, ngunit kapag ang dalawang tao ay sumulat ng isang bagay sa parehong chat sa parehong oras, kung gayon sa pangkalahatan ay hindi napakahalaga kung kaninong mensahe ang unang lalabas. Kaya, para sa partikular na sitwasyong ito, hindi kakailanganin ang ACID.

Ang isa pang posibleng halimbawa ay bioinformatics. Hindi ko maintindihan ito, ngunit ipinapalagay ko na ang pagkakasunud-sunod ay mahalaga kapag nag-decipher ng genome ng tao. Gayunpaman, narinig ko na ang mga bioinformatician ay karaniwang gumagamit ng ilan sa kanilang mga tool para sa lahat - marahil mayroon silang sariling mga database.

Kapag hindi ka makapagbigay ng user o makapagproseso ng lipas na data.

At muli - mga transaksyon sa pananalapi. Sa totoo lang, wala akong maisip na ibang halimbawa.

Kapag ang mga nakabinbing transaksyon ay nauugnay sa mga makabuluhang gastos. Isipin ang mga problema na maaaring lumitaw kapag ang isang doktor at isang nars ay parehong nag-update ng isang rekord ng pasyente at binubura ang mga pagbabago ng isa't isa sa parehong oras, dahil ang database ay hindi maaaring ihiwalay ang mga transaksyon. Ang sistema ng pangangalagang pangkalusugan ay isa pang lugar, bukod sa pananalapi, kung saan ang mga garantiya ng ACID ay may posibilidad na maging kritikal.

8.5 Kailan ko hindi kailangan ng ACID?

Kapag ang mga user ay nag-update lamang ng ilan sa kanilang pribadong data.

Halimbawa, ang isang user ay nag-iiwan ng mga komento o malagkit na tala sa isang web page. O nag-e-edit ng personal na data sa isang personal na account sa isang provider ng anumang mga serbisyo.

Kapag ang mga user ay hindi nag-a-update ng data, ngunit nagdaragdag lamang ng mga bago (idagdag).

Halimbawa, isang tumatakbong application na nagse-save ng data sa iyong mga pagtakbo: kung gaano ka tumakbo, para sa anong oras, ruta, atbp. Ang bawat bagong run ay bagong data, at ang mga luma ay hindi na-edit. Marahil, batay sa data, nakakakuha ka ng analytics - at NoSQL database lang ang mainam para sa sitwasyong ito.

Kapag ang lohika ng negosyo ay hindi matukoy ang pangangailangan para sa isang tiyak na pagkakasunud-sunod kung saan ang mga transaksyon ay ginanap.

Marahil, para sa isang blogger sa Youtube na nangongolekta ng mga donasyon para sa paggawa ng bagong materyal sa susunod na live na broadcast, hindi gaanong mahalaga kung sino, kailan at sa anong pagkakasunud-sunod, ang naghagis sa kanya ng pera.

Kapag ang mga user ay mananatili sa parehong web page o application window sa loob ng ilang segundo o kahit na minuto, at samakatuwid ay makikita nila kahit papaano ang lipas na data.

Sa teorya, ang mga ito ay anumang online na media ng balita, o ang parehong Youtube. O "Habr". Kapag hindi mahalaga sa iyo na ang mga hindi kumpletong transaksyon ay maaaring pansamantalang maimbak sa system, maaari mong balewalain ang mga ito nang walang anumang pinsala.

Kung pinagsasama-sama mo ang data mula sa maraming mapagkukunan, at data na ina-update sa mataas na dalas - halimbawa, ang data sa occupancy ng mga parking space sa isang lungsod na nagbabago ng hindi bababa sa bawat 5 minuto, kung gayon sa teorya ay hindi ito magiging isang malaking problema para sa iyo kung sa isang punto ay hindi mapupunta ang transaksyon para sa isa sa mga paradahan. Bagaman, siyempre, depende ito sa kung ano ang eksaktong gusto mong gawin sa data na ito.