If you didn't have any delayed job workers and submitted multiple reports for the same node, when you started multiple workers they would each try to create the same node. There was no database constraint to prevent this, and Rails validation was worthless since the workers were running their own transactions.
By adding the database constraint the insert will fail for workers who don't create the node first. Rather than discard the reports and create an error for those workers, we're retrying the report submission 3 times before putting in a DelayedJobFailure entry. Reviewed-by: Jacob Helwig <ja...@puppetlabs.com> Signed-off-by: Matt Robinson <m...@puppetlabs.com> --- Local-branch: ticket/1.2rc/8686 app/models/report.rb | 8 ++- ...28195829_add_node_host_uniqueness_constraint.rb | 15 ++++++ spec/models/report_spec.rb | 54 ++++++++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20110728195829_add_node_host_uniqueness_constraint.rb diff --git a/app/models/report.rb b/app/models/report.rb index 09c2b9e..95a5809 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -84,8 +84,12 @@ class Report < ActiveRecord::Base File.unlink(report_file) if options[:delete] return report rescue Exception => e - DelayedJobFailure.create!(:summary => "Importing report #{File.basename(report_file)}", - :details => e.to_s) + retries ||= 3 + retry if (retries -= 1) > 0 + DelayedJobFailure.create!( + :summary => "Importing report #{File.basename(report_file)}", + :details => e.to_s + ) return nil end diff --git a/db/migrate/20110728195829_add_node_host_uniqueness_constraint.rb b/db/migrate/20110728195829_add_node_host_uniqueness_constraint.rb new file mode 100644 index 0000000..dead775 --- /dev/null +++ b/db/migrate/20110728195829_add_node_host_uniqueness_constraint.rb @@ -0,0 +1,15 @@ +class AddNodeHostUniquenessConstraint < ActiveRecord::Migration + def self.up + execute <<-SQL + ALTER TABLE nodes + ADD CONSTRAINT uc_node_name UNIQUE (name) + SQL + end + + def self.down + execute <<-SQL + ALTER TABLE nodes + DROP INDEX uc_node_name + SQL + end +end diff --git a/spec/models/report_spec.rb b/spec/models/report_spec.rb index cf08f11..06dda9c 100644 --- a/spec/models/report_spec.rb +++ b/spec/models/report_spec.rb @@ -397,6 +397,60 @@ describe Report do end end + describe "#create_from_yaml_file" do + describe "when create_from_yaml is successful" do + before do + File.expects(:read).with('/tmp/foo').returns(@report_yaml) + Report.expects(:create_from_yaml).returns('i can haz report') + end + + it "should call create_from_yaml on the file passed in and return the results" do + Report.create_from_yaml_file('/tmp/foo').should == "i can haz report" + end + + it "should delete the file if delete option is specified" do + File.expects(:unlink).with('/tmp/foo') + Report.create_from_yaml_file('/tmp/foo', :delete => true) + end + end + + + describe "when create_from_yaml fails" do + before do + File.expects(:read).at_least_once.with('/tmp/foo').returns(@report_yaml) + end + + it "not unlink the file if create_from_yaml fails" do + File.expects(:unlink).with('/tmp/foo').never + Report.expects(:create_from_yaml).raises(ActiveRecord::StatementInvalid) + Report.create_from_yaml_file('/tmp/foo', :delete => true) + end + + it "should retry 3 times in the case of a failure" do + Report.expects(:create_from_yaml).times(3). + raises(ActiveRecord::StatementInvalid).then. + raises(ActiveRecord::StatementInvalid).then. + returns("FINALLY!") + + Report.create_from_yaml_file('/tmp/foo').should == "FINALLY!" + end + + it "should create a DelayedJobFailure after 3 failures and return nil" do + Report.expects(:create_from_yaml).times(3). + raises(ActiveRecord::StatementInvalid).then. + raises(ActiveRecord::StatementInvalid).then. + raises(ActiveRecord::StatementInvalid).then. + returns("Sir not appearing in this expectation") + + Report.create_from_yaml_file('/tmp/foo').should == nil + + DelayedJobFailure.count.should == 1 + DelayedJobFailure.first.summary.should == 'Importing report foo' + DelayedJobFailure.first.details.should == 'ActiveRecord::StatementInvalid' + end + end + end + describe "When destroying" do it "should destroy all dependent model objects" do -- 1.7.3.1 -- You received this message because you are subscribed to the Google Groups "Puppet Developers" group. To post to this group, send email to puppet-dev@googlegroups.com. To unsubscribe from this group, send email to puppet-dev+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/puppet-dev?hl=en.