[python] db a thready
Petr Kolesa
kolisko na matfyz.cz
Pondělí Duben 4 10:42:17 CEST 2005
> Neni mi jasne, jak by tohle melo fungovat, protoze i kdyz k databazi budu
> pristupovat vyhradne pomoci jednoho objektu, tak, kdyz jeho metody budou
> volany z ruznych threadu, a pobezi v jejich kontextu. A mel-li jsem
> podminku, ze spojeni nesmi byt sdileno mezi thready, tak to imho muj
> problem neresi. A nebo neco hrube nechapu.
Myslim, ze si nerozumime, ale presne nevim v cem :). Pokusim se to tedy
rict jinak: Pokud k jednem datum pristupuje vic klientu (z hlediska tech
dat je dost jedno, jestli jsou to ruzne thready nebo ruzne procesy na
ruchnych pocitacich) musi byt nekde mezi daty a klientem neco, co
zajistuje, ze pristup k datum bude atomicky (teda za predpokladu, ze
atomicitu potrebujete). Jedina otazka je, kde ten "serializator" bude.
Obecne nema smysl zajistovat atomicitu vice nez jednou, protoze vetsina
metod s sebou nese jistou rezii. Pokud tedy mate DB, ktera umi zajistit
atomicitu, nemusite ji resit ve svych programech (teda skoro, protoze
pak obcas musite resit limit na pocet soucasnych spojeni). Pak jsou DB,
ktere umoznuji vice spojeni soucasne, ale neposkytuji atomicitu -- co to
presne znamena, zavisi na konkretni DB. Pak je nutne vyvarovat se
urcitem typu soubehu pozadavku (napr. ze vic vlaken nemuze soucasne
zapisovat). To se nejcasteji resi tak, ze si do DB otevrete pouze jedno
spojeni a to obsluhuje nejaky objekt, ktery zaruci atomicitu. Obecne
neni nutne aby to bylo pouze jedno spojeni, jen je dulezite, aby se
nejvyse v jednom spojeni dela ta "kriticka operace", coz je stejne nutne
nekde centralne zajistit.
(Omlouvam se, pokud vysvetluju prilisne triviality).
Domnivam se, ze pokud pouzijete DB, ktera muze mit vic spojeni, ale
nesmi se zapisovat ve vice najednou, budete muset tuhle podminku nejak
zajistit vy sam. (Obecne lze zkusit pustit X klientu soucasne a podivat
se, co to s DB dela. Nicmene to, ze to pri tomhle testovani projde, nic
neznamena. Soubeh (race condition) je typ chyby, ktery se rad objevuje
az v ostrem provozu a blbe se ladi).
Takze pokud to DB nezarucuje a vy chcete mit jistotu, ze k race
condition nedojde, nezbyde vam, nez pouzit jedno spojeni.
> Ano, o necem takovem jsem uvazoval, server by mel frontu, do ktere by
> komunikacni thready ukladaly pozadavky a jeden thread obsluhujici databazi
> by je vyrizoval. Pro ukladani dat by to bylo skvele, ale ri cteni dat bych
> musel resit, jak nactena data predat prislusnemu threadu, ktery by je pak
> vratil klientovi a to uz se mi zdalo moc komplikovane, skvela prilezitost
> k rade nahodnych tezko detekovatelnych chyb a tak se mi do toho nechtelo.
No moje prvni myslenka byla, ze by zapis byl asynchrnoni, zatimco cteni
synchrnonni. Tedy asi takhle:
dataQueue = []
def save(data):
"""Data ulozi do bufferu a vraci se. Nepristupuje do DB."""
dataQueue.append(data)
return
def load(condition):
"""Blokujici. Nacte data z DB a vati je."""
dataSet = dbConnection.command("SELECT firstField FROM mytable WHERE
" + condition) #cteni z DB
data = gainDataFrom(dataSet) #nejak dostane data z recodSetu, co vratila DB
return data;
Pak musi jeste existovat thread, ktery bude posilat data z dataQueue:
# ...
serverIsRunning = true;
def run():
while(serverIsRunning):
while(len(dataQueue) > 0): sleep(200)
data = dataQueue.popo(0)
dbConnection.command("INSERT \"%s\" INTO mytable" , data) #zapis do DB
(omlouvam se za przneni Jazyka, ale v P. jsem uz dlouho nic nepsal, tak
jsem trochu pozapomnel)
Takze zapis (casta operace) se provede rychle a klienta nezdrzuje,
zatimco cteni musi pockat na precteni dat z DB (a je tedy pomalejsi).
K tomu kodu jeste tri poznamky:
1) obecne je lepsi pouzivat nejake synchronizacni primitivum na
komunikaci mezi thready. Idealni jsou condition variables. Ve fci save
by se provadelo neco jako
dataQueue.notify() #dava najevo, ze v bufferu cekaji data
a run by cekal na data :
dataQueue.wait() # misto sleep. Pokud nejsou data usne, a probudi ho
dataQueue.notify()
Syntaxe condition variables asi neodpovida Jazyku. Nikdy jsem v P. cv
nepouzival a jsme linej to hledat, tak jen odhaduju.
2) Muze se stat, ze load uvidi nejaky starsi stav DB, protoze data,
ktera prisla jeste pred volanim load jsou v dataQueue a nejsou zapsana v
DB. To se da vyresit tak, ze load pocka, nez se vsechna doposud dosla
data zapisou a az pak cte z DB.
3) pokud neni list thread-safe, musi byt fce save synchronizovana.
Jeste vlastne 4 poznamka. Pokud bude komunikace probihat na urovni TCP a
ne treba pomoci RPC, je to jeste jednodussi. Zapis do soketu je
neblokujici (pokud se nezaplni buffery u prijemce), takze neni potreba
psat zadnou metodu save (jsme uz tak z toho middleware tak zblblej, ze
me tohle ocividny reseni ani nenapadlo). Predpokkladam, ze kazdej klient
bude mit svy otevreni TCP spojeni na serveru, takze pak staci akorat dat
neco jako select/poll na tahle spojeni, a ty co obsahuji nejaka data
postupne zpracovat (fce bude velmi podobna fci run()). Load se bude
delat bud zvlastnim spojenim, ktery navaze klient se serverem nebo
pomoci toho jiz existujiciho predanim nejakeho priznaku, ze jde o
operaci load.
kolisko
Další informace o konferenci Python