Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 204 lines (167 sloc) 4.364 kb
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
1 module ActiveRecord
2 module ConnectionAdapters
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
3 class Transaction #:nodoc:
4 attr_reader :connection
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
5
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
6 def initialize(connection)
7 @connection = connection
67d8bb9 @wangjohn Reduced memory leak problem in transactions by lazily updating AR obj…
wangjohn authored
8 @state = TransactionState.new
26853e8 @wangjohn Refactored transaction state into its own object. Each transaction cr…
wangjohn authored
9 end
10
11 def state
12 @state
13 end
14 end
15
16 class TransactionState
67d8bb9 @wangjohn Reduced memory leak problem in transactions by lazily updating AR obj…
wangjohn authored
17 attr_accessor :parent
26853e8 @wangjohn Refactored transaction state into its own object. Each transaction cr…
wangjohn authored
18
19 VALID_STATES = Set.new([:committed, :rolledback, nil])
20
21 def initialize(state = nil)
22 @state = state
67d8bb9 @wangjohn Reduced memory leak problem in transactions by lazily updating AR obj…
wangjohn authored
23 @parent = nil
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
24 end
0355962 @wangjohn Created state for a transaction and added tests.
wangjohn authored
25
26 def committed?
26853e8 @wangjohn Refactored transaction state into its own object. Each transaction cr…
wangjohn authored
27 @state == :committed
0355962 @wangjohn Created state for a transaction and added tests.
wangjohn authored
28 end
29
30 def rolledback?
26853e8 @wangjohn Refactored transaction state into its own object. Each transaction cr…
wangjohn authored
31 @state == :rolledback
0355962 @wangjohn Created state for a transaction and added tests.
wangjohn authored
32 end
33
26853e8 @wangjohn Refactored transaction state into its own object. Each transaction cr…
wangjohn authored
34 def set_state(state)
35 if !VALID_STATES.include?(state)
36 raise ArgumentError, "Invalid transaction state: #{state}"
37 end
38 @state = state
39 end
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
40 end
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
41
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
42 class ClosedTransaction < Transaction #:nodoc:
43 def number
44 0
45 end
9296e69 @jonleighton Store the transaction number in the transaction object
jonleighton authored
46
392eeec @jonleighton Support for specifying transaction isolation level
jonleighton authored
47 def begin(options = {})
48 RealTransaction.new(connection, self, options)
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
49 end
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
50
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
51 def closed?
52 true
53 end
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
54
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
55 def open?
56 false
57 end
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
58
2805875 @jonleighton Move transaction joinability into the transaction object
jonleighton authored
59 def joinable?
60 false
61 end
62
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
63 # This is a noop when there are no open transactions
64 def add_record(record)
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
65 end
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
66 end
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
67
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
68 class OpenTransaction < Transaction #:nodoc:
60c88e6 @jonleighton Fix test
jonleighton authored
69 attr_reader :parent, :records
70 attr_writer :joinable
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
71
392eeec @jonleighton Support for specifying transaction isolation level
jonleighton authored
72 def initialize(connection, parent, options = {})
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
73 super connection
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
74
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
75 @parent = parent
76 @records = []
77 @finishing = false
392eeec @jonleighton Support for specifying transaction isolation level
jonleighton authored
78 @joinable = options.fetch(:joinable, true)
60c88e6 @jonleighton Fix test
jonleighton authored
79 end
80
55686dd @vipulnsward fix typos in AR. lots of them.
vipulnsward authored
81 # This state is necessary so that we correctly handle stuff that might
60c88e6 @jonleighton Fix test
jonleighton authored
82 # happen in a commit/rollback. But it's kinda distasteful. Maybe we can
83 # find a better way to structure it in the future.
84 def finishing?
85 @finishing
86 end
87
88 def joinable?
89 @joinable && !finishing?
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
90 end
9296e69 @jonleighton Store the transaction number in the transaction object
jonleighton authored
91
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
92 def number
93 if finishing?
94 parent.number
95 else
96 parent.number + 1
9296e69 @jonleighton Store the transaction number in the transaction object
jonleighton authored
97 end
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
98 end
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
99
392eeec @jonleighton Support for specifying transaction isolation level
jonleighton authored
100 def begin(options = {})
60c88e6 @jonleighton Fix test
jonleighton authored
101 if finishing?
102 parent.begin
103 else
392eeec @jonleighton Support for specifying transaction isolation level
jonleighton authored
104 SavepointTransaction.new(connection, self, options)
60c88e6 @jonleighton Fix test
jonleighton authored
105 end
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
106 end
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
107
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
108 def rollback
109 @finishing = true
110 perform_rollback
111 parent
112 end
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
113
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
114 def commit
115 @finishing = true
116 perform_commit
117 parent
118 end
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
119
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
120 def add_record(record)
67d8bb9 @wangjohn Reduced memory leak problem in transactions by lazily updating AR obj…
wangjohn authored
121 if record.has_transactional_callbacks?
122 records << record
123 else
124 record.set_transaction_state(@state)
125 end
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
126 end
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
127
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
128 def rollback_records
26853e8 @wangjohn Refactored transaction state into its own object. Each transaction cr…
wangjohn authored
129 @state.set_state(:rolledback)
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
130 records.uniq.each do |record|
131 begin
132 record.rolledback!(parent.closed?)
133 rescue => e
134 record.logger.error(e) if record.respond_to?(:logger) && record.logger
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
135 end
136 end
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
137 end
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
138
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
139 def commit_records
26853e8 @wangjohn Refactored transaction state into its own object. Each transaction cr…
wangjohn authored
140 @state.set_state(:committed)
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
141 records.uniq.each do |record|
142 begin
143 record.committed!
144 rescue => e
145 record.logger.error(e) if record.respond_to?(:logger) && record.logger
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
146 end
147 end
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
148 end
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
149
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
150 def closed?
151 false
152 end
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
153
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
154 def open?
155 true
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
156 end
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
157 end
6d9e6b3 @jonleighton Split Open into Real and Savepoint
jonleighton authored
158
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
159 class RealTransaction < OpenTransaction #:nodoc:
392eeec @jonleighton Support for specifying transaction isolation level
jonleighton authored
160 def initialize(connection, parent, options = {})
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
161 super
392eeec @jonleighton Support for specifying transaction isolation level
jonleighton authored
162
163 if options[:isolation]
164 connection.begin_isolated_db_transaction(options[:isolation])
165 else
166 connection.begin_db_transaction
167 end
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
168 end
6d9e6b3 @jonleighton Split Open into Real and Savepoint
jonleighton authored
169
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
170 def perform_rollback
171 connection.rollback_db_transaction
172 rollback_records
173 end
6d9e6b3 @jonleighton Split Open into Real and Savepoint
jonleighton authored
174
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
175 def perform_commit
176 connection.commit_db_transaction
177 commit_records
6d9e6b3 @jonleighton Split Open into Real and Savepoint
jonleighton authored
178 end
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
179 end
6d9e6b3 @jonleighton Split Open into Real and Savepoint
jonleighton authored
180
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
181 class SavepointTransaction < OpenTransaction #:nodoc:
392eeec @jonleighton Support for specifying transaction isolation level
jonleighton authored
182 def initialize(connection, parent, options = {})
183 if options[:isolation]
184 raise ActiveRecord::TransactionIsolationError, "cannot set transaction isolation in a nested transaction"
185 end
186
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
187 super
188 connection.create_savepoint
189 end
6d9e6b3 @jonleighton Split Open into Real and Savepoint
jonleighton authored
190
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
191 def perform_rollback
192 connection.rollback_to_savepoint
193 rollback_records
194 end
6d9e6b3 @jonleighton Split Open into Real and Savepoint
jonleighton authored
195
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
196 def perform_commit
67d8bb9 @wangjohn Reduced memory leak problem in transactions by lazily updating AR obj…
wangjohn authored
197 @state.set_state(:committed)
198 @state.parent = parent.state
a6fbddb @jonleighton Alter the naming structure a bit
jonleighton authored
199 connection.release_savepoint
6d9e6b3 @jonleighton Split Open into Real and Savepoint
jonleighton authored
200 end
02f25a2 @jonleighton Start to tease out transaction handling into a state machine
jonleighton authored
201 end
202 end
203 end
Something went wrong with that request. Please try again.