vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/debugenv.go (about)

     1  /*
     2  Copyright 2020 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package tabletserver
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"html"
    23  	"net/http"
    24  	"strconv"
    25  	"text/template"
    26  	"time"
    27  
    28  	"vitess.io/vitess/go/acl"
    29  	"vitess.io/vitess/go/vt/log"
    30  )
    31  
    32  var (
    33  	debugEnvHeader = []byte(`
    34  	<thead><tr>
    35  		<th>Variable Name</th>
    36  		<th>Value</th>
    37  		<th>Action</th>
    38  	</tr></thead>
    39  	`)
    40  	debugEnvRow = template.Must(template.New("debugenv").Parse(`
    41  	<tr><form method="POST">
    42  		<td>{{.Name}}</td>
    43  		<td>
    44  			<input type="hidden" name="varname" value="{{.Name}}"></input>
    45  			<input type="text" name="value" value="{{.Value}}"></input>
    46  		</td>
    47  		<td><input type="submit" name="Action" value="Modify"></input></td>
    48  	</form></tr>
    49  	`))
    50  )
    51  
    52  type envValue struct {
    53  	Name  string
    54  	Value string
    55  }
    56  
    57  // this cannot be an anonymous function within debugEnvHandler because those kinds
    58  // of functions cannot (currently) have type params.
    59  func addVar[T any](vars []envValue, name string, f func() T) []envValue {
    60  	return append(vars, envValue{
    61  		Name:  name,
    62  		Value: fmt.Sprintf("%v", f()),
    63  	})
    64  }
    65  
    66  func debugEnvHandler(tsv *TabletServer, w http.ResponseWriter, r *http.Request) {
    67  	if err := acl.CheckAccessHTTP(r, acl.ADMIN); err != nil {
    68  		acl.SendError(w, err)
    69  		return
    70  	}
    71  
    72  	var msg string
    73  	if r.Method == "POST" {
    74  		varname := r.FormValue("varname")
    75  		value := r.FormValue("value")
    76  		setIntVal := func(f func(int)) {
    77  			ival, err := strconv.Atoi(value)
    78  			if err != nil {
    79  				msg = fmt.Sprintf("Failed setting value for %v: %v", varname, err)
    80  				return
    81  			}
    82  			f(ival)
    83  			msg = fmt.Sprintf("Setting %v to: %v", varname, value)
    84  		}
    85  		setInt64Val := func(f func(int64)) {
    86  			ival, err := strconv.ParseInt(value, 10, 64)
    87  			if err != nil {
    88  				msg = fmt.Sprintf("Failed setting value for %v: %v", varname, err)
    89  				return
    90  			}
    91  			f(ival)
    92  			msg = fmt.Sprintf("Setting %v to: %v", varname, value)
    93  		}
    94  		setDurationVal := func(f func(time.Duration)) {
    95  			durationVal, err := time.ParseDuration(value)
    96  			if err != nil {
    97  				msg = fmt.Sprintf("Failed setting value for %v: %v", varname, err)
    98  				return
    99  			}
   100  			f(durationVal)
   101  			msg = fmt.Sprintf("Setting %v to: %v", varname, value)
   102  		}
   103  		setFloat64Val := func(f func(float64)) {
   104  			fval, err := strconv.ParseFloat(value, 64)
   105  			if err != nil {
   106  				msg = fmt.Sprintf("Failed setting value for %v: %v", varname, err)
   107  				return
   108  			}
   109  			f(fval)
   110  			msg = fmt.Sprintf("Setting %v to: %v", varname, value)
   111  		}
   112  		switch varname {
   113  		case "PoolSize":
   114  			setIntVal(tsv.SetPoolSize)
   115  		case "StreamPoolSize":
   116  			setIntVal(tsv.SetStreamPoolSize)
   117  		case "TxPoolSize":
   118  			setIntVal(tsv.SetTxPoolSize)
   119  		case "QueryCacheCapacity":
   120  			setIntVal(tsv.SetQueryPlanCacheCap)
   121  		case "MaxResultSize":
   122  			setIntVal(tsv.SetMaxResultSize)
   123  		case "WarnResultSize":
   124  			setIntVal(tsv.SetWarnResultSize)
   125  		case "RowStreamerMaxInnoDBTrxHistLen":
   126  			setInt64Val(func(val int64) { tsv.Config().RowStreamer.MaxInnoDBTrxHistLen = val })
   127  		case "RowStreamerMaxMySQLReplLagSecs":
   128  			setInt64Val(func(val int64) { tsv.Config().RowStreamer.MaxMySQLReplLagSecs = val })
   129  		case "UnhealthyThreshold":
   130  			setDurationVal(tsv.Config().Healthcheck.UnhealthyThresholdSeconds.Set)
   131  			setDurationVal(tsv.hs.SetUnhealthyThreshold)
   132  			setDurationVal(tsv.sm.SetUnhealthyThreshold)
   133  		case "ThrottleMetricThreshold":
   134  			setFloat64Val(tsv.SetThrottleMetricThreshold)
   135  		case "Consolidator":
   136  			tsv.SetConsolidatorMode(value)
   137  			msg = fmt.Sprintf("Setting %v to: %v", varname, value)
   138  		}
   139  	}
   140  
   141  	var vars []envValue
   142  	vars = addVar(vars, "PoolSize", tsv.PoolSize)
   143  	vars = addVar(vars, "StreamPoolSize", tsv.StreamPoolSize)
   144  	vars = addVar(vars, "TxPoolSize", tsv.TxPoolSize)
   145  	vars = addVar(vars, "QueryCacheCapacity", tsv.QueryPlanCacheCap)
   146  	vars = addVar(vars, "MaxResultSize", tsv.MaxResultSize)
   147  	vars = addVar(vars, "WarnResultSize", tsv.WarnResultSize)
   148  	vars = addVar(vars, "RowStreamerMaxInnoDBTrxHistLen", func() int64 { return tsv.Config().RowStreamer.MaxInnoDBTrxHistLen })
   149  	vars = addVar(vars, "RowStreamerMaxMySQLReplLagSecs", func() int64 { return tsv.Config().RowStreamer.MaxMySQLReplLagSecs })
   150  	vars = addVar(vars, "UnhealthyThreshold", tsv.Config().Healthcheck.UnhealthyThresholdSeconds.Get)
   151  	vars = addVar(vars, "ThrottleMetricThreshold", tsv.ThrottleMetricThreshold)
   152  	vars = append(vars, envValue{
   153  		Name:  "Consolidator",
   154  		Value: tsv.ConsolidatorMode(),
   155  	})
   156  
   157  	format := r.FormValue("format")
   158  	if format == "json" {
   159  		mvars := make(map[string]string)
   160  		for _, v := range vars {
   161  			mvars[v.Name] = v.Value
   162  		}
   163  		w.Header().Set("Content-Type", "application/json")
   164  		_ = json.NewEncoder(w).Encode(mvars)
   165  		return
   166  	}
   167  
   168  	// gridTable is reused from twopcz.go.
   169  	w.Write(gridTable)
   170  	w.Write([]byte("<h3>Internal Variables</h3>\n"))
   171  	if msg != "" {
   172  		fmt.Fprintf(w, "<b>%s</b><br /><br />\n", html.EscapeString(msg))
   173  	}
   174  	w.Write(startTable)
   175  	w.Write(debugEnvHeader)
   176  	for _, v := range vars {
   177  		if err := debugEnvRow.Execute(w, v); err != nil {
   178  			log.Errorf("debugenv: couldn't execute template: %v", err)
   179  		}
   180  	}
   181  	w.Write(endTable)
   182  }