Transakcyjność w Django
Marcin Baran @Sharpekk
internetowykantor.pl
Zagnieżdżone transakcje
W pewnej aplikacji w Django..
@transaction.commit_on_success
def run():
Counter.objects.update(views= 1)
foo()
bar()
@tran...
#HEHESZKA ;-)
BEGIN
UPDATE "bar_counter" SET "views" = 1
UPDATE "bar_counter" SET "views" = 2
COMMIT
BEGIN
UPDATE "bar_cou...
@commit_on_success_unless_managed
@commit_on_success_unless_managed
def run():
Counter.objects.update(views= 1)
foo()
bar(...
Race Condition
Counter.objects.update(views= 1)

# Watęk A (w tym samym czasie)

# Watęk B (w tym samym czasie)
counter = ...
SELECT … FOR UPDATE
print Counter.objects.update(views= 1)

# Watęk A
counter = Counter.objects.
select_for_update().get(p...
SELECT … FOR UPDATE
● select_for_update tylko gdy będzie update
● w django od wersji 1.4
● lub na podstawie snippeta https...
Co jest nie tak z .save()
W pewnym frameworku podczas logowania…
# django/contrib/auth/models.py
def update_last_login(sen...
Dlaczego taki save jest zły ?
● brak kontroli nad tym co jest zapisywane do
bazy
● wydajność bazy danych
● nadpisywanie zm...
Model.save (the right way)
● Użycie save(update_fields=[..]) - trudne w
utrzymaniu!
● Użycie django-dirtyfields + własny s...
TransactionMiddleware (depercated)
It works like this: When a request starts, Django
starts a transaction. If the response...
TransactionMiddleware
def home(request):
Counter.objects.update(views= 1)
foo()
bar()
@transaction.commit_on_success
def f...
Testy w Django
● domyślnie TestCase nie działa
transakcyjnie!
● TransactionTestCase gdy testujemy kod
transakcyjny
● Jak t...
Pytania ?
kontakt@marcinbaran.be
Dziękuje za uwagę!
Upcoming SlideShare
Loading in …5
×

Transakcyjność w django

508 views

Published on

Published in: Technology, Design
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
508
On SlideShare
0
From Embeds
0
Number of Embeds
14
Actions
Shares
0
Downloads
1
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Transakcyjność w django

  1. 1. Transakcyjność w Django Marcin Baran @Sharpekk internetowykantor.pl
  2. 2. Zagnieżdżone transakcje
  3. 3. W pewnej aplikacji w Django.. @transaction.commit_on_success def run(): Counter.objects.update(views= 1) foo() bar() @transaction.commit_on_success def foo(): Counter.objects.update(views= 2) @transaction.commit_on_success def bar(): Counter.objects.update(views= 3) BEGIN UPDATE "bar_counter" SET "views" = 1 UPDATE "bar_counter" SET "views" = 2 COMMIT BEGIN UPDATE "bar_counter" SET "views" = 3 COMMIT
  4. 4. #HEHESZKA ;-) BEGIN UPDATE "bar_counter" SET "views" = 1 UPDATE "bar_counter" SET "views" = 2 COMMIT BEGIN UPDATE "bar_counter" SET "views" = 3 COMMIT
  5. 5. @commit_on_success_unless_managed @commit_on_success_unless_managed def run(): Counter.objects.update(views= 1) foo() bar() @commit_on_success_unless_managed def foo(): Counter.objects.update(views= 2) @commit_on_success_unless_managed def bar(): Counter.objects.update(views= 3) BEGIN UPDATE "bar_counter" SET "views" = 1 UPDATE "bar_counter" SET "views" = 2 UPDATE "bar_counter" SET "views" = 3 COMMIT
  6. 6. Race Condition Counter.objects.update(views= 1) # Watęk A (w tym samym czasie) # Watęk B (w tym samym czasie) counter = Counter.objects.get(pk= 1) counter = Counter.objects.get(pk= 1) counter.views += 1 counter.save() print Couter.objects.get(pk= 1).counter >>> 2 counter.views += 1 counter.save()
  7. 7. SELECT … FOR UPDATE print Counter.objects.update(views= 1) # Watęk A counter = Counter.objects. select_for_update().get(pk= 1) time.sleep( 100) counter.views += 1 counter.save() # Watęk B (+2 po Wątku B) counter = Counter.objects. select_for_update().get(pk= 1) counter.views += 1 counter.save()
  8. 8. SELECT … FOR UPDATE ● select_for_update tylko gdy będzie update ● w django od wersji 1.4 ● lub na podstawie snippeta https: //djangosnippets.org/snippets/2766/ w django < 1.4
  9. 9. Co jest nie tak z .save() W pewnym frameworku podczas logowania… # django/contrib/auth/models.py def update_last_login(sender, user, **kwargs): user.last_login = timezone.now() user.save() UPDATE `auth_user` SET `username` = %s, `first_name` = %s, `last_name` = %s, `email` = %s, `password` = %s, `is_staff` = %s, `is_active` = %s, `is_superuser` = %s, `last_login` = %s, `date_joined` = %s WHERE `auth_user`.`id` = %s
  10. 10. Dlaczego taki save jest zły ? ● brak kontroli nad tym co jest zapisywane do bazy ● wydajność bazy danych ● nadpisywanie zmian (cudzych) ● utrudnia debugowanie - serio! ● django.admin :(
  11. 11. Model.save (the right way) ● Użycie save(update_fields=[..]) - trudne w utrzymaniu! ● Użycie django-dirtyfields + własny save ● Własny update(field=val,..)
  12. 12. TransactionMiddleware (depercated) It works like this: When a request starts, Django starts a transaction. If the response is produced without problems, Django commits any pending transactions docs.djangoproject.com
  13. 13. TransactionMiddleware def home(request): Counter.objects.update(views= 1) foo() bar() @transaction.commit_on_success def foo(): Counter.objects.update(views= 2) @transaction.commit_on_success def bar(): Counter.objects.update(views= 3) raise ValueError BEGIN UPDATE "bar_counter" SET "views" = 1 UPDATE "bar_counter" SET "views" = 2 COMMIT BEGIN UPDATE "bar_counter" SET "views" = 3 ROLLBACK
  14. 14. Testy w Django ● domyślnie TestCase nie działa transakcyjnie! ● TransactionTestCase gdy testujemy kod transakcyjny ● Jak testować transakcyjność?
  15. 15. Pytania ? kontakt@marcinbaran.be
  16. 16. Dziękuje za uwagę!

×