[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