vitess.io/vitess@v0.16.2/examples/demo/demo.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 main
    18  
    19  import (
    20  	"bufio"
    21  	"context"
    22  	"encoding/hex"
    23  	"encoding/json"
    24  	"flag"
    25  	"fmt"
    26  	"net/http"
    27  	"os"
    28  	"os/signal"
    29  	"path"
    30  	"strings"
    31  	"syscall"
    32  	"time"
    33  
    34  	"vitess.io/vitess/go/mysql"
    35  	"vitess.io/vitess/go/sqltypes"
    36  	"vitess.io/vitess/go/vt/log"
    37  	vttestpb "vitess.io/vitess/go/vt/proto/vttest"
    38  	"vitess.io/vitess/go/vt/vttest"
    39  )
    40  
    41  var cluster *vttest.LocalCluster
    42  var querylog <-chan string
    43  
    44  func main() {
    45  	flag.Parse()
    46  
    47  	runCluster()
    48  	querylog, _ = streamQuerylog(cluster.Env.PortForProtocol("vtcombo", ""))
    49  
    50  	mux := http.NewServeMux()
    51  	mux.Handle("/", http.FileServer(http.Dir("./")))
    52  	mux.HandleFunc("/exec", exec)
    53  	go http.ListenAndServe(":8000", mux)
    54  
    55  	wait()
    56  	cluster.TearDown()
    57  }
    58  
    59  func runCluster() {
    60  	cluster = &vttest.LocalCluster{
    61  		Config: vttest.Config{
    62  			Topology: &vttestpb.VTTestTopology{
    63  				Keyspaces: []*vttestpb.Keyspace{{
    64  					Name: "customer",
    65  					Shards: []*vttestpb.Shard{{
    66  						Name: "-80",
    67  					}, {
    68  						Name: "80-",
    69  					}},
    70  				}, {
    71  					Name: "product",
    72  					Shards: []*vttestpb.Shard{{
    73  						Name: "0",
    74  					}},
    75  				}},
    76  			},
    77  			SchemaDir:     path.Join(os.Getenv("VTROOT"), "examples/demo/schema"),
    78  			MySQLBindHost: "0.0.0.0",
    79  			// VSchemaDDLAuthorizedUsers allows you to experiment with vschema DDLs.
    80  			VSchemaDDLAuthorizedUsers: "%",
    81  		},
    82  	}
    83  	env, err := vttest.NewLocalTestEnv("", 12345)
    84  	if err != nil {
    85  		log.Exitf("Error: %v", err)
    86  	}
    87  	cluster.Env = env
    88  	err = cluster.Setup()
    89  	if err != nil {
    90  		cluster.TearDown()
    91  		log.Exitf("Error: %v", err)
    92  	}
    93  }
    94  
    95  func wait() {
    96  	c := make(chan os.Signal, 2)
    97  	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
    98  	<-c
    99  }
   100  
   101  func exec(w http.ResponseWriter, req *http.Request) {
   102  	w.Header().Set("Content-Type", "application/json; charset=utf-8")
   103  	enc := json.NewEncoder(w)
   104  	enc.SetEscapeHTML(true)
   105  
   106  	cp := &mysql.ConnParams{
   107  		Host: "127.0.0.1",
   108  		Port: cluster.Env.PortForProtocol("vtcombo_mysql_port", ""),
   109  	}
   110  	conn, err := mysql.Connect(context.Background(), cp)
   111  	if err != nil {
   112  		response := map[string]string{
   113  			"error": err.Error(),
   114  		}
   115  		enc.Encode(response)
   116  		return
   117  	}
   118  	defer conn.Close()
   119  	query := req.FormValue("query")
   120  	response := make(map[string]any)
   121  
   122  	var queries []string
   123  	// Clear existing log.
   124  	for {
   125  		select {
   126  		case <-querylog:
   127  			continue
   128  		default:
   129  		}
   130  		break
   131  	}
   132  	execQuery(conn, "result", query, "", "", response)
   133  	// Collect
   134  	time.Sleep(250 * time.Millisecond)
   135  	for {
   136  		select {
   137  		case val := <-querylog:
   138  			queries = append(queries, val)
   139  			continue
   140  		default:
   141  		}
   142  		break
   143  	}
   144  	response["queries"] = queries
   145  
   146  	execQuery(conn, "customer0", "select * from customer", "customer", "-80", response)
   147  	execQuery(conn, "customer1", "select * from customer", "customer", "80-", response)
   148  	execQuery(conn, "corder0", "select * from corder", "customer", "-80", response)
   149  	execQuery(conn, "corder1", "select * from corder", "customer", "80-", response)
   150  	execQuery(conn, "corder_event0", "select * from corder_event", "customer", "-80", response)
   151  	execQuery(conn, "corder_event1", "select * from corder_event", "customer", "80-", response)
   152  	execQuery(conn, "oname_keyspace_idx0", "select * from oname_keyspace_idx", "customer", "-80", response)
   153  	execQuery(conn, "oname_keyspace_idx1", "select * from oname_keyspace_idx", "customer", "80-", response)
   154  	execQuery(conn, "product", "select * from product", "product", "0", response)
   155  	execQuery(conn, "customer_seq", "select * from customer_seq", "product", "0", response)
   156  	execQuery(conn, "corder_keyspace_idx", "select * from corder_keyspace_idx", "product", "0", response)
   157  	enc.Encode(response)
   158  }
   159  
   160  func execQuery(conn *mysql.Conn, key, query, keyspace, shard string, response map[string]any) {
   161  	if query == "" || query == "undefined" {
   162  		return
   163  	}
   164  	if keyspace != "" {
   165  		_, err := conn.ExecuteFetch(fmt.Sprintf("use `%v:%v`", keyspace, shard), 10000, true)
   166  		if err != nil {
   167  			response[key] = map[string]any{
   168  				"title": key,
   169  				"error": err.Error(),
   170  			}
   171  			return
   172  		}
   173  	}
   174  	title := key
   175  	switch {
   176  	case strings.HasSuffix(key, "0"):
   177  		title = key[:len(key)-1] + ":-80"
   178  	case strings.HasSuffix(key, "1"):
   179  		title = key[:len(key)-1] + ":80-"
   180  	}
   181  	qr, err := conn.ExecuteFetch(query, 10000, true)
   182  	if err != nil {
   183  		if strings.Contains(err.Error(), "doesn't exist") {
   184  			return
   185  		}
   186  		response[key] = map[string]any{
   187  			"title": title,
   188  			"error": err.Error(),
   189  		}
   190  		log.Errorf("error: %v", err)
   191  		return
   192  	}
   193  	response[key] = resultToMap(title, qr)
   194  }
   195  
   196  func resultToMap(title string, qr *sqltypes.Result) map[string]any {
   197  	fields := make([]string, 0, len(qr.Fields))
   198  	for _, field := range qr.Fields {
   199  		fields = append(fields, field.Name)
   200  	}
   201  	rows := make([][]string, 0, len(qr.Rows))
   202  	for _, row := range qr.Rows {
   203  		srow := make([]string, 0, len(row))
   204  		for _, value := range row {
   205  			if value.Type() == sqltypes.VarBinary {
   206  				bytes, err := value.ToBytes()
   207  				if err != nil {
   208  					log.Errorf("Error converting value to bytes: %v", err)
   209  					return nil
   210  				}
   211  				srow = append(srow, hex.EncodeToString(bytes))
   212  			} else {
   213  				srow = append(srow, value.ToString())
   214  			}
   215  		}
   216  		rows = append(rows, srow)
   217  	}
   218  	return map[string]any{
   219  		"title":        title,
   220  		"fields":       fields,
   221  		"rows":         rows,
   222  		"rowsaffected": int64(qr.RowsAffected),
   223  		"insertid":     int64(qr.InsertID),
   224  	}
   225  }
   226  
   227  func streamQuerylog(port int) (<-chan string, error) {
   228  	request := fmt.Sprintf("http://localhost:%d/debug/querylog", port)
   229  	resp, err := http.Get(request)
   230  	if err != nil {
   231  		log.Errorf("Error reading stream: %v: %v", request, err)
   232  		return nil, err
   233  	}
   234  	ch := make(chan string, 100)
   235  	go func() {
   236  		buffered := bufio.NewReader(resp.Body)
   237  		for {
   238  			str, err := buffered.ReadString('\n')
   239  			if err != nil {
   240  				log.Errorf("Error reading stream: %v: %v", request, err)
   241  				close(ch)
   242  				resp.Body.Close()
   243  				return
   244  			}
   245  			splits := strings.Split(str, "\t")
   246  			if len(splits) < 13 {
   247  				continue
   248  			}
   249  			trimmed := strings.Trim(splits[12], `"`)
   250  			if trimmed == "" {
   251  				continue
   252  			}
   253  			splitQueries := strings.Split(trimmed, ";")
   254  			var final []string
   255  			for _, query := range splitQueries {
   256  				if strings.Contains(query, "1 != 1") {
   257  					continue
   258  				}
   259  				final = append(final, query)
   260  			}
   261  			select {
   262  			case ch <- strings.Join(final, "; "):
   263  			default:
   264  			}
   265  		}
   266  	}()
   267  	return ch, nil
   268  }