# HG changeset patch # User jittat # Date 2008-02-19 01:22:19 # Node ID c1451a3676141df057e3294533416761420c2bad # Parent cf924ea7dc0a8d539579b327ea8a91926f84ca51 add some lock in task git-svn-id: http://theory.cpe.ku.ac.th/grader/web/trunk@55 6386c4cd-e34a-4fa8-8920-d93eb39b512e diff --git a/app/models/task.rb b/app/models/task.rb --- a/app/models/task.rb +++ b/app/models/task.rb @@ -1,2 +1,40 @@ class Task < ActiveRecord::Base + + STATUS_GRADING = 0 + STATUS_INQUEUE = 1 + STATUS_COMPLETE = 2 + + def status_inqueue + self.status = Task::STATUS_INQUEUE + end + + def status_grading + self.status = Task::STATUS_GRADING + end + + def status_complete + self.status = Task::STATUS_COMPLETE + end + + def self.get_inqueue_and_change_status(status) + task = nil + begin + Task.transaction do + task = Task.find(:first, + :order => "created_at", + :conditions => {:status=> Task::STATUS_INQUEUE}, + :lock => true) + if task!=nil + task.status = status + task.save! + end + end + + rescue + task = nil + + end + task + end + end diff --git a/db/migrate/011_add_language_ext.rb b/db/migrate/011_add_language_ext.rb --- a/db/migrate/011_add_language_ext.rb +++ b/db/migrate/011_add_language_ext.rb @@ -1,7 +1,8 @@ class AddLanguageExt < ActiveRecord::Migration def self.up add_column :languages, :ext, :string, :limit => 10 - + + Language.reset_column_information langs = Language.find(:all) langs.each do |l| l.ext = l.name diff --git a/db/migrate/015_add_status_to_tasks.rb b/db/migrate/015_add_status_to_tasks.rb new file mode 100644 --- /dev/null +++ b/db/migrate/015_add_status_to_tasks.rb @@ -0,0 +1,17 @@ +class AddStatusToTasks < ActiveRecord::Migration + def self.up + add_column :tasks, :status, :integer + add_column :tasks, :updated_at, :datetime + + Task.reset_column_information + Task.find(:all).each do |task| + task.status_complete + task.save + end + end + + def self.down + remove_column :tasks, :updated_at + remove_column :tasks, :status + end +end diff --git a/db/schema.rb b/db/schema.rb --- a/db/schema.rb +++ b/db/schema.rb @@ -9,7 +9,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 14) do +ActiveRecord::Schema.define(:version => 15) do create_table "grader_processes", :force => true do |t| t.string "ip", :limit => 20 @@ -89,6 +89,8 @@ create_table "tasks", :force => true do |t| t.integer "submission_id" t.datetime "created_at" + t.integer "status" + t.datetime "updated_at" end create_table "users", :force => true do |t| diff --git a/test/concurrent/task_test_get.rb b/test/concurrent/task_test_get.rb new file mode 100644 --- /dev/null +++ b/test/concurrent/task_test_get.rb @@ -0,0 +1,16 @@ +ENV["RAILS_ENV"] = "test" +require File.expand_path(File.dirname(__FILE__) + "/../../config/environment") + +def take_wait_return + task = Task.get_inqueue_and_change_status(Task::STATUS_GRADING) + sleep (rand)/10.0 + task.status_complete + task.save! +end + +n = 300 + +n.times do |i| + take_wait_return + puts i +end diff --git a/test/concurrent/task_test_setup.rb b/test/concurrent/task_test_setup.rb new file mode 100644 --- /dev/null +++ b/test/concurrent/task_test_setup.rb @@ -0,0 +1,18 @@ +ENV["RAILS_ENV"] = "test" +require File.expand_path(File.dirname(__FILE__) + "/../../config/environment") + +def clear_all_tasks + Task.find(:all).each do |task| + task.destroy + end +end + + +clear_all_tasks + +(1..1000).each do |i| + Task.create(:id => i, + :submission_id => i, + :status => Task::STATUS_INQUEUE) +end + diff --git a/test/concurrent/task_test_teardown.rb b/test/concurrent/task_test_teardown.rb new file mode 100644 --- /dev/null +++ b/test/concurrent/task_test_teardown.rb @@ -0,0 +1,14 @@ +ENV["RAILS_ENV"] = "test" +require File.expand_path(File.dirname(__FILE__) + "/../../config/environment") + +def clear_all_tasks + Task.find(:all).each do |task| + task.destroy + end +end + +puts Task.find(:all, + :conditions => {:status => Task::STATUS_COMPLETE}).length + +clear_all_tasks + diff --git a/test/fixtures/tasks.yml b/test/fixtures/tasks.yml --- a/test/fixtures/tasks.yml +++ b/test/fixtures/tasks.yml @@ -1,5 +1,31 @@ # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html -one: + +sub1: id: 1 -two: + submission_id: 1 + status: <%= Task::STATUS_COMPLETE %> + created_at: <%= Time.local(2000,1,1,10,10) %> + +sub2: id: 2 + submission_id: 2 + status: <%= Task::STATUS_GRADING %> + created_at: <%= Time.local(2000,1,1,10,20) %> + +sub3: + id: 3 + submission_id: 3 + status: <%= Task::STATUS_INQUEUE %> + created_at: <%= Time.local(2000,1,1,10,30) %> + +sub4: + id: 4 + submission_id: 4 + status: <%= Task::STATUS_INQUEUE %> + created_at: <%= Time.local(2000,1,1,10,40) %> + +sub5: + id: 5 + submission_id: 5 + status: <%= Task::STATUS_INQUEUE %> + created_at: <%= Time.local(2000,1,1,10,50) %> diff --git a/test/unit/task_test.rb b/test/unit/task_test.rb --- a/test/unit/task_test.rb +++ b/test/unit/task_test.rb @@ -3,8 +3,39 @@ class TaskTest < Test::Unit::TestCase fixtures :tasks - # Replace this with your real tests. - def test_truth - assert true + self.use_transactional_fixtures = false + + def test_get_inqueue_simple + task1 = Task.get_inqueue_and_change_status(Task::STATUS_GRADING) + + assert_equal task1.id, 3, "should get the earliest task" + assert_equal task1.status, Task::STATUS_GRADING, "status changes" + + task2 = Task.get_inqueue_and_change_status(Task::STATUS_GRADING) + + assert_equal task2.id, 4, "should get the next task" + assert_equal task2.status, Task::STATUS_GRADING, "status changes" end + + def generate_tasks(n) + n.times do |i| + Task.create(:submission_id => i, + :status => Task::STATUS_INQUEUE, + :create_at => Time.now + i.minutes) + end + end + + # use the process version in /test/concurrent instead + def UNUSED_test_get_inqueue_concurrent + ActiveRecord::Base.allow_concurrency = true + + task1 = Task.get_inqueue_and_change_status(Task::STATUS_GRADING) + + assert_equal task1.id, 3, "should get the earliest task" + assert_equal task1.status, Task::STATUS_GRADING, "status changes" + + ActiveRecord::Base.verify_active_connections! + end + end +