issue_nested_set.rb.patch

deadlocks detection for testing - Pavel Rosick√Ĺ, 2017-05-19 12:32

Download (4.86 KB)

View differences:

issue_nested_set.rb (working copy)
54 54
        self.root_id = parent.root_id
55 55
        self.lft = target_lft
56 56
        self.rgt = lft + 1
57
        self.class.where(:root_id => root_id).where("lft >= ? OR rgt >= ?", lft, lft).update_all([
57
        get_update {
58
        self.class.reorder('lft desc').where(:root_id => root_id).where("lft >= ? OR rgt >= ?", lft, lft).update_all([
58 59
          "lft = CASE WHEN lft >= :lft THEN lft + 2 ELSE lft END, " +
59 60
          "rgt = CASE WHEN rgt >= :lft THEN rgt + 2 ELSE rgt END",
60 61
          {:lft => lft}
61 62
        ])
63
        }
62 64
      end
63 65

  
64 66
      def add_as_root
......
84 86
        if parent
85 87
          previous_root_id = root_id
86 88
          self.root_id = parent.root_id
87
          
89

  
88 90
          lft_after_move = target_lft
89 91
          self.class.where(:root_id => parent.root_id).update_all([
90 92
            "lft = CASE WHEN lft >= :lft THEN lft + :shift ELSE lft END, " +
91 93
            "rgt = CASE WHEN rgt >= :lft THEN rgt + :shift ELSE rgt END",
92 94
            {:lft => lft_after_move, :shift => (rgt - lft + 1)}
93 95
          ])
94
    
96

  
95 97
          self.class.where(:root_id => previous_root_id).update_all([
96 98
            "root_id = :root_id, lft = lft + :shift, rgt = rgt + :shift",
97 99
            {:root_id => parent.root_id, :shift => lft_after_move - lft}
98 100
          ])
99
    
101

  
100 102
          self.lft, self.rgt = lft_after_move, (rgt - lft + lft_after_move)
101 103
          parent.send :reload_nested_set_values
102 104
        end
......
105 107
      def remove_from_nested_set
106 108
        self.class.where(:root_id => root_id).where("lft >= ? AND rgt <= ?", lft, rgt).
107 109
          update_all(["root_id = :id, lft = lft - :shift, rgt = rgt - :shift", {:id => id, :shift => lft - 1}])
108
    
110

  
109 111
        self.class.where(:root_id => root_id).update_all([
110 112
          "lft = CASE WHEN lft >= :lft THEN lft - :shift ELSE lft END, " +
111 113
          "rgt = CASE WHEN rgt >= :lft THEN rgt - :shift ELSE rgt END",
......
123 125
        children.each {|c| c.send :destroy_without_nested_set_update}
124 126
        reload
125 127
        unless @without_nested_set_update
126
          self.class.where(:root_id => root_id).where("lft > ? OR rgt > ?", lft, lft).update_all([
128
          get_update {
129
          self.class.reorder('lft asc').where(:root_id => root_id).where("lft > ? OR rgt > ?", lft, lft).update_all([
127 130
            "lft = CASE WHEN lft > :lft THEN lft - :shift ELSE lft END, " +
128 131
            "rgt = CASE WHEN rgt > :lft THEN rgt - :shift ELSE rgt END",
129 132
            {:lft => lft, :shift => rgt - lft + 1}
130 133
          ])
134
          }
131 135
        end
132 136
      end
133 137

  
......
148 152
        new_record? || !is_or_is_ancestor_of?(issue)
149 153
      end
150 154

  
151
      def lock_nested_set
155
      def get_update(&block)
156
        retry_count = 0
157
        begin
158
          yield
159
        rescue ActiveRecord::StatementInvalid => error
160
          raise unless error.message =~ /[Dd]eadlock|Lock wait timeout exceeded|[Dd]uplicate/
161
          raise unless retry_count < 20
162
          retry_count += 1
163
          if error.message =~ /[Dd]uplicate/
164
            puts "Duplicate retry ##{retry_count} thread: #{Thread.current.object_id}"
165
          else
166
            puts "Deadlock detected on update, restarting transaction retry ##{retry_count} thread: #{Thread.current.object_id}"
167
          end
168
          sleep(rand(retry_count)*0.5)
169
          retry
170
        end
171
      end
172

  
173
      def get_lock_with_retry(&block)
174
        retry_count = 0
175
        begin
176
          yield
177
        rescue ActiveRecord::StatementInvalid => error
178
          raise unless error.message =~ /[Dd]eadlock|Lock wait timeout exceeded/
179
          raise unless retry_count < 20
180
          retry_count += 1
181
          puts "Deadlock detected on getting lock, restarting transaction retry ##{retry_count} thread: #{Thread.current.object_id}"
182
          sleep(rand(retry_count)*0.5)
183
          retry
184
        end
185
      end
186

  
187
       def lock_nested_set
188
        get_lock_with_retry { get_lock }
189
      end
190

  
191
      def get_lock
152 192
        if self.class.connection.adapter_name =~ /sqlserver/i
153 193
          lock = "WITH (ROWLOCK HOLDLOCK UPDLOCK)"
154 194
          # Custom lock for SQLServer
......
158 198
          self.class.reorder(:id).where(:root_id => sets_to_lock).lock(lock).ids
159 199
        else
160 200
          sets_to_lock = [id, parent_id].compact
161
          self.class.reorder(:id).where("root_id IN (SELECT root_id FROM #{self.class.table_name} WHERE id IN (?))", sets_to_lock).lock.ids
201
          #self.class.reorder(:id).where("root_id IN (SELECT root_id FROM #{self.class.table_name} WHERE id IN (?))", sets_to_lock).lock.ids
202
          self.class.connection.execute(self.class.reorder(:id).where(:root_id => self.class.where(:id => sets_to_lock).select(:root_id)).lock.to_sql)
162 203
        end
163 204
      end
164 205