Just quick query that's been bugging me this afternoon:
Is .save / .save! synchronous?
i.e. After I call that line - should the database row be created in
the DB before executing the next line?
I'm just puzzled as to why step by step debugging, I don't see the DB
row created at all even several lines afterwards.
Is this because the .save! is inside a transaction block, so Rails on
purpose does not actually do any DB actions until the very end of the
transaction block?
(I always though it committed, and then does a rollback if the
transaction fails... meaning I should still see that DB entry in there
after the .save line is executed irrespective of the transaction
block)
Thanks,
Chris
Yes they are synchronous, one thing which might stop it saving is validations failing, .save should return true if it works, otherwise check .errors
-IV
> --
> You received this message because you are subscribed to the Google Groups "Ruby or Rails Oceania" group.
> To post to this group, send email to rails-...@googlegroups.com.
> To unsubscribe from this group, send email to rails-oceani...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/rails-oceania?hl=en.
>
Hi all,
Just quick query that's been bugging me this afternoon:
Is .save / .save! synchronous?
i.e. After I call that line - should the database row be created in
the DB before executing the next line?
I'm just puzzled as to why step by step debugging, I don't see the DB
row created at all even several lines afterwards.
Is this because the .save! is inside a transaction block, so Rails on
purpose does not actually do any DB actions until the very end of the
transaction block?
�Ben
And until it's committed, if you try to read it, you will *block* or
fail on a timeout/deadlock.
What *won't* happen is that you get to see the data in either the
before or after state.
Clifford Heath.
Clarification from my last post: the reason that you might not see the new row even from WITHIN the same transaction is because the result of a query is essentially cached. I.e. if you took out the first Task.all count from my example, the second one WOULD include the new row.
No, I mean that you can's see the change from any other transaction,
say a
command-line SQL command or a database query tool. You'll only see the
results from within the transaction that created them.
Clifford Heath.
> It's definitely a synchronous call, in that the database has received the
> INSERT/UPDATE command before #save[!] returns.
>
> But, if you're in a transaction you won't be able to see the data in any
> other context until the database receives COMMIT when the transaction block
> closes.
So just confirming:
Suppose i Have
ARObject, ARObjectObserver, and ARObjectHistory (which is an audit
recording all history changes of an ARObject)
So in rough pseudo code,
#==ARObject
class ARObject < ActiveRecord::Base
has_many ARObjectHistories
def action()
ARObject.transaction do
self.status = UPDATED_STATUS
self.save!
self.ar_object_histories(true).find(# latest one just created)
# Can't find it... Even in NaviCat, even with the reload of
associations...
end
end
def add_history!(blah)
obj_hist = ARObjectHistory.new(blah)
obj_hist.ar_object = self
obj_hist.save!
end
end
# ==ARObjectObserver
class ARObjectObserver < ActiveRecord::Observer
def before_update(record)
blah = # History info etc..
record.add_history!(blah)
end
end
Is that right? I won't be able to see the latest history just created
from within the same transaction until it completes?
Thanks
Chris
P.s. yes to using MySQL :(
> Is that right? I won't be able to see the latest history just created
> from within the same transaction until it completes?
Not quite, you won't be able to see it in *other* transactions.
Running the above script says you can see your own inserts, as long as
you explicitly reload your association (since it gets cached). There are
circumstances where you won't be able to see them across transactions,
but that's perhaps out of scope for this question.
This also gives you a minimal test case to play around with to see
exactly when, what and where is executed.
Cheers,
Xavier
[1]
require 'logger'
require 'active_record'
require 'mysql2'
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Base.configurations = {'development' => {'adapter' =>
'mysql2', 'user' => 'root', 'database' => 'test'}}
ActiveRecord::Base.establish_connection('development')
ActiveRecord::Schema.define :version => 0 do
create_table :ar_objects, :force => true do |t|
t.string :status
end
create_table :ar_object_histories, :force => true do |t|
t.references :ar_object
t.string :desc
end
end
class ArObject < ActiveRecord::Base
has_many :ar_object_histories
def action
transaction do
puts ar_object_histories.inspect
self.status = "actioned"
self.save
add_history!("Actioning")
puts ar_object_histories.inspect
puts ar_object_histories(true).inspect
end
end
def add_history!(desc)
obj_hist = ArObjectHistory.new(:desc => desc)
obj_hist.ar_object = self
obj_hist.save!
end
end
class ArObjectHistory < ActiveRecord::Base
belongs_to :ar_object
end
class ArObjectObserver < ActiveRecord::Observer
observe :ar_object
def before_update(record)
record.add_history!("observer yeah")
end
end
ActiveRecord::Base.observers = [ArObjectObserver]
ActiveRecord::Base.instantiate_observers
obj = ArObject.create!(:status => 'new')
obj.action
So in rough pseudo code,
In real code:
https://gist.github.com/8e2403de7ad332c3c20b [1]
Running the above script says you can see your own inserts, as long as you explicitly reload your association (since it gets cached). There are circumstances where you won't be able to see them across transactions, but that's perhaps out of scope for this question.
This also gives you a minimal test case to play around with to see exactly when, what and where is executed.
Look at repeatable read here:
http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html
(unless I'm just misinterpreting things)
Simon.
On 2/02/11 10:16 AM, Simon Russell wrote:
> I'm reasonably sure that depending on your MySQL transaction isolation
> level, if you run the same query twice inside a transaction, it'll
> return the same results -- even if you've inserted data that should
> appear in the second query.
>
> Look at repeatable read here:
> http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html
>
> (unless I'm just misinterpreting things)
You are not quite correct - you will always see data that you have
inserted, regardless of your isolation level [1].
Isolation levels are only relevant when talking about isolation between
different transactions.
Also repeatable read does allow you to see data from other transactions
in some circumstances (phantom reads), for full protection you need to
bump up to SERIALIZABLE (side note: this is why acts_as_list is broken
by default with mysql + rails). Please see the blog post I linked up in
a previous reply for more detail.
I encourage everyone to try out these different scenarios using minimal
test cases, it really helps you get your head around it.
Cheers,
Xavier
[1]
mysql> SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM users;
+----+
| id |
+----+
| 1 |
+----+
1 row in set (0.00 sec)
mysql> INSERT INTO users VALUES (2);
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM users;
+----+
| id |
+----+
| 1 |
| 2 |
+----+
2 rows in set (0.00 sec)
mysql> ROLLBACK;
Query OK, 0 rows affected, 1 warning (0.00 sec)
http://www.dbisyourfriend.com/
Xavier