Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support performance trace #32973

Merged
merged 18 commits into from
Jan 21, 2025
Merged

Support performance trace #32973

merged 18 commits into from
Jan 21, 2025

Conversation

wxiaoguang
Copy link
Contributor

@wxiaoguang wxiaoguang commented Dec 24, 2024

  1. Add a OpenTelemetry-like shim-layer to collect traces
  2. Add a simple builtin trace collector and exporter, end users could download the diagnosis report to get the traces.

This PR's design is quite lightweight, no hard-dependency, and it is easy to improve or remove. We can try it on gitea.com first to see whether it works well, and fine tune the details.

@GiteaBot GiteaBot added the lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. label Dec 24, 2024
@pull-request-size pull-request-size bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Dec 24, 2024
@wxiaoguang wxiaoguang marked this pull request as draft December 24, 2024 17:33
@github-actions github-actions bot added modifies/translation modifies/go Pull requests that update Go code modifies/templates This PR modifies the template files labels Dec 24, 2024
@wxiaoguang wxiaoguang changed the title Support performance trace WIP: Support performance trace Dec 24, 2024
@wxiaoguang wxiaoguang force-pushed the support-trace branch 6 times, most recently from bd47ebb to 4b7547b Compare December 25, 2024 03:19
# Conflicts:
#	modules/web/routing/context.go
@wxiaoguang
Copy link
Contributor Author

wxiaoguang commented Jan 16, 2025

Background: OpenTelemetry is cool for k8s, or users who have setup a complete infrastructure.

For many small to medium instance users who only use single binary installation or docker-compose installation, it seems too heavy for them to install OpenTelemetry eco-system tools to collect performance reports.

This PR introduces an OpenTelemetry-like "tracer" shim-layer and introduces a simple builtin tracer, then every site admin could open their admin panel to find the performance trace logs and report them to Gitea's issue tracker to help to resolve the performance problems.

For advanced users, they could also introduce the OpenTelemetry tracer by implementing "traceStarter interface" (just like trace_builtin.go). To be discussed: In the future, if the OpenTelemetry integration is mature enough and eco-system tools could be packed into Gitea's release package and many users need to use it out-of-box, then we could provide OpenTelemetry exporter officially.

Discussions could go to #32866

screenshots

image

image


Sample:
http duration=0.1796s http.route=/{username}/{reponame}/{type:pulls}/{index}/files
  http.func duration=0.1796s func=proxy.ForwardedHeaders
    http.func duration=0.1796s func=routing.(*requestRecordsManager).handler-fm
      http.func duration=0.1795s func=gzhttp.NewWrapper
        http.func duration=0.1787s func=session.Sessioner
          http.func duration=0.1783s func=context.Contexter
            http.func duration=0.1783s func=web.webAuth
              database duration=0.0028s db.sql=SELECT `id`, `lower_name`, `name`, `full_name`, `email`, `keep_email_private`, `email_notifications_preference`, `passwd`, `passwd_hash_algo`, `must_change_password`, `login_type`, `login_source`, `login_name`, `type`, `location`, `website`, `rands`, `salt`, `language`, `description`, `created_unix`, `updated_unix`, `last_login_unix`, `last_repo_visibility`, `max_repo_creation`, `is_active`, `is_admin`, `is_restricted`, `allow_git_hook`, `allow_import_local`, `allow_create_organization`, `prohibit_login`, `avatar`, `avatar_email`, `use_custom_avatar`, `num_followers`, `num_following`, `num_stars`, `num_repos`, `num_teams`, `num_members`, `visibility`, `repo_admin_change_team_access`, `diff_view_style`, `theme`, `keep_activity_private` FROM `user` WHERE `id`=? LIMIT 1
              http.func duration=0.1752s func=middleware.GetHead
                http.func duration=0.1752s func=user.GetNotificationCount
                  http.func duration=0.1752s func=repo.GetActiveStopwatch
                    database duration=0.0013s db.sql=SELECT * FROM `stopwatch` INNER JOIN `issue` ON issue.id = stopwatch.issue_id INNER JOIN `repository` ON repository.id = issue.repo_id WHERE (user_id = ?) LIMIT 1
                    http.func duration=0.1737s func=web.goGet
                      http.func duration=0.1737s func=web.verifyAuthWithOptions
                        http.func duration=0.1737s func=context.RepoAssignment
                          database duration=0.0011s db.sql=SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `default_wiki_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `num_projects`, `num_closed_projects`, `num_action_runs`, `num_closed_action_runs`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `git_size`, `lfs_size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `object_format_name`, `trust_model`, `avatar`, `created_unix`, `updated_unix`, `archived_unix` FROM `repository` WHERE (`owner_id`=?) AND (`lower_name`=?) LIMIT 1
                          database duration=0.0010s db.sql=SELECT `id`, `repo_id`, `type`, `config`, `created_unix`, `everyone_access_mode` FROM `repo_unit` WHERE (repo_id = ?)
                          database duration=0.0008s db.sql=SELECT `id`, `repo_id`, `user_id`, `mode`, `created_unix`, `updated_unix` FROM `collaboration` WHERE `repo_id`=? AND `user_id`=? LIMIT 1
                          database duration=0.0008s db.sql=SELECT count(*) FROM `release` WHERE repo_id=? AND sha1<>?
                          database duration=0.0015s db.sql=SELECT count(*) FROM `release` WHERE repo_id=? AND is_draft=? AND is_tag=?
                          database duration=0.0010s db.sql=SELECT 1 FROM `repository` WHERE (owner_id=? AND fork_id=?) LIMIT 1
                          database duration=0.0010s db.sql=SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `default_wiki_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `num_projects`, `num_closed_projects`, `num_action_runs`, `num_closed_action_runs`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `git_size`, `lfs_size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `object_format_name`, `trust_model`, `avatar`, `created_unix`, `updated_unix`, `archived_unix` FROM `repository` WHERE (fork_id = ?) AND (owner_id = ?) LIMIT 1
                          database duration=0.0014s db.sql=SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `default_wiki_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `num_projects`, `num_closed_projects`, `num_action_runs`, `num_closed_action_runs`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `git_size`, `lfs_size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `object_format_name`, `trust_model`, `avatar`, `created_unix`, `updated_unix`, `archived_unix` FROM `repository` WHERE fork_id=? AND owner_id IN (SELECT org_id FROM org_user WHERE uid=?)
                          database duration=0.0009s db.sql=SELECT `id`, `user_id`, `repo_id`, `mode`, `created_unix`, `updated_unix` FROM `watch` WHERE `user_id`=? AND `repo_id`=? LIMIT 1
                          database duration=0.0009s db.sql=SELECT `id`, `uid`, `repo_id`, `created_unix` FROM `star` WHERE `uid`=? AND `repo_id`=? LIMIT 1
                          database duration=0.0013s db.sql=SELECT count(*) FROM `branch` WHERE repo_id=? AND is_deleted=?
                          http.func duration=0.1610s func=repo.MustAllowPulls
                            database duration=0.0010s db.sql=SELECT 1 FROM `repository` WHERE (owner_id=? AND fork_id=?) LIMIT 1
                            http.func duration=0.1601s func=context.RequireUnitReader
                              http.func duration=0.1601s func=repo.SetEditorconfigIfExists
                                git-run duration=0.0087s func.caller=git.ensureValidGitRepository git.command=/opt/homebrew/bin/git ...global... rev-parse
                                git-run duration: (not ended) func.caller=git.catFileBatchCheck.func3 git.command=/opt/homebrew/bin/git ...global... cat-file --batch-check
                                git-run duration=0.0061s func.caller=git.(*Repository).hashObject git.command=/opt/homebrew/bin/git ...global... hash-object --stdin
                                git-run duration=0.0072s func.caller=git.ensureValidGitRepository git.command=/opt/homebrew/bin/git ...global... rev-parse
                                git-run duration: (not ended) func.caller=git.catFileBatch.func3 git.command=/opt/homebrew/bin/git ...global... cat-file --batch
                                http.func duration=0.1276s func=repo.SetDiffViewStyle
                                  database duration=0.0054s db.sql=UPDATE `user` SET `diff_view_style` = ?, `updated_unix` = ? WHERE `id`=?
                                  http.func duration=0.1220s func=repo.SetWhitespaceBehavior
                                    database duration=0.0010s db.sql=SELECT `id`, `user_id`, `setting_key`, `setting_value` FROM `user_setting` WHERE `user_id`=? AND `setting_key`=? LIMIT 1
                                    http.func duration=0.1209s func=repo.SetShowOutdatedComments
                                      database duration=0.0011s db.sql=SELECT `id`, `user_id`, `setting_key`, `setting_value` FROM `user_setting` WHERE `user_id`=? AND `setting_key`=? LIMIT 1
                                      http.func duration=0.1197s func=repo.ViewPullFilesForAllCommitsOfPr
                                        database duration=0.0008s db.sql=SELECT `id`, `repo_id`, `index`, `poster_id`, `original_author`, `original_author_id`, `name`, `content`, `content_version`, `milestone_id`, `priority`, `is_closed`, `is_pull`, `num_comments`, `ref`, `pin_order`, `deadline_unix`, `created_unix`, `updated_unix`, `closed_unix`, `is_locked`, `time_estimate` FROM `issue` WHERE `repo_id`=? AND `index`=? LIMIT 1
                                        database duration=0.0006s db.sql=SELECT `id`, `lower_name`, `name`, `full_name`, `email`, `keep_email_private`, `email_notifications_preference`, `passwd`, `passwd_hash_algo`, `must_change_password`, `login_type`, `login_source`, `login_name`, `type`, `location`, `website`, `rands`, `salt`, `language`, `description`, `created_unix`, `updated_unix`, `last_login_unix`, `last_repo_visibility`, `max_repo_creation`, `is_active`, `is_admin`, `is_restricted`, `allow_git_hook`, `allow_import_local`, `allow_create_organization`, `prohibit_login`, `avatar`, `avatar_email`, `use_custom_avatar`, `num_followers`, `num_following`, `num_stars`, `num_repos`, `num_teams`, `num_members`, `visibility`, `repo_admin_change_team_access`, `diff_view_style`, `theme`, `keep_activity_private` FROM `user` WHERE `id`=? LIMIT 1
                                        database duration=0.0006s db.sql=SELECT `id`, `owner_id`, `owner_name`, `lower_name`, `name`, `description`, `website`, `original_service_type`, `original_url`, `default_branch`, `default_wiki_branch`, `num_watches`, `num_stars`, `num_forks`, `num_issues`, `num_closed_issues`, `num_pulls`, `num_closed_pulls`, `num_milestones`, `num_closed_milestones`, `num_projects`, `num_closed_projects`, `num_action_runs`, `num_closed_action_runs`, `is_private`, `is_empty`, `is_archived`, `is_mirror`, `status`, `is_fork`, `fork_id`, `is_template`, `template_id`, `size`, `git_size`, `lfs_size`, `is_fsck_enabled`, `close_issues_via_commit_in_any_branch`, `topics`, `object_format_name`, `trust_model`, `avatar`, `created_unix`, `updated_unix`, `archived_unix` FROM `repository` WHERE `id`=? LIMIT 1
                                        database duration=0.0006s db.sql=SELECT `id`, `type`, `status`, `conflicted_files`, `commits_ahead`, `commits_behind`, `changed_protected_files`, `issue_id`, `index`, `head_repo_id`, `base_repo_id`, `head_branch`, `base_branch`, `merge_base`, `allow_maintainer_edit`, `has_merged`, `merged_commit_id`, `merger_id`, `merged_unix`, `flow` FROM `pull_request` WHERE issue_id=? LIMIT 1
                                        database duration=0.0013s db.sql=UPDATE `issue_user` SET is_read=? WHERE uid=? AND issue_id=?
                                        database duration=0.0005s db.sql=SELECT `id`, `user_id`, `repo_id`, `status`, `source`, `issue_id`, `commit_id`, `comment_id`, `updated_by`, `created_unix`, `updated_unix` FROM `notification` WHERE (user_id = ?) AND (issue_id = ?) LIMIT 1
                                        database duration=0.0013s db.sql=SELECT `id`, `repo_id`, `name`, `commit_id`, `commit_message`, `pusher_id`, `is_deleted`, `deleted_by_id`, `deleted_unix`, `commit_time`, `created_unix`, `updated_unix` FROM `branch` WHERE (repo_id=?) AND (name=?) LIMIT 1
                                        database duration=0.0008s db.sql=SELECT `id`, `repo_id`, `branch_name`, `priority`, `can_push`, `enable_whitelist`, `whitelist_user_i_ds`, `whitelist_team_i_ds`, `enable_merge_whitelist`, `whitelist_deploy_keys`, `merge_whitelist_user_i_ds`, `merge_whitelist_team_i_ds`, `can_force_push`, `enable_force_push_allowlist`, `force_push_allowlist_user_i_ds`, `force_push_allowlist_team_i_ds`, `force_push_allowlist_deploy_keys`, `enable_status_check`, `status_check_contexts`, `enable_approvals_whitelist`, `approvals_whitelist_user_i_ds`, `approvals_whitelist_team_i_ds`, `required_approvals`, `block_on_rejected_reviews`, `block_on_official_review_requests`, `block_on_outdated_branch`, `dismiss_stale_approvals`, `ignore_stale_approvals`, `require_signed_commits`, `protected_file_patterns`, `unprotected_file_patterns`, `block_admin_merge_override`, `created_unix`, `updated_unix` FROM `protected_branch` WHERE (repo_id = ?) ORDER BY `created_unix` ASC
                                        database duration=0.0006s db.sql=SELECT `id`, `repo_id`, `type`, `config`, `created_unix`, `everyone_access_mode` FROM `repo_unit` WHERE (repo_id = ?)
                                        database duration=0.0007s db.sql=SELECT `id`, `repo_id`, `user_id`, `mode`, `created_unix`, `updated_unix` FROM `collaboration` WHERE `repo_id`=? AND `user_id`=? LIMIT 1
                                        database duration=0.0008s db.sql=SELECT `id`, `lower_name`, `name`, `full_name`, `email`, `keep_email_private`, `email_notifications_preference`, `passwd`, `passwd_hash_algo`, `must_change_password`, `login_type`, `login_source`, `login_name`, `type`, `location`, `website`, `rands`, `salt`, `language`, `description`, `created_unix`, `updated_unix`, `last_login_unix`, `last_repo_visibility`, `max_repo_creation`, `is_active`, `is_admin`, `is_restricted`, `allow_git_hook`, `allow_import_local`, `allow_create_organization`, `prohibit_login`, `avatar`, `avatar_email`, `use_custom_avatar`, `num_followers`, `num_following`, `num_stars`, `num_repos`, `num_teams`, `num_members`, `visibility`, `repo_admin_change_team_access`, `diff_view_style`, `theme`, `keep_activity_private` FROM `user` WHERE `id`=? LIMIT 1
                                        database duration=0.0008s db.sql=SELECT `id`, `repo_id`, `branch_name`, `priority`, `can_push`, `enable_whitelist`, `whitelist_user_i_ds`, `whitelist_team_i_ds`, `enable_merge_whitelist`, `whitelist_deploy_keys`, `merge_whitelist_user_i_ds`, `merge_whitelist_team_i_ds`, `can_force_push`, `enable_force_push_allowlist`, `force_push_allowlist_user_i_ds`, `force_push_allowlist_team_i_ds`, `force_push_allowlist_deploy_keys`, `enable_status_check`, `status_check_contexts`, `enable_approvals_whitelist`, `approvals_whitelist_user_i_ds`, `approvals_whitelist_team_i_ds`, `required_approvals`, `block_on_rejected_reviews`, `block_on_official_review_requests`, `block_on_outdated_branch`, `dismiss_stale_approvals`, `ignore_stale_approvals`, `require_signed_commits`, `protected_file_patterns`, `unprotected_file_patterns`, `block_admin_merge_override`, `created_unix`, `updated_unix` FROM `protected_branch` WHERE (repo_id = ?) ORDER BY `created_unix` ASC
                                        database duration=0.0006s db.sql=SELECT `id`, `repo_id`, `user_id`, `mode`, `created_unix`, `updated_unix` FROM `collaboration` WHERE `repo_id`=? AND `user_id`=? LIMIT 1
                                        database duration=0.0007s db.sql=SELECT `id`, `repo_id`, `branch_name`, `priority`, `can_push`, `enable_whitelist`, `whitelist_user_i_ds`, `whitelist_team_i_ds`, `enable_merge_whitelist`, `whitelist_deploy_keys`, `merge_whitelist_user_i_ds`, `merge_whitelist_team_i_ds`, `can_force_push`, `enable_force_push_allowlist`, `force_push_allowlist_user_i_ds`, `force_push_allowlist_team_i_ds`, `force_push_allowlist_deploy_keys`, `enable_status_check`, `status_check_contexts`, `enable_approvals_whitelist`, `approvals_whitelist_user_i_ds`, `approvals_whitelist_team_i_ds`, `required_approvals`, `block_on_rejected_reviews`, `block_on_official_review_requests`, `block_on_outdated_branch`, `dismiss_stale_approvals`, `ignore_stale_approvals`, `require_signed_commits`, `protected_file_patterns`, `unprotected_file_patterns`, `block_admin_merge_override`, `created_unix`, `updated_unix` FROM `protected_branch` WHERE (repo_id = ?) ORDER BY `created_unix` ASC
                                        git-run duration=0.0078s func.caller=git.(*Repository).CommitsBetweenLimit git.command=/opt/homebrew/bin/git ...global... rev-list --max-count 50 --skip 0 be87cde17745a3b8c294db652e4f19f7cb54dd7e..2eaac46dd26790ba72556f18ccdbc07c7bebde32
                                        database duration=0.0008s db.sql=SELECT max( `index` ) as `index` FROM `commit_status` WHERE (repo_id = ?) AND (sha = ?) GROUP BY context_hash ORDER BY max( `index` ) desc
                                        database duration=0.0007s db.sql=SELECT count(*) FROM (SELECT context_hash FROM `commit_status` WHERE (repo_id = ?) AND (sha = ?) GROUP BY context_hash) sub
                                        git-run duration=0.0058s func.caller=git.GetFullCommitID git.command=/opt/homebrew/bin/git ...global... rev-parse refs/pull/36/head
                                        git-run duration=0.0065s func.caller=git.(*Repository).GetMergeBase git.command=/opt/homebrew/bin/git ...global... merge-base -- refs/heads/branch-a refs/pull/36/head
                                        git-run duration=0.0058s func.caller=git.GetFullCommitID git.command=/opt/homebrew/bin/git ...global... rev-parse refs/heads/branch-a
                                        git-run duration=0.0066s func.caller=git.(*Repository).GetCompareInfo git.command=/opt/homebrew/bin/git ...global... log --pretty=format:%H be87cde17745a3b8c294db652e4f19f7cb54dd7e...refs/pull/36/head --
                                        git-run duration=0.0068s func.caller=git.(*Repository).GetDiffNumChangedFiles git.command=/opt/homebrew/bin/git ...global... diff -z --name-only refs/heads/branch-a...refs/pull/36/head --
                                        git-run duration=0.0060s func.caller=gitdiff.GetDiff.func2 git.command=/opt/homebrew/bin/git ...global... diff --src-prefix=\a/ --dst-prefix=\b/ -M be87cde17745a3b8c294db652e4f19f7cb54dd7e 2eaac46dd26790ba72556f18ccdbc07c7bebde32 --
                                        git-run duration=0.0068s func.caller=git.(*Repository).readTreeToIndex git.command=/opt/homebrew/bin/git ...global... read-tree 2eaac46dd26790ba72556f18ccdbc07c7bebde32
                                        git-run duration=0.0132s func.caller=git.(*CheckAttributeReader).Run git.command=/opt/homebrew/bin/git ...global... check-attr --stdin -z --cached linguist-vendored linguist-generated linguist-documentation linguist-detectable linguist-language gitlab-language
                                        git-run duration=0.0062s func.caller=git.GetDiffShortStat git.command=/opt/homebrew/bin/git ...global... diff --shortstat be87cde17745a3b8c294db652e4f19f7cb54dd7e...2eaac46dd26790ba72556f18ccdbc07c7bebde32
                                        database duration=0.0030s db.sql=SELECT `id`, `user_id`, `pull_id`, `commit_sha`, `updated_files`, `updated_unix` FROM `review_state` WHERE (user_id = ?) AND (pull_id = ?) ORDER BY updated_unix DESC LIMIT 1
                                        database duration=0.0026s db.sql=SELECT `id`, `type`, `poster_id`, `original_author`, `original_author_id`, `issue_id`, `label_id`, `old_project_id`, `project_id`, `old_milestone_id`, `milestone_id`, `time_id`, `assignee_id`, `removed_assignee`, `assignee_team_id`, `resolve_doer_id`, `old_title`, `new_title`, `old_ref`, `new_ref`, `dependent_issue_id`, `commit_id`, `line`, `tree_path`, `content`, `content_version`, `patch`, `created_unix`, `updated_unix`, `commit_sha`, `review_id`, `invalidated`, `ref_repo_id`, `ref_issue_id`, `ref_comment_id`, `ref_action`, `ref_is_pull`, `comment_meta_data` FROM `comment` WHERE comment.issue_id=? AND comment.type=? AND invalidated=? ORDER BY `comment`.`created_unix` ASC, `comment`.`id` ASC
                                        database duration=0.0013s db.sql=SELECT `id`, `lower_name`, `name`, `full_name`, `email`, `keep_email_private`, `email_notifications_preference`, `passwd`, `passwd_hash_algo`, `must_change_password`, `login_type`, `login_source`, `login_name`, `type`, `location`, `website`, `rands`, `salt`, `language`, `description`, `created_unix`, `updated_unix`, `last_login_unix`, `last_repo_visibility`, `max_repo_creation`, `is_active`, `is_admin`, `is_restricted`, `allow_git_hook`, `allow_import_local`, `allow_create_organization`, `prohibit_login`, `avatar`, `avatar_email`, `use_custom_avatar`, `num_followers`, `num_following`, `num_stars`, `num_repos`, `num_teams`, `num_members`, `visibility`, `repo_admin_change_team_access`, `diff_view_style`, `theme`, `keep_activity_private` FROM `user` WHERE `id` IN (?)
                                        database duration=0.0017s db.sql=SELECT `id`, `uuid`, `repo_id`, `issue_id`, `release_id`, `uploader_id`, `comment_id`, `name`, `download_count`, `size`, `created_unix` FROM `attachment` WHERE `comment_id` IN (?)
                                        database duration=0.0014s db.sql=SELECT `id`, `type`, `reviewer_id`, `reviewer_team_id`, `original_author`, `original_author_id`, `issue_id`, `content`, `official`, `commit_id`, `stale`, `dismissed`, `created_unix`, `updated_unix` FROM `review` WHERE `id` IN (?)
                                        database duration=0.0011s db.sql=SELECT `id`, `type`, `issue_id`, `comment_id`, `user_id`, `original_author_id`, `original_author`, `created_unix` FROM `reaction` WHERE reaction.issue_id=? AND reaction.comment_id=? AND `reaction`.`type` IN (?,?,?,?,?,?,?,?) ORDER BY `reaction`.`issue_id` ASC, `reaction`.`comment_id` ASC, `reaction`.`created_unix` ASC, `reaction`.`id` ASC
                                        database duration=0.0007s db.sql=SELECT count(*) FROM `reaction` WHERE reaction.issue_id=? AND reaction.comment_id=? AND `reaction`.`type` IN (?,?,?,?,?,?,?,?)
                                        database duration=0.0006s db.sql=SELECT `id`, `uuid`, `repo_id`, `issue_id`, `release_id`, `uploader_id`, `comment_id`, `name`, `download_count`, `size`, `created_unix` FROM `attachment` WHERE (comment_id=?)
                                        database duration=0.0006s db.sql=SELECT `id`, `repo_id`, `branch_name`, `priority`, `can_push`, `enable_whitelist`, `whitelist_user_i_ds`, `whitelist_team_i_ds`, `enable_merge_whitelist`, `whitelist_deploy_keys`, `merge_whitelist_user_i_ds`, `merge_whitelist_team_i_ds`, `can_force_push`, `enable_force_push_allowlist`, `force_push_allowlist_user_i_ds`, `force_push_allowlist_team_i_ds`, `force_push_allowlist_deploy_keys`, `enable_status_check`, `status_check_contexts`, `enable_approvals_whitelist`, `approvals_whitelist_user_i_ds`, `approvals_whitelist_team_i_ds`, `required_approvals`, `block_on_rejected_reviews`, `block_on_official_review_requests`, `block_on_outdated_branch`, `dismiss_stale_approvals`, `ignore_stale_approvals`, `require_signed_commits`, `protected_file_patterns`, `unprotected_file_patterns`, `block_admin_merge_override`, `created_unix`, `updated_unix` FROM `protected_branch` WHERE (repo_id = ?) ORDER BY `created_unix` ASC
                                        database duration=0.0006s db.sql=SELECT user_id FROM `access` WHERE (repo_id = ? AND mode >= ?)
                                        database duration=0.0008s db.sql=SELECT `id`, `lower_name`, `name`, `full_name`, `email`, `keep_email_private`, `email_notifications_preference`, `passwd`, `passwd_hash_algo`, `must_change_password`, `login_type`, `login_source`, `login_name`, `type`, `location`, `website`, `rands`, `salt`, `language`, `description`, `created_unix`, `updated_unix`, `last_login_unix`, `last_repo_visibility`, `max_repo_creation`, `is_active`, `is_admin`, `is_restricted`, `allow_git_hook`, `allow_import_local`, `allow_create_organization`, `prohibit_login`, `avatar`, `avatar_email`, `use_custom_avatar`, `num_followers`, `num_following`, `num_stars`, `num_repos`, `num_teams`, `num_members`, `visibility`, `repo_admin_change_team_access`, `diff_view_style`, `theme`, `keep_activity_private` FROM `user` WHERE `id` IN (?,?,?,?,?,?) AND `user`.is_active=? ORDER BY name
                                        database duration=0.0010s db.sql=SELECT `id`, `type`, `reviewer_id`, `reviewer_team_id`, `original_author`, `original_author_id`, `issue_id`, `content`, `official`, `commit_id`, `stale`, `dismissed`, `created_unix`, `updated_unix` FROM `review` WHERE issue_id=? AND reviewer_id=? AND type IN (?) ORDER BY `created_unix` ASC, `id` ASC
                                        database duration=0.0006s db.sql=SELECT name FROM `branch` WHERE repo_id=? AND is_deleted=? ORDER BY commit_time DESC, name ASC
                                        database duration=0.0006s db.sql=SELECT `id`, `repo_id`, `user_id`, `mode`, `created_unix`, `updated_unix` FROM `collaboration` WHERE `repo_id`=? AND `user_id`=? LIMIT 1
                                        database duration=0.0005s db.sql=SELECT `id`, `setting_key`, `setting_value`, `version`, `created`, `updated` FROM `system_setting` WHERE setting_key=? LIMIT 1
                                        database duration=0.0005s db.sql=SELECT count(*) FROM `notification` WHERE notification.user_id=? AND notification.status=?
                                        database duration=0.0007s db.sql=SELECT `id`, `blocker_id`, `blockee_id`, `note`, `created_unix` FROM `user_blocking` WHERE user_blocking.blockee_id=? AND user_blocking.blocker_id IN (?) LIMIT 1

@github-actions github-actions bot added the modifies/cli PR changes something on the CLI, i.e. gitea doctor or gitea admin label Jan 16, 2025
@wxiaoguang wxiaoguang changed the title WIP: Support performance trace Support performance trace Jan 16, 2025
@wxiaoguang wxiaoguang marked this pull request as ready for review January 16, 2025 13:43
@wxiaoguang wxiaoguang added this to the 1.24.0 milestone Jan 16, 2025
@wxiaoguang
Copy link
Contributor Author

It would be better if the padding on the right could be removed, but it is also fine if it is not removed.It's just my personal opinion

It is only used internally, end users won't see it.

@hiifong
Copy link
Member

hiifong commented Jan 18, 2025

image
image

It seems that I can't track the data being migrated, or maybe I don't understand this feature well enough.

@TheFox0x7
Copy link
Contributor

I did see the logger system. I do find it impressive and I mean this as a compliment - I tried to port to to log/slog and got so lost I gave up trying. It does a lot and it does it well. I don't mean to question your skills, sorry if it came out this way.

I do however question the actual need here. There's no legacy system to swap, noop is built-in and default if no tracer is configured. API isn't that big and it's a standard.
It's the fact that the actual conversion from semconv has to take place, it's the fact that there's need to add those 70 lines of wrappers and I'm limited to single tracer.
This feels to me like over-engineering a solution which already gets frowned on for being over-engineered.

I would have one last argument for using the otel API but I don't think any library with uses it is being included so it's dead on arrival: Integrating with the api would allow for cross library traces to be captured.


I do not mean "no". Actually I do not see why we can't work in our shim framework. The otel part could be done separately without touching existing code.

That sounds like "no" to me though. I don't understand why can't we use the api to our advantage over putting a shim, over an abstraction. So I'm on the opposite end of the argument and on a losing position because of 500KB (otel api size) at this point.

Ultimately, I do think that this implementation will end up in main regardless of how much I'll oppose the approach here and I'll end up having to have an out of tree patch with otel...

@wxiaoguang
Copy link
Contributor Author

It seems that I can't track the data being migrated, or maybe I don't understand this feature well enough.

This PR is only an initial. To trace a system correctly, it needs a lot of fine-tune works. It's impossible to make the trace system work well by just adding a 3rd library.

To answer your question:

  • /migrate is a background task, so you are not able to trace it by the HTTP request.
  • /user/events should be omitted in most cases -- if there is no slow query.

These improvements should be done in the future.

@wxiaoguang
Copy link
Contributor Author

I would have one last argument for using the otel API but I don't think any library with uses it is being included so it's dead on arrival: Integrating with the api would allow for cross library traces to be captured.

I think it's not in Gitea's current scope because there is no cross-library calls in Gitea. Otel is well-designed for micro-services in K8s. If one day Gitea becomes large enough and need to cross-trace between services, we could simple drop the gtprof trace and only use otel, it seems to be another story and I do not know when it would really happen.

@TheFox0x7
Copy link
Contributor

I would have one last argument for using the otel API but I don't think any library with uses it is being included so it's dead on arrival: Integrating with the api would allow for cross library traces to be captured.

I think it's not in Gitea's current scope because there is no cross-library calls in Gitea. Otel is well-designed for micro-services in K8s. If one day Gitea becomes large enough and need to cross-trace between services, we could simple drop the gtprof trace and only use otel, it seems to be another story and I do not know when it would really happen.

I think I might've phrased it wrong. Suppose xorm has an otel integration (it doesn't but let's assume this for arguments sake). By plugging into otel system you can trace xorm operations directly, which gtprof can't do.
This is what I had in mind. Now is this a good argument for it? Well... no. I don't think any library pulls otel.


I believe merging this will close my proposal as "not planned".

@wxiaoguang
Copy link
Contributor Author

This is what I had in mind. Now is this a good argument for it? Well... no. I don't think any library pulls otel.

To be honest, I do not think (and seldom see) any general library would really integrate otel by default -- otel is really good for a in-house system, but not for a general library. The otel package itself isn't in a stable stage yet -- changes a lot.

If otel wins in the future (in almost every library), we could also just integrate it without any concern. But I guess we won't see it in near future.

@wxiaoguang
Copy link
Contributor Author

I believe merging this will close my proposal as "not planned".

Well, my proposal is to add otel as a "trace plugin" (the For advanced users section in the second comment), and we can maintain a branch for it, and improve the tracing system step by step. But if you insist to drop the bulitin tracer and only use otel, I don't know how to get a consensus because at the moment the builtin tracer is important/necessary to help end users ......

@TheFox0x7
Copy link
Contributor

Well, my proposal is to add otel as a "trace plugin" (the For advanced users section in the second comment), and we can maintain a branch for it, and improve the tracing system step by step. But if you insist to drop the bulitin tracer and only use otel, I don't know how to get a consensus because at the moment the builtin tracer is important/necessary to help end users ......

I don't insist on dropping built-in tracer at all! In fact the fact it works is really impressive and a lot better than what I tried to do with custom exporter. And it is a really great idea!
What I want (or ask for) is to have your system as a provider in regular otel as I did in my PoC. I fully believe this meets my requirements while keeping internal tracer fully featured.

@lunny
Copy link
Member

lunny commented Jan 18, 2025

I think we have a traditional to have an abstract layer for third-party libraries. This will make it easier to introduce another tracer plugin that has a different interface from OTEL. If we use OTEL's interface, it will lose the flexibility. I think the current design will not prevent the use of OTEL's plugins for a OTEL implementation.

@wxiaoguang
Copy link
Contributor Author

I don't insist on dropping built-in tracer at all! In fact the fact it works is really impressive and a lot better than what I tried to do with custom exporter. And it is a really great idea!
What I want (or ask for) is to have your system as a provider in regular otel as I did in my PoC. I fully believe this meets my requirements while keeping internal tracer fully featured.

Hmm .... maybe because that when I read your branch, I see it removes the builtin tracer to add otel, it misleads me. It's not quite clear to me about what's your idea to make the builtin and otel tracers work together. My proposal is to just write Start() once in business code (git/http/db) and let the gtprof trace package handle the details.

Anyway, I think this PR won't block the otel's integration, I think we can move on and try to figure out a best solution step by step.

@TheFox0x7
Copy link
Contributor

I think we have a traditional to have an abstract layer for third-party libraries. This will make it easier to introduce another tracer plugin that has a different interface from OTEL.

Well... that's fair. Unfortunate for me but fair.

Hmm .... maybe because that when I read your branch, I see it removes the builtin tracer to add otel, it misleads me. It's not quite clear to me about what's your idea to make the builtin and otel tracers work together.

Then to make it clear what my idea was - my proposal is to have internal tracer because it's a great idea, but register it as a otel provider. I'm not even arguing for adding sdk and exporters because you're absolutely right that they weigh a lot and most users would consider it bloat instead of a feature.
I'm arguing for usage of the api so we both have even playing field, with all default tricks I can do with otel working with no (or close to no) effort on your side and internal tracer gets to ignore all things it deems useless bloat.

In semi valid golang
// Skipping imports

// Most useful line here, which isn't the greatest idea to do in init but it will do until external exporters would be added
func init(){
  otel.SetTracerProvider(NewInternalProvider())
}

type GiteaInternalTracerProvider struct {embedded.TracerProvider ,tracer GiteaInternalTracer}
type GiteaInternalTracer struct {embedded.Tracer} // Which is basically Tracer already in place
type GiteaInternalSpan struct {noop.Span} // Which is basically your span implementation without the need for internal spans. Noop is added so every non defined method is automatically a noop

// Create new provider
func NewInternalProvider() trace.TracerProvider{
return GiteaInternalTracerProvider{tracer: GiteaInternalTracer{}}
}
// Return tracer
func (itp GiteaInternalTracerProvider) Tracer(name string, options ...trace.TracerOption) trace.Tracer{ return it.tracer }

func (it GiteaInternalTracer) Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span){
// Current Start func
}

Anyway, I think this PR won't block the otel's integration, I think we can move on and try to figure out a best solution step by step.

Worst case scenario I'll maintain a patch for myself. I'll complain about it but I can't do much besides that.

@GiteaBot GiteaBot added lgtm/need 1 This PR needs approval from one additional maintainer to be merged. and removed lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. labels Jan 20, 2025
@GiteaBot GiteaBot added lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. and removed lgtm/need 1 This PR needs approval from one additional maintainer to be merged. labels Jan 20, 2025
@lunny lunny added the reviewed/wait-merge This pull request is part of the merge queue. It will be merged soon. label Jan 21, 2025
@lunny lunny enabled auto-merge (squash) January 21, 2025 18:31
@lunny lunny merged commit 7069369 into go-gitea:main Jan 21, 2025
26 checks passed
@GiteaBot GiteaBot removed the reviewed/wait-merge This pull request is part of the merge queue. It will be merged soon. label Jan 21, 2025
@wxiaoguang wxiaoguang deleted the support-trace branch January 22, 2025 01:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. modifies/cli PR changes something on the CLI, i.e. gitea doctor or gitea admin modifies/go Pull requests that update Go code modifies/templates This PR modifies the template files modifies/translation size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants