Revision 2c21381e

b/snf-cyclades-app/synnefo/db/managers.py
27 27
# those of the authors and should not be interpreted as representing official
28 28
# policies, either expressed or implied, of GRNET S.A.
29 29

  
30
from django.db import connections
31 30
from django.db.models import Manager
32 31
from django.db.models.query import QuerySet
33 32

  
34 33

  
35
class ForUpdateManager(Manager):
36
    """Model manager implementing SELECT .. FOR UPDATE statement
37

  
38
    This manager implements select_for_update() method in order to use
39
    row-level locking in the database and guarantee exclusive access, since
40
    this method is only implemented in Django>=1.4.
41

  
42
    Non-blocking reads are not implemented, and each query including a row
43
    that is locked by another transaction will block until the lock is
44
    released. Also care must be taken in order to avoid deadlocks or retry
45
    transactions that abort due to deadlocks.
46

  
47
    Example:
48
        networks = Network.objects.select_for_update().filter(public=True)
49

  
50
    """
51

  
52
    def select_for_update(self, *args, **kwargs):
53
        return ForUpdateQuerySet(self.model, using=self.db)
54

  
55

  
56
class ForUpdateQuerySet(QuerySet):
57
    """QuerySet implmenting SELECT .. FOR UPDATE statement
58

  
59
    This QuerySet overrides filter and get methods in order to implement
60
    select_for_update() statement, by appending 'FOR UPDATE' to the end
61
    for the SQL query.
62

  
63
    """
64

  
65
    def filter(self, *args, **kwargs):
66
        query = super(ForUpdateQuerySet, self).filter(*args, **kwargs)
67
        return for_update(query)
68

  
69
    def get(self, *args, **kwargs):
70
        query = self.filter(*args, **kwargs)
71
        query = list(query)
72
        num = len(query)
73
        if num == 1:
74
            return query[0]
75
        if not num:
76
            raise self.model.DoesNotExist("%s matching query does not exist. "
77
                                          "Lookup parameters were %s" %
78
                                          (self.model._meta.object_name,
79
                                           kwargs))
80
        raise self.model.MultipleObjectsReturned(
81
            "get() returned more than one %s -- it returned %s! "
82
            "Lookup parameters were %s" %
83
            (self.model._meta.object_name, num, kwargs))
84

  
85

  
86
def for_update(query):
87
    """Rewrite query using SELECT .. FOR UPDATE."""
88
    if 'sqlite' in connections[query.db].settings_dict['ENGINE'].lower():
89
        # SQLite  does not support FOR UPDATE
90
        return query
91
    sql, params = query.query.get_compiler(query.db).as_sql()
92
    return query.model._default_manager.raw(sql.rstrip() + ' FOR UPDATE',
93
                                            params)
94

  
95

  
96
class ProtectedDeleteManager(ForUpdateManager):
34
class ProtectedDeleteManager(Manager):
97 35
    """Manager for protecting Backend deletion.
98 36

  
99 37
    Call Backend delete() method in order to prevent deletion
b/snf-cyclades-app/synnefo/db/models.py
41 41
from django.conf import settings as snf_settings
42 42
from aes_encrypt import encrypt_db_charfield, decrypt_db_charfield
43 43

  
44
from synnefo.db.managers import ForUpdateManager, ProtectedDeleteManager
44
from synnefo.db.managers import ProtectedDeleteManager
45 45
from synnefo.db import pools, fields
46 46

  
47 47
from synnefo.logic.rapi_pool import (get_rapi_client,
......
360 360
    task = models.CharField(max_length=64, null=True)
361 361
    task_job_id = models.BigIntegerField(null=True)
362 362

  
363
    objects = ForUpdateManager()
364

  
365 363
    def get_client(self):
366 364
        if self.backend:
367 365
            return self.backend.get_client()
......
530 528
    serial = models.ForeignKey(QuotaHolderSerial, related_name='network',
531 529
                               null=True)
532 530

  
533
    objects = ForUpdateManager()
534

  
535 531
    def __unicode__(self):
536 532
        return "<Network: %s>" % str(self.id)
537 533

  
......
738 734
    serial = models.ForeignKey(QuotaHolderSerial,
739 735
                               related_name="floating_ips", null=True)
740 736

  
741
    objects = ForUpdateManager()
742

  
743 737
    def __unicode__(self):
744 738
        return "<FIP: %s@%s>" % (self.ipv4, self.network.id)
745 739

  
......
759 753
    base = models.CharField(null=True, max_length=32)
760 754
    offset = models.IntegerField(null=True)
761 755

  
762
    objects = ForUpdateManager()
763

  
764 756
    class Meta:
765 757
        abstract = True
766 758

  

Also available in: Unified diff