Skip to content

Commit

Permalink
Merge pull request #610 from vitessio/enable-pprof
Browse files Browse the repository at this point in the history
Enable profiling of Vitess on demand
  • Loading branch information
frouioui authored Oct 29, 2024
2 parents de60466 + 724acdb commit 67c056c
Show file tree
Hide file tree
Showing 19 changed files with 513 additions and 207 deletions.
8 changes: 8 additions & 0 deletions ansible/macrobench.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,18 @@
name: sysbench
tasks_from: install

- hosts: macrobench
roles:
- profile

- hosts: macrobench
roles:
- macrobench

- hosts: macrobench
roles:
- profile

- name: Clean Post Macrobench
when: arewefastyet_next_exec_is_same is undefined
import_playbook: clean_macrobench.yml
16 changes: 16 additions & 0 deletions ansible/roles/profile/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright 2024 The Vitess Authors.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

---
- name: Toggle profiling
shell: |
kill -s USR1 $(pgrep -d " " {{vitess_profile_binary}})
when: vitess_profile_binary is defined
3 changes: 3 additions & 0 deletions ansible/roles/vtgate/templates/vtgate.conf.j2
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ EXTRA_VTGATE_FLAGS="\
--vschema_ddl_authorized_users \"%\" \
--vtgate-config-terse-errors \
--tablet_types_to_wait REPLICA \
{% if vitess_profile_binary == 'vtgate' %}\
--pprof=\"{{vitess_profile_mode}},waitSig,path=/pprof/{{arewefastyet_exec_uuid}}/vtgate-{{ gateway.id }}\" \
{% endif %}\
{{gateway.extra_flags|default("")}} \
{{extra_vtgate_flags|default("")}} \
"
10 changes: 4 additions & 6 deletions ansible/roles/vttablet/templates/vttablet.conf.j2
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ EXTRA_VTTABLET_FLAGS="--alsologtostderr \
--queryserver-config-pool-size={{ tablet.connection_pool_size | default(vttablet_connection_pool_size) }} \
--queryserver-config-stream-pool-size={{ tablet.stream_pool_size | default(vttablet_stream_pool_size) }} \
--queryserver-config-transaction-cap={{ tablet.transaction_cap | default(vttablet_transaction_cap) }} \
{% if vitess_major_version <= 18 %}
{% if vitess_major_version > 1 and vitess_major_version <= 18 %}
--queryserver-config-query-timeout=900 \
--queryserver-config-schema-reload-time=60 \
--queryserver-config-transaction-timeout=300 \
Expand All @@ -44,11 +44,9 @@ EXTRA_VTTABLET_FLAGS="--alsologtostderr \
{% endif %}
--grpc_max_message_size=67108864 \
--db_charset=utf8 \
{% if pprof_targets is defined %}
{% for target in pprof_targets if target == "vttablet" %}
-pprof {{ pprof_args }},path=/tmp/pprof/vttablet-{{ tablet.id }},waitSig \
{%- endfor %}
{% endif %}
{% if vitess_profile_binary == 'vttablet' %}\
--pprof=\"{{vitess_profile_mode}},waitSig,path=/pprof/{{arewefastyet_exec_uuid}}/vttablet-{{ tablet.id }}\" \
{% endif %}\
{{ tablet.extra_flags | default("") }} \
{{ extra_vttablet_flags | default("") }} \
"
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ services:
volumes:
- "./config/dev/config.yaml:/config/config.yaml"
- "./config/dev/secrets.yaml:/config/secrets.yaml"
- "./go/admin/templates/:/go/admin/templates/"
labels:
- "traefik.enable=true"
- "traefik.http.routers.admin.rule=Host(`localhost`)"
Expand Down
11 changes: 9 additions & 2 deletions go/admin/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,19 @@ func (a *Admin) Run() error {
MaxAge: 12 * time.Hour,
}))

// API
// OAuth
a.router.GET("/admin", a.login)
a.router.GET("/admin/login", a.handleGitHubLogin)
a.router.GET("/admin/auth/callback", a.handleGitHubCallback)

// API
a.router.POST("/admin/executions/add", a.authMiddleware(), a.handleExecutionsAdd)
a.router.GET("/admin/dashboard", a.authMiddleware(), a.dashboard)
a.router.POST("/admin/executions/clear", a.authMiddleware(), a.handleClearQueue)

// Pages
a.router.GET("/admin/dashboard", a.authMiddleware(), a.homePage)
a.router.GET("/admin/dashboard/newexec", a.authMiddleware(), a.newExecutionsPage)
a.router.GET("/admin/dashboard/clearqueue", a.authMiddleware(), a.clearQueuePage)

return a.router.Run(":" + a.port)
}
Expand Down
144 changes: 117 additions & 27 deletions go/admin/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,22 +53,39 @@ const (
arewefastyetTeamGitHub = "arewefastyet"
)

type ExecutionRequest struct {
Auth string `json:"auth"`
Source string `json:"source"`
SHA string `json:"sha"`
Workloads []string `json:"workloads"`
NumberOfExecutions string `json:"number_of_executions"`
}
type (
executionRequest struct {
Auth string `json:"auth"`
Source string `json:"source"`
SHA string `json:"sha"`
Workloads []string `json:"workloads"`
NumberOfExecutions string `json:"number_of_executions"`
EnableProfile bool `json:"enable_profile"`
BinaryToProfile string `json:"binary_to_profile"`
ProfileMode string `json:"profile_mode"`
}

clearQueueRequest struct {
Auth string `json:"auth"`
}
)

func (a *Admin) login(c *gin.Context) {
a.render(c, gin.H{}, "login.html")
}

func (a *Admin) dashboard(c *gin.Context) {
func (a *Admin) homePage(c *gin.Context) {
a.render(c, gin.H{}, "base.html")
}

func (a *Admin) newExecutionsPage(c *gin.Context) {
a.render(c, gin.H{"Page": "newexec"}, "base.html")
}

func (a *Admin) clearQueuePage(c *gin.Context) {
a.render(c, gin.H{"Page": "clearqueue"}, "base.html")
}

func CreateGhClient(token *oauth2.Token) *goGithub.Client {
return goGithub.NewClient(oauthConf.Client(context.Background(), token))
}
Expand Down Expand Up @@ -206,13 +223,23 @@ func (a *Admin) checkUserOrgMembership(client *goGithub.Client, username, orgNam
}

func (a *Admin) handleExecutionsAdd(c *gin.Context) {
source := c.PostForm("source")
sha := c.PostForm("sha")
workloads := c.PostFormArray("workloads")
numberOfExecutions := c.PostForm("numberOfExecutions")
requestPayload := executionRequest{
Source: c.PostForm("source"),
SHA: c.PostForm("sha"),
Workloads: c.PostFormArray("workloads"),
NumberOfExecutions: c.PostForm("numberOfExecutions"),
EnableProfile: c.PostForm("enableProfiling") != "",
BinaryToProfile: c.PostForm("binaryToProfile"),
ProfileMode: c.PostForm("profileMode"),
}

if source == "" || sha == "" || len(workloads) == 0 || numberOfExecutions == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Missing required fields: Source and/or SHA"})
if requestPayload.Source == "" || requestPayload.SHA == "" || len(requestPayload.Workloads) == 0 || requestPayload.NumberOfExecutions == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Missing required fields: Source, SHA, workflows, numberOfExecutions"})
return
}

if requestPayload.EnableProfile && (requestPayload.BinaryToProfile == "" || requestPayload.ProfileMode == "") {
c.JSON(http.StatusBadRequest, gin.H{"error": "When enabling profiling, please provide a binary to profile and a mode"})
return
}

Expand All @@ -234,22 +261,14 @@ func (a *Admin) handleExecutionsAdd(c *gin.Context) {
return
}

encryptedToken, err := server.Encrypt(token.AccessToken, a.auth)
requestPayload.Auth, err = server.Encrypt(token.AccessToken, a.auth)

if err != nil {
slog.Error("Failed to encrypt token: ", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to encrypt token"})
return
}

requestPayload := ExecutionRequest{
Auth: encryptedToken,
Source: source,
SHA: sha,
Workloads: workloads,
NumberOfExecutions: numberOfExecutions,
}

jsonData, err := json.Marshal(requestPayload)

if err != nil {
Expand All @@ -258,10 +277,7 @@ func (a *Admin) handleExecutionsAdd(c *gin.Context) {
return
}

serverAPIURL := "http://traefik/api/executions/add"
if a.Mode == server.ProductionMode {
serverAPIURL = "https://benchmark.vitess.io/api/executions/add"
}
serverAPIURL := getAPIURL(a.Mode, "/executions/add")

req, err := http.NewRequest("POST", serverAPIURL, bytes.NewBuffer(jsonData))
if err != nil {
Expand Down Expand Up @@ -289,3 +305,77 @@ func (a *Admin) handleExecutionsAdd(c *gin.Context) {

c.JSON(http.StatusCreated, gin.H{"message": "Execution(s) added successfully"})
}

func (a *Admin) handleClearQueue(c *gin.Context) {
tokenKey, err := c.Cookie("tk")
if err != nil {
slog.Error("Failed to get token from cookie: ", err)
c.AbortWithStatus(http.StatusUnauthorized)
return
}

mu.Lock()
token, exists := tokens[tokenKey]
mu.Unlock()

if !exists {
slog.Error("Failed to get token from map")
c.AbortWithStatus(http.StatusUnauthorized)
return
}

encryptedToken, err := server.Encrypt(token.AccessToken, a.auth)
if err != nil {
slog.Error("Failed to encrypt token: ", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to encrypt token"})
return
}

requestPayload := clearQueueRequest{
Auth: encryptedToken,
}

jsonData, err := json.Marshal(requestPayload)

if err != nil {
slog.Error("Failed to marshal request payload: ", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to marshal request payload"})
return
}

serverAPIURL := getAPIURL(a.Mode, "/executions/clear")

req, err := http.NewRequest("POST", serverAPIURL, bytes.NewBuffer(jsonData))
if err != nil {
slog.Error("Failed to create new HTTP request: ", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create request to server API"})
return
}
req.Header.Set("Content-Type", "application/json")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
slog.Error("Failed to send request to server API: ", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to send request to server API"})
return
}

defer resp.Body.Close()

if resp.StatusCode != http.StatusAccepted {
slog.Error("Server API returned an error: ", resp.Status)
c.JSON(resp.StatusCode, gin.H{"error": "Failed to process request on server API"})
return
}

c.JSON(http.StatusAccepted, gin.H{"message": "Execution(s) added successfully"})
}

func getAPIURL(mode server.Mode, endpoint string) string {
serverAPIURL := "http://traefik/api" + endpoint
if mode == server.ProductionMode {
serverAPIURL = "https://benchmark.vitess.io/api" + endpoint
}
return serverAPIURL
}
Loading

0 comments on commit 67c056c

Please sign in to comment.