summaryrefslogtreecommitdiff
path: root/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
diff options
context:
space:
mode:
Diffstat (limited to 'doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md')
-rw-r--r--doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md1040
1 files changed, 1040 insertions, 0 deletions
diff --git a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
new file mode 100644
index 00000000000..0c5611aa6cd
--- /dev/null
+++ b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
@@ -0,0 +1,1040 @@
+---
+type: reference
+---
+
+# GitLab Rails Console Cheat Sheet
+
+This is the GitLab Support Team's collection of information regarding the GitLab rails
+console, for use while troubleshooting. It is listed here for transparency,
+and it may be useful for users with experience with these tools. If you are currently
+having an issue with GitLab, it is highly recommended that you check your
+[support options](https://about.gitlab.com/support/) first, before attempting to use
+this information.
+
+CAUTION: **CAUTION:**
+Please note that some of these scripts could be damaging if not run correctly,
+or under the right conditions. We highly recommend running them under the
+guidance of a Support Engineer, or running them in a test environment with a
+backup of the instance ready to be restored, just in case.
+
+CAUTION: **CAUTION:**
+Please also note that as GitLab changes, changes to the code are inevitable,
+and so some scripts may not work as they once used to. These are not kept
+up-to-date as these scripts/commands were added as they were found/needed. As
+mentioned above, we recommend running these scripts under the supervision of a
+Support Engineer, who can also verify that they will continue to work as they
+should and, if needed, update the script for the latest version of GitLab.
+
+## Use the Rails Runner
+
+If the script you want to run is short, you can use the Rails Runner to avoid
+entering the rails console in the first place. Here's an example of its use:
+
+```bash
+gitlab-rails runner "RAILS_COMMAND"
+
+# Example with a 2-line script
+gitlab-rails runner "user = User.first; puts user.username"
+```
+
+## Enable debug logging on rails console
+
+```ruby
+Rails.logger.level = 0
+```
+
+## Enable debug logging for ActiveRecord (db issues)
+
+```ruby
+ActiveRecord::Base.logger = Logger.new(STDOUT)
+```
+
+## Temporarily Disable Timeout
+
+```ruby
+ActiveRecord::Base.connection.execute('SET statement_timeout TO 0')
+```
+
+## Find specific methods for an object
+
+```ruby
+Array.methods.select { |m| m.to_s.include? "sing" }
+Array.methods.grep(/sing/)
+```
+
+## Find method source
+
+Works for [non-instrumented methods](https://docs.gitlab.com/ce/development/instrumentation.html#checking-instrumented-methods):
+
+```ruby
+instance_of_object.method(:foo).source_location
+
+# Example for when we would call project.private?
+project.method(:private?).source_location
+```
+
+## Query an object
+
+```ruby
+o = Object.where('attribute like ?', 'ex')
+```
+
+## View all keys in cache
+
+```ruby
+Rails.cache.instance_variable_get(:@data).keys
+```
+
+## Rails console history
+
+```ruby
+puts Readline::HISTORY.to_a
+```
+
+## Profile a page
+
+```ruby
+# Before 11.6.0
+logger = Logger.new(STDOUT)
+admin_token = User.find_by_username('ADMIN_USERNAME').personal_access_tokens.first.token
+app.get("URL/?private_token=#{admin_token}")
+
+# From 11.6.0
+admin = User.find_by_username('ADMIN_USERNAME')
+url = "/url/goes/here"
+Gitlab::Profiler.with_user(admin) { app.get(url) }
+```
+
+## Using the GitLab profiler inside console (used as of 10.5)
+
+```ruby
+logger = Logger.new(STDOUT)
+admin = User.find_by_username('ADMIN_USERNAME')
+Gitlab::Profiler.profile('URL', logger: logger, user: admin)
+```
+
+## Time an operation
+
+```ruby
+# A single operation
+Benchmark.measure { <operation> }
+
+# A breakdown of multiple operations
+Benchmark.bm do |x|
+ x.report(:label1) { <operation_1> }
+ x.report(:label2) { <operation_2> }
+end
+```
+
+## Command Line
+
+### Check the GitLab version fast
+
+```bash
+grep -m 1 gitlab /opt/gitlab/version-manifest.txt
+```
+
+### Debugging SSH
+
+```bash
+GIT_SSH_COMMAND="ssh -vvv" git clone <repository>
+```
+
+### Debugging over HTTPS
+
+```bash
+GIT_CURL_VERBOSE=1 GIT_TRACE=1 git clone <repository>
+```
+
+## Projects
+
+### Find projects
+
+```ruby
+# A single project
+project = Project.find_by_full_path('PROJECT_PATH')
+
+# All projects in a particular namespace. Can be a username, a group
+# ('gitlab-org'), or even include subgroups ('gitlab-org/distribution')
+namespace = Namespace.find_by_full_path('NAMESPACE_PATH')
+projects = namespace.all_projects
+```
+
+### Clear a project's cache
+
+```ruby
+ProjectCacheWorker.perform_async(project.id)
+```
+
+### Expire the .exists? cache
+
+```ruby
+project.repository.expire_exists_cache
+```
+
+### Make all projects private
+
+```ruby
+Project.update_all(visibility_level: 0)
+```
+
+### Find & remove projects that are pending deletion
+
+```ruby
+#
+# This section will list all the projects which are pending deletion
+#
+projects = Project.where(pending_delete: true)
+projects.each do |p|
+ puts "Project name: #{p.id}"
+ puts "Project name: #{p.name}"
+ puts "Repository path: #{p.repository.storage_path}"
+end
+
+#
+# Assign a user (the root user will do)
+#
+user = User.find_by_username('root')
+
+#
+# For each project listed repeat these two commands
+#
+
+# Find the project, update the xxx-changeme values from above
+project = Project.find_by_full_path('group-changeme/project-changeme')
+
+# Delete the project
+::Projects::DestroyService.new(project, user, {}).execute
+```
+
+Next, run `sudo gitlab-rake gitlab:cleanup:repos` on the command line to finish.
+
+### Destroy a project
+
+```ruby
+project = Project.find_by_full_path('')
+user = User.find_by_username('')
+ProjectDestroyWorker.perform_async(project.id, user.id, {})
+# or ProjectDestroyWorker.new.perform(project.id, user.id, {})
+# or Projects::DestroyService.new(project, user).execute
+```
+
+### Remove fork relationship manually
+
+```ruby
+p = Project.find_by_full_path('')
+u = User.find_by_username('')
+::Projects::UnlinkForkService.new(p, u).execute
+```
+
+### Make a project read-only (can only be done in the console)
+
+```ruby
+# Make a project read-only
+project.repository_read_only = true; project.save
+
+# OR
+project.update!(repository_read_only: true)
+```
+
+### Bulk update service integration password for _all_ projects
+
+For example, change the Jira user's password for all projects that have the Jira
+integration active:
+
+```ruby
+p = Project.find_by_sql("SELECT p.id FROM projects p LEFT JOIN services s ON p.id = s.project_id WHERE s.type = 'JiraService' AND s.active = true")
+
+p.each do |project|
+ project.jira_service.update_attribute(:password, '<your-new-password>')
+end
+```
+
+### Identify un-indexed projects
+
+```ruby
+Project.find_each do |project|
+ puts "id #{project.id}: #{project.namespace.name.to_s}/#{project.name.to_s}" if project.index_status.nil?
+end
+```
+
+## Imports / Exports
+
+```ruby
+# Find the project and get the error
+p = Project.find_by_full_path('<username-or-group>/<project-name>')
+
+p.import_error
+
+# To finish the import on GitLab running version before 11.6
+p.import_finish
+
+# To finish the import on GitLab running version 11.6 or after
+p.import_state.mark_as_failed("Failed manually through console.")
+```
+
+### Rename imported repository
+
+In a specific situation, an imported repository needed to be renamed. The Support
+Team was informed of a backup restore that failed on a single repository, which created
+the project with an empty repository. The project was successfully restored to a dev
+instance, then exported, and imported into a new project under a different name.
+
+The Support Team was able to transfer the incorrectly named imported project into the
+correctly named empty project using the steps below.
+
+Move the new repository to the empty repository:
+
+```bash
+mv /var/opt/gitlab/git-data/repositories/<group>/<new-project> /var/opt/gitlab/git-data/repositories/<group>/<empty-project>
+```
+
+Make sure the permissions are correct:
+
+```bash
+chown -R git:git <path-to-directory>.git
+```
+
+Clear the cache:
+
+```bash
+sudo gitlab-rake cache:clear
+```
+
+## Repository
+
+### Search sequence of pushes to a repository
+
+If it seems that a commit has gone "missing", search the sequence of pushes to a repository.
+[This StackOverflow article](https://stackoverflow.com/questions/13468027/the-mystery-of-the-missing-commit-across-merges)
+describes how you can end up in this state without a force push.
+
+If you look at the output from the sample code below for the target branch, you will
+see a discontinuity in the from/to commits as you step through the output. Each new
+push should be "from" the "to" SHA of the previous push. When this discontinuity happens,
+you will see two pushes with the same "from" SHA:
+
+```ruby
+p = Project.find_with_namespace('u/p')
+p.events.code_push.last(100).each do |e|
+ printf "%-20.20s %8s...%8s (%s)\n", e.data[:ref], e.data[:before], e.data[:after], e.author.try(:username)
+end
+```
+
+GitLab 9.5 and above:
+
+```ruby
+p = Project.find_by_full_path('u/p')
+p.events.code_push.last(100).each do |e|
+ printf "%-20.20s %8s...%8s (%s)\n", e.push_event_payload[:ref], e.push_event_payload[:commit_from], e.push_event_payload[:commit_to], e.author.try(:username)
+end
+```
+
+## Mirrors
+
+### Find mirrors with "bad decrypt" errors
+
+```ruby
+total = 0
+bad = []
+ProjectImportData.find_each do |data|
+ begin
+ total += 1
+ data.credentials
+ rescue => e
+ bad << data
+ end
+end
+
+puts "Bad count: #{bad.count} / #{total}"
+bad.each do |repo|
+ puts Project.find(repo.project_id).full_path
+end; bad.count
+```
+
+### Transfer mirror users and tokens to a single service account
+
+Use case: If you have multiple users using their own GitHub credentials to set up
+repository mirroring, mirroring breaks when people leave the company. Use this
+script to migrate disparate mirroring users and tokens into a single service account:
+
+```ruby
+svc_user = User.find_by(username: 'ourServiceUser')
+token = 'githubAccessToken'
+
+Project.where(mirror: true).each do |project|
+ import_url = project.import_url
+
+ # The url we want is https://token@project/path.git
+ repo_url = if import_url.include?('@')
+ # Case 1: The url is something like https://23423432@project/path.git
+ import_url.split('@').last
+ elsif import_url.include?('//')
+ # Case 2: The url is something like https://project/path.git
+ import_url.split('//').last
+ end
+
+ next unless repo_url
+
+ final_url = "https://#{token}@#{repo_url}"
+
+ project.mirror_user = svc_user
+ project.import_url = final_url
+ project.username_only_import_url = final_url
+ project.save
+end
+```
+
+## Users
+
+### Finding users
+
+```ruby
+# By username
+user = User.find_by(username: '')
+
+# By primary email
+user = User.find_by(email: '')
+
+# By any email (primary or secondary)
+user = User.find_by_any_email('')
+
+# Admins
+User.admins
+admin = User.admins.first
+```
+
+### Block
+
+```ruby
+User.find_by_username().block!
+```
+
+### Unblock
+
+```ruby
+User.find_by_username().active
+```
+
+### Skip reconfirmation
+
+```ruby
+user = User.find_by_username ''
+user.skip_reconfirmation!
+```
+
+### Get an admin token
+
+```ruby
+# Get the first admin's first access token (no longer works on 11.9+. see: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22743)
+User.where(admin:true).first.personal_access_tokens.first.token
+
+# Get the first admin's private token (no longer works on 10.2+)
+User.where(admin:true).private_token
+```
+
+### Create personal access token
+
+```ruby
+personal_access_token = User.find(123).personal_access_tokens.create(
+ name: 'apitoken',
+ impersonation: false,
+ scopes: [:api]
+)
+
+personal_access_token.token
+```
+
+You might also want to manually set the token string:
+
+```ruby
+User.find(123).personal_access_tokens.create(
+ name: 'apitoken',
+ token_digest: Gitlab::CryptoHelper.sha256('some-token-string-here'),
+ impersonation: false,
+ scopes: [:api]
+)
+```
+
+### Disable 2FA on a user
+
+```ruby
+user = User.find_by_username('username')
+user.disable_two_factor!
+```
+
+### Active users & Historical users
+
+```ruby
+# Active users on the instance, now
+User.active.count
+
+# The historical max on the instance as of the past year
+::HistoricalData.max_historical_user_count
+```
+
+```bash
+# Using curl and jq (up to a max 100, see [pagination](https://docs.gitlab.com/ee/api/#pagination)
+curl --silent --header "Private-Token: ********************" "https://gitlab.example.com/api/v4/users?per_page=100&active" | jq --compact-output '.[] | [.id,.name,.username]'
+```
+
+### Block or Delete Users that have no projects or groups
+
+```ruby
+users = User.where('id NOT IN (select distinct(user_id) from project_authorizations)')
+
+# How many users will be removed?
+users.count
+
+# If that count looks sane:
+
+# You can either block the users:
+users.each { |user| user.block! }
+
+# Or you can delete them:
+ # need 'current user' (your user) for auditing purposes
+current_user = User.find_by(username: '<your username>')
+
+users.each do |user|
+ DeleteUserWorker.perform_async(current_user.id, user.id)
+end
+```
+
+### Block Users that have no recent activity
+
+```ruby
+days_inactive = 60
+inactive_users = User.active.where("last_activity_on <= ?", days_inactive.days.ago)
+
+inactive_users.each do |user|
+ puts "user '#{user.username}': #{user.last_activity_on}"
+ user.block!
+end
+```
+
+### Find Max permissions for project/group
+
+```ruby
+user = User.find_by_username 'username'
+project = Project.find_by_full_path 'group/project'
+user.max_member_access_for_project project.id
+```
+
+```ruby
+user = User.find_by_username 'username'
+group = Group.find_by_full_path 'group'
+user.max_member_access_for_group group.id
+```
+
+## Groups
+
+### Count unique users in a group and sub-groups
+
+```ruby
+group = Group.find_by_path_or_name("groupname")
+members = []
+for member in group.members_with_descendants
+ members.push(member.user_name)
+end
+
+members.uniq.length
+```
+
+```ruby
+group = Group.find_by_path_or_name("groupname")
+
+# Count users from subgroup and up (inherited)
+group.members_with_parents.count
+
+# Count users from parent group and down (specific grants)
+parent.members_with_descendants.count
+```
+
+### Delete a group
+
+```ruby
+GroupDestroyWorker.perform_async(group_id, user_id)
+```
+
+## LDAP
+
+### LDAP commands in the rails console
+
+TIP: **TIP:**
+Use the rails runner to avoid entering the rails console in the first place.
+This is great when only a single command (such as a UserSync or GroupSync)
+is needed.
+
+```ruby
+# Get debug output
+Rails.logger.level = Logger::DEBUG
+
+# Run a UserSync (normally performed once a day)
+LdapSyncWorker.new.perform
+
+# Run a GroupSync for all groups (9.3-)
+LdapGroupSyncWorker.new.perform
+
+# Run a GroupSync for all groups (9.3+)
+LdapAllGroupsSyncWorker.new.perform
+
+# Run a GroupSync for a single group (10.6-)
+group = Group.find_by(name: 'my_gitlab_group')
+EE::Gitlab::LDAP::Sync::Group.execute_all_providers(group)
+
+# Run a GroupSync for a single group (10.6+)
+group = Group.find_by(name: 'my_gitlab_group')
+EE::Gitlab::Auth::LDAP::Sync::Group.execute_all_providers(group)
+
+# Query an LDAP group directly (10.6-)
+adapter = Gitlab::LDAP::Adapter.new('ldapmain') # If `main` is the LDAP provider
+ldap_group = EE::Gitlab::LDAP::Group.find_by_cn('group_cn_here', adapter)
+ldap_group.member_dns
+ldap_group.member_uids
+
+# Query an LDAP group directly (10.6+)
+adapter = Gitlab::Auth::LDAP::Adapter.new('ldapmain') # If `main` is the LDAP provider
+ldap_group = EE::Gitlab::Auth::LDAP::Group.find_by_cn('group_cn_here', adapter)
+ldap_group.member_dns
+ldap_group.member_uids
+
+# Lookup a particular user (10.6+)
+# This could expose potential errors connecting to and/or querying LDAP that may seem to
+# fail silently in the GitLab UI
+adapter = Gitlab::Auth::LDAP::Adapter.new('ldapmain') # If `main` is the LDAP provider
+user = Gitlab::Auth::LDAP::Person.find_by_uid('<username>',adapter)
+
+# Query the LDAP server directly (10.6+)
+## For an example, see https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/ee/gitlab/auth/ldap/adapter.rb
+adapter = Gitlab::Auth::LDAP::Adapter.new('ldapmain')
+options = {
+ # the :base is required
+ # use adapter.config.base for the base or .group_base for the group_base
+ base: adapter.config.group_base,
+
+ # :filter is optional
+ # 'cn' looks for all "cn"s under :base
+ # '*' is the search string - here, it's a wildcard
+ filter: Net::LDAP::Filter.eq('cn', '*'),
+
+ # :attributes is optional
+ # the attributes we want to get returned
+ attributes: %w(dn cn memberuid member submember uniquemember memberof)
+}
+adapter.ldap_search(options)
+```
+
+### Update user accounts when the `dn` and email change
+
+The following will require that any accounts with the new email address are removed.
+Emails have to be unique in GitLab. This is expected to work but unverified as of yet:
+
+```ruby
+# Here's an example with a couple users.
+# Each entry will have to include the old username and the new email
+emails = {
+ 'ORIGINAL_USERNAME' => 'NEW_EMAIL_ADDRESS',
+ ...
+}
+
+emails.each do |username, email|
+ user = User.find_by_username(username)
+ user.email = email
+ user.skip_reconfirmation!
+ user.save!
+end
+
+# Run the UserSync to update the above users' data
+LdapSyncWorker.new.perform
+```
+
+## Routes
+
+### Remove redirecting routes
+
+See <https://gitlab.com/gitlab-org/gitlab-ce/issues/41758#note_54828133>.
+
+```ruby
+path = 'foo'
+conflicting_permanent_redirects = RedirectRoute.matching_path_and_descendants(path)
+
+# Check that conflicting_permanent_redirects is as expected
+conflicting_permanent_redirects.destroy_all
+```
+
+## Merge Requests
+
+### Find Merge Request
+
+```ruby
+m = project.merge_requests.find_by(iid: <IID>)
+m = MergeRequest.find_by_title('NEEDS UNIQUE TITLE!!!')
+```
+
+### Close a merge request properly (if merged but still marked as open)
+
+```ruby
+p = Project.find_by_full_path('')
+m = project.merge_requests.find_by(iid: )
+u = User.find_by_username('')
+MergeRequests::PostMergeService.new(p, u).execute(m)
+```
+
+### Rebase manually
+
+```ruby
+p = Project.find_by_full_path('')
+m = project.merge_requests.find_by(iid: )
+u = User.find_by_username('')
+MergeRequests::RebaseService.new(m.target_project, u).execute(m)
+```
+
+## CI
+
+### Cancel stuck pending pipelines
+
+See <https://gitlab.com/gitlab-com/support-forum/issues/2449#note_41929707>.
+
+```ruby
+Ci::Pipeline.where(project_id: p.id).where(status: 'pending').count
+Ci::Pipeline.where(project_id: p.id).where(status: 'pending').each {|p| p.cancel}
+Ci::Pipeline.where(project_id: p.id).where(status: 'pending').count
+```
+
+### Manually modify runner minutes
+
+```ruby
+Namespace.find_by_full_path("user/proj").namespace_statistics.update(shared_runners_seconds: 27360)
+```
+
+### Remove artifacts more than a week old
+
+```ruby
+### SELECTING THE BUILDS TO CLEAR
+# For a single project:
+project = Project.find_by_full_path('')
+builds_with_artifacts = project.builds.with_artifacts_archive
+
+# Prior to 10.6 the above line would be:
+# builds_with_artifacts = project.builds.with_artifacts
+
+# Instance-wide:
+builds_with_artifacts = Ci::Build.with_artifacts
+
+### CLEAR THEM OUT
+builds_to_clear = builds_with_artifacts.where("finished_at < ?", 1.week.ago)
+builds_to_clear.each do |build|
+ build.artifacts_expire_at = Time.now
+ build.erase_erasable_artifacts!
+end
+```
+
+### Find reason failure (for when build trace is empty) (Introduced in 10.3.0)
+
+See <https://gitlab.com/gitlab-org/gitlab-ce/issues/41111>.
+
+```ruby
+build = Ci::Build.find(78420)
+
+build.failure_reason
+
+build.dependencies.each do |d| { puts "status: #{d.status}, finished at: #{d.finished_at},
+ completed: #{d.complete?}, artifacts_expired: #{d.artifacts_expired?}, erased: #{d.erased?}" }
+```
+
+### Disable strict artifact checking (Introduced in GitLab 10.3.0)
+
+See <https://docs.gitlab.com/ee/administration/job_artifacts.html#validation-for-dependencies>.
+
+```ruby
+Feature.enable('ci_disable_validates_dependencies')
+```
+
+### Remove CI traces older than 6 months
+
+```ruby
+current_user = User.find_by_email('cindy@gitlap.com')
+Ci::Build.where("finished_at < ?", 6.months.ago.to_date).each {|b| puts b.id; b.erase(erased_by: current_user) if b.erasable?};nil
+```
+
+### Try CI service
+
+```ruby
+p = Project.find_by_full_path('')
+m = project.merge_requests.find_by(iid: )
+m.project.try(:ci_service)
+```
+
+### Disable AutoDevOps on Existing Projects
+
+```ruby
+Project.all.each do |p|
+ p.auto_devops_attributes={"enabled"=>"0"}
+ p.save
+end
+```
+
+## License
+
+### See license plan name (since v9.3.0-ee)
+
+```ruby
+License.current.plan
+```
+
+### Check if a project feature is available on the instance
+
+Features listed in <https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/app/models/license.rb>.
+
+```ruby
+License.current.feature_available?(:jira_dev_panel_integration)
+```
+
+### Check if a project feature is available in a project
+
+Features listed in <https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/app/models/license.rb>.
+
+```ruby
+p = Project.find_by_full_path('<group>/<project>')
+p.feature_available?(:jira_dev_panel_integration)
+```
+
+### Add a license through the console
+
+```ruby
+key = "<key>"
+license = License.new(data: key)
+license.save
+License.current # check to make sure it applied
+```
+
+## Unicorn
+
+From [Zendesk ticket #91083](https://gitlab.zendesk.com/agent/tickets/91083) (internal)
+
+### Poll unicorn requests by seconds
+
+```ruby
+require 'rubygems'
+require 'unicorn'
+
+# Usage for this program
+def usage
+ puts "ruby unicorn_status.rb <path to unix socket> <poll interval in seconds>"
+ puts "Polls the given Unix socket every interval in seconds. Will not allow you to drop below 3 second poll intervals."
+ puts "Example: /opt/gitlab/embedded/bin/ruby poll_unicorn.rb /var/opt/gitlab/gitlab-rails/sockets/gitlab.socket 10"
+end
+
+# Look for required args. Throw usage and exit if they don't exist.
+if ARGV.count < 2
+ usage
+ exit 1
+end
+
+# Get the socket and threshold values.
+socket = ARGV[0]
+threshold = (ARGV[1]).to_i
+
+# Check threshold - is it less than 3? If so, set to 3 seconds. Safety first!
+if threshold.to_i < 3
+ threshold = 3
+end
+
+# Check - does that socket exist?
+unless File.exist?(socket)
+ puts "Socket file not found: #{socket}"
+ exit 1
+end
+
+# Poll the given socket every THRESHOLD seconds as specified above.
+puts "Running infinite loop. Use CTRL+C to exit."
+puts "------------------------------------------"
+loop do
+ Raindrops::Linux.unix_listener_stats([socket]).each do |addr, stats|
+ puts DateTime.now.to_s + " Active: " + stats.active.to_s + " Queued: " + stats.queued.to_s
+ end
+ sleep threshold
+end
+```
+
+## Sidekiq
+
+### Size of a queue
+
+```ruby
+Sidekiq::Queue.new('background_migration').size
+```
+
+### Kill a worker's Sidekiq jobs
+
+```ruby
+queue = Sidekiq::Queue.new('repository_import')
+queue.each { |job| job.delete if <condition>}
+```
+
+### Enable debug logging of Sidekiq
+
+```ruby
+gitlab_rails['env'] = {
+ 'SIDEKIQ_LOG_ARGUMENTS' => "1"
+}
+```
+
+Then `gitlab-ctl reconfigure; gitlab-ctl restart sidekiq`. The Sidekiq logs will now include additional data for troubleshooting.
+
+### Sidekiq kill signals
+
+See <https://github.com/mperham/sidekiq/wiki/Signals#ttin>.
+
+## Redis
+
+### Connect to redis (omnibus)
+
+```sh
+/opt/gitlab/embedded/bin/redis-cli -s /var/opt/gitlab/redis/redis.socket
+```
+
+### Connect to redis (HA)
+
+```sh
+/opt/gitlab/embedded/bin/redis-cli -h <host ip> -a <password>
+```
+
+## LFS
+
+### Get info about LFS objects and associated project
+
+```ruby
+o=LfsObject.find_by(oid: "<oid>")
+p=Project.find(LfsObjectsProject.find_by_lfs_object_id(o.id).project_id)
+```
+
+You can then delete these records from the database with:
+
+```ruby
+LfsObjectsProject.find_by_lfs_object_id(o.id).destroy
+o.destroy
+```
+
+You would also want to combine this with deleting the LFS file in the LFS storage
+area on disk. It remains to be seen exactly how or whether the deletion is useful, however.
+
+## Decryption Problems
+
+### Bad Decrypt Script (for encrypted variables)
+
+See <https://gitlab.com/snippets/1730735/raw>.
+
+This script will go through all the encrypted variables and count how many are not able
+to be decrypted. Might be helpful to run on multiple nodes to see which `gitlab-secrets.json`
+file is most up to date:
+
+```bash
+wget -O /tmp/bad-decrypt.rb https://gitlab.com/snippets/1730735/raw
+gitlab-rails runner /tmp/bad-decrypt.rb
+```
+
+If `ProjectImportData Bad count:` is detected and the decision is made to delete the
+encrypted credentials to allow manual reentry:
+
+```ruby
+ # Find the ids of the corrupt ProjectImportData objects
+ total = 0
+ bad = []
+ ProjectImportData.find_each do |data|
+ begin
+ total += 1
+ data.credentials
+ rescue => e
+ bad << data.id
+ end
+ end
+
+ puts "Bad count: #{bad.count} / #{total}"
+
+ # See the bad ProjectImportData ids
+ bad
+
+ # Remove the corrupted credentials
+ import_data = ProjectImportData.where(id: bad)
+ import_data.each do |data|
+ data.update_columns({ encrypted_credentials: nil, encrypted_credentials_iv: nil, encrypted_credentials_salt: nil})
+ end
+```
+
+If `User OTP Secret Bad count:` is detected. For each user listed disable/enable
+two-factor authentication.
+
+### Decrypt Script for encrypted tokens
+
+This script will search for all encrypted tokens that are causing decryption errors,
+and update or reset as needed:
+
+```bash
+wget -O /tmp/encrypted-tokens.rb https://gitlab.com/snippets/1876342/raw
+gitlab-rails runner /tmp/encrypted-tokens.rb
+```
+
+## Geo
+
+### Artifacts
+
+#### Find failed artifacts
+
+```ruby
+Geo::JobArtifactRegistry.failed
+```
+
+#### Download artifact
+
+```ruby
+Gitlab::Geo::JobArtifactDownloader.new(:job_artifact, <artifact_id>).execute
+```
+
+#### Get a count of the synced artifacts
+
+```ruby
+Geo::JobArtifactRegistry.synced.count
+```
+
+#### Find `ID` of synced artifacts that are missing on primary
+
+```ruby
+Geo::JobArtifactRegistry.synced.missing_on_primary.pluck(:artifact_id)
+```
+
+### Repository verification failures
+
+#### Get the number of verification failed repositories
+
+```ruby
+Geo::ProjectRegistryFinder.new.count_verification_failed_repositories
+```
+
+#### Find the verification failed repositories
+
+```ruby
+Geo::ProjectRegistry.verification_failed_repos
+```
+
+### Find repositories that failed to sync
+
+```ruby
+Geo::ProjectRegistryFinder.new.find_failed_project_registries('repository')
+```
+
+### Resync repositories
+
+#### Queue up all repositories for resync. Sidekiq will handle each sync
+
+```ruby
+Geo::ProjectRegistry.update_all(resync_repository: true, resync_wiki: true)
+```
+
+#### Sync individual repository now
+
+```ruby
+project = Project.find_by_full_path('<group/project>')
+
+Geo::RepositorySyncService.new(project).execute
+```