|
1
|
require "benchmark"
|
|
2
|
|
|
3
|
STATUS_COUNTS = [10, 20, 40, 80].freeze
|
|
4
|
TRACKER_IDS = [1].freeze
|
|
5
|
ROLE_IDS = [1].freeze
|
|
6
|
ITERATIONS = 5
|
|
7
|
|
|
8
|
def build_status_ids(count)
|
|
9
|
(100_000..(100_000 + count - 1)).to_a
|
|
10
|
end
|
|
11
|
|
|
12
|
def build_transitions(status_ids)
|
|
13
|
status_ids.each_with_object({}) do |old_status_id, transitions|
|
|
14
|
transitions[old_status_id.to_s] =
|
|
15
|
status_ids.each_with_object({}) do |new_status_id, by_new_status|
|
|
16
|
by_new_status[new_status_id.to_s] = {
|
|
17
|
"always" => "1",
|
|
18
|
"author" => "1",
|
|
19
|
"assignee" => "1"
|
|
20
|
}
|
|
21
|
end
|
|
22
|
end
|
|
23
|
end
|
|
24
|
|
|
25
|
def seed_records!(status_ids, tracker_ids, role_ids)
|
|
26
|
rows = []
|
|
27
|
|
|
28
|
tracker_ids.each do |tracker_id|
|
|
29
|
role_ids.each do |role_id|
|
|
30
|
status_ids.each do |old_status_id|
|
|
31
|
status_ids.each do |new_status_id|
|
|
32
|
rows << {
|
|
33
|
:tracker_id => tracker_id,
|
|
34
|
:role_id => role_id,
|
|
35
|
:old_status_id => old_status_id,
|
|
36
|
:new_status_id => new_status_id,
|
|
37
|
:author => false,
|
|
38
|
:assignee => false
|
|
39
|
}
|
|
40
|
rows << {
|
|
41
|
:tracker_id => tracker_id,
|
|
42
|
:role_id => role_id,
|
|
43
|
:old_status_id => old_status_id,
|
|
44
|
:new_status_id => new_status_id,
|
|
45
|
:author => false,
|
|
46
|
:assignee => true
|
|
47
|
}
|
|
48
|
end
|
|
49
|
end
|
|
50
|
end
|
|
51
|
end
|
|
52
|
|
|
53
|
WorkflowTransition.insert_all!(rows)
|
|
54
|
end
|
|
55
|
|
|
56
|
def delete_seed_records!(status_ids, tracker_ids, role_ids)
|
|
57
|
WorkflowTransition.where(
|
|
58
|
:tracker_id => tracker_ids,
|
|
59
|
:role_id => role_ids,
|
|
60
|
:old_status_id => status_ids,
|
|
61
|
:new_status_id => status_ids
|
|
62
|
).delete_all
|
|
63
|
end
|
|
64
|
|
|
65
|
def original_replace_transitions(trackers, roles, transitions)
|
|
66
|
trackers = Array.wrap(trackers)
|
|
67
|
roles = Array.wrap(roles)
|
|
68
|
|
|
69
|
WorkflowTransition.transaction do
|
|
70
|
records = WorkflowTransition.where(:tracker_id => trackers.map(&:id), :role_id => roles.map(&:id)).to_a
|
|
71
|
|
|
72
|
transitions.each do |old_status_id, transitions_by_new_status|
|
|
73
|
transitions_by_new_status.each do |new_status_id, transition_by_rule|
|
|
74
|
transition_by_rule.each do |rule, transition|
|
|
75
|
trackers.each do |tracker|
|
|
76
|
roles.each do |role|
|
|
77
|
w = records.select do |r|
|
|
78
|
r.old_status_id == old_status_id.to_i &&
|
|
79
|
r.new_status_id == new_status_id.to_i &&
|
|
80
|
r.tracker_id == tracker.id &&
|
|
81
|
r.role_id == role.id &&
|
|
82
|
!r.destroyed?
|
|
83
|
end
|
|
84
|
if rule == "always"
|
|
85
|
w = w.select {|r| !r.author && !r.assignee}
|
|
86
|
else
|
|
87
|
w = w.select {|r| r.author || r.assignee}
|
|
88
|
end
|
|
89
|
if w.size > 1
|
|
90
|
w[1..-1].each(&:destroy)
|
|
91
|
end
|
|
92
|
w = w.first
|
|
93
|
|
|
94
|
if ["1", true].include?(transition)
|
|
95
|
unless w
|
|
96
|
w = WorkflowTransition.new(
|
|
97
|
:old_status_id => old_status_id,
|
|
98
|
:new_status_id => new_status_id,
|
|
99
|
:tracker_id => tracker.id,
|
|
100
|
:role_id => role.id
|
|
101
|
)
|
|
102
|
records << w
|
|
103
|
end
|
|
104
|
w.author = true if rule == "author"
|
|
105
|
w.assignee = true if rule == "assignee"
|
|
106
|
w.save if w.changed?
|
|
107
|
elsif w
|
|
108
|
if rule == "always"
|
|
109
|
w.destroy
|
|
110
|
elsif rule == "author"
|
|
111
|
if w.assignee
|
|
112
|
w.author = false
|
|
113
|
w.save if w.changed?
|
|
114
|
else
|
|
115
|
w.destroy
|
|
116
|
end
|
|
117
|
elsif rule == "assignee"
|
|
118
|
if w.author
|
|
119
|
w.assignee = false
|
|
120
|
w.save if w.changed?
|
|
121
|
else
|
|
122
|
w.destroy
|
|
123
|
end
|
|
124
|
end
|
|
125
|
end
|
|
126
|
end
|
|
127
|
end
|
|
128
|
end
|
|
129
|
end
|
|
130
|
end
|
|
131
|
end
|
|
132
|
end
|
|
133
|
|
|
134
|
def benchmark_variant(status_ids)
|
|
135
|
tracker = Tracker.find(TRACKER_IDS.first)
|
|
136
|
role = Role.find(ROLE_IDS.first)
|
|
137
|
transitions = build_transitions(status_ids)
|
|
138
|
|
|
139
|
before_durations = []
|
|
140
|
after_durations = []
|
|
141
|
|
|
142
|
ITERATIONS.times do
|
|
143
|
delete_seed_records!(status_ids, TRACKER_IDS, ROLE_IDS)
|
|
144
|
seed_records!(status_ids, TRACKER_IDS, ROLE_IDS)
|
|
145
|
before_durations << Benchmark.realtime { original_replace_transitions(tracker, role, transitions) }
|
|
146
|
|
|
147
|
delete_seed_records!(status_ids, TRACKER_IDS, ROLE_IDS)
|
|
148
|
seed_records!(status_ids, TRACKER_IDS, ROLE_IDS)
|
|
149
|
after_durations << Benchmark.realtime { WorkflowTransition.replace_transitions(tracker, role, transitions) }
|
|
150
|
ensure
|
|
151
|
delete_seed_records!(status_ids, TRACKER_IDS, ROLE_IDS)
|
|
152
|
end
|
|
153
|
|
|
154
|
{
|
|
155
|
:count => status_ids.size,
|
|
156
|
:before_avg => before_durations.sum / before_durations.size,
|
|
157
|
:after_avg => after_durations.sum / after_durations.size
|
|
158
|
}
|
|
159
|
end
|
|
160
|
|
|
161
|
results = STATUS_COUNTS.map do |count|
|
|
162
|
benchmark_variant(build_status_ids(count))
|
|
163
|
end
|
|
164
|
|
|
165
|
puts "| statuses | before_avg_s | after_avg_s | speedup | reduction |"
|
|
166
|
puts "| ---: | ---: | ---: | ---: | ---: |"
|
|
167
|
results.each do |result|
|
|
168
|
speedup = result[:before_avg] / result[:after_avg]
|
|
169
|
reduction = (1 - (result[:after_avg] / result[:before_avg])) * 100
|
|
170
|
puts "| #{result[:count]} | #{format('%.4f', result[:before_avg])} | #{format('%.4f', result[:after_avg])} | #{format('%.2f', speedup)}x | #{format('%.1f', reduction)}% |"
|
|
171
|
end
|
|
172
|
|