Description:
add some lock in task
git-svn-id: http://theory.cpe.ku.ac.th/grader/web/trunk@55 6386c4cd-e34a-4fa8-8920-d93eb39b512e
Commit status:
[Not Reviewed]
References:
Diff options:
Comments:
0 Commit comments
0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
r30:c1451a367614 - - 9 files changed: 169 inserted, 6 deleted
@@ -0,0 +1,17 | |||||
|
|
1 | + class AddStatusToTasks < ActiveRecord::Migration | ||
|
|
2 | + def self.up | ||
|
|
3 | + add_column :tasks, :status, :integer | ||
|
|
4 | + add_column :tasks, :updated_at, :datetime | ||
|
|
5 | + | ||
|
|
6 | + Task.reset_column_information | ||
|
|
7 | + Task.find(:all).each do |task| | ||
|
|
8 | + task.status_complete | ||
|
|
9 | + task.save | ||
|
|
10 | + end | ||
|
|
11 | + end | ||
|
|
12 | + | ||
|
|
13 | + def self.down | ||
|
|
14 | + remove_column :tasks, :updated_at | ||
|
|
15 | + remove_column :tasks, :status | ||
|
|
16 | + end | ||
|
|
17 | + end |
@@ -0,0 +1,16 | |||||
|
|
1 | + ENV["RAILS_ENV"] = "test" | ||
|
|
2 | + require File.expand_path(File.dirname(__FILE__) + "/../../config/environment") | ||
|
|
3 | + | ||
|
|
4 | + def take_wait_return | ||
|
|
5 | + task = Task.get_inqueue_and_change_status(Task::STATUS_GRADING) | ||
|
|
6 | + sleep (rand)/10.0 | ||
|
|
7 | + task.status_complete | ||
|
|
8 | + task.save! | ||
|
|
9 | + end | ||
|
|
10 | + | ||
|
|
11 | + n = 300 | ||
|
|
12 | + | ||
|
|
13 | + n.times do |i| | ||
|
|
14 | + take_wait_return | ||
|
|
15 | + puts i | ||
|
|
16 | + end |
@@ -0,0 +1,18 | |||||
|
|
1 | + ENV["RAILS_ENV"] = "test" | ||
|
|
2 | + require File.expand_path(File.dirname(__FILE__) + "/../../config/environment") | ||
|
|
3 | + | ||
|
|
4 | + def clear_all_tasks | ||
|
|
5 | + Task.find(:all).each do |task| | ||
|
|
6 | + task.destroy | ||
|
|
7 | + end | ||
|
|
8 | + end | ||
|
|
9 | + | ||
|
|
10 | + | ||
|
|
11 | + clear_all_tasks | ||
|
|
12 | + | ||
|
|
13 | + (1..1000).each do |i| | ||
|
|
14 | + Task.create(:id => i, | ||
|
|
15 | + :submission_id => i, | ||
|
|
16 | + :status => Task::STATUS_INQUEUE) | ||
|
|
17 | + end | ||
|
|
18 | + |
@@ -0,0 +1,14 | |||||
|
|
1 | + ENV["RAILS_ENV"] = "test" | ||
|
|
2 | + require File.expand_path(File.dirname(__FILE__) + "/../../config/environment") | ||
|
|
3 | + | ||
|
|
4 | + def clear_all_tasks | ||
|
|
5 | + Task.find(:all).each do |task| | ||
|
|
6 | + task.destroy | ||
|
|
7 | + end | ||
|
|
8 | + end | ||
|
|
9 | + | ||
|
|
10 | + puts Task.find(:all, | ||
|
|
11 | + :conditions => {:status => Task::STATUS_COMPLETE}).length | ||
|
|
12 | + | ||
|
|
13 | + clear_all_tasks | ||
|
|
14 | + |
@@ -1,2 +1,40 | |||||
|
1 | class Task < ActiveRecord::Base |
|
1 | class Task < ActiveRecord::Base |
|
|
2 | + | ||
|
|
3 | + STATUS_GRADING = 0 | ||
|
|
4 | + STATUS_INQUEUE = 1 | ||
|
|
5 | + STATUS_COMPLETE = 2 | ||
|
|
6 | + | ||
|
|
7 | + def status_inqueue | ||
|
|
8 | + self.status = Task::STATUS_INQUEUE | ||
|
2 | end |
|
9 | end |
|
|
10 | + | ||
|
|
11 | + def status_grading | ||
|
|
12 | + self.status = Task::STATUS_GRADING | ||
|
|
13 | + end | ||
|
|
14 | + | ||
|
|
15 | + def status_complete | ||
|
|
16 | + self.status = Task::STATUS_COMPLETE | ||
|
|
17 | + end | ||
|
|
18 | + | ||
|
|
19 | + def self.get_inqueue_and_change_status(status) | ||
|
|
20 | + task = nil | ||
|
|
21 | + begin | ||
|
|
22 | + Task.transaction do | ||
|
|
23 | + task = Task.find(:first, | ||
|
|
24 | + :order => "created_at", | ||
|
|
25 | + :conditions => {:status=> Task::STATUS_INQUEUE}, | ||
|
|
26 | + :lock => true) | ||
|
|
27 | + if task!=nil | ||
|
|
28 | + task.status = status | ||
|
|
29 | + task.save! | ||
|
|
30 | + end | ||
|
|
31 | + end | ||
|
|
32 | + | ||
|
|
33 | + rescue | ||
|
|
34 | + task = nil | ||
|
|
35 | + | ||
|
|
36 | + end | ||
|
|
37 | + task | ||
|
|
38 | + end | ||
|
|
39 | + | ||
|
|
40 | + end |
@@ -1,10 +1,11 | |||||
|
1 | class AddLanguageExt < ActiveRecord::Migration |
|
1 | class AddLanguageExt < ActiveRecord::Migration |
|
2 | def self.up |
|
2 | def self.up |
|
3 | add_column :languages, :ext, :string, :limit => 10 |
|
3 | add_column :languages, :ext, :string, :limit => 10 |
|
4 |
|
4 | ||
|
|
5 | + Language.reset_column_information | ||
|
5 | langs = Language.find(:all) |
|
6 | langs = Language.find(:all) |
|
6 | langs.each do |l| |
|
7 | langs.each do |l| |
|
7 | l.ext = l.name |
|
8 | l.ext = l.name |
|
8 | l.save |
|
9 | l.save |
|
9 | end |
|
10 | end |
|
10 | end |
|
11 | end |
@@ -6,13 +6,13 | |||||
|
6 | # to create the application database on another system, you should be using db:schema:load, not running |
|
6 | # to create the application database on another system, you should be using db:schema:load, not running |
|
7 | # all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations |
|
7 | # all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations |
|
8 | # you'll amass, the slower it'll run and the greater likelihood for issues). |
|
8 | # you'll amass, the slower it'll run and the greater likelihood for issues). |
|
9 | # |
|
9 | # |
|
10 | # It's strongly recommended to check this file into your version control system. |
|
10 | # It's strongly recommended to check this file into your version control system. |
|
11 |
|
11 | ||
|
12 |
- ActiveRecord::Schema.define(:version => 1 |
|
12 | + ActiveRecord::Schema.define(:version => 15) do |
|
13 |
|
13 | ||
|
14 | create_table "grader_processes", :force => true do |t| |
|
14 | create_table "grader_processes", :force => true do |t| |
|
15 | t.string "ip", :limit => 20 |
|
15 | t.string "ip", :limit => 20 |
|
16 | t.integer "pid" |
|
16 | t.integer "pid" |
|
17 | t.string "mode" |
|
17 | t.string "mode" |
|
18 | t.boolean "active" |
|
18 | t.boolean "active" |
@@ -86,12 +86,14 | |||||
|
86 |
|
86 | ||
|
87 | add_index "submissions", ["user_id", "problem_id"], :name => "index_submissions_on_user_id_and_problem_id" |
|
87 | add_index "submissions", ["user_id", "problem_id"], :name => "index_submissions_on_user_id_and_problem_id" |
|
88 |
|
88 | ||
|
89 | create_table "tasks", :force => true do |t| |
|
89 | create_table "tasks", :force => true do |t| |
|
90 | t.integer "submission_id" |
|
90 | t.integer "submission_id" |
|
91 | t.datetime "created_at" |
|
91 | t.datetime "created_at" |
|
|
92 | + t.integer "status" | ||
|
|
93 | + t.datetime "updated_at" | ||
|
92 | end |
|
94 | end |
|
93 |
|
95 | ||
|
94 | create_table "users", :force => true do |t| |
|
96 | create_table "users", :force => true do |t| |
|
95 | t.string "login", :limit => 10 |
|
97 | t.string "login", :limit => 10 |
|
96 | t.string "full_name" |
|
98 | t.string "full_name" |
|
97 | t.string "hashed_password" |
|
99 | t.string "hashed_password" |
@@ -1,5 +1,31 | |||||
|
1 | # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html |
|
1 | # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html |
|
2 | - one: |
|
2 | + |
|
|
3 | + sub1: | ||
|
3 | id: 1 |
|
4 | id: 1 |
|
4 | - two: |
|
5 | + submission_id: 1 |
|
|
6 | + status: <%= Task::STATUS_COMPLETE %> | ||
|
|
7 | + created_at: <%= Time.local(2000,1,1,10,10) %> | ||
|
|
8 | + | ||
|
|
9 | + sub2: | ||
|
5 | id: 2 |
|
10 | id: 2 |
|
|
11 | + submission_id: 2 | ||
|
|
12 | + status: <%= Task::STATUS_GRADING %> | ||
|
|
13 | + created_at: <%= Time.local(2000,1,1,10,20) %> | ||
|
|
14 | + | ||
|
|
15 | + sub3: | ||
|
|
16 | + id: 3 | ||
|
|
17 | + submission_id: 3 | ||
|
|
18 | + status: <%= Task::STATUS_INQUEUE %> | ||
|
|
19 | + created_at: <%= Time.local(2000,1,1,10,30) %> | ||
|
|
20 | + | ||
|
|
21 | + sub4: | ||
|
|
22 | + id: 4 | ||
|
|
23 | + submission_id: 4 | ||
|
|
24 | + status: <%= Task::STATUS_INQUEUE %> | ||
|
|
25 | + created_at: <%= Time.local(2000,1,1,10,40) %> | ||
|
|
26 | + | ||
|
|
27 | + sub5: | ||
|
|
28 | + id: 5 | ||
|
|
29 | + submission_id: 5 | ||
|
|
30 | + status: <%= Task::STATUS_INQUEUE %> | ||
|
|
31 | + created_at: <%= Time.local(2000,1,1,10,50) %> |
@@ -1,10 +1,41 | |||||
|
1 | require File.dirname(__FILE__) + '/../test_helper' |
|
1 | require File.dirname(__FILE__) + '/../test_helper' |
|
2 |
|
2 | ||
|
3 | class TaskTest < Test::Unit::TestCase |
|
3 | class TaskTest < Test::Unit::TestCase |
|
4 | fixtures :tasks |
|
4 | fixtures :tasks |
|
5 |
|
5 | ||
|
6 | - # Replace this with your real tests. |
|
6 | + self.use_transactional_fixtures = false |
|
7 | - def test_truth |
|
7 | + |
|
8 | - assert true |
|
8 | + def test_get_inqueue_simple |
|
|
9 | + task1 = Task.get_inqueue_and_change_status(Task::STATUS_GRADING) | ||
|
|
10 | + | ||
|
|
11 | + assert_equal task1.id, 3, "should get the earliest task" | ||
|
|
12 | + assert_equal task1.status, Task::STATUS_GRADING, "status changes" | ||
|
|
13 | + | ||
|
|
14 | + task2 = Task.get_inqueue_and_change_status(Task::STATUS_GRADING) | ||
|
|
15 | + | ||
|
|
16 | + assert_equal task2.id, 4, "should get the next task" | ||
|
|
17 | + assert_equal task2.status, Task::STATUS_GRADING, "status changes" | ||
|
|
18 | + end | ||
|
|
19 | + | ||
|
|
20 | + def generate_tasks(n) | ||
|
|
21 | + n.times do |i| | ||
|
|
22 | + Task.create(:submission_id => i, | ||
|
|
23 | + :status => Task::STATUS_INQUEUE, | ||
|
|
24 | + :create_at => Time.now + i.minutes) | ||
|
9 | end |
|
25 | end |
|
10 | end |
|
26 | end |
|
|
27 | + | ||
|
|
28 | + # use the process version in /test/concurrent instead | ||
|
|
29 | + def UNUSED_test_get_inqueue_concurrent | ||
|
|
30 | + ActiveRecord::Base.allow_concurrency = true | ||
|
|
31 | + | ||
|
|
32 | + task1 = Task.get_inqueue_and_change_status(Task::STATUS_GRADING) | ||
|
|
33 | + | ||
|
|
34 | + assert_equal task1.id, 3, "should get the earliest task" | ||
|
|
35 | + assert_equal task1.status, Task::STATUS_GRADING, "status changes" | ||
|
|
36 | + | ||
|
|
37 | + ActiveRecord::Base.verify_active_connections! | ||
|
|
38 | + end | ||
|
|
39 | + | ||
|
|
40 | + end | ||
|
|
41 | + |
You need to be logged in to leave comments.
Login now