Statistics
| Branch: | Tag: | Revision:

root / snf-common / synnefo / lib / db / xctx.py @ 11ed5cdc

History | View | Annotate | Download (4.9 kB)

1 d55d4f34 Giorgos Korfiatis
# Copyright 2013 GRNET S.A. All rights reserved.
2 d55d4f34 Giorgos Korfiatis
#
3 d55d4f34 Giorgos Korfiatis
# Redistribution and use in source and binary forms, with or
4 d55d4f34 Giorgos Korfiatis
# without modification, are permitted provided that the following
5 d55d4f34 Giorgos Korfiatis
# conditions are met:
6 d55d4f34 Giorgos Korfiatis
#
7 d55d4f34 Giorgos Korfiatis
#   1. Redistributions of source code must retain the above
8 d55d4f34 Giorgos Korfiatis
#      copyright notice, this list of conditions and the following
9 d55d4f34 Giorgos Korfiatis
#      disclaimer.
10 d55d4f34 Giorgos Korfiatis
#
11 d55d4f34 Giorgos Korfiatis
#   2. Redistributions in binary form must reproduce the above
12 d55d4f34 Giorgos Korfiatis
#      copyright notice, this list of conditions and the following
13 d55d4f34 Giorgos Korfiatis
#      disclaimer in the documentation and/or other materials
14 d55d4f34 Giorgos Korfiatis
#      provided with the distribution.
15 d55d4f34 Giorgos Korfiatis
#
16 d55d4f34 Giorgos Korfiatis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 d55d4f34 Giorgos Korfiatis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 d55d4f34 Giorgos Korfiatis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 d55d4f34 Giorgos Korfiatis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 d55d4f34 Giorgos Korfiatis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 d55d4f34 Giorgos Korfiatis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 d55d4f34 Giorgos Korfiatis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 d55d4f34 Giorgos Korfiatis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 d55d4f34 Giorgos Korfiatis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 d55d4f34 Giorgos Korfiatis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 d55d4f34 Giorgos Korfiatis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 d55d4f34 Giorgos Korfiatis
# POSSIBILITY OF SUCH DAMAGE.
28 d55d4f34 Giorgos Korfiatis
#
29 d55d4f34 Giorgos Korfiatis
# The views and conclusions contained in the software and
30 d55d4f34 Giorgos Korfiatis
# documentation are those of the authors and should not be
31 d55d4f34 Giorgos Korfiatis
# interpreted as representing official policies, either expressed
32 d55d4f34 Giorgos Korfiatis
# or implied, of GRNET S.A.
33 d55d4f34 Giorgos Korfiatis
34 d55d4f34 Giorgos Korfiatis
from django.db import transaction
35 d55d4f34 Giorgos Korfiatis
36 d55d4f34 Giorgos Korfiatis
# USAGE
37 d55d4f34 Giorgos Korfiatis
# =====
38 d55d4f34 Giorgos Korfiatis
# @transaction_context()
39 d55d4f34 Giorgos Korfiatis
# def a_view(args, ctx=None):
40 d55d4f34 Giorgos Korfiatis
#     ...
41 d55d4f34 Giorgos Korfiatis
#     if ctx:
42 d55d4f34 Giorgos Korfiatis
#         ctx.mark_rollback()
43 d55d4f34 Giorgos Korfiatis
#     ...
44 d55d4f34 Giorgos Korfiatis
#     return http response
45 d55d4f34 Giorgos Korfiatis
#
46 7d95385b Giorgos Korfiatis
# OR (more cleanly)
47 d55d4f34 Giorgos Korfiatis
#
48 d55d4f34 Giorgos Korfiatis
# def a_view(args):
49 d55d4f34 Giorgos Korfiatis
#     with transaction_context() as ctx:
50 d55d4f34 Giorgos Korfiatis
#         ...
51 d55d4f34 Giorgos Korfiatis
#         ctx.mark_rollback()
52 d55d4f34 Giorgos Korfiatis
#         ...
53 d55d4f34 Giorgos Korfiatis
#         return http response
54 d55d4f34 Giorgos Korfiatis
55 d55d4f34 Giorgos Korfiatis
def transaction_context(**kwargs):
56 d55d4f34 Giorgos Korfiatis
    return TransactionHandler(ctx=TransactionContext, **kwargs)
57 d55d4f34 Giorgos Korfiatis
58 d55d4f34 Giorgos Korfiatis
59 d55d4f34 Giorgos Korfiatis
class TransactionContext(object):
60 26e15cff Giorgos Korfiatis
    def __init__(self, **kwargs):
61 d55d4f34 Giorgos Korfiatis
        self._rollback = False
62 d55d4f34 Giorgos Korfiatis
63 d55d4f34 Giorgos Korfiatis
    def mark_rollback(self):
64 d55d4f34 Giorgos Korfiatis
        self._rollback = True
65 d55d4f34 Giorgos Korfiatis
66 d55d4f34 Giorgos Korfiatis
    def is_marked_rollback(self):
67 d55d4f34 Giorgos Korfiatis
        return self._rollback
68 d55d4f34 Giorgos Korfiatis
69 d55d4f34 Giorgos Korfiatis
    def postprocess(self):
70 d55d4f34 Giorgos Korfiatis
        pass
71 d55d4f34 Giorgos Korfiatis
72 d55d4f34 Giorgos Korfiatis
73 7d95385b Giorgos Korfiatis
class TransactionHandler(object):
74 26e15cff Giorgos Korfiatis
    def __init__(self, ctx=None, allow_postprocess=True, using=None, **kwargs):
75 26e15cff Giorgos Korfiatis
        self.using             = using
76 26e15cff Giorgos Korfiatis
        self.db                = (using if using is not None
77 26e15cff Giorgos Korfiatis
                                  else transaction.DEFAULT_DB_ALIAS)
78 26e15cff Giorgos Korfiatis
        self.ctx_class         = ctx
79 26e15cff Giorgos Korfiatis
        self.ctx_kwargs        = kwargs
80 26e15cff Giorgos Korfiatis
        self.allow_postprocess = allow_postprocess
81 d55d4f34 Giorgos Korfiatis
82 d55d4f34 Giorgos Korfiatis
    def __call__(self, func):
83 d55d4f34 Giorgos Korfiatis
        def wrap(*args, **kwargs):
84 d55d4f34 Giorgos Korfiatis
            ctx = self.__enter__()
85 d55d4f34 Giorgos Korfiatis
            kwargs['ctx'] = ctx
86 d55d4f34 Giorgos Korfiatis
            typ = value = trace = None
87 d55d4f34 Giorgos Korfiatis
            result = None
88 d55d4f34 Giorgos Korfiatis
            try:
89 d55d4f34 Giorgos Korfiatis
                result = func(*args, **kwargs)
90 d55d4f34 Giorgos Korfiatis
            except Exception as e:
91 d55d4f34 Giorgos Korfiatis
                typ = type(e)
92 d55d4f34 Giorgos Korfiatis
                value = e
93 d55d4f34 Giorgos Korfiatis
                trace = None
94 d55d4f34 Giorgos Korfiatis
            finally:
95 d55d4f34 Giorgos Korfiatis
                silent = self.__exit__(typ, value, trace)
96 d55d4f34 Giorgos Korfiatis
                if not silent and value:
97 d55d4f34 Giorgos Korfiatis
                    raise value
98 d55d4f34 Giorgos Korfiatis
            return result
99 d55d4f34 Giorgos Korfiatis
        return wrap
100 d55d4f34 Giorgos Korfiatis
101 d55d4f34 Giorgos Korfiatis
    def __enter__(self):
102 d55d4f34 Giorgos Korfiatis
        db = self.db
103 d55d4f34 Giorgos Korfiatis
        transaction.enter_transaction_management(using=db)
104 d55d4f34 Giorgos Korfiatis
        transaction.managed(True, using=db)
105 d55d4f34 Giorgos Korfiatis
        self.ctx = self.ctx_class(self.ctx_kwargs)
106 d55d4f34 Giorgos Korfiatis
        return self.ctx
107 d55d4f34 Giorgos Korfiatis
108 d55d4f34 Giorgos Korfiatis
    def __exit__(self, type, value, traceback):
109 d55d4f34 Giorgos Korfiatis
        db = self.db
110 26e15cff Giorgos Korfiatis
        trigger_postprocess = False
111 d55d4f34 Giorgos Korfiatis
        try:
112 d55d4f34 Giorgos Korfiatis
            if value is not None: # exception
113 d55d4f34 Giorgos Korfiatis
                if transaction.is_dirty(using=db):
114 d55d4f34 Giorgos Korfiatis
                    transaction.rollback(using=db)
115 d55d4f34 Giorgos Korfiatis
            else:
116 d55d4f34 Giorgos Korfiatis
                if transaction.is_dirty(using=db):
117 d55d4f34 Giorgos Korfiatis
                    if self.ctx.is_marked_rollback():
118 d55d4f34 Giorgos Korfiatis
                        transaction.rollback(using=db)
119 d55d4f34 Giorgos Korfiatis
                    else:
120 d55d4f34 Giorgos Korfiatis
                        try:
121 d55d4f34 Giorgos Korfiatis
                            transaction.commit(using=db)
122 d55d4f34 Giorgos Korfiatis
                        except:
123 d55d4f34 Giorgos Korfiatis
                            transaction.rollback(using=db)
124 d55d4f34 Giorgos Korfiatis
                            raise
125 d55d4f34 Giorgos Korfiatis
                        else:
126 26e15cff Giorgos Korfiatis
                            trigger_postprocess = True
127 11ed5cdc Giorgos Korfiatis
128 11ed5cdc Giorgos Korfiatis
                # postprocess,
129 11ed5cdc Giorgos Korfiatis
                # even if there was nothing to commit
130 11ed5cdc Giorgos Korfiatis
                # as long as it's not marked for rollback
131 11ed5cdc Giorgos Korfiatis
                elif not self.ctx.is_marked_rollback():
132 26e15cff Giorgos Korfiatis
                    trigger_postprocess = True
133 d55d4f34 Giorgos Korfiatis
        finally:
134 d55d4f34 Giorgos Korfiatis
            transaction.leave_transaction_management(using=db)
135 26e15cff Giorgos Korfiatis
136 26e15cff Giorgos Korfiatis
            # checking allow_postprocess is needed
137 26e15cff Giorgos Korfiatis
            # in order to avoid endless recursion
138 26e15cff Giorgos Korfiatis
            if trigger_postprocess and self.allow_postprocess:
139 26e15cff Giorgos Korfiatis
                with TransactionHandler(ctx=self.ctx_class,
140 26e15cff Giorgos Korfiatis
                                        allow_postprocess=False,
141 26e15cff Giorgos Korfiatis
                                        using=self.using,
142 26e15cff Giorgos Korfiatis
                                        **self.ctx_kwargs):
143 26e15cff Giorgos Korfiatis
                    self.ctx.postprocess()