2020-10-30 17:08:52 -04:00
---
stage: none
group: unassigned
2022-09-21 17:13:33 -04:00
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
2020-10-30 17:08:52 -04:00
---
2017-10-13 12:50:36 -04:00
# Working with the GitHub importer
In GitLab 10.2 a new version of the GitHub importer was introduced. This new
importer performs its work in parallel using Sidekiq, greatly reducing the time
necessary to import GitHub projects into a GitLab instance.
The GitHub importer offers two different types of importers: a sequential
importer and a parallel importer. The Rake task `import:github` uses the
sequential importer, while everything else uses the parallel importer. The
difference between these two importers is quite simple: the sequential importer
does all work in a single thread, making it more useful for debugging purposes
2020-03-13 14:09:39 -04:00
or Rake tasks. The parallel importer, on the other hand, uses Sidekiq.
2017-10-13 12:50:36 -04:00
## Requirements
2018-11-13 01:07:16 -05:00
- GitLab CE 10.2.0 or newer.
- Sidekiq workers that process the `github_importer` and
2017-10-13 12:50:36 -04:00
`github_importer_advance_stage` queues (this is enabled by default).
2018-11-13 01:07:16 -05:00
- Octokit (used for interacting with the GitHub API).
2017-10-13 12:50:36 -04:00
## Code structure
The importer's codebase is broken up into the following directories:
2018-11-13 01:07:16 -05:00
- `lib/gitlab/github_import` : this directory contains most of the code such as
2017-10-13 12:50:36 -04:00
the classes used for importing resources.
2018-11-13 01:07:16 -05:00
- `app/workers/gitlab/github_import` : this directory contains the Sidekiq
2017-10-13 12:50:36 -04:00
workers.
2018-11-13 01:07:16 -05:00
- `app/workers/concerns/gitlab/github_import` : this directory contains a few
2017-10-13 12:50:36 -04:00
modules reused by the various Sidekiq workers.
## Architecture overview
2020-03-13 14:09:39 -04:00
When a GitHub project is imported, we schedule and execute a job for the
`RepositoryImportWorker` worker as all other importers. However, unlike other
importers, we don't immediately perform the work necessary. Instead work is
2017-10-13 12:50:36 -04:00
divided into separate stages, with each stage consisting out of a set of Sidekiq
jobs that are executed. Between every stage a job is scheduled that periodically
checks if all work of the current stage is completed, advancing the import
process to the next stage when this is the case. The worker handling this is
called `Gitlab::GithubImport::AdvanceStageWorker` .
## Stages
### 1. RepositoryImportWorker
2020-12-11 13:09:57 -05:00
This worker starts the import process by scheduling a job for the
2017-10-13 12:50:36 -04:00
next worker.
### 2. Stage::ImportRepositoryWorker
2020-12-11 13:09:57 -05:00
This worker imports the repository and wiki, scheduling the next stage when
2017-10-13 12:50:36 -04:00
done.
### 3. Stage::ImportBaseDataWorker
2020-12-11 13:09:57 -05:00
This worker imports base data such as labels, milestones, and releases. This
work is done in a single thread because it can be performed fast enough that we
2017-10-13 12:50:36 -04:00
don't need to perform this work in parallel.
### 4. Stage::ImportPullRequestsWorker
2020-12-11 13:09:57 -05:00
This worker imports all pull requests. For every pull request a job for the
2017-10-13 12:50:36 -04:00
`Gitlab::GithubImport::ImportPullRequestWorker` worker is scheduled.
2021-07-23 14:10:06 -04:00
### 5. Stage::ImportPullRequestsMergedByWorker
2022-08-23 20:12:25 -04:00
This worker imports the pull requests' _merged-by_ user information. The
2022-08-07 23:09:21 -04:00
[_List pull requests_ ](https://docs.github.com/en/rest/pulls#list-pull-requests )
2021-07-23 14:10:06 -04:00
API doesn't provide this information. Therefore, this stage must fetch each merged pull request
individually to import this information. A
`Gitlab::GithubImport::ImportPullRequestMergedByWorker` job is scheduled for each fetched pull
request.
### 6. Stage::ImportPullRequestsReviewsWorker
This worker imports the pull requests' reviews. For each pull request, this worker:
- Fetches all the pages of reviews.
- Schedules a `Gitlab::GithubImport::ImportPullRequestReviewWorker` job for each fetched review.
### 7. Stage::ImportIssuesAndDiffNotesWorker
2017-10-13 12:50:36 -04:00
2020-12-11 13:09:57 -05:00
This worker imports all issues and pull request comments. For every issue, we
2017-10-13 12:50:36 -04:00
schedule a job for the `Gitlab::GithubImport::ImportIssueWorker` worker. For
2020-03-13 14:09:39 -04:00
pull request comments, we instead schedule jobs for the
2017-10-13 12:50:36 -04:00
`Gitlab::GithubImport::DiffNoteImporter` worker.
This worker processes both issues and diff notes in parallel so we don't need to
schedule a separate stage and wait for the previous one to complete.
Issues are imported separately from pull requests because only the "issues" API
includes labels for both issue and pull requests. Importing issues and setting
label links in the same worker removes the need for performing a separate crawl
through the API data, reducing the number of API calls necessary to import a
project.
2022-08-30 11:10:02 -04:00
### 8. Stage::ImportIssueEventsWorker
This worker imports all issues and pull request events. For every event, we
schedule a job for the `Gitlab::GithubImport::ImportIssueEventWorker` worker.
We can import both issues and pull request events by single stage because of a specific aspect of the GitHub API. It looks like that under the hood, issues and pull requests
GitHub are stored in a single table. Therefore, they have globally-unique IDs and so:
- Every pull request is an issue.
- Issues aren't pull requests.
Therefore, both issues and pull requests have a common API for most related things.
### 9. Stage::ImportNotesWorker
2017-10-13 12:50:36 -04:00
This worker imports regular comments for both issues and pull requests. For
2020-03-13 14:09:39 -04:00
every comment, we schedule a job for the
2017-10-13 12:50:36 -04:00
`Gitlab::GithubImport::ImportNoteWorker` worker.
2020-12-11 13:09:57 -05:00
Regular comments have to be imported at the end because the GitHub API used
2017-10-13 12:50:36 -04:00
returns comments for both issues and pull requests. This means we have to wait
for all issues and pull requests to be imported before we can import regular
comments.
2022-08-30 23:10:23 -04:00
### 10. Stage::ImportAttachmentsWorker
This worker imports release notes attachments that are linked inside Markdown.
For every release of the project, we schedule a job of
`Gitlab::GithubImport::ImportReleaseAttachmentsWorker` for every comment.
Each job:
1. Iterates over all attachment links inside of a specific release note.
1. Downloads the attachment.
1. Replaces the old link with a newly-generated link to GitLab.
2022-09-14 08:12:34 -04:00
### 11. Stage::ImportProtectedBranchesWorker
This worker imports protected branch rules.
For every rule that exists on GitHub, we schedule a job of
`Gitlab::GithubImport::ImportProtectedBranchWorker` .
Each job compares the branch protection rules from GitHub and GitLab and applies
the strictest of the rules to the branches in GitLab.
### 12. Stage::FinishImportWorker
2017-10-13 12:50:36 -04:00
2020-12-11 13:09:57 -05:00
This worker completes the import process by performing some housekeeping
2017-10-13 12:50:36 -04:00
(such as flushing any caches) and by marking the import as completed.
## Advancing stages
Advancing stages is done in one of two ways:
2018-11-12 19:39:21 -05:00
- Scheduling the worker for the next stage directly.
- Scheduling a job for `Gitlab::GithubImport::AdvanceStageWorker` which will
2017-10-13 12:50:36 -04:00
advance the stage when all work of the current stage has been completed.
The first approach should only be used by workers that perform all their work in
a single thread, while `AdvanceStageWorker` should be used for everything else.
The way `AdvanceStageWorker` works is fairly simple. When scheduling a job it
2020-12-11 13:09:57 -05:00
is given a project ID, a list of Redis keys, and the name of the next
2017-10-13 12:50:36 -04:00
stage. The Redis keys (produced by `Gitlab::JobWaiter` ) are used to check if the
currently running stage has been completed or not. If the stage has not yet been
2020-12-11 13:09:57 -05:00
completed `AdvanceStageWorker` reschedules itself. After a stage finishes
`AdvanceStageworker` refreshes the import JID (more on this below) and
2017-10-13 12:50:36 -04:00
schedule the worker of the next stage.
2020-12-11 13:09:57 -05:00
To reduce the number of `AdvanceStageWorker` jobs scheduled this worker
briefly waits for jobs to complete before deciding what the next action should
be. For small projects, this may slow down the import process a bit, but it
also reduces pressure on the system as a whole.
2017-10-13 12:50:36 -04:00
## Refreshing import JIDs
2020-06-08 14:08:27 -04:00
GitLab includes a worker called `Gitlab::Import::StuckProjectImportJobsWorker`
2020-12-11 13:09:57 -05:00
that periodically runs and marks project imports as failed if they have been
2020-06-08 14:08:27 -04:00
running for more than 15 hours. For GitHub projects, this poses a bit of a
problem: importing large projects could take several hours depending on how
often we hit the GitHub rate limit (more on this below), but we don't want
`Gitlab::Import::StuckProjectImportJobsWorker` to mark our import as failed because of this.
2017-10-13 12:50:36 -04:00
To prevent this from happening we periodically refresh the expiration time of
the import process. This works by storing the JID of the import job in the
database, then refreshing this JID's TTL at various stages throughout the import
2018-11-27 04:41:27 -05:00
process. This is done by calling `ProjectImportState#refresh_jid_expiration` . By
2017-10-13 12:50:36 -04:00
refreshing this TTL we can ensure our import does not get marked as failed so
long we're still performing work.
## GitHub rate limit
2020-03-13 14:09:39 -04:00
GitHub has a rate limit of 5,000 API calls per hour. The number of requests
2017-10-13 12:50:36 -04:00
necessary to import a project is largely dominated by the number of unique users
2021-07-19 14:08:23 -04:00
involved in a project (for example, issue authors). Other data such as issue pages
2020-02-11 16:08:44 -05:00
and comments typically only requires a few dozen requests to import. This is
2017-10-13 12:50:36 -04:00
because we need the Email address of users in order to map them to GitLab users.
We handle this by doing the following:
2020-12-11 13:09:57 -05:00
1. After we hit the rate limit all jobs automatically reschedule themselves
2017-10-13 12:50:36 -04:00
in such a way that they are not executed until the rate limit has been reset.
2018-11-12 19:39:21 -05:00
1. We cache the mapping of GitHub users to GitLab users in Redis.
2017-10-13 12:50:36 -04:00
More information on user caching can be found below.
## Caching user lookups
When mapping GitHub users to GitLab users we need to (in the worst case)
perform:
1. One API call to get the user's Email address.
2018-11-12 19:39:21 -05:00
1. Two database queries to see if a corresponding GitLab user exists. One query
2020-12-11 13:09:57 -05:00
tries to find the user based on the GitHub user ID, while the second query
2017-10-13 12:50:36 -04:00
is used to find the user using their GitHub Email address.
2021-07-26 08:10:08 -04:00
To avoid mismatching users, the search by GitHub user ID is not done when importing from GitHub
Enterprise.
2017-10-13 12:50:36 -04:00
Because this process is quite expensive we cache the result of these lookups in
Redis. For every user looked up we store three keys:
2018-11-12 19:39:21 -05:00
- A Redis key mapping GitHub usernames to their Email addresses.
- A Redis key mapping a GitHub Email addresses to a GitLab user ID.
- A Redis key mapping a GitHub user ID to GitLab user ID.
2017-10-13 12:50:36 -04:00
There are two types of lookups we cache:
2018-11-12 19:39:21 -05:00
- A positive lookup, meaning we found a GitLab user ID.
- A negative lookup, meaning we didn't find a GitLab user ID. Caching this
2017-10-13 12:50:36 -04:00
prevents us from performing the same work for users that we know don't exist
in our GitLab database.
The expiration time of these keys is 24 hours. When retrieving the cache of a
2020-03-13 14:09:39 -04:00
positive lookup, we refresh the TTL automatically. The TTL of false lookups is
2017-10-13 12:50:36 -04:00
never refreshed.
2020-03-13 14:09:39 -04:00
Because of this caching layer, it's possible newly registered GitLab accounts
2020-12-11 13:09:57 -05:00
aren't linked to their corresponding GitHub accounts. This, however, is resolved
after the cached keys expire.
2017-10-13 12:50:36 -04:00
2020-12-11 13:09:57 -05:00
The user cache lookup is shared across projects. This means that the greater the number of
projects that are imported, fewer GitHub API calls are needed.
2017-10-13 12:50:36 -04:00
The code for this resides in:
2018-11-13 01:07:16 -05:00
- `lib/gitlab/github_import/user_finder.rb`
- `lib/gitlab/github_import/caching.rb`
2017-10-13 12:50:36 -04:00
## Mapping labels and milestones
To reduce pressure on the database we do not query it when setting labels and
2020-03-13 14:09:39 -04:00
milestones on issues and merge requests. Instead, we cache this data when we
2017-10-13 12:50:36 -04:00
import labels and milestones, then we reuse this cache when assigning them to
issues/merge requests. Similar to the user lookups these cache keys are expired
automatically after 24 hours of not being used.
2020-03-13 14:09:39 -04:00
Unlike the user lookup caches, these label and milestone caches are scoped to the
2017-10-13 12:50:36 -04:00
project that is being imported.
The code for this resides in:
2018-11-13 01:07:16 -05:00
- `lib/gitlab/github_import/label_finder.rb`
- `lib/gitlab/github_import/milestone_finder.rb`
- `lib/gitlab/github_import/caching.rb`
2021-07-14 17:09:44 -04:00
## Logs
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48512/diffs) in GitLab 13.7.
> - Number of imported objects [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64256) in GitLab 14.1.
2021-08-04 14:09:57 -04:00
> - `Gitlab::GithubImport::Logger` [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65968) in GitLab 14.2.
2021-08-16 11:10:05 -04:00
> - `import_source` [renamed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67726) to `import_type` in GitLab 14.2.
2021-07-14 17:09:44 -04:00
The import progress can be checked in the `logs/importer.log` file. Each relevant import is logged
2021-08-16 11:10:05 -04:00
with `"import_type": "github"` and the `"project_id"` .
2021-07-14 17:09:44 -04:00
The last log entry reports the number of objects fetched and imported:
```json
{
"message": "GitHub project import finished",
"duration_s": 347.25,
"objects_imported": {
"fetched": {
"diff_note": 93,
"issue": 321,
"note": 794,
"pull_request": 108,
"pull_request_merged_by": 92,
"pull_request_review": 81
},
"imported": {
"diff_note": 93,
"issue": 321,
"note": 794,
"pull_request": 108,
"pull_request_merged_by": 92,
"pull_request_review": 81
}
},
"import_source": "github",
"project_id": 47,
"import_stage": "Gitlab::GithubImport::Stage::FinishImportWorker"
}
```
2021-08-19 14:10:32 -04:00
## Errors when importing large projects
The GitHub importer may encounter errors when importing large projects. For help with this, see the
documentation for the following use cases:
- [Alternative way to import notes and diff notes ](../user/project/import/github.md#alternative-way-to-import-notes-and-diff-notes )
- [Reduce GitHub API request objects per page ](../user/project/import/github.md#reduce-github-api-request-objects-per-page )
2021-08-23 20:11:18 -04:00
## Metrics dashboards
To assess the GitHub importer health, the [GitHub importer dashboard ](https://dashboards.gitlab.net/d/importers-github-importer/importers-github-importer )
provides information about the total number of objects fetched vs. imported over time.