Commit 2e7f0316 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab-ce master

parents 5376e281 d8bbaae6
......@@ -6,4 +6,12 @@ class ApplicationRecord < ActiveRecord::Base
def self.id_in(ids)
where(id: ids)
end
def self.safe_find_or_create_by(*args)
transaction(requires_new: true) do
find_or_create_by(*args)
end
rescue ActiveRecord::RecordNotUnique
retry
end
end
......@@ -256,32 +256,12 @@ violation, for example.
Using transactions does not solve this problem.
The following pattern should be used to avoid the problem:
To solve this we've added the `ApplicationRecord.safe_find_or_create_by`.
```ruby
Project.transaction do
begin
User.find_or_create_by(username: "foo")
rescue ActiveRecord::RecordNotUnique
retry
end
end
```
If the above block is run inside a transaction and hits the race
condition, the transaction is aborted and we cannot simply retry (any
further queries inside the aborted transaction are going to fail). We
can employ [nested transactions](http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#module-ActiveRecord::Transactions::ClassMethods-label-Nested+transactions)
here to only rollback the "inner transaction". Note that `requires_new: true` is required here.
This method can be used just as you would the normal
`find_or_create_by` but it wraps the call in a *new* transaction and
retries if it were to fail because of an
`ActiveRecord::RecordNotUnique` error.
```ruby
Project.transaction do
begin
User.transaction(requires_new: true) do
User.find_or_create_by(username: "foo")
end
rescue ActiveRecord::RecordNotUnique
retry
end
end
```
To be able to use this method, make sure the model you want to use
this on inherits from `ApplicationRecord`.
......@@ -31,12 +31,26 @@ The optional settings, custom domain, DNS records, and SSL/TLS certificates, are
## Project
Your GitLab Pages project is a regular project created the
same way you do for the other ones. To get started with GitLab Pages, you have two ways:
same way you do for the other ones. To get started with GitLab Pages, you have three ways:
- Use one of the popular templates already in the app,
- Fork one of the templates from Page Examples, or
- Create a new project from scratch
Let's go over both options.
Let's go over each option.
### Use one of the popular Pages templates bundled with GitLab
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/47857)
in GitLab 11.8.
The simplest way to create a GitLab Pages site is to use one of the most
popular templates, which come already bundled and ready to go. To use one
of these templates:
1. From the top navigation, click the **+** button and select **New project**
1. Select **Create from Template**
1. Choose one of the templates starting with **Pages**
### Fork a project to get started from
......
......@@ -91,8 +91,8 @@ site under the HTTPS protocol.
## Getting started
To get started with GitLab Pages, you can either [create a project from scratch](getting_started_part_two.md#create-a-project-from-scratch)
or quickly start from copying an existing example project, as follows:
To get started with GitLab Pages, you can either [create a project from scratch](getting_started_part_two.md#create-a-project-from-scratch),
use a [bundled template](getting_started_part_two.md#use-one-of-the-popular-pages-templates-bundled-with-gitlab), or copy any of our existing example projects:
1. Choose an [example project](https://gitlab.com/pages) to [fork](../../../gitlab-basics/fork-project.md#how-to-fork-a-project):
by forking a project, you create a copy of the codebase you're forking from to start from a template instead of starting from scratch.
......
......@@ -10,4 +10,14 @@ describe ApplicationRecord do
expect(User.id_in(records.last(2).map(&:id))).to eq(records.last(2))
end
end
describe '#safe_find_or_create_by' do
it 'creates the user avoiding race conditions' do
expect(Suggestion).to receive(:find_or_create_by).and_raise(ActiveRecord::RecordNotUnique)
allow(Suggestion).to receive(:find_or_create_by).and_call_original
expect { Suggestion.safe_find_or_create_by(build(:suggestion).attributes) }
.to change { Suggestion.count }.by(1)
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment