diff --git a/config/initializers/ar_monkey_patch.rb b/config/initializers/ar_monkey_patch.rb
index 0da584626eeaef2bc4e0f91f4c1bb7b712f0d85c..6979f4641b013e591adff9d92e22618ff9f0ca40 100644
--- a/config/initializers/ar_monkey_patch.rb
+++ b/config/initializers/ar_monkey_patch.rb
@@ -52,6 +52,23 @@ module ActiveRecord
           raise
         end
       end
+
+      # This is patched because we need it to query `lock_version IS NULL`
+      # rather than `lock_version = 0` whenever lock_version is NULL.
+      def relation_for_destroy
+        return super unless locking_enabled?
+
+        column_name = self.class.locking_column
+        super.where(self.class.arel_table[column_name].eq(self[column_name]))
+      end
+    end
+
+    # This is patched because we want `lock_version` default to `NULL`
+    # rather than `0`
+    class LockingType < SimpleDelegator
+      def type_cast_from_database(value)
+        super
+      end
     end
   end
 end
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index 7dcd03496bbcae7df6be43437d632d175e260041..90771825f5ce85280921ce1703b987b8db5eadc5 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -7,15 +7,21 @@ describe Projects::DestroyService, services: true do
   let!(:remove_path) { path.sub(/\.git\Z/, "+#{project.id}+deleted.git") }
   let!(:async) { false } # execute or async_execute
 
+  shared_examples 'deleting the project' do
+    it 'deletes the project' do
+      expect(Project.all).not_to include(project)
+      expect(Dir.exist?(path)).to be_falsey
+      expect(Dir.exist?(remove_path)).to be_falsey
+    end
+  end
+
   context 'Sidekiq inline' do
     before do
       # Run sidekiq immediatly to check that renamed repository will be removed
       Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
     end
 
-    it { expect(Project.all).not_to include(project) }
-    it { expect(Dir.exist?(path)).to be_falsey }
-    it { expect(Dir.exist?(remove_path)).to be_falsey }
+    it_behaves_like 'deleting the project'
   end
 
   context 'Sidekiq fake' do
@@ -38,11 +44,21 @@ describe Projects::DestroyService, services: true do
       Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
     end
 
-    it 'deletes the project' do
-      expect(Project.all).not_to include(project)
-      expect(Dir.exist?(path)).to be_falsey
-      expect(Dir.exist?(remove_path)).to be_falsey
+    it_behaves_like 'deleting the project'
+  end
+
+  context 'delete with pipeline' do # which has optimistic locking
+    let!(:pipeline) { create(:ci_pipeline, project: project) }
+
+    before do
+      expect(project).to receive(:destroy!).and_call_original
+
+      perform_enqueued_jobs do
+        destroy_project(project, user, {})
+      end
     end
+
+    it_behaves_like 'deleting the project'
   end
 
   context 'container registry' do