Sort vulnerability identifiers on ingestion to prevent Deadlock errors
What does this MR do and why?
It is possible to have multiple threads trying to ingest vulnerability identifiers for the same project in different orders which can cause multiple connections to wait for each other to release row locks to proceed which introduces a deadlock error.
By sorting the identifiers by fingerprint before ingestion, we prevent the risk of having multiple transactions to wait for each other.
Related to #343332 (closed).
Related Sentry errors;
- https://sentry.gitlab.net/gitlab/gitlabcom/issues/3170879
- https://sentry.gitlab.net/gitlab/gitlabcom/issues/3170876
- https://sentry.gitlab.net/gitlab/gitlabcom/issues/3169149
- https://sentry.gitlab.net/gitlab/gitlabcom/issues/3170881
Here is a script to reproduce the deadlock case
begin
require 'bundler/inline'
rescue LoadError => e
$stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler'
raise e
end
gemfile(false) do
source 'https://rubygems.org'
gem 'activerecord', '6.0.0'
gem 'sqlite3'
gem 'pg'
end
require 'active_record'
require 'minitest/autorun'
require 'logger'
require 'sqlite3'
require 'pg'
Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
# ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: 'dont use in memory database', pool: 6)
ActiveRecord::Base.establish_connection(adapter: 'postgresql', host: 'localhost', database: '...', username: '...', pool: 6)
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :authors, force: true do |t|
t.string :name
t.string :uuid
t.index :uuid, unique: true
end
end
class Author < ActiveRecord::Base
validates :name, presence: true
end
def create_authors
5.times.map { |i| Author.create(name: "Author #{i}", uuid: SecureRandom.uuid) }
end
class BugTest < Minitest::Test
def test_reproduce_deadlock
authors = create_authors
5.times.map do
Thread.new do
ActiveRecord::Base.transaction do
new_authors = authors.shuffle.map(&:dup).as_json(except: :id)
Author.upsert_all(new_authors, unique_by: :uuid)
end
end
end.each(&:join)
end
end
MR acceptance checklist
This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
-
I have evaluated the MR acceptance checklist for this MR.
Edited by Mehmet Emin INAC