Commit 42f1296b authored by Sincheol (David) Kim's avatar Sincheol (David) Kim

Merge branch 'incremental_repository_backup' into 'master'

Create feature flag for incremental repository backups

See merge request gitlab-org/gitlab!79589
parents 8f2a6637 fb790bf6
---
name: incremental_repository_backup
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79589
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/355945
milestone: '14.9'
type: development
group: group::gitaly
default_enabled: false
...@@ -1854,3 +1854,22 @@ To enable it: ...@@ -1854,3 +1854,22 @@ To enable it:
```ruby ```ruby
Feature.enable(:gitaly_backup) Feature.enable(:gitaly_backup)
``` ```
### Incremental repository backups
> Introduced in GitLab 14.9 [with a flag](../administration/feature_flags.md) named `incremental_repository_backup`. Disabled by default.
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `incremental_repository_backup`.
On GitLab.com, this feature is not available.
This feature is not ready for production use.
Incremental backups can be faster than full backups because they only pack changes since the last backup into the backup
bundle for each repository. Because incremental backups require access to the previous backup, you can't use incremental
backups with tar files.
To create an incremental backup, run:
```shell
sudo gitlab-backup create SKIP=tar INCREMENTAL=yes
```
...@@ -9,10 +9,13 @@ module Backup ...@@ -9,10 +9,13 @@ module Backup
# @param [StringIO] progress IO interface to output progress # @param [StringIO] progress IO interface to output progress
# @param [Integer] max_parallelism max parallelism when running backups # @param [Integer] max_parallelism max parallelism when running backups
# @param [Integer] storage_parallelism max parallelism per storage (is affected by max_parallelism) # @param [Integer] storage_parallelism max parallelism per storage (is affected by max_parallelism)
def initialize(progress, max_parallelism: nil, storage_parallelism: nil) # @param [String] backup_id unique identifier for the backup
def initialize(progress, max_parallelism: nil, storage_parallelism: nil, incremental: false, backup_id: nil)
@progress = progress @progress = progress
@max_parallelism = max_parallelism @max_parallelism = max_parallelism
@storage_parallelism = storage_parallelism @storage_parallelism = storage_parallelism
@incremental = incremental
@backup_id = backup_id
end end
def start(type, backup_repos_path) def start(type, backup_repos_path)
...@@ -30,6 +33,13 @@ module Backup ...@@ -30,6 +33,13 @@ module Backup
args = [] args = []
args += ['-parallel', @max_parallelism.to_s] if @max_parallelism args += ['-parallel', @max_parallelism.to_s] if @max_parallelism
args += ['-parallel-storage', @storage_parallelism.to_s] if @storage_parallelism args += ['-parallel-storage', @storage_parallelism.to_s] if @storage_parallelism
if Feature.enabled?(:incremental_repository_backup, default_enabled: :yaml)
args += ['-layout', 'pointer']
if type == :create
args += ['-incremental'] if @incremental
args += ['-id', @backup_id] if @backup_id
end
end
@input_stream, stdout, @thread = Open3.popen2(build_env, bin_path, command, '-path', backup_repos_path, *args) @input_stream, stdout, @thread = Open3.popen2(build_env, bin_path, command, '-path', backup_repos_path, *args)
......
...@@ -21,6 +21,7 @@ module Backup ...@@ -21,6 +21,7 @@ module Backup
max_concurrency = ENV.fetch('GITLAB_BACKUP_MAX_CONCURRENCY', 1).to_i max_concurrency = ENV.fetch('GITLAB_BACKUP_MAX_CONCURRENCY', 1).to_i
max_storage_concurrency = ENV.fetch('GITLAB_BACKUP_MAX_STORAGE_CONCURRENCY', 1).to_i max_storage_concurrency = ENV.fetch('GITLAB_BACKUP_MAX_STORAGE_CONCURRENCY', 1).to_i
force = ENV['force'] == 'yes' force = ENV['force'] == 'yes'
incremental = Gitlab::Utils.to_boolean(ENV['INCREMENTAL'], default: false)
@definitions = definitions || { @definitions = definitions || {
'db' => TaskDefinition.new( 'db' => TaskDefinition.new(
...@@ -32,7 +33,7 @@ module Backup ...@@ -32,7 +33,7 @@ module Backup
destination_path: 'repositories', destination_path: 'repositories',
destination_optional: true, destination_optional: true,
task: Repositories.new(progress, task: Repositories.new(progress,
strategy: repository_backup_strategy, strategy: repository_backup_strategy(incremental),
max_concurrency: max_concurrency, max_concurrency: max_concurrency,
max_storage_concurrency: max_storage_concurrency) max_storage_concurrency: max_storage_concurrency)
), ),
...@@ -481,11 +482,11 @@ module Backup ...@@ -481,11 +482,11 @@ module Backup
Gitlab.config.backup.upload.connection&.provider&.downcase == 'google' Gitlab.config.backup.upload.connection&.provider&.downcase == 'google'
end end
def repository_backup_strategy def repository_backup_strategy(incremental)
if Feature.enabled?(:gitaly_backup, default_enabled: :yaml) if Feature.enabled?(:gitaly_backup, default_enabled: :yaml)
max_concurrency = ENV['GITLAB_BACKUP_MAX_CONCURRENCY'].presence max_concurrency = ENV['GITLAB_BACKUP_MAX_CONCURRENCY'].presence
max_storage_concurrency = ENV['GITLAB_BACKUP_MAX_STORAGE_CONCURRENCY'].presence max_storage_concurrency = ENV['GITLAB_BACKUP_MAX_STORAGE_CONCURRENCY'].presence
Backup::GitalyBackup.new(progress, max_parallelism: max_concurrency, storage_parallelism: max_storage_concurrency) Backup::GitalyBackup.new(progress, incremental: incremental, max_parallelism: max_concurrency, storage_parallelism: max_storage_concurrency)
else else
Backup::GitalyRpcBackup.new(progress) Backup::GitalyRpcBackup.new(progress)
end end
......
This diff is collapsed.
...@@ -176,8 +176,8 @@ RSpec.describe 'gitlab:app namespace rake task', :delete do ...@@ -176,8 +176,8 @@ RSpec.describe 'gitlab:app namespace rake task', :delete do
expect(exit_status).to eq(0) expect(exit_status).to eq(0)
expect(tar_contents).to match(user_backup_path) expect(tar_contents).to match(user_backup_path)
expect(tar_contents).to match("#{user_backup_path}/custom_hooks.tar") expect(tar_contents).to match("#{user_backup_path}/.+/001.custom_hooks.tar")
expect(tar_contents).to match("#{user_backup_path}.bundle") expect(tar_contents).to match("#{user_backup_path}/.+/001.bundle")
end end
it 'restores files correctly' do it 'restores files correctly' do
...@@ -360,14 +360,14 @@ RSpec.describe 'gitlab:app namespace rake task', :delete do ...@@ -360,14 +360,14 @@ RSpec.describe 'gitlab:app namespace rake task', :delete do
expect(exit_status).to eq(0) expect(exit_status).to eq(0)
[ [
"#{project_a.disk_path}.bundle", "#{project_a.disk_path}/.+/001.bundle",
"#{project_a.disk_path}.wiki.bundle", "#{project_a.disk_path}.wiki/.+/001.bundle",
"#{project_a.disk_path}.design.bundle", "#{project_a.disk_path}.design/.+/001.bundle",
"#{project_b.disk_path}.bundle", "#{project_b.disk_path}/.+/001.bundle",
"#{project_snippet_a.disk_path}.bundle", "#{project_snippet_a.disk_path}/.+/001.bundle",
"#{project_snippet_b.disk_path}.bundle" "#{project_snippet_b.disk_path}/.+/001.bundle"
].each do |repo_name| ].each do |repo_name|
expect(tar_lines.grep(/#{repo_name}/).size).to eq 1 expect(tar_lines).to include(a_string_matching(repo_name))
end end
end end
...@@ -428,7 +428,7 @@ RSpec.describe 'gitlab:app namespace rake task', :delete do ...@@ -428,7 +428,7 @@ RSpec.describe 'gitlab:app namespace rake task', :delete do
expect(::Backup::Repositories).to receive(:new) expect(::Backup::Repositories).to receive(:new)
.with(anything, strategy: anything, max_concurrency: 5, max_storage_concurrency: 2) .with(anything, strategy: anything, max_concurrency: 5, max_storage_concurrency: 2)
.and_call_original .and_call_original
expect(::Backup::GitalyBackup).to receive(:new).with(anything, max_parallelism: 5, storage_parallelism: 2).and_call_original expect(::Backup::GitalyBackup).to receive(:new).with(anything, max_parallelism: 5, storage_parallelism: 2, incremental: false).and_call_original
expect { run_rake_task('gitlab:backup:create') }.to output.to_stdout_from_any_process expect { run_rake_task('gitlab:backup:create') }.to output.to_stdout_from_any_process
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