使用 GitHub Actions 構(gòu)建 Golang PGO
共 31529字,需瀏覽 64分鐘
·
2024-04-24 19:53
今年 2 月,我宣布 Dolt 版本現(xiàn)已構(gòu)建為配置文件引導(dǎo)優(yōu)化 (pgo) 二進(jìn)制文件,利用 Golang 1.20 的強(qiáng)大功能將 Dolt 的讀取性能提高 5.3%。
在我宣布這一消息之前,我們的一位常駐 Golang 專(zhuān)家 Zach 試驗(yàn)并測(cè)試了 Golang 的 pgo 功能,并寫(xiě)下了他在使用 Dolt 構(gòu)建后觀(guān)察到的性能提升,該配置文件首先針對(duì) Dolt 運(yùn)行我們的 Sysbench 基準(zhǔn)測(cè)試。從那時(shí)起,我們知道我們必須將這些性能提升納入我們發(fā)布的二進(jìn)制文件中,因此我們重新設(shè)計(jì)了 Dolt 的發(fā)布流程來(lái)構(gòu)建 pgo 版本。
今天,我將介紹 Dolt 的一般發(fā)布流程,該流程使用 GitHub Actions,并且我將詳細(xì)介紹發(fā)布流程的所有階段。我還將回顧一下我們?cè)陂_(kāi)始發(fā)布 pgo 版本的過(guò)程中所做的更改。希望這能讓您收集一些可用于將 pgo 構(gòu)建到您自己的 Golang 版本中的見(jiàn)解!
讓我們開(kāi)始吧。
Dolt 通過(guò) GitHub Actions 發(fā)布
Dolt 利用 GitHub Actions 執(zhí)行許多自動(dòng)化任務(wù),其中之一是創(chuàng)建和發(fā)布版本。
GitHub Actions 使用稱(chēng)為工作流的文件來(lái)定義作業(yè),它將執(zhí)行工作流文件中定義的工作。這些作業(yè)部署到運(yùn)行器或主機(jī)上,您可以自行托管或允許 GitHub 為您托管。
自托管運(yùn)行器由您在 GitHub Actions 外部配置和維護(hù)。GitHub 托管的運(yùn)行器可免費(fèi)用于公共存儲(chǔ)庫(kù),全部由 GitHub 托管和維護(hù),但它們具有特定的存儲(chǔ)、內(nèi)存和 CPU 限制,具體取決于您的訂閱級(jí)別。對(duì)于 Dolt,我們使用免費(fèi)的 GitHub 托管運(yùn)行器。
從高層次來(lái)看,Dolt 發(fā)布流程需要實(shí)現(xiàn)一些目標(biāo)。
首先,也是最重要的,該過(guò)程需要為新版本的 Dolt 創(chuàng)建標(biāo)簽和發(fā)布,并將 Dolt 的預(yù)編譯二進(jìn)制文件上傳到發(fā)布資產(chǎn)。
其次,發(fā)布過(guò)程需要針對(duì)這個(gè)新版本的 Dolt 運(yùn)行我們的 Sysbench 基準(zhǔn)測(cè)試,并將結(jié)果通過(guò)電子郵件發(fā)送給我們的 DoltHub 團(tuán)隊(duì)。
第三,與本博客不太相關(guān),該流程需要啟動(dòng)我們?cè)诎l(fā)布期間需要執(zhí)行的任何其他輔助任務(wù),例如創(chuàng)建依賴(lài)于多個(gè)存儲(chǔ)庫(kù)的拉取請(qǐng)求描述的 Dolt 發(fā)行說(shuō)明、將版本發(fā)布到各種包管理器這樣就可以輕松地從它們安裝,將新的 Docker 鏡像推送到 DockerHub,或者升級(jí)我們擁有的各種存儲(chǔ)庫(kù)中的 Dolt 依賴(lài)項(xiàng)。
因此,考慮到這些目標(biāo),我們提出了一套 GitHub Actions 工作流程,利用repository_dispatch 事件,以便我們可以實(shí)現(xiàn)每個(gè)目標(biāo)。讓我們看一個(gè)圖表,該圖表顯示了此設(shè)計(jì)的原理,然后我們將深入了解工作流程的細(xì)節(jié)。
在上圖中,您將看到兩個(gè)上下文:GitHub Actions 上下文和 Kubernetes (K8s) 上下文。我們首先討論 GitHub Actions 上下文。
對(duì)于 Dolt 的原始發(fā)布流程,我們使用了三個(gè)工作流程:“發(fā)布 Dolt”工作流程、“部署 K8s Sysbench 基準(zhǔn)測(cè)試作業(yè)”工作流程和“電子郵件團(tuán)隊(duì)”工作流程。
“發(fā)布 Dolt”工作流程啟動(dòng)整個(gè) Dolt 發(fā)布流程,并由我們的工程團(tuán)隊(duì)在準(zhǔn)備發(fā)布新版本 Dolt 時(shí)手動(dòng)運(yùn)行。這是工作流程的簡(jiǎn)化版本,引用了上圖中顯示的步驟。
name: Release Dolton:workflow_dispatch:inputs:version:description: 'SemVer format release tag, i.e. 0.24.5'required: truejobs::: ubuntu-22.04outputs:version: ${{ steps.format_version.outputs.version }}steps:name: Format Inputid: format_versionrun: |version="${{ github.event.inputs.version }}"if [[ $version == v* ]];thenversion="${version:1}"fiecho "version=$version" >> $GITHUB_OUTPUT:needs: format-versionname: Create release: ubuntu-22.04outputs:release_id: ${{ steps.create_release.outputs.id }}steps:name: Checkout codeuses: actions/checkout@v3name: Set up Go 1.xuses: actions/setup-go@v3with:: ^1.21name: Update dolt version commandrun: sed -i -e 's/ Version = ".*"/ Version = "'"$NEW_VERSION"'"/' "$FILE"env:FILE: ${{ format('{0}/go/cmd/dolt/dolt.go', github.workspace) }}NEW_VERSION: ${{ needs.format-version.outputs.version }}name: Set minver TBD to versionrun: sed -i -e 's/minver:"TBD"/minver:"'"$NEW_VERSION"'"/' "$FILE"env:FILE: ${{ format('{0}/go/cmd/dolt/commands/sqlserver/yaml_config.go', github.workspace) }}NEW_VERSION: ${{ needs.format-version.outputs.version }}name: update minver_validation.txt: ./gorun: go run -mod=readonly ./utils/genminver_validation/ $FILEenv:FILE: ${{ format('{0}/go/cmd/dolt/commands/sqlserver/testdata/minver_validation.txt', github.workspace) }}uses: EndBug/[email protected]with:message: ${{ format('[ga-bump-release] Update Dolt version to {0} and release v{0}', needs.format-version.outputs.version) }}add: ${{ format('["{0}/go/cmd/dolt/dolt.go", "{0}/go/cmd/dolt/commands/sqlserver/yaml_config.go", "{0}/go/cmd/dolt/commands/sqlserver/testdata/minver_validation.txt"]', github.workspace) }}cwd: "."pull: "--ff"name: Build Binariesid: build_binariesrun: |latest=$(git rev-parse HEAD)echo "commitish=$latest" >> $GITHUB_OUTPUTGO_BUILD_VERSION=1.21 go/utils/publishrelease/buildbinaries.shname: Create Releaseid: create_releaseuses: dolthub/create-release@v1env:GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}with:tag_name: v${{ needs.format-version.outputs.version }}release_name: ${{ needs.format-version.outputs.version }}draft: falseprerelease: falsecommitish: ${{ steps.build_binaries.outputs.commitish }}name: Upload Linux AMD64 Distroid: upload-linux-amd64-distrouses: dolthub/upload-release-asset@v1env:GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}with:upload_url: ${{ steps.create_release.outputs.upload_url }}asset_path: go/out/dolt-linux-amd64.tar.gzasset_name: dolt-linux-amd64.tar.gzasset_content_type: application/zip...name: Upload Install Scriptid: upload-install-scriptuses: dolthub/upload-release-asset@v1env:GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}with:upload_url: ${{ steps.create_release.outputs.upload_url }}asset_path: go/out/install.shasset_name: install.shasset_content_type: text/plain:needs: [format-version, create-release]: ubuntu-22.04steps:name: Trigger Performance Benchmarksuses: peter-evans/[email protected]with:token: ${{ secrets.REPO_ACCESS_TOKEN }}: release-dolt: '{"version": "${{ needs.format-version.outputs.version }}", "actor": "${{ github.actor }}"}'
此工作流是使用workflow_dispatch 事件手動(dòng)觸發(fā)的,并且需要新的版本號(hào)作為輸入。從那里,它對(duì)版本輸入進(jìn)行一些快速格式化,然后將此新版本寫(xiě)入并提交到 Dolt 的 main 分支,以便發(fā)布的二進(jìn)制文件將從 dolt version 命令輸出此新版本。
在“構(gòu)建二進(jìn)制文件”步驟中, create-release 作業(yè)運(yùn)行 buildbinaries.sh 腳本,該腳本使用運(yùn)行 go build 命令的 Golang Docker 容器從源代碼構(gòu)建 Dolt。
我們使用 Docker 容器來(lái)構(gòu)建 Dolt,以便堆棧跟蹤的路徑輸出是通用 Linux go 路徑,而不是引用運(yùn)行器或我們的一臺(tái)個(gè)人計(jì)算機(jī)上的 go 安裝的路徑(這發(fā)生在 Dolt ?? 的早期版本中)。
接下來(lái),“創(chuàng)建版本”步驟創(chuàng)建標(biāo)簽并在 GitHub 上發(fā)布版本。它還提供了一個(gè) upload_url ,用于 create-release 作業(yè)的所有后續(xù)步驟,將已編譯的二進(jìn)制文件上傳到新的 GitHub 版本。
此工作流程的最后部分是在所有先前作業(yè)完成后運(yùn)行的另一個(gè)作業(yè)。該作業(yè)稱(chēng)為 trigger-performance-benchmark-email 。它使用我們?cè)谄涫袌?chǎng)上找到的 GitHub Action 來(lái)發(fā)出 repository_dispatch 事件,該事件啟動(dòng)單獨(dú)的 Dolt 工作流程。如果我們回顧一下我們的圖表,我們就可以看到這一點(diǎn)。
我們的圖表顯示了“發(fā)布 Dolt”工作流程的最后一步,該工作流程指向另一個(gè)名為“部署 K8s Sysbench 基準(zhǔn)測(cè)試作業(yè)”的工作流程。這是由 trigger-performance-benchmark-email 工作流作業(yè)啟動(dòng)的工作流。
此工作流以及其他類(lèi)似工作流被設(shè)計(jì)為部分異步分派,以便它不會(huì)僅與“Release Dolt”工作流緊密耦合。
事實(shí)上,各種工作流程將使用 repository_dispatch 事件觸發(fā)此工作流程,因?yàn)槲覀冃枰诓煌瑫r(shí)間運(yùn)行性能基準(zhǔn)測(cè)試,而不僅僅是在發(fā)布期間運(yùn)行。有趣的是,這個(gè)工作流程本身啟動(dòng)了另一個(gè)異步流程,我們可以在箭頭所示的圖中看到它 - 它部署了一個(gè)運(yùn)行我們的 Sysbench 基準(zhǔn)測(cè)試的 K8s 作業(yè)。
事實(shí)上,我們已經(jīng)寫(xiě)了很多關(guān)于 Dolt 使用 Sysbench 對(duì) Dolt 和 MySQL 進(jìn)行基準(zhǔn)測(cè)試來(lái)比較它們的性能的文章,但我認(rèn)為我們沒(méi)有討論具體如何做到這一點(diǎn)的實(shí)現(xiàn)細(xì)節(jié)。這個(gè)博客對(duì)我來(lái)說(shuō)是一個(gè)很好的回顧,所以我會(huì)暫時(shí)回顧一下。在開(kāi)始之前,讓我們簡(jiǎn)要了解一下“部署 K8s Sysbench 基準(zhǔn)測(cè)試作業(yè)”工作流程。
name: Benchmark Latencyon:repository_dispatch:types: [ benchmark-latency ]jobs:performance:: ubuntu-22.04name: Benchmark Performancestrategy:matrix:dolt_fmt: [ "__DOLT__" ]steps:name: Checkoutuses: actions/checkout@v3uses: azure/[email protected]with:version: 'v1.23.6'name: Install aws-iam-authenticatorrun: |curl -o aws-iam-authenticator https://amazon-eks.s3.us-west-2.amazonaws.com/1.18.8/2020-09-18/bin/linux/amd64/aws-iam-authenticator && \chmod +x ./aws-iam-authenticator && \sudo cp ./aws-iam-authenticator /usr/local/bin/aws-iam-authenticatorversionname: Configure AWS Credentialsuses: aws-actions/[email protected]with:: ${{ secrets.AWS_ACCESS_KEY_ID }}: ${{ secrets.AWS_SECRET_ACCESS_KEY }}: us-west-2name: Create and Auth kubeconfigrun: |echo "$CONFIG" > kubeconfigKUBECONFIG=kubeconfig kubectl config set-credentials github-actions-dolt --exec-api-version=client.authentication.k8s.io/v1alpha1 --exec-command=aws-iam-authenticator --exec-arg=token --exec-arg=-i --exec-arg=eks-cluster-1KUBECONFIG=kubeconfig kubectl config set-context github-actions-dolt-context --cluster=eks-cluster-1 --user=github-actions-dolt --namespace=performance-benchmarkingKUBECONFIG=kubeconfig kubectl config use-context github-actions-dolt-contextenv:CONFIG: ${{ secrets.CORP_KUBECONFIG }}name: Create Sysbench Performance Benchmarking K8s Jobrun: ./.github/scripts/performance-benchmarking/run-benchmarks.shenv:FROM_SERVER: ${{ github.event.client_payload.from_server }}FROM_VERSION: ${{ github.event.client_payload.from_version }}TO_SERVER: ${{ github.event.client_payload.to_server }}TO_VERSION: ${{ github.event.client_payload.to_version }}MODE: ${{ github.event.client_payload.mode }}ISSUE_NUMBER: ${{ github.event.client_payload.issue_number }}ACTOR: ${{ github.event.client_payload.actor }}ACTOR_EMAIL: ${{ github.event.client_payload.actor_email }}REPO_ACCESS_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}KUBECONFIG: "./kubeconfig"INIT_BIG_REPO: ${{ github.event.client_payload.init_big_repo }}NOMS_BIN_FORMAT: ${{ matrix.dolt_fmt }}TEMPLATE_SCRIPT: ${{ github.event.client_payload.template_script }}name: Create TPCC Performance Benchmarking K8s Jobrun: ./.github/scripts/performance-benchmarking/run-benchmarks.shenv:FROM_SERVER: ${{ github.event.client_payload.from_server }}FROM_VERSION: ${{ github.event.client_payload.from_version }}TO_SERVER: ${{ github.event.client_payload.to_server }}TO_VERSION: ${{ github.event.client_payload.to_version }}MODE: ${{ github.event.client_payload.mode }}ISSUE_NUMBER: ${{ github.event.client_payload.issue_number }}ACTOR: ${{ github.event.client_payload.actor }}ACTOR_EMAIL: ${{ github.event.client_payload.actor_email }}REPO_ACCESS_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}KUBECONFIG: "./kubeconfig"INIT_BIG_REPO: ${{ github.event.client_payload.init_big_repo }}NOMS_BIN_FORMAT: ${{ matrix.dolt_fmt }}WITH_TPCC: "true"TEMPLATE_SCRIPT: ${{ github.event.client_payload.template_script }}
這個(gè)工作流程雖然簡(jiǎn)短,但有點(diǎn)忙碌,它根據(jù) K8s 集群對(duì) kubectl 客戶(hù)端進(jìn)行身份驗(yàn)證,我們?cè)谄渲羞\(yùn)行 Sysbench 基準(zhǔn)測(cè)試,并提供運(yùn)行名為 run-benchmarks.sh 的腳本所需的環(huán)境變量。該腳本使用這些變量的值編寫(xiě) K8s 作業(yè)配置文件,然后應(yīng)用它,從而在我們的 K8s 集群中部署基準(zhǔn)測(cè)試作業(yè)。
此時(shí),您可能想知道為什么我們選擇在 K8s 集群中運(yùn)行 Dolt 基準(zhǔn)測(cè)試,而不是僅僅使用 GitHub Actions 及其運(yùn)行程序來(lái)對(duì) Dolt 進(jìn)行基準(zhǔn)測(cè)試。這有幾個(gè)原因。
第一,GitHub 托管的運(yùn)行程序有非常具體的限制,至少對(duì)于免費(fèi)層來(lái)說(shuō)是這樣,并且為了對(duì)我們的數(shù)據(jù)庫(kù)進(jìn)行基準(zhǔn)測(cè)試,我們不一定希望受到這些限制。
此外,當(dāng)我們進(jìn)行基準(zhǔn)測(cè)試運(yùn)行時(shí),我們無(wú)法知道或控制 GitHub 托管的運(yùn)行器上正在運(yùn)行哪些其他進(jìn)程或軟件,這可能會(huì)以不可預(yù)測(cè)的方式對(duì)運(yùn)行結(jié)果產(chǎn)生負(fù)面影響。
雖然當(dāng)然可以在 GitHub Actions 中使用自托管運(yùn)行器來(lái)規(guī)避這兩個(gè)問(wèn)題,在這種情況下,我們可以?xún)H使用 GitHub Actions 對(duì) Dolt 進(jìn)行基準(zhǔn)測(cè)試,但我們已經(jīng)在 K8s 集群中提供了可輕松配置的主機(jī),因此我們選擇只需使用它們即可。
事實(shí)上, applying 我們的 K8s 基準(zhǔn)測(cè)試作業(yè)將使用 K8s 集群自動(dòng)縮放器配置一個(gè)新的基準(zhǔn)測(cè)試主機(jī),這非??帷?/span>
無(wú)論如何,暫時(shí)回到我們的圖表,我們看到在驗(yàn)證 kubectl 客戶(hù)端之后,“部署 Sysbench 基準(zhǔn)測(cè)試作業(yè)”工作流程部署 K8s 作業(yè),并且該流程移動(dòng)到 K8s 上下文,并且“K8s Sysbench 基準(zhǔn)測(cè)試作業(yè)”正在運(yùn)行。
現(xiàn)在從技術(shù)上講,原始 Dolt 發(fā)布過(guò)程的這一部分更多的是發(fā)布后步驟。在 GitHub 上創(chuàng)建新的 Dolt 版本不需要運(yùn)行基準(zhǔn)測(cè)試作業(yè),它只是為我們的團(tuán)隊(duì)提供有關(guān)該版本延遲的報(bào)告。但重要的是要了解我們最初發(fā)布過(guò)程的這一部分,這樣我們對(duì) Dolt 發(fā)布過(guò)程的 pgo 更新才會(huì)更有意義,但稍后會(huì)詳細(xì)介紹。
在該圖的 K8s 上下文中,我們可以看到基準(zhǔn)測(cè)試作業(yè)執(zhí)行了幾個(gè)步驟。它根據(jù)提供的提交 SHA 構(gòu)建 Dolt 二進(jìn)制文件。在本例中,它是來(lái)自 Dolt main HEAD 的 SHA。
接下來(lái),它針對(duì)已編譯的 Dolt 版本運(yùn)行 Sysbench 測(cè)試,然后將 Sysbench 運(yùn)行結(jié)果上傳到 AWS S3 存儲(chǔ)桶。最后,它觸發(fā)位于 Dolt 存儲(chǔ)庫(kù)中的不同 GitHub Actions 工作流程,稱(chēng)為“電子郵件團(tuán)隊(duì)”工作流程。
為了執(zhí)行所有這些基準(zhǔn)測(cè)試以及上傳和觸發(fā),我們編寫(xiě)了一個(gè)內(nèi)部工具,可用于針對(duì) MySQL 版本對(duì) Dolt 版本進(jìn)行基準(zhǔn)測(cè)試。
該工具使用我們?cè)?Dolt 存儲(chǔ)庫(kù)中維護(hù)的一些庫(kù)代碼,但我將提供內(nèi)部工具和庫(kù)代碼中的一些相關(guān)片段,以便您了解我們?nèi)绾螌?shí)現(xiàn)這些代碼來(lái)運(yùn)行基準(zhǔn)測(cè)試。
我們的內(nèi)部基準(zhǔn)測(cè)試工具代碼本質(zhì)上是以下 go 函數(shù):
func compare(ctx context.Context,fromServer,toServer runner.ServerType,fromVersion,toVersion,fromProfile,toProfile,dir,doltCommand,doltgresCommand,mysqlExec,mysqlProtocol,mysqlSocketPath,postgresExec,initDbExec,nomsBinFormat,resultsDir,resultsPrefix,resultsFilename,scriptDir,schema,outputFormat string,defaultRuns int,initBigRepo,useDoltHubLuaScriptsRepo,writeResultsToFile bool,queries []string) (string, error) {config := benchmark.NewComparisonBenchmarkingConfig(fromServer,toServer,fromVersion,toVersion,fromProfile,toProfile,dir,doltCommand,doltgresCommand,mysqlExec,mysqlProtocol,mysqlSocketPath,postgresExec,initDbExec,nomsBinFormat,scriptDir,defaultRuns,initBigRepo,useDoltHubLuaScriptsRepo)sr := benchmark.NewSysbenchComparer(config)err := sr.Run(ctx)if err != nil {return "", err}fromServerConfig, err := config.GetFromServerConfig(ctx)if err != nil {return "", err}toServerConfig, err := config.GetToServerConfig(ctx)if err != nil {return "", err}resultsDbName := fmt.Sprintf("sysbench-%s", benchmark.ComparisonDbFilename)db := benchmark.NewSqlite3ResultsDb(fromServerConfig, toServerConfig, dir, schema, resultsDir, resultsPrefix, resultsFilename, resultsDbName, outputFormat, queries, writeResultsToFile)uploadDir, err := db.QueryResults(ctx)if err != nil {return "", err}return uploadDir, nil}
compare 用于將一個(gè)數(shù)據(jù)庫(kù)版本與另一個(gè)版本的 Sysbench 結(jié)果進(jìn)行比較。從函數(shù)的參數(shù)可以看出,這個(gè)工具不僅用于Dolt和MySQL,還用于對(duì)我們最新產(chǎn)品DoltgreSQL及其競(jìng)爭(zhēng)對(duì)手PostgreSQL進(jìn)行基準(zhǔn)測(cè)試。
compare 函數(shù)引用 fromServerConfig ,這是“from”數(shù)據(jù)庫(kù)服務(wù)器的配置,并引用 toServerConfig ,這是“from”數(shù)據(jù)庫(kù)服務(wù)器的配置“到”數(shù)據(jù)庫(kù)服務(wù)器。從語(yǔ)義上講,這里該工具將并排比較“源”數(shù)據(jù)庫(kù)和“目標(biāo)”數(shù)據(jù)庫(kù),以便于分析。在 Dolt 發(fā)布過(guò)程中,MySQL 將是“源”服務(wù)器,Dolt 將是“目標(biāo)”服務(wù)器。
您可能還注意到,我們?cè)诖斯ぞ咧惺褂?sqlite3,如 benchmark.NewSqlite3ResultsDb 所引用,它是 Dolt v1.0.0 之前的遺留工件,但它在這里仍然具有一些獨(dú)特的價(jià)值。
在后臺(tái),使用 sr.Run() 運(yùn)行基準(zhǔn)測(cè)試后,我們將結(jié)果加載到 sqlite3 數(shù)據(jù)庫(kù)中,并對(duì)其運(yùn)行一些查詢(xún)以獲得每個(gè)數(shù)據(jù)庫(kù)服務(wù)器的比較結(jié)果。與 Dolt 相比,使用 sqlite3 的一個(gè)好處是, sqlite3 使用簡(jiǎn)單的標(biāo)志以多種格式返回查詢(xún)輸出,如 --html ,這使我們不必編寫(xiě)查詢(xún)結(jié)果轉(zhuǎn)換邏輯。
從 db.QueryResults() 返回的 uploadDir 包含比較查詢(xún)的結(jié)果以及要上傳到 S3 的 sqlite3 數(shù)據(jù)庫(kù)的副本。這些結(jié)果很快就會(huì)被“電子郵件團(tuán)隊(duì)”工作流程下載,我們很快就會(huì)看到。
當(dāng)實(shí)際運(yùn)行 Sysbench 基準(zhǔn)測(cè)試時(shí), benchmark.NewSysbenchComparer(config) 只是我們?cè)?Dolt 存儲(chǔ)庫(kù)中維護(hù)的一些基準(zhǔn)測(cè)試庫(kù)代碼中的 Run 函數(shù)的包裝結(jié)構(gòu)。
func Run(config *Config) error {err := config.Validate()if err != nil {return err}ctx := context.Background()err = sysbenchVersion(ctx)if err != nil {return err}cwd, err := os.Getwd()if err != nil {return err}for _, serverConfig := range config.Servers {var results Resultsvar b Benchmarkerswitch serverConfig.Server {case Dolt:fmt.Println("Running dolt sysbench tests")b = NewDoltBenchmarker(cwd, config, serverConfig)case Doltgres:fmt.Println("Running doltgres sysbench tests")b = NewDoltgresBenchmarker(cwd, config, serverConfig)case MySql:fmt.Println("Running mysql sysbench tests")b = NewMysqlBenchmarker(cwd, config, serverConfig)case Postgres:fmt.Println("Running postgres sysbench tests")b = NewPostgresBenchmarker(cwd, config, serverConfig)default:panic(fmt.Sprintf("unexpected server type: %s", serverConfig.Server))}results, err = b.Benchmark(ctx)if err != nil {return err}fmt.Printf("Successfuly finished %s\n", serverConfig.Server)err = WriteResults(serverConfig, results)if err != nil {return err}fmt.Printf("Successfuly wrote results for %s\n", serverConfig.Server)}return nil}
此函數(shù)根據(jù)它看到的服務(wù)器類(lèi)型創(chuàng)建一個(gè) Benchmarker ,然后調(diào)用 Benchmark() ,后者針對(duì)該服務(wù)器運(yùn)行 Sysbench 測(cè)試。下面是 Dolt Benchmarker 的 Benchmark() 實(shí)現(xiàn)的示例:
func (b *doltBenchmarkerImpl) Benchmark(ctx context.Context) (Results, error) {err := b.checkInstallation(ctx)if err != nil {return nil, err}err = b.updateGlobalConfig(ctx)if err != nil {return nil, err}testRepo, err := b.initDoltRepo(ctx)if err != nil {return nil, err}serverParams, err := b.serverConfig.GetServerArgs()if err != nil {return nil, err}server := NewServer(ctx, testRepo, b.serverConfig, syscall.SIGTERM, serverParams)err = server.Start(ctx)if err != nil {return nil, err}tests, err := GetTests(b.config, b.serverConfig, nil)if err != nil {return nil, err}results := make(Results, 0)for i := 0; i < b.config.Runs; i++ {for _, test := range tests {tester := NewSysbenchTester(b.config, b.serverConfig, test, stampFunc)r, err := tester.Test(ctx)if err != nil {server.Stop(ctx)return nil, err}results = append(results, r)}}err = server.Stop(ctx)if err != nil {return nil, err}return results, os.RemoveAll(testRepo)}
在 Benchmark() 調(diào)用期間,此實(shí)現(xiàn)將檢查 Dolt 安裝,更新一些全局 Dolt 配置,獲取用于啟動(dòng) Dolt SQL 服務(wù)器的參數(shù),啟動(dòng)服務(wù)器,獲取將要運(yùn)行的 Sysbench 測(cè)試,然后通過(guò)調(diào)用 tester.Test() 運(yùn)行這些測(cè)試。
完成后,它返回 results 并清除寫(xiě)入磁盤(pán)的內(nèi)容。
而且,正如我們?cè)趦?nèi)部工具的 compare 功能中所看到的,這些結(jié)果被加載到 sqlite3 中并上傳到 S3,以便可以通過(guò)電子郵件發(fā)送給 DoltHub 團(tuán)隊(duì)。但是,我們?nèi)匀蝗鄙僖徊?,即在?nèi)部基準(zhǔn)測(cè)試工具完成上傳結(jié)果后,通過(guò) repository_dispatch 事件觸發(fā)“電子郵件團(tuán)隊(duì)”工作流程。
因此,我們內(nèi)部工具的最后一部分包括:
err := d.DispatchEmailReportEvent(ctx, *toVersion, *nomsBinFormat, *bucket, key)if err != nil {log.Fatal(err)}
DispatchEmailReportEvent() 方法位于我們編寫(xiě)的 Dispatcher 接口上。它只是向 GitHub Actions Workflow REST API 發(fā)出 HTTP 請(qǐng)求,該 API 發(fā)出 repository_dispatch 事件,觸發(fā)“電子郵件團(tuán)隊(duì)”工作流運(yùn)行。那么讓我們接下來(lái)看看。
與“部署 K8s Sysbench 基準(zhǔn)測(cè)試作業(yè)”工作流程一樣,“電子郵件團(tuán)隊(duì)”工作流程除了 Dolt 發(fā)布流程之外還被多個(gè)流程使用,因此這就是我們使用 repository_dispatch 事件觸發(fā)它的原因。工作流程文件如下:
name: Email Team Memberson:repository_dispatch:types: [ email-report ]jobs::: ubuntu-22.04name: Email Team Memberssteps:uses: actions/checkout@v3name: Configure AWS Credentialsuses: aws-actions/[email protected]with:: ${{ secrets.AWS_ACCESS_KEY_ID }}: ${{ secrets.AWS_SECRET_ACCESS_KEY }}: us-west-2name: Get Resultsid: get-resultsrun: aws s3api get-object --bucket="$BUCKET" --key="$KEY" results.logenv:KEY: ${{ github.event.client_payload.key }}BUCKET: ${{ github.event.client_payload.bucket }}name: Get Addressesid: get-addressesrun: |addresses="$TEAM"if [ ! -z "$RECIPIENT" ]; thenaddresses="[\"$RECIPIENT\"]"fiecho "addresses=$addresses" >> $GITHUB_OUTPUTenv:RECIPIENT: ${{ github.event.client_payload.email_recipient }}TEAM: '["${{ secrets.PERF_REPORTS_EMAIL_ADDRESS }}"]'name: Send Emailuses: ./.github/actions/ses-email-actionwith:template: ${{ github.event.client_payload.template }}region: us-west-2version: ${{ github.event.client_payload.version }}format: ${{ github.event.client_payload.noms_bin_format }}toAddresses: ${{ steps.get-addresses.outputs.addresses }}dataFile: ${{ format('{0}/results.log', github.workspace) }}
如圖所示,此工作流程的摘要是下載 Dolt 版本的 Sysbench 結(jié)果,然后通過(guò)電子郵件將其發(fā)送給我們的團(tuán)隊(duì);沒(méi)什么瘋狂的。
這就是 Dolt 的發(fā)布過(guò)程?;蛘哌@就是 Dolt 的發(fā)布過(guò)程。現(xiàn)在我將回顧一下我們?nèi)绾胃麓肆鞒桃栽诎l(fā)布時(shí)開(kāi)始構(gòu)建 Dolt 的 pgo 二進(jìn)制文件。
PGO 通過(guò) GitHub Actions 發(fā)布
對(duì)于那些不熟悉 pgo 構(gòu)建的人,他們需要 -pgo 標(biāo)志以及在 go build 命令期間提供的 Golang 配置文件的路徑。這部分其實(shí)很簡(jiǎn)單。但在此之前,您需要?jiǎng)?chuàng)建要用于優(yōu)化構(gòu)建的配置文件,這需要我們更新一些基準(zhǔn)庫(kù)代碼和內(nèi)部工具代碼,以便它們既可以生成配置文件并接受配置文件作為輸入。讓我更詳細(xì)地解釋一下。
在我們的基準(zhǔn)測(cè)試庫(kù)代碼中,我們使用另一個(gè)名為 dolt_builder 的 Dolt 實(shí)用程序來(lái)實(shí)際從源代碼構(gòu)建 Dolt 二進(jìn)制文件。要使用此工具,您只需提供要從中構(gòu)建 Dolt 的提交 SHA 或標(biāo)記,它就會(huì)為您構(gòu)建它。因此,我們?cè)诤芏嗟胤绞褂眠@個(gè)工具來(lái)輕松地同時(shí)構(gòu)建多個(gè)版本的 Dolt。
因此,我們做的第一件事就是更新這個(gè)工具以接受可用于構(gòu)建 Dolt 的 Golang 配置文件:
// goBuild builds the dolt binary and returns the path to the binaryfunc goBuild(ctx context.Context, source, dest, profilePath string) (string, error) {goDir := filepath.Join(source, "go")doltFileName := "dolt"if runtime.GOOS == "windows" {doltFileName = "dolt.exe"}args := make([]string, 0)args = append(args, "build")if profilePath != "" {args = append(args, fmt.Sprintf("-pgo=%s", profilePath))}toBuild := filepath.Join(dest, doltFileName)args = append(args, "-o", toBuild, filepath.Join(goDir, "cmd", "dolt"))build := ExecCommand(ctx, "go", args...)build.Dir = goDirerr := build.Run()if err != nil {return "", err}return toBuild, nil}
我們做的下一件事是更新基準(zhǔn)庫(kù)代碼以在“分析”模式下運(yùn)行。在默認(rèn)模式下,如上所述,此代碼調(diào)用 Benchmark() 并返回結(jié)果。在新的“分析”模式中,代碼在 Profiler 接口上調(diào)用 Profile() :
...case Dolt:// handle a profiling runsc, ok := serverConfig.(ProfilingServerConfig)if ok {if string(sc.GetServerProfile()) != "" {fmt.Println("Profiling dolt while running sysbench tests")p := NewDoltProfiler(cwd, config, sc)return p.Profile(ctx)}}...
Profile() 的工作方式與 Benchmark() 類(lèi)似,但會(huì)創(chuàng)建一個(gè)在運(yùn)行 Sysbench 基準(zhǔn)測(cè)試時(shí)獲取的 golang 配置文件。這使我們能夠輕松生成可在新發(fā)布流程中使用的 Dolt 配置文件。
我們還更新了此庫(kù)代碼以接受配置文件作為輸入。這樣我們就可以向它提供一個(gè)配置文件,它又將其提供給 dolt_builder 以創(chuàng)建 pgo 二進(jìn)制文件,然后運(yùn)行 Sysbench 并輸出這些結(jié)果。
為了澄清,我們基本上更新了這個(gè)庫(kù)代碼,以便我們可以在一種模式下運(yùn)行它來(lái)生成 Golang 配置文件,然后在默認(rèn)模式下運(yùn)行它以獲得正常的基準(zhǔn)測(cè)試結(jié)果,但它也接受 Golang 配置文件作為輸入,并用它來(lái)構(gòu)建 Dolt 和 go build -pgo 。希望這對(duì)你來(lái)說(shuō)有意義,因?yàn)閷?duì)我來(lái)說(shuō)描述有點(diǎn)棘手??。
接下來(lái),我們需要更新使用所有這些庫(kù)代碼的內(nèi)部工具,以擁有“分析”模式并接受 Golang 配置文件作為輸入。我們對(duì)新發(fā)布流程的計(jì)劃是在分析模式下運(yùn)行內(nèi)部工具一次,以創(chuàng)建 Golang 配置文件。然后,在默認(rèn)模式下再次運(yùn)行內(nèi)部工具,但向其提供 Golang 配置文件,這將針對(duì) pgo 構(gòu)建的 Dolt 生成基準(zhǔn)測(cè)試結(jié)果。
因此,與 compare 函數(shù)一樣,我們能夠?qū)?nbsp;profile 函數(shù)添加到生成 Dolt 版本的 Golang cpu 配置文件的內(nèi)部工具中。
func profile(ctx context.Context, dir, profileDir, resultsDir, resultsPrefix, version, profile, doltCommand, scriptsDir string, useDoltHubLuaScriptsRepo bool) (string, error) {config := benchmark.NewProfilingConfig(dir,profileDir,version,profile,doltCommand,scriptsDir,useDoltHubLuaScriptsRepo)toUpload := filepath.Join(resultsDir, resultsPrefix)sr := benchmark.NewSysbenchProfiler(config, toUpload, profileDir)return toUpload, sr.Run(ctx)}
此函數(shù)像 compare 一樣返回其 toUpload 目錄,但這次它包含要上傳到 S3 的配置文件。
對(duì)代碼進(jìn)行這些更改后,我們準(zhǔn)備更新 GitHub Actions 工作流程以開(kāi)始創(chuàng)建 Dolt 的 pgo 版本。下面的圖表顯示了使用 GitHub Actions 的新 Dolt 發(fā)布流程。
從新的發(fā)布工作流程圖中可以看到,我們添加了一些新的 GitHub Actions 工作流程,但它們與原始工作流程類(lèi)似。讓我們更仔細(xì)地看看它們。
對(duì)于新的 Dolt 發(fā)布流程,我們運(yùn)行的第一個(gè)工作流程稱(chēng)為“發(fā)布 Dolt(配置文件)”,實(shí)際上并不創(chuàng)建 GitHub 版本或構(gòu)建任何 Dolt 二進(jìn)制文件。
相反,它的唯一功能是觸發(fā)第二個(gè)工作流程,稱(chēng)為“部署 K8s Sysbench 分析作業(yè)”。
name: Release Dolt (Profile)on:workflow_dispatch:inputs:version:description: 'SemVer format release tag, i.e. 0.24.5'required: truejobs::: ubuntu-22.04outputs:version: ${{ steps.format_version.outputs.version }}steps:name: Format Inputid: format_versionrun: |version="${{ github.event.inputs.version }}"if [[ $version == v* ]];thenversion="${version:1}"fiecho "version=$version" >> $GITHUB_OUTPUT:: ubuntu-22.04needs: format-versionname: Trigger Benchmark Profile K8s Workflowssteps:uses: actions/checkout@v4with:ref: mainname: Get shaid: get_sharun: |sha=$(git rev-parse --short HEAD)echo "sha=$sha" >> $GITHUB_OUTPUTuses: peter-evans/repository-dispatch@v3with:token: ${{ secrets.REPO_ACCESS_TOKEN }}: profile-dolt: '{"from_version": "${{ steps.get_sha.outputs.sha }}", "future_version": "${{ needs.format-version.outputs.version }}", "mode": "release", "actor": "${{ github.actor }}", "actor_email": "[email protected]", "template_script": "./.github/scripts/performance-benchmarking/get-dolt-profile-job-json.sh"}'
“部署 K8s Sysbench 分析作業(yè)”的工作原理與“部署 K8s Sysbench 基準(zhǔn)測(cè)試作業(yè)”幾乎相同,不同之處在于它將以“分析”模式運(yùn)行的基準(zhǔn)測(cè)試作業(yè)部署到 K8s 集群,以便我們使用 HEAD 的 。
name: Profile Dolt while Benchmarkingon:repository_dispatch:types: [ profile-dolt ]jobs:performance:: ubuntu-22.04name: Profile Dolt while Benchmarkingsteps:name: Checkoutuses: actions/checkout@v4uses: azure/setup-kubectl@v4with:version: 'v1.23.6'name: Install aws-iam-authenticatorrun: |curl -o aws-iam-authenticator https://amazon-eks.s3.us-west-2.amazonaws.com/1.18.8/2020-09-18/bin/linux/amd64/aws-iam-authenticator && \chmod +x ./aws-iam-authenticator && \sudo cp ./aws-iam-authenticator /usr/local/bin/aws-iam-authenticatorversionname: Configure AWS Credentialsuses: aws-actions/configure-aws-credentials@v4with:: ${{ secrets.AWS_ACCESS_KEY_ID }}: ${{ secrets.AWS_SECRET_ACCESS_KEY }}: us-west-2name: Create and Auth kubeconfigrun: |echo "$CONFIG" > kubeconfigKUBECONFIG=kubeconfig kubectl config set-credentials github-actions-dolt --exec-api-version=client.authentication.k8s.io/v1alpha1 --exec-command=aws-iam-authenticator --exec-arg=token --exec-arg=-i --exec-arg=eks-cluster-1KUBECONFIG=kubeconfig kubectl config set-context github-actions-dolt-context --cluster=eks-cluster-1 --user=github-actions-dolt --namespace=performance-benchmarkingKUBECONFIG=kubeconfig kubectl config use-context github-actions-dolt-contextenv:CONFIG: ${{ secrets.CORP_KUBECONFIG }}name: Create Profile Benchmarking K8s Jobrun: ./.github/scripts/performance-benchmarking/run-benchmarks.shenv:PROFILE: "true"FUTURE_VERSION: ${{ github.event.client_payload.future_version }}FROM_VERSION: ${{ github.event.client_payload.from_version }}MODE: ${{ github.event.client_payload.mode }}ACTOR: ${{ github.event.client_payload.actor }}ACTOR_EMAIL: ${{ github.event.client_payload.actor_email }}REPO_ACCESS_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}KUBECONFIG: "./kubeconfig"INIT_BIG_REPO: ${{ github.event.client_payload.init_big_repo }}NOMS_BIN_FORMAT: "__DOLT__"TEMPLATE_SCRIPT: ${{ github.event.client_payload.template_script }}
一旦基準(zhǔn)測(cè)試 K8s 作業(yè)在“分析”模式下運(yùn)行,我們就可以在更新的圖表中看到它執(zhí)行的步驟。我們還看到此作業(yè)的輸出是一個(gè)新的 Golang 配置文件,已上傳到 S3,可供我們流程的其余步驟用于創(chuàng)建 pgo 構(gòu)建。
在分析 K8s 作業(yè)結(jié)束時(shí),上傳配置文件后,它會(huì)觸發(fā)“Release Dolt”工作流程。這個(gè)工作流程的工作原理與原來(lái)的“Release Dolt”工作流程基本相同,只是它的第一步是下載分析作業(yè)上傳的 Golang 配置文件。
...create-pgo-release:needs: format-versionruns-on: ubuntu-22.04name: Release PGO Doltoutputs:release_id: ${{ steps.create_release.outputs.id }}steps:- uses: actions/checkout@v4with:ref: main- name: Set up Go 1.xuses: actions/setup-go@v5with:go-version-file: go/go.mod- name: Configure AWS Credentialsuses: aws-actions/configure-aws-credentials@v4with:aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}aws-region: us-west-2- name: Get Resultsid: get-resultsrun: aws s3api get-object --bucket="$BUCKET" --key="$KEY" dolt-cpu-profile.pprofenv:KEY: ${{ github.event.inputs.profile_key || github.event.client_payload.profile_key }}BUCKET: ${{ github.event.inputs.profile_bucket || github.event.client_payload.bucket }}...
然后,它將下載的配置文件(此處稱(chēng)為 dolt-cpu-profile.pprof )提供給 buildbinaries.sh 腳本,該腳本運(yùn)行 go build -pgo=./dolt-cpu-profile.pprof ,編譯新的 Dolt 二進(jìn)制文件。然后,與工作流程的原始版本一樣,它創(chuàng)建一個(gè) GitHub 版本并將這些二進(jìn)制文件作為版本資產(chǎn)上傳。
在完成之前,此工作流程中的最后一項(xiàng)作業(yè)將啟動(dòng)另一項(xiàng)基準(zhǔn)測(cè)試 K8s 作業(yè),只是這次為該作業(yè)提供用于構(gòu)建 Dolt 二進(jìn)制文件的 Golang 配置文件的 S3 密鑰。
...trigger-performance-benchmark-email:needs: [format-version, create-pgo-release]runs-on: ubuntu-22.04steps:- name: Trigger Performance Benchmarksuses: peter-evans/repository-dispatch@v3with:token: ${{ secrets.REPO_ACCESS_TOKEN }}event-type: release-doltclient-payload: '{"version": "${{ needs.format-version.outputs.version }}", "actor": "${{ github.actor }}", "profile_key": "${{ github.event.inputs.profile_key || github.event.client_payload.profile_key }}"}'
這會(huì)再次將基準(zhǔn)測(cè)試作業(yè)部署到我們的 K8s 集群,但現(xiàn)在該作業(yè)將從 S3 下載 Golang 配置文件,并使用它構(gòu)建 Dolt 的 pgo 二進(jìn)制文件,以用于基準(zhǔn)測(cè)試和生成結(jié)果。
從 K8s 上下文中的圖表中我們可以看到,第二個(gè)基準(zhǔn)測(cè)試作業(yè)的最后一步啟動(dòng)了“電子郵件團(tuán)隊(duì)”工作流程,以便我們的團(tuán)隊(duì)獲得現(xiàn)在 pgo 的 Dolt 的基準(zhǔn)測(cè)試結(jié)果。
我們已經(jīng)做到了!我們現(xiàn)在正在發(fā)布 Dolt 的 pgo 版本。
結(jié)論
正如您所看到的,更新發(fā)布過(guò)程以生成 pgo 二進(jìn)制文件涉及一些復(fù)雜性,至少對(duì)我們來(lái)說(shuō)是這樣。但我們所看到的性能提升絕對(duì)值得付出努力。
我希望您發(fā)現(xiàn)這對(duì)您自己的努力有幫助,我們鼓勵(lì)您也嘗試更新您的版本。如果您愿意,我們很想聽(tīng)聽(tīng)。過(guò)來(lái)并在我們的 Discord 上分享您的經(jīng)驗(yàn)。
