$30 off During Our Annual Pro Sale. View Details »

Oops ho sovrascritto le tue modifiche

Oops ho sovrascritto le tue modifiche

Mai capitato di sovrascrivere dati involontariamente dall’interfaccia web o tramite il pannello di amministrazione di una applicazione Django? In questo talk vedremo qualche strategia per ovviare al problema.

Vedremo delle strategie generiche come salvare un log delle nostre modifiche, fare il locking delle righe del database oppure usare la concorrenza ottimistica. Quindi vedremo una strategia specifica di PostgreSQL implementando un sistema di concorrenza ottimistica per le modifiche fatte da utenti tramite il pannello di amministrazione di Django.

Riccardo Magliocchetti

June 06, 2022
Tweet

More Decks by Riccardo Magliocchetti

Other Decks in Programming

Transcript

  1. Oops ho sovrascritto le tue
    modifiche
    Riccardo Magliocchetti

    View Slide

  2. whoami
    Cat herder @
    Dev @
    Maieutical Labs
    Djungle Studio

    View Slide

  3. Mai sovrascritto dati involontariamente
    dall'admin di Django?

    View Slide

  4. Capita solo da admin?
    # processo 1 # processo 2

    t = Talk.objects.get(

    name=”oops”

    )

    t.done = True

    Talk.objects.update(

    name=”more oops”

    )

    t.save()

    View Slide

  5. save() in SQL
    UPDATE "pycon_talk"

    SET "name" = 'oops', "done" = true

    WHERE "pycon_talk"."id" = 1

    View Slide

  6. In questo caso è facile
    # processo 1 # processo 2

    t = Talk.objects.get(

    name=”oops”

    )

    t.done = True

    Talk.objects.update(

    name=”more oops”

    )

    t.save(update_fields=["done"])

    View Slide

  7. save(update_fields=[...]) in SQL
    UPDATE "pycon_talk"

    SET "done" = true

    WHERE "pycon_talk"."id" = 1

    View Slide

  8. 3 possibili strategie

    View Slide

  9. 1. Salvare un log delle modifiche

    View Slide

  10. 1. Salvare un log delle modifiche
    2. Locking delle righe

    View Slide

  11. 1. Salvare un log delle modifiche
    2. Locking delle righe
    3. Concorrenza ottimistica

    View Slide

  12. Salvare un log delle
    modifiche

    View Slide

  13. Ad ogni aggiornamento o cancellazione di un modello
    serializzo la versione precedente con un nome di
    versione e dei metadati.
    Con lo storico delle modifiche è possibile provare a
    ripristinare i dati.

    View Slide

  14. Applicazioni per Django
    django-reversion
    django-reversion-rest-framework
    django-simple-history
    django-auditlog

    View Slide

  15. Salvare un log delle modifiche: recap
    abbiamo un log di audit incluso
    abbiamo una buona chance di ripristinare i dati

    View Slide

  16. Locking delle righe
    o concorrenza pessimistica

    View Slide

  17. select_for_update
    SELECT ... FOR UPDATE

    View Slide

  18. select_for_update
    blocca la transazione per default
    DatabaseError con nowait=True

    View Slide

  19. select_for_update
    talks = (

    Talk.objects.select_for_update()

    .filter(day=Talk.Day.SUNDAY)

    )

    with transaction.atomic():

    for talk in talks:

    ...

    View Slide

  20. Locking delle righe: recap
    risolviamo corse critiche sull'aggiornamento
    non possiamo tenere il lock troppo a lungo per
    mantenere concorrenza

    View Slide

  21. Concorrenza ottimistica

    View Slide

  22. Concorrenza ottimistica
    Aggiorno solo quello che mi aspetto senza prendere
    lock

    View Slide

  23. Concorrenza ottimistica in SQL
    UPDATE pycon_talk

    SET version = version + 1, ...

    WHERE id = %s AND version = %s

    View Slide

  24. Implementazioni per Django
    django-optimistic-lock
    django-concurrency

    View Slide

  25. Concorrenza ottimistica: recap
    non prendiamo lock
    dobbiamo gestire il caso in cui non vengono
    aggiornate righe

    View Slide

  26. Concorrenza in PostgreSQL
    MVCC (Multi-Version Concurrency Control):

    alla modifica di una riga si crea una nuova riga con id
    di transazione maggiore e poi il VACUUM elimina
    quelle vecchie

    View Slide

  27. Concorrenza in PostgreSQL
    L'id di transazione si chiama xmin:
    SELECT xmin from pycon_talk LIMIT 1;

    View Slide

  28. xmin in Django
    from django.db import models

    from django.db.models import Expression

    class XMin(Expression):

    output_field = models.PositiveIntegerField()

    def as_postgresql(self, compiler, connection):

    return f'"{compiler.query.base_table}"."xmin"', ()
    Talk.objects.all().annotate(row_version=XMin())

    View Slide

  29. Bonus: una piccola applicazione

    View Slide

  30. Problema: corsa critica tra admin e
    sistema ad eventi
    Condizioni
    basta impedire l'override di modifiche da admin
    basta che funzioni su PostgreSQL

    View Slide

  31. riduce la possibilità di sovrascrivere dati tramite
    l'admin
    implementa concorrenza ottimistica su PostgreSQL
    senza modificare i modelli
    semplice: due mixin da usare, 62 righe in totale
    django-optimistic-admin-pg

    View Slide

  32. from optimisticadmin.mixins import OptimisticAdminModelFormM
    class TalkForm(OptimisticAdminModelFormMixin, forms.ModelFor
    row_version = forms.IntegerField(

    required=False, widget=forms.HiddenInput()

    )
    from optimisticadmin.mixins import OptimisticAdminMixin

    from .forms import TalkForm

    @admin.register(Talk)

    class TalkAdmin(OptimisticAdminMixin, admin.ModelAdmin):

    form = TalkForm

    View Slide

  33. Risultato
    Non è possibile salvare se row_version è stata
    aggiornata nel frattempo

    View Slide

  34. Conclusioni
    tenere un log delle modifiche per evitare di perdere
    dati
    usare dei lock per serializzare gli aggiornamenti
    implementare un sistema di concorrenza
    ottimistica

    View Slide

  35. Conclusioni
    strategia rischio tradeoff quando
    Log modifiche no robusta audit, recovery
    Locking righe no robusta assegnazione risorse,

    soldi :)
    Concorrenza
    ottimistica
    si perf poco rischio di collisione
    Una combinazione

    delle precedenti

    View Slide

  36. Grazie! Domande?
    github.com/xrmx
    speakerdeck.com/xrmx

    View Slide

  37. Riferimenti
    con implementazione semplice di
    concorrenza ottimistica su PostgreSQL ed API con
    Django Rest Framework
    di Haki Benita su concorrenza pessimistica
    vs ottimistica
    su concorrenza MVCC di
    PostgreSQL
    MVCC PostgreSQL
    Articolo
    articolo
    articolo Heroku
    Documentazione ufficiale

    View Slide