The first step is to tell Vitess how we want to partition the data. We do this by providing a VSchema definition as follows:
{
- "Sharded": true,
- "Vindexes": {
+ "sharded": true,
+ "vindexes": {
"hash": {
- "Type": "hash"
+ "type": "hash"
}
},
- "Tables": {
+ "tables": {
"messages": {
- "ColVindexes": [
+ "column_vindexes": [
{
- "Col": "page",
- "Name": "hash"
+ "column": "page",
+ "name": "hash"
}
]
}
diff --git a/docs/user-guide/sharding-kubernetes.html b/docs/user-guide/sharding-kubernetes.html
index 330d78cc6b9..5acafa7c412 100644
--- a/docs/user-guide/sharding-kubernetes.html
+++ b/docs/user-guide/sharding-kubernetes.html
@@ -315,18 +315,18 @@ Configure sharding information
The first step is to tell Vitess how we want to partition the data.
We do this by providing a VSchema definition as follows:
{
- "Sharded": true,
- "Vindexes": {
+ "sharded": true,
+ "vindexes": {
"hash": {
- "Type": "hash"
+ "type": "hash"
}
},
- "Tables": {
+ "tables": {
"messages": {
- "ColVindexes": [
+ "column_vindexes": [
{
- "Col": "page",
- "Name": "hash"
+ "column": "page",
+ "name": "hash"
}
]
}
diff --git a/examples/kubernetes/env.sh b/examples/kubernetes/env.sh
index 60ef6c8c6a0..4474dbcd42b 100644
--- a/examples/kubernetes/env.sh
+++ b/examples/kubernetes/env.sh
@@ -6,9 +6,17 @@
# use cases just need KUBECTL=kubectl, we'll make that the default.
KUBECTL=${KUBECTL:-kubectl}
-# Kuberentes namespace for Vitess and components.
+# Kubernetes api address for $KUBECTL
+# The default value is 127.0.0.1:8080
+# When the Kubernetes api server is not local, We can easily access the api by edit KUBERNETES_API_SERVER's value
+KUBERNETES_API_SERVER=${KUBERNETES_API_SERVER:-'127.0.0.1:8080'}
+
+# Kubernetes namespace for Vitess and components.
VITESS_NAME=${VITESS_NAME:-'default'}
+# Kubernetes options config
+KUBECTL_OPTIONS="--namespace=$VITESS_NAME --server=$KUBERNETES_API_SERVER"
+
# CELLS should be a comma separated list of cells
# the first cell listed will become local to vtctld.
CELLS=${CELLS:-'test'}
@@ -18,7 +26,7 @@ VTCTLD_PORT=${VTCTLD_PORT:-30001}
# Get the ExternalIP of any node.
get_node_ip() {
- $KUBECTL get -o template --template '{{range (index .items 0).status.addresses}}{{if eq .type "ExternalIP" "LegacyHostIP"}}{{.address}}{{end}}{{end}}' nodes --namespace=$VITESS_NAME
+ $KUBECTL $KUBECTL_OPTIONS get -o template --template '{{range (index .items 0).status.addresses}}{{if eq .type "ExternalIP" "LegacyHostIP"}}{{.address}}{{end}}{{end}}' nodes
}
# Try to find vtctld address if not provided.
@@ -33,7 +41,7 @@ get_vtctld_addr() {
# Find the name of a vtctld pod.
get_vtctld_pod() {
- $KUBECTL get -o template --template "{{if ge (len .items) 1 }}{{(index .items 0).metadata.name}}{{end}}" -l 'app=vitess,component=vtctld' pods --namespace=$VITESS_NAME
+ $KUBECTL $KUBECTL_OPTIONS get -o template --template "{{if ge (len .items) 1 }}{{(index .items 0).metadata.name}}{{end}}" -l 'app=vitess,component=vtctld' pods
}
start_vtctld_forward() {
@@ -44,7 +52,7 @@ start_vtctld_forward() {
fi
tmpfile=`mktemp`
- $KUBECTL port-forward -p $pod 0:15999 --namespace=$VITESS_NAME &> $tmpfile &
+ $KUBECTL $KUBECTL_OPTIONS port-forward -p $pod 0:15999 &> $tmpfile &
vtctld_forward_pid=$!
until [[ `cat $tmpfile` =~ :([0-9]+)\ -\> ]]; do :; done
diff --git a/examples/kubernetes/etcd-down.sh b/examples/kubernetes/etcd-down.sh
index f0e2103551e..18b1a824422 100755
--- a/examples/kubernetes/etcd-down.sh
+++ b/examples/kubernetes/etcd-down.sh
@@ -13,10 +13,10 @@ cells=`echo $CELLS | tr ',' ' '`
# Delete replication controllers
for cell in 'global' $cells; do
echo "Stopping etcd replicationcontroller for $cell cell..."
- $KUBECTL delete replicationcontroller etcd-$cell --namespace=$VITESS_NAME
+ $KUBECTL $KUBECTL_OPTIONS delete replicationcontroller etcd-$cell
echo "Deleting etcd service for $cell cell..."
- $KUBECTL delete service etcd-$cell --namespace=$VITESS_NAME
- $KUBECTL delete service etcd-$cell-srv --namespace=$VITESS_NAME
+ $KUBECTL $KUBECTL_OPTIONS delete service etcd-$cell
+ $KUBECTL $KUBECTL_OPTIONS delete service etcd-$cell-srv
done
diff --git a/examples/kubernetes/etcd-up.sh b/examples/kubernetes/etcd-up.sh
index 3fc1c0b9c11..38718dc5a11 100755
--- a/examples/kubernetes/etcd-up.sh
+++ b/examples/kubernetes/etcd-up.sh
@@ -21,7 +21,7 @@ for cell in 'global' $cells; do
echo "Creating etcd service for $cell cell..."
cat etcd-service-template.yaml | \
sed -e "s/{{cell}}/$cell/g" | \
- $KUBECTL create --namespace=$VITESS_NAME -f -
+ $KUBECTL $KUBECTL_OPTIONS create -f -
# Expand template variables
sed_script=""
@@ -31,6 +31,6 @@ for cell in 'global' $cells; do
# Create the replication controller.
echo "Creating etcd replicationcontroller for $cell cell..."
- cat etcd-controller-template.yaml | sed -e "$sed_script" | $KUBECTL create --namespace=$VITESS_NAME -f -
+ cat etcd-controller-template.yaml | sed -e "$sed_script" | $KUBECTL $KUBECTL_OPTIONS create -f -
done
diff --git a/examples/kubernetes/guestbook-controller.yaml b/examples/kubernetes/guestbook-controller-template.yaml
similarity index 82%
rename from examples/kubernetes/guestbook-controller.yaml
rename to examples/kubernetes/guestbook-controller-template.yaml
index 308dbd7ef46..2897b3f5929 100644
--- a/examples/kubernetes/guestbook-controller.yaml
+++ b/examples/kubernetes/guestbook-controller-template.yaml
@@ -26,3 +26,4 @@ spec:
limits:
memory: "128Mi"
cpu: "100m"
+ args: ["--port", "{{port}}", "--cell", "{{cell}}", "--keyspace", "{{keyspace}}", "--vtgate_port", "{{vtgate_port}}"]
diff --git a/examples/kubernetes/guestbook-down.sh b/examples/kubernetes/guestbook-down.sh
index 81cbe54dc52..f7c41413abb 100755
--- a/examples/kubernetes/guestbook-down.sh
+++ b/examples/kubernetes/guestbook-down.sh
@@ -2,13 +2,11 @@
# This is an example script that stops guestbook.
-set -e
-
script_root=`dirname "${BASH_SOURCE}"`
source $script_root/env.sh
echo "Stopping guestbook replicationcontroller..."
-$KUBECTL delete replicationcontroller guestbook --namespace=$VITESS_NAME
+$KUBECTL $KUBECTL_OPTIONS delete replicationcontroller guestbook
echo "Deleting guestbook service..."
-$KUBECTL delete service guestbook --namespace=$VITESS_NAME
+$KUBECTL $KUBECTL_OPTIONS delete service guestbook
diff --git a/examples/kubernetes/guestbook-up.sh b/examples/kubernetes/guestbook-up.sh
index b280764d5f9..cea846ce4d4 100755
--- a/examples/kubernetes/guestbook-up.sh
+++ b/examples/kubernetes/guestbook-up.sh
@@ -4,11 +4,21 @@
set -e
+port=${GUESTBOOK_PORT:-8080}
+cell=${GUESTBOOK_CELL:-"test"}
+keyspace=${GUESTBOOK_KEYSPACE:-"test_keyspace"}
+vtgate_port=${VTGATE_PORT:-15991}
+
script_root=`dirname "${BASH_SOURCE}"`
source $script_root/env.sh
echo "Creating guestbook service..."
-$KUBECTL create --namespace=$VITESS_NAME -f guestbook-service.yaml
+$KUBECTL $KUBECTL_OPTIONS create -f guestbook-service.yaml
+
+sed_script=""
+for var in port cell keyspace vtgate_port; do
+ sed_script+="s,{{$var}},${!var},g;"
+done
echo "Creating guestbook replicationcontroller..."
-$KUBECTL create --namespace=$VITESS_NAME -f guestbook-controller.yaml
+sed -e "$sed_script" guestbook-controller-template.yaml | $KUBECTL $KUBECTL_OPTIONS create -f -
diff --git a/examples/kubernetes/guestbook/main.py b/examples/kubernetes/guestbook/main.py
index 8038b58ceee..2ce83de2960 100644
--- a/examples/kubernetes/guestbook/main.py
+++ b/examples/kubernetes/guestbook/main.py
@@ -1,5 +1,6 @@
"""Main python file."""
+import argparse
import os
import time
import json
@@ -15,6 +16,7 @@
# conn is the connection to vtgate.
conn = None
+keyspace = None
@app.route('/')
@@ -31,8 +33,7 @@ def view(page):
@app.route('/lrange/guestbook/')
def list_guestbook(page):
"""Read the list from a replica."""
- cursor = conn.cursor(
- tablet_type='replica', keyspace='test_keyspace')
+ cursor = conn.cursor(tablet_type='replica', keyspace=keyspace)
cursor.execute(
'SELECT message, time_created_ns FROM messages WHERE page=:page'
@@ -47,8 +48,7 @@ def list_guestbook(page):
@app.route('/rpush/guestbook//')
def add_entry(page, value):
"""Insert a row on the master."""
- cursor = conn.cursor(
- tablet_type='master', keyspace='test_keyspace', writable=True)
+ cursor = conn.cursor(tablet_type='master', keyspace=keyspace, writable=True)
cursor.begin()
cursor.execute(
@@ -77,13 +77,25 @@ def add_entry(page, value):
def env():
return json.dumps(dict(os.environ))
+
if __name__ == '__main__':
- timeout = 10 # connect timeout in seconds
+ parser = argparse.ArgumentParser(description='Run guestbook app')
+ parser.add_argument('--port', help='Port', default=8080, type=int)
+ parser.add_argument('--cell', help='Cell', default='test', type=str)
+ parser.add_argument(
+ '--keyspace', help='Keyspace', default='test_keyspace', type=str)
+ parser.add_argument(
+ '--timeout', help='Connect timeout (s)', default=10, type=int)
+ parser.add_argument(
+ '--vtgate_port', help='Vtgate Port', default=15991, type=int)
+ guestbook_args = parser.parse_args()
# Get vtgate service address from Kubernetes DNS.
- addr = 'vtgate-test:15991'
+ addr = 'vtgate-%s:%d' % (guestbook_args.cell, guestbook_args.vtgate_port)
# Connect to vtgate.
- conn = vtgate_client.connect('grpc', addr, timeout)
+ conn = vtgate_client.connect('grpc', addr, guestbook_args.timeout)
+
+ keyspace = guestbook_args.keyspace
- app.run(host='0.0.0.0', port=8080, debug=True)
+ app.run(host='0.0.0.0', port=guestbook_args.port, debug=True)
diff --git a/examples/kubernetes/namespace-down.sh b/examples/kubernetes/namespace-down.sh
index e35c96e6868..6ffbf762a40 100755
--- a/examples/kubernetes/namespace-down.sh
+++ b/examples/kubernetes/namespace-down.sh
@@ -10,4 +10,4 @@ source $script_root/env.sh
namespace=${VITESS_NAME:-'vitess'}
echo "Deleting namespace $namespace..."
-$KUBECTL delete namespace $namespace
+$KUBECTL $KUBECTL_OPTIONS delete namespace $namespace
diff --git a/examples/kubernetes/namespace-up.sh b/examples/kubernetes/namespace-up.sh
index 2ca34aedbe1..f585c3989fd 100755
--- a/examples/kubernetes/namespace-up.sh
+++ b/examples/kubernetes/namespace-up.sh
@@ -14,5 +14,5 @@ sed_script=""
for var in namespace; do
sed_script+="s,{{$var}},${!var},g;"
done
-cat namespace-template.yaml | sed -e "$sed_script" | $KUBECTL create -f -
+cat namespace-template.yaml | sed -e "$sed_script" | $KUBECTL $KUBECTL_OPTIONS create -f -
diff --git a/examples/kubernetes/orchestrator-down.sh b/examples/kubernetes/orchestrator-down.sh
index ddad5fb9140..957ddfd4a96 100755
--- a/examples/kubernetes/orchestrator-down.sh
+++ b/examples/kubernetes/orchestrator-down.sh
@@ -8,10 +8,10 @@ script_root=`dirname "${BASH_SOURCE}"`
source $script_root/env.sh
echo "Stopping orchestrator replicationcontroller..."
-$KUBECTL delete replicationcontroller orchestrator --namespace=$VITESS_NAME
+$KUBECTL $KUBECTL_OPTIONS delete replicationcontroller orchestrator
echo "Deleting orchestrator service..."
-$KUBECTL delete service orchestrator --namespace=$VITESS_NAME
+$KUBECTL $KUBECTL_OPTIONS delete service orchestrator
echo "Deleting orchestrator configmap..."
-$KUBECTL delete --namespace=$VITESS_NAME configmap orchestrator-conf
+$KUBECTL $KUBECTL_OPTIONS delete configmap orchestrator-conf
diff --git a/examples/kubernetes/orchestrator-up.sh b/examples/kubernetes/orchestrator-up.sh
index 893209af3fe..cdfa4425f9d 100755
--- a/examples/kubernetes/orchestrator-up.sh
+++ b/examples/kubernetes/orchestrator-up.sh
@@ -14,9 +14,9 @@ for var in service_type; do
done
# Create configmap from orchestrator docker config file
-$KUBECTL create --namespace=$VITESS_NAME configmap orchestrator-conf --from-file="${script_root}/../../docker/orchestrator/orchestrator.conf.json"
+$KUBECTL $KUBECTL_OPTIONS create configmap orchestrator-conf --from-file="${script_root}/../../docker/orchestrator/orchestrator.conf.json"
-cat orchestrator-template.yaml | sed -e "$sed_script" | $KUBECTL create --namespace=$VITESS_NAME -f -
+cat orchestrator-template.yaml | sed -e "$sed_script" | $KUBECTL $KUBECTL_OPTIONS create -f -
echo
echo "To access orchestrator web UI, start kubectl proxy in another terminal:"
diff --git a/examples/kubernetes/sharded-vtworker.sh b/examples/kubernetes/sharded-vtworker.sh
index 9da3c8a886c..9afa922eaa2 100755
--- a/examples/kubernetes/sharded-vtworker.sh
+++ b/examples/kubernetes/sharded-vtworker.sh
@@ -19,29 +19,29 @@ done
# Instantiate template and send to kubectl.
echo "Creating vtworker pod in cell $cell..."
-cat vtworker-pod-template.yaml | sed -e "$sed_script" | $KUBECTL create -f -
+cat vtworker-pod-template.yaml | sed -e "$sed_script" | $KUBECTL $KUBECTL_OPTIONS create -f -
set +e
# Wait for vtworker pod to show up.
-until [ $($KUBECTL get pod -o template --template '{{.status.phase}}' vtworker 2> /dev/null) = "Running" ]; do
+until [ $($KUBECTL $KUBECTL_OPTIONS get pod -o template --template '{{.status.phase}}' vtworker 2> /dev/null) = "Running" ]; do
echo "Waiting for vtworker pod to be created..."
sleep 1
done
echo "Following vtworker logs until termination..."
-$KUBECTL logs -f vtworker
+$KUBECTL $KUBECTL_OPTIONS logs -f vtworker
# Get vtworker exit code. Wait for complete shutdown.
# (Although logs -f exited, the pod isn't fully shutdown yet and the exit code is not available yet.)
-until [ $($KUBECTL get pod -o template --template '{{.status.phase}}' vtworker 2> /dev/null) != "Running" ]; do
+until [ $($KUBECTL $KUBECTL_OPTIONS get pod -o template --template '{{.status.phase}}' vtworker 2> /dev/null) != "Running" ]; do
echo "Waiting for vtworker pod to shutdown completely..."
sleep 1
done
-exit_code=$($KUBECTL get -o template --template '{{(index .status.containerStatuses 0).state.terminated.exitCode}}' pods vtworker)
+exit_code=$($KUBECTL $KUBECTL_OPTIONS get -o template --template '{{(index .status.containerStatuses 0).state.terminated.exitCode}}' pods vtworker)
echo "Deleting vtworker pod..."
-$KUBECTL delete pod vtworker
+$KUBECTL $KUBECTL_OPTIONS delete pod vtworker
if [ "$exit_code" != "0" ]; then
echo
diff --git a/examples/kubernetes/vtctld-down.sh b/examples/kubernetes/vtctld-down.sh
index 0b888ba8ff7..0191f6f295f 100755
--- a/examples/kubernetes/vtctld-down.sh
+++ b/examples/kubernetes/vtctld-down.sh
@@ -8,7 +8,7 @@ script_root=`dirname "${BASH_SOURCE}"`
source $script_root/env.sh
echo "Stopping vtctld replicationcontroller..."
-$KUBECTL delete replicationcontroller vtctld --namespace=$VITESS_NAME
+$KUBECTL $KUBECTL_OPTIONS delete replicationcontroller vtctld
echo "Deleting vtctld service..."
-$KUBECTL delete service vtctld --namespace=$VITESS_NAME
+$KUBECTL $KUBECTL_OPTIONS delete service vtctld
diff --git a/examples/kubernetes/vtctld-up.sh b/examples/kubernetes/vtctld-up.sh
index b5041d2efeb..306e367f2cb 100755
--- a/examples/kubernetes/vtctld-up.sh
+++ b/examples/kubernetes/vtctld-up.sh
@@ -18,7 +18,7 @@ sed_script=""
for var in service_type; do
sed_script+="s,{{$var}},${!var},g;"
done
-cat vtctld-service-template.yaml | sed -e "$sed_script" | $KUBECTL create --namespace=$VITESS_NAME -f -
+cat vtctld-service-template.yaml | sed -e "$sed_script" | $KUBECTL $KUBECTL_OPTIONS create -f -
echo "Creating vtctld replicationcontroller..."
# Expand template variables
@@ -28,7 +28,7 @@ for var in vitess_image backup_flags test_flags cell; do
done
# Instantiate template and send to kubectl.
-cat vtctld-controller-template.yaml | sed -e "$sed_script" | $KUBECTL create --namespace=$VITESS_NAME -f -
+cat vtctld-controller-template.yaml | sed -e "$sed_script" | $KUBECTL $KUBECTL_OPTIONS create -f -
echo
echo "To access vtctld web UI, start kubectl proxy in another terminal:"
diff --git a/examples/kubernetes/vtgate-controller-template.yaml b/examples/kubernetes/vtgate-controller-template.yaml
index ccb5376c86a..1c303a8ad5f 100644
--- a/examples/kubernetes/vtgate-controller-template.yaml
+++ b/examples/kubernetes/vtgate-controller-template.yaml
@@ -43,6 +43,7 @@ spec:
-port 15001
-grpc_port 15991
-mysql_server_port {{mysql_server_port}}
+ -mysql_auth_server_config_string '{\"mysql_user\":{\"Password\":\"mysql_password\"}}'
-service_map 'grpc-vtgateservice'
-cells_to_watch {{cell}}
-tablet_types_to_wait MASTER,REPLICA
diff --git a/examples/kubernetes/vtgate-down.sh b/examples/kubernetes/vtgate-down.sh
index 26b87ac8380..fadd2a0073c 100755
--- a/examples/kubernetes/vtgate-down.sh
+++ b/examples/kubernetes/vtgate-down.sh
@@ -11,9 +11,9 @@ cells=`echo $CELLS | tr ',' ' '`
for cell in $cells; do
echo "Stopping vtgate replicationcontroller in cell $cell..."
- $KUBECTL delete replicationcontroller vtgate-$cell --namespace=$VITESS_NAME
+ $KUBECTL $KUBECTL_OPTIONS delete replicationcontroller vtgate-$cell
echo "Deleting vtgate service in cell $cell..."
- $KUBECTL delete service vtgate-$cell --namespace=$VITESS_NAME
+ $KUBECTL $KUBECTL_OPTIONS delete service vtgate-$cell
done
diff --git a/examples/kubernetes/vtgate-up.sh b/examples/kubernetes/vtgate-up.sh
index 46965608398..c5a754dd7e1 100755
--- a/examples/kubernetes/vtgate-up.sh
+++ b/examples/kubernetes/vtgate-up.sh
@@ -31,7 +31,7 @@ for cell in $cells; do
sed_script+="s,{{mysql_server_port}},$mysql_server_port,g;"
echo "Creating vtgate service in cell $cell..."
- cat vtgate-service-template.yaml | sed -e "$sed_script" | $KUBECTL create --namespace=$VITESS_NAME -f -
+ cat vtgate-service-template.yaml | sed -e "$sed_script" | $KUBECTL $KUBECTL_OPTIONS create -f -
sed_script=""
for var in vitess_image replicas vtdataroot_volume cell mysql_server_port; do
@@ -39,5 +39,5 @@ for cell in $cells; do
done
echo "Creating vtgate replicationcontroller in cell $cell..."
- cat $VTGATE_TEMPLATE | sed -e "$sed_script" | $KUBECTL create --namespace=$VITESS_NAME -f -
+ cat $VTGATE_TEMPLATE | sed -e "$sed_script" | $KUBECTL $KUBECTL_OPTIONS create -f -
done
diff --git a/examples/kubernetes/vttablet-down.sh b/examples/kubernetes/vttablet-down.sh
index 205730de21f..1ea3c6e48a1 100755
--- a/examples/kubernetes/vttablet-down.sh
+++ b/examples/kubernetes/vttablet-down.sh
@@ -32,7 +32,7 @@ for shard in `seq 1 $num_shards`; do
printf -v alias '%s-%010d' $cell $uid
echo "Deleting pod for tablet $alias..."
- $KUBECTL delete pod vttablet-$uid --namespace=$VITESS_NAME
+ $KUBECTL $KUBECTL_OPTIONS delete pod vttablet-$uid
done
let cell_index=cell_index+100000000
done
diff --git a/examples/kubernetes/vttablet-up.sh b/examples/kubernetes/vttablet-up.sh
index 7cc84b5435c..22202996a86 100755
--- a/examples/kubernetes/vttablet-up.sh
+++ b/examples/kubernetes/vttablet-up.sh
@@ -55,7 +55,7 @@ for shard in $(echo $SHARDS | tr "," " "); do
done
# Instantiate template and send to kubectl.
- cat $VTTABLET_TEMPLATE | sed -e "$sed_script" | $KUBECTL create --namespace=$VITESS_NAME -f -
+ cat $VTTABLET_TEMPLATE | sed -e "$sed_script" | $KUBECTL $KUBECTL_OPTIONS create -f -
done
let cell_index=cell_index+100000000
done
diff --git a/examples/local/vtgate-up.sh b/examples/local/vtgate-up.sh
index e5e98eaceae..d19c4bccbf3 100755
--- a/examples/local/vtgate-up.sh
+++ b/examples/local/vtgate-up.sh
@@ -46,6 +46,7 @@ $VTROOT/bin/vtgate \
-port $web_port \
-grpc_port $grpc_port \
-mysql_server_port $mysql_server_port \
+ -mysql_auth_server_config_string '{"mysql_user":{"Password":"mysql_password"}}' \
-cell $cell \
-cells_to_watch $cell \
-tablet_types_to_wait MASTER,REPLICA \
diff --git a/go/cmd/l2vtgate/plugin_grpcqueryservice.go b/go/cmd/l2vtgate/plugin_grpcqueryservice.go
index 609c977adc2..d8bf82553b3 100644
--- a/go/cmd/l2vtgate/plugin_grpcqueryservice.go
+++ b/go/cmd/l2vtgate/plugin_grpcqueryservice.go
@@ -8,9 +8,9 @@ package main
import (
"github.com/youtube/vitess/go/vt/servenv"
- "github.com/youtube/vitess/go/vt/tabletserver/grpcqueryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
"github.com/youtube/vitess/go/vt/vtgate/l2vtgate"
+ "github.com/youtube/vitess/go/vt/vttablet/grpcqueryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
)
func init() {
diff --git a/go/cmd/l2vtgate/plugin_grpctabletconn.go b/go/cmd/l2vtgate/plugin_grpctabletconn.go
index 282f56094ad..e773721e758 100644
--- a/go/cmd/l2vtgate/plugin_grpctabletconn.go
+++ b/go/cmd/l2vtgate/plugin_grpctabletconn.go
@@ -7,5 +7,5 @@ package main
// Imports and register the gRPC tabletconn client
import (
- _ "github.com/youtube/vitess/go/vt/tabletserver/grpctabletconn"
+ _ "github.com/youtube/vitess/go/vt/vttablet/grpctabletconn"
)
diff --git a/go/cmd/vtcombo/main.go b/go/cmd/vtcombo/main.go
index edad1c63cc9..572e8bb36b8 100644
--- a/go/cmd/vtcombo/main.go
+++ b/go/cmd/vtcombo/main.go
@@ -23,11 +23,11 @@ import (
"github.com/youtube/vitess/go/vt/discovery"
"github.com/youtube/vitess/go/vt/mysqlctl"
"github.com/youtube/vitess/go/vt/servenv"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
"github.com/youtube/vitess/go/vt/vtctld"
"github.com/youtube/vitess/go/vt/vtgate"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
vttestpb "github.com/youtube/vitess/go/vt/proto/vttest"
diff --git a/go/cmd/vtcombo/tablet_map.go b/go/cmd/vtcombo/tablet_map.go
index 347c1b10734..76e8c1271a7 100644
--- a/go/cmd/vtcombo/tablet_map.go
+++ b/go/cmd/vtcombo/tablet_map.go
@@ -18,23 +18,24 @@ import (
"github.com/youtube/vitess/go/vt/logutil"
"github.com/youtube/vitess/go/vt/mysqlctl"
"github.com/youtube/vitess/go/vt/mysqlctl/tmutils"
- "github.com/youtube/vitess/go/vt/tabletmanager"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
- "github.com/youtube/vitess/go/vt/tabletserver"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
"github.com/youtube/vitess/go/vt/topotools"
"github.com/youtube/vitess/go/vt/vterrors"
"github.com/youtube/vitess/go/vt/vtgate/vindexes"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletconn"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletmanager"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
querypb "github.com/youtube/vitess/go/vt/proto/query"
replicationdatapb "github.com/youtube/vitess/go/vt/proto/replicationdata"
tabletmanagerdatapb "github.com/youtube/vitess/go/vt/proto/tabletmanagerdata"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
+ vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
vttestpb "github.com/youtube/vitess/go/vt/proto/vttest"
)
@@ -64,7 +65,7 @@ func createTablet(ctx context.Context, ts topo.Server, cell string, uid uint32,
log.Infof("Creating %v tablet %v for %v/%v", tabletType, topoproto.TabletAliasString(alias), keyspace, shard)
flag.Set("debug-url-prefix", fmt.Sprintf("/debug-%d", uid))
- controller := tabletserver.NewServer()
+ controller := tabletserver.NewServer(ts)
initTabletType := tabletType
if tabletType == topodatapb.TabletType_MASTER {
initTabletType = topodatapb.TabletType_REPLICA
@@ -252,7 +253,7 @@ func initTabletMap(ts topo.Server, tpb *vttestpb.VTTestTopology, mysqld mysqlctl
func dialer(tablet *topodatapb.Tablet, timeout time.Duration) (queryservice.QueryService, error) {
t, ok := tabletMap[tablet.Alias.Uid]
if !ok {
- return nil, tabletconn.OperationalError("connection refused")
+ return nil, vterrors.New(vtrpcpb.Code_UNAVAILABLE, "connection refused")
}
return &internalTabletConn{
@@ -281,7 +282,7 @@ func (itc *internalTabletConn) Execute(ctx context.Context, target *querypb.Targ
}
reply, err := itc.tablet.qsc.QueryService().Execute(ctx, target, query, bindVars, transactionID, options)
if err != nil {
- return nil, tabletconn.TabletErrorFromGRPC(vterrors.ToGRPCError(err))
+ return nil, tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err))
}
return reply, nil
}
@@ -304,7 +305,7 @@ func (itc *internalTabletConn) ExecuteBatch(ctx context.Context, target *querypb
}
results, err := itc.tablet.qsc.QueryService().ExecuteBatch(ctx, target, q, asTransaction, transactionID, options)
if err != nil {
- return nil, tabletconn.TabletErrorFromGRPC(vterrors.ToGRPCError(err))
+ return nil, tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err))
}
return results, nil
}
@@ -322,14 +323,14 @@ func (itc *internalTabletConn) StreamExecute(ctx context.Context, target *queryp
}
err = itc.tablet.qsc.QueryService().StreamExecute(ctx, target, query, bindVars, options, callback)
- return tabletconn.TabletErrorFromGRPC(vterrors.ToGRPCError(err))
+ return tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err))
}
// Begin is part of queryservice.QueryService
func (itc *internalTabletConn) Begin(ctx context.Context, target *querypb.Target) (int64, error) {
transactionID, err := itc.tablet.qsc.QueryService().Begin(ctx, target)
if err != nil {
- return 0, tabletconn.TabletErrorFromGRPC(vterrors.ToGRPCError(err))
+ return 0, tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err))
}
return transactionID, nil
}
@@ -337,61 +338,61 @@ func (itc *internalTabletConn) Begin(ctx context.Context, target *querypb.Target
// Commit is part of queryservice.QueryService
func (itc *internalTabletConn) Commit(ctx context.Context, target *querypb.Target, transactionID int64) error {
err := itc.tablet.qsc.QueryService().Commit(ctx, target, transactionID)
- return tabletconn.TabletErrorFromGRPC(vterrors.ToGRPCError(err))
+ return tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err))
}
// Rollback is part of queryservice.QueryService
func (itc *internalTabletConn) Rollback(ctx context.Context, target *querypb.Target, transactionID int64) error {
err := itc.tablet.qsc.QueryService().Rollback(ctx, target, transactionID)
- return tabletconn.TabletErrorFromGRPC(vterrors.ToGRPCError(err))
+ return tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err))
}
// Prepare is part of queryservice.QueryService
func (itc *internalTabletConn) Prepare(ctx context.Context, target *querypb.Target, transactionID int64, dtid string) error {
err := itc.tablet.qsc.QueryService().Prepare(ctx, target, transactionID, dtid)
- return tabletconn.TabletErrorFromGRPC(vterrors.ToGRPCError(err))
+ return tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err))
}
// CommitPrepared is part of queryservice.QueryService
func (itc *internalTabletConn) CommitPrepared(ctx context.Context, target *querypb.Target, dtid string) error {
err := itc.tablet.qsc.QueryService().CommitPrepared(ctx, target, dtid)
- return tabletconn.TabletErrorFromGRPC(vterrors.ToGRPCError(err))
+ return tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err))
}
// RollbackPrepared is part of queryservice.QueryService
func (itc *internalTabletConn) RollbackPrepared(ctx context.Context, target *querypb.Target, dtid string, originalID int64) error {
err := itc.tablet.qsc.QueryService().RollbackPrepared(ctx, target, dtid, originalID)
- return tabletconn.TabletErrorFromGRPC(vterrors.ToGRPCError(err))
+ return tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err))
}
// CreateTransaction is part of queryservice.QueryService
func (itc *internalTabletConn) CreateTransaction(ctx context.Context, target *querypb.Target, dtid string, participants []*querypb.Target) error {
err := itc.tablet.qsc.QueryService().CreateTransaction(ctx, target, dtid, participants)
- return tabletconn.TabletErrorFromGRPC(vterrors.ToGRPCError(err))
+ return tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err))
}
// StartCommit is part of queryservice.QueryService
func (itc *internalTabletConn) StartCommit(ctx context.Context, target *querypb.Target, transactionID int64, dtid string) error {
err := itc.tablet.qsc.QueryService().StartCommit(ctx, target, transactionID, dtid)
- return tabletconn.TabletErrorFromGRPC(vterrors.ToGRPCError(err))
+ return tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err))
}
// SetRollback is part of queryservice.QueryService
func (itc *internalTabletConn) SetRollback(ctx context.Context, target *querypb.Target, dtid string, transactionID int64) error {
err := itc.tablet.qsc.QueryService().SetRollback(ctx, target, dtid, transactionID)
- return tabletconn.TabletErrorFromGRPC(vterrors.ToGRPCError(err))
+ return tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err))
}
// ConcludeTransaction is part of queryservice.QueryService
func (itc *internalTabletConn) ConcludeTransaction(ctx context.Context, target *querypb.Target, dtid string) error {
err := itc.tablet.qsc.QueryService().ConcludeTransaction(ctx, target, dtid)
- return tabletconn.TabletErrorFromGRPC(vterrors.ToGRPCError(err))
+ return tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err))
}
// ReadTransaction is part of queryservice.QueryService
func (itc *internalTabletConn) ReadTransaction(ctx context.Context, target *querypb.Target, dtid string) (metadata *querypb.TransactionMetadata, err error) {
metadata, err = itc.tablet.qsc.QueryService().ReadTransaction(ctx, target, dtid)
- return metadata, tabletconn.TabletErrorFromGRPC(vterrors.ToGRPCError(err))
+ return metadata, tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err))
}
// BeginExecute is part of queryservice.QueryService
@@ -417,13 +418,13 @@ func (itc *internalTabletConn) BeginExecuteBatch(ctx context.Context, target *qu
// MessageStream is part of queryservice.QueryService
func (itc *internalTabletConn) MessageStream(ctx context.Context, target *querypb.Target, name string, callback func(*sqltypes.Result) error) error {
err := itc.tablet.qsc.QueryService().MessageStream(ctx, target, name, callback)
- return tabletconn.TabletErrorFromGRPC(vterrors.ToGRPCError(err))
+ return tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err))
}
// MessageAck is part of queryservice.QueryService
func (itc *internalTabletConn) MessageAck(ctx context.Context, target *querypb.Target, name string, ids []*querypb.Value) (int64, error) {
count, err := itc.tablet.qsc.QueryService().MessageAck(ctx, target, name, ids)
- return count, tabletconn.TabletErrorFromGRPC(vterrors.ToGRPCError(err))
+ return count, tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err))
}
// Handle panic is part of the QueryService interface.
@@ -459,7 +460,7 @@ func (itc *internalTabletConn) SplitQuery(
numRowsPerQueryPart,
algorithm)
if err != nil {
- return nil, tabletconn.TabletErrorFromGRPC(vterrors.ToGRPCError(err))
+ return nil, tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err))
}
return splits, nil
}
@@ -467,13 +468,13 @@ func (itc *internalTabletConn) SplitQuery(
// StreamHealth is part of queryservice.QueryService
func (itc *internalTabletConn) StreamHealth(ctx context.Context, callback func(*querypb.StreamHealthResponse) error) error {
err := itc.tablet.qsc.QueryService().StreamHealth(ctx, callback)
- return tabletconn.TabletErrorFromGRPC(vterrors.ToGRPCError(err))
+ return tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err))
}
// UpdateStream is part of queryservice.QueryService.
func (itc *internalTabletConn) UpdateStream(ctx context.Context, target *querypb.Target, position string, timestamp int64, callback func(*querypb.StreamEvent) error) error {
err := itc.tablet.qsc.QueryService().UpdateStream(ctx, target, position, timestamp, callback)
- return tabletconn.TabletErrorFromGRPC(vterrors.ToGRPCError(err))
+ return tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err))
}
//
diff --git a/go/cmd/vtctl/plugin_grpctabletconn.go b/go/cmd/vtctl/plugin_grpctabletconn.go
index 282f56094ad..e773721e758 100644
--- a/go/cmd/vtctl/plugin_grpctabletconn.go
+++ b/go/cmd/vtctl/plugin_grpctabletconn.go
@@ -7,5 +7,5 @@ package main
// Imports and register the gRPC tabletconn client
import (
- _ "github.com/youtube/vitess/go/vt/tabletserver/grpctabletconn"
+ _ "github.com/youtube/vitess/go/vt/vttablet/grpctabletconn"
)
diff --git a/go/cmd/vtctl/plugin_grpctmclient.go b/go/cmd/vtctl/plugin_grpctmclient.go
index a41f5ccad3e..16db003c134 100644
--- a/go/cmd/vtctl/plugin_grpctmclient.go
+++ b/go/cmd/vtctl/plugin_grpctmclient.go
@@ -7,5 +7,5 @@ package main
// Imports and register the gRPC tabletmanager client
import (
- _ "github.com/youtube/vitess/go/vt/tabletmanager/grpctmclient"
+ _ "github.com/youtube/vitess/go/vt/vttablet/grpctmclient"
)
diff --git a/go/cmd/vtctl/vtctl.go b/go/cmd/vtctl/vtctl.go
index b186ba3ffa2..21087254e68 100644
--- a/go/cmd/vtctl/vtctl.go
+++ b/go/cmd/vtctl/vtctl.go
@@ -18,9 +18,9 @@ import (
"github.com/youtube/vitess/go/exit"
"github.com/youtube/vitess/go/vt/logutil"
"github.com/youtube/vitess/go/vt/servenv"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/vtctl"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
"golang.org/x/net/context"
)
diff --git a/go/cmd/vtctld/plugin_grpctabletconn.go b/go/cmd/vtctld/plugin_grpctabletconn.go
index 282f56094ad..e773721e758 100644
--- a/go/cmd/vtctld/plugin_grpctabletconn.go
+++ b/go/cmd/vtctld/plugin_grpctabletconn.go
@@ -7,5 +7,5 @@ package main
// Imports and register the gRPC tabletconn client
import (
- _ "github.com/youtube/vitess/go/vt/tabletserver/grpctabletconn"
+ _ "github.com/youtube/vitess/go/vt/vttablet/grpctabletconn"
)
diff --git a/go/cmd/vtctld/plugin_grpctmclient.go b/go/cmd/vtctld/plugin_grpctmclient.go
index a41f5ccad3e..16db003c134 100644
--- a/go/cmd/vtctld/plugin_grpctmclient.go
+++ b/go/cmd/vtctld/plugin_grpctmclient.go
@@ -7,5 +7,5 @@ package main
// Imports and register the gRPC tabletmanager client
import (
- _ "github.com/youtube/vitess/go/vt/tabletmanager/grpctmclient"
+ _ "github.com/youtube/vitess/go/vt/vttablet/grpctmclient"
)
diff --git a/go/cmd/vtctld/schema.go b/go/cmd/vtctld/schema.go
index 2d6ba85e37a..bae3baa211b 100644
--- a/go/cmd/vtctld/schema.go
+++ b/go/cmd/vtctld/schema.go
@@ -11,7 +11,7 @@ import (
"github.com/youtube/vitess/go/vt/logutil"
"github.com/youtube/vitess/go/vt/schemamanager"
"github.com/youtube/vitess/go/vt/servenv"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
)
diff --git a/go/cmd/vtgate/plugin_grpctabletconn.go b/go/cmd/vtgate/plugin_grpctabletconn.go
index 282f56094ad..e773721e758 100644
--- a/go/cmd/vtgate/plugin_grpctabletconn.go
+++ b/go/cmd/vtgate/plugin_grpctabletconn.go
@@ -7,5 +7,5 @@ package main
// Imports and register the gRPC tabletconn client
import (
- _ "github.com/youtube/vitess/go/vt/tabletserver/grpctabletconn"
+ _ "github.com/youtube/vitess/go/vt/vttablet/grpctabletconn"
)
diff --git a/go/cmd/vtgateclienttest/goclienttest/echo.go b/go/cmd/vtgateclienttest/goclienttest/echo.go
index 6251aa69bda..25c708b207d 100644
--- a/go/cmd/vtgateclienttest/goclienttest/echo.go
+++ b/go/cmd/vtgateclienttest/goclienttest/echo.go
@@ -11,8 +11,8 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/callerid"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
"github.com/youtube/vitess/go/vt/vtgate/vtgateconn"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
diff --git a/go/cmd/vtgateclienttest/goclienttest/errors.go b/go/cmd/vtgateclienttest/goclienttest/errors.go
index af55e68843c..125806fecb8 100644
--- a/go/cmd/vtgateclienttest/goclienttest/errors.go
+++ b/go/cmd/vtgateclienttest/goclienttest/errors.go
@@ -24,14 +24,14 @@ var (
errorPrefix = "error://"
partialErrorPrefix = "partialerror://"
- executeErrors = map[string]vtrpcpb.ErrorCode{
- "bad input": vtrpcpb.ErrorCode_BAD_INPUT,
- "deadline exceeded": vtrpcpb.ErrorCode_DEADLINE_EXCEEDED,
- "integrity error": vtrpcpb.ErrorCode_INTEGRITY_ERROR,
- "transient error": vtrpcpb.ErrorCode_TRANSIENT_ERROR,
- "unauthenticated": vtrpcpb.ErrorCode_UNAUTHENTICATED,
- "aborted": vtrpcpb.ErrorCode_NOT_IN_TX,
- "unknown error": vtrpcpb.ErrorCode_UNKNOWN_ERROR,
+ executeErrors = map[string]vtrpcpb.Code{
+ "bad input": vtrpcpb.Code_INVALID_ARGUMENT,
+ "deadline exceeded": vtrpcpb.Code_DEADLINE_EXCEEDED,
+ "integrity error": vtrpcpb.Code_ALREADY_EXISTS,
+ "transient error": vtrpcpb.Code_UNAVAILABLE,
+ "unauthenticated": vtrpcpb.Code_UNAUTHENTICATED,
+ "aborted": vtrpcpb.Code_ABORTED,
+ "unknown error": vtrpcpb.Code_UNKNOWN,
}
)
@@ -258,19 +258,12 @@ func checkTransactionExecuteErrors(t *testing.T, conn *vtgateconn.VTGateConn, ex
}
}
-func checkError(t *testing.T, err error, query, errStr string, errCode vtrpcpb.ErrorCode) {
+func checkError(t *testing.T, err error, query, errStr string, errCode vtrpcpb.Code) {
if err == nil {
t.Errorf("[%v] expected error, got nil", query)
return
}
- switch vtErr := err.(type) {
- case *vterrors.VitessError:
- if got, want := vtErr.VtErrorCode(), errCode; got != want {
- t.Errorf("[%v] error code = %v, want %v", query, got, want)
- }
- default:
- t.Errorf("[%v] unrecognized error type: %T, error: %#v", query, err, err)
- return
+ if got, want := vterrors.Code(err), errCode; got != want {
+ t.Errorf("[%v] error code = %v, want %v", query, got, want)
}
-
}
diff --git a/go/cmd/vtgateclienttest/services/echo.go b/go/cmd/vtgateclienttest/services/echo.go
index dc431eba745..f799e3a7bf0 100644
--- a/go/cmd/vtgateclienttest/services/echo.go
+++ b/go/cmd/vtgateclienttest/services/echo.go
@@ -15,8 +15,8 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/callerid"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
"github.com/youtube/vitess/go/vt/vtgate/vtgateservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
diff --git a/go/cmd/vtgateclienttest/services/errors.go b/go/cmd/vtgateclienttest/services/errors.go
index 0c0e552dc97..fbe5d2eb7f2 100644
--- a/go/cmd/vtgateclienttest/services/errors.go
+++ b/go/cmd/vtgateclienttest/services/errors.go
@@ -5,8 +5,6 @@
package services
import (
- "errors"
- "fmt"
"strings"
"golang.org/x/net/context"
@@ -80,56 +78,26 @@ func requestToPartialError(request string, session *vtgatepb.Session) error {
func trimmedRequestToError(received string) error {
switch received {
case "bad input":
- return vterrors.FromError(
- vtrpcpb.ErrorCode_BAD_INPUT,
- errors.New("vtgate test client forced error: bad input"),
- )
+ return vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "vtgate test client forced error: bad input")
case "deadline exceeded":
- return vterrors.FromError(
- vtrpcpb.ErrorCode_DEADLINE_EXCEEDED,
- errors.New("vtgate test client forced error: deadline exceeded"),
- )
+ return vterrors.New(vtrpcpb.Code_DEADLINE_EXCEEDED, "vtgate test client forced error: deadline exceeded")
case "integrity error":
- return vterrors.FromError(
- vtrpcpb.ErrorCode_INTEGRITY_ERROR,
- errors.New("vtgate test client forced error: integrity error (errno 1062) (sqlstate 23000)"),
- )
+ return vterrors.New(vtrpcpb.Code_ALREADY_EXISTS, "vtgate test client forced error: integrity error (errno 1062) (sqlstate 23000)")
// request backlog and general throttling type errors
case "transient error":
- return vterrors.FromError(
- vtrpcpb.ErrorCode_TRANSIENT_ERROR,
- errors.New("request_backlog: too many requests in flight: vtgate test client forced error: transient error"),
- )
+ return vterrors.New(vtrpcpb.Code_UNAVAILABLE, "request_backlog: too many requests in flight: vtgate test client forced error: transient error")
case "throttled error":
- return vterrors.FromError(
- vtrpcpb.ErrorCode_TRANSIENT_ERROR,
- errors.New("request_backlog: exceeded XXX quota, rate limiting: vtgate test client forced error: transient error"),
- )
+ return vterrors.New(vtrpcpb.Code_UNAVAILABLE, "request_backlog: exceeded XXX quota, rate limiting: vtgate test client forced error: transient error")
case "unauthenticated":
- return vterrors.FromError(
- vtrpcpb.ErrorCode_UNAUTHENTICATED,
- errors.New("vtgate test client forced error: unauthenticated"),
- )
+ return vterrors.New(vtrpcpb.Code_UNAUTHENTICATED, "vtgate test client forced error: unauthenticated")
case "aborted":
- return vterrors.FromError(
- vtrpcpb.ErrorCode_NOT_IN_TX,
- errors.New("vtgate test client forced error: aborted"),
- )
+ return vterrors.New(vtrpcpb.Code_ABORTED, "vtgate test client forced error: aborted")
case "query not served":
- return vterrors.FromError(
- vtrpcpb.ErrorCode_QUERY_NOT_SERVED,
- errors.New("vtgate test client forced error: query not served"),
- )
+ return vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION, "vtgate test client forced error: query not served")
case "unknown error":
- return vterrors.FromError(
- vtrpcpb.ErrorCode_UNKNOWN_ERROR,
- errors.New("vtgate test client forced error: unknown error"),
- )
+ return vterrors.New(vtrpcpb.Code_UNKNOWN, "vtgate test client forced error: unknown error")
default:
- return vterrors.FromError(
- vtrpcpb.ErrorCode_UNKNOWN_ERROR,
- fmt.Errorf("vtgate test client error request unrecognized: %v", received),
- )
+ return vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "vtgate test client error request unrecognized: %v", received)
}
}
diff --git a/go/cmd/vttablet/plugin_filecustomrule.go b/go/cmd/vttablet/plugin_filecustomrule.go
index 84c68d85560..893198092f4 100644
--- a/go/cmd/vttablet/plugin_filecustomrule.go
+++ b/go/cmd/vttablet/plugin_filecustomrule.go
@@ -7,5 +7,5 @@ package main
// Imports and register the file custom rule source
import (
- _ "github.com/youtube/vitess/go/vt/tabletserver/customrule/filecustomrule"
+ _ "github.com/youtube/vitess/go/vt/vttablet/customrule/filecustomrule"
)
diff --git a/go/cmd/vttablet/plugin_grpcqueryservice.go b/go/cmd/vttablet/plugin_grpcqueryservice.go
index 4a6aa17088b..4077bdedff6 100644
--- a/go/cmd/vttablet/plugin_grpcqueryservice.go
+++ b/go/cmd/vttablet/plugin_grpcqueryservice.go
@@ -8,8 +8,8 @@ package main
import (
"github.com/youtube/vitess/go/vt/servenv"
- "github.com/youtube/vitess/go/vt/tabletserver"
- "github.com/youtube/vitess/go/vt/tabletserver/grpcqueryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/grpcqueryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver"
)
func init() {
diff --git a/go/cmd/vttablet/plugin_grpctabletconn.go b/go/cmd/vttablet/plugin_grpctabletconn.go
index 282f56094ad..e773721e758 100644
--- a/go/cmd/vttablet/plugin_grpctabletconn.go
+++ b/go/cmd/vttablet/plugin_grpctabletconn.go
@@ -7,5 +7,5 @@ package main
// Imports and register the gRPC tabletconn client
import (
- _ "github.com/youtube/vitess/go/vt/tabletserver/grpctabletconn"
+ _ "github.com/youtube/vitess/go/vt/vttablet/grpctabletconn"
)
diff --git a/go/cmd/vttablet/plugin_grpctmclient.go b/go/cmd/vttablet/plugin_grpctmclient.go
index a41f5ccad3e..16db003c134 100644
--- a/go/cmd/vttablet/plugin_grpctmclient.go
+++ b/go/cmd/vttablet/plugin_grpctmclient.go
@@ -7,5 +7,5 @@ package main
// Imports and register the gRPC tabletmanager client
import (
- _ "github.com/youtube/vitess/go/vt/tabletmanager/grpctmclient"
+ _ "github.com/youtube/vitess/go/vt/vttablet/grpctmclient"
)
diff --git a/go/cmd/vttablet/plugin_grpctmserver.go b/go/cmd/vttablet/plugin_grpctmserver.go
index e965ac26b8a..07ad21dd3da 100644
--- a/go/cmd/vttablet/plugin_grpctmserver.go
+++ b/go/cmd/vttablet/plugin_grpctmserver.go
@@ -7,5 +7,5 @@ package main
// Imports and register the gRPC tabletmanager server
import (
- _ "github.com/youtube/vitess/go/vt/tabletmanager/grpctmserver"
+ _ "github.com/youtube/vitess/go/vt/vttablet/grpctmserver"
)
diff --git a/go/cmd/vttablet/plugin_sysloglogger.go b/go/cmd/vttablet/plugin_sysloglogger.go
index caeea7706c1..0507bfbe9bb 100644
--- a/go/cmd/vttablet/plugin_sysloglogger.go
+++ b/go/cmd/vttablet/plugin_sysloglogger.go
@@ -3,5 +3,5 @@ package main
// Imports and register the syslog-based query logger
import (
- _ "github.com/youtube/vitess/go/vt/tabletserver/sysloglogger"
+ _ "github.com/youtube/vitess/go/vt/vttablet/sysloglogger"
)
diff --git a/go/cmd/vttablet/plugin_zkcustomrule.go b/go/cmd/vttablet/plugin_zkcustomrule.go
index f36a2092342..3573fa2bc4a 100644
--- a/go/cmd/vttablet/plugin_zkcustomrule.go
+++ b/go/cmd/vttablet/plugin_zkcustomrule.go
@@ -7,5 +7,5 @@ package main
// Imports and register the zookeeper custom rule source
import (
- _ "github.com/youtube/vitess/go/vt/tabletserver/customrule/zkcustomrule"
+ _ "github.com/youtube/vitess/go/vt/vttablet/customrule/zkcustomrule"
)
diff --git a/go/cmd/vttablet/status.go b/go/cmd/vttablet/status.go
index e35e900bb55..bb8a37f2ec8 100644
--- a/go/cmd/vttablet/status.go
+++ b/go/cmd/vttablet/status.go
@@ -6,9 +6,9 @@ import (
"github.com/youtube/vitess/go/vt/health"
"github.com/youtube/vitess/go/vt/servenv"
_ "github.com/youtube/vitess/go/vt/status"
- "github.com/youtube/vitess/go/vt/tabletmanager"
- "github.com/youtube/vitess/go/vt/tabletserver"
"github.com/youtube/vitess/go/vt/topo"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletmanager"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver"
)
var (
diff --git a/go/cmd/vttablet/vttablet.go b/go/cmd/vttablet/vttablet.go
index 75347b53184..864f29f379f 100644
--- a/go/cmd/vttablet/vttablet.go
+++ b/go/cmd/vttablet/vttablet.go
@@ -15,11 +15,11 @@ import (
"github.com/youtube/vitess/go/vt/servenv"
"github.com/youtube/vitess/go/vt/tableacl"
"github.com/youtube/vitess/go/vt/tableacl/simpleacl"
- "github.com/youtube/vitess/go/vt/tabletmanager"
- "github.com/youtube/vitess/go/vt/tabletserver"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletmanager"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
"golang.org/x/net/context"
)
@@ -74,7 +74,8 @@ func main() {
}
// creates and registers the query service
- qsc := tabletserver.NewServer()
+ ts := topo.Open()
+ qsc := tabletserver.NewServer(ts)
servenv.OnRun(func() {
qsc.Register()
addStatusParts(qsc)
@@ -118,7 +119,6 @@ func main() {
if servenv.GRPCPort != nil {
gRPCPort = int32(*servenv.GRPCPort)
}
- ts := topo.Open()
agent, err = tabletmanager.NewActionAgent(context.Background(), ts, mysqld, qsc, tabletAlias, *dbcfgs, mycnf, int32(*servenv.Port), gRPCPort)
if err != nil {
log.Error(err)
diff --git a/go/cmd/vttlstest/vttlstest.go b/go/cmd/vttlstest/vttlstest.go
new file mode 100644
index 00000000000..b09e2ba9b71
--- /dev/null
+++ b/go/cmd/vttlstest/vttlstest.go
@@ -0,0 +1,94 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+
+ log "github.com/golang/glog"
+
+ "github.com/youtube/vitess/go/exit"
+ "github.com/youtube/vitess/go/vt/logutil"
+ "github.com/youtube/vitess/go/vt/tlstest"
+)
+
+var doc = `
+vttlstest is a tool for generating test certificates and keys for TLS tests.
+
+To create a toplevel CA, use:
+ $ vttlstest -root /tmp CreateCA
+
+To create an intermediate or leaf CA, use:
+ $ vttlstest -root /tmp CreateSignedCert servers
+ $ vttlstest -root /tmp CreateSignedCert -parent servers server
+
+To get help on a command, use:
+ $ vttlstest -help
+`
+
+type cmdFunc func(subFlags *flag.FlagSet, args []string)
+
+var cmdMap map[string]cmdFunc
+
+func init() {
+ cmdMap = map[string]cmdFunc{
+ "CreateCA": cmdCreateCA,
+ "CreateSignedCert": cmdCreateSignedCert,
+ }
+}
+
+var (
+ root = flag.String("root", ".", "root directory for certificates and keys")
+)
+
+func cmdCreateCA(subFlags *flag.FlagSet, args []string) {
+ subFlags.Parse(args)
+ if subFlags.NArg() > 0 {
+ log.Fatalf("CreateCA command doesn't take any parameter")
+ }
+
+ tlstest.CreateCA(*root)
+}
+
+func cmdCreateSignedCert(subFlags *flag.FlagSet, args []string) {
+ parent := subFlags.String("parent", "ca", "Parent cert name to use. Use 'ca' for the toplevel CA.")
+ serial := subFlags.String("serial", "01", "Serial number for the certificate to create. Should be different for two certificates with the same parent.")
+ commonName := subFlags.String("common_name", "", "Common name for the certificate. If empty, uses the name.")
+
+ subFlags.Parse(args)
+ if subFlags.NArg() != 1 {
+ log.Fatalf("CreateSignedCert command takes a single name as a parameter")
+ }
+ if *commonName == "" {
+ *commonName = subFlags.Arg(0)
+ }
+
+ tlstest.CreateSignedCert(*root, *parent, *serial, subFlags.Arg(0), *commonName)
+}
+
+func main() {
+ defer exit.Recover()
+ defer logutil.Flush()
+ flag.Usage = func() {
+ fmt.Fprintf(os.Stderr, "Usage of %v:\n", os.Args[0])
+ flag.PrintDefaults()
+ fmt.Fprintf(os.Stderr, doc)
+ }
+ flag.Parse()
+ args := flag.Args()
+ if len(args) == 0 {
+ flag.Usage()
+ exit.Return(1)
+ }
+
+ cmdName := args[0]
+ args = args[1:]
+ cmd, ok := cmdMap[cmdName]
+ if !ok {
+ log.Fatalf("Unknown command %v", cmdName)
+ }
+ subFlags := flag.NewFlagSet(cmdName, flag.ExitOnError)
+
+ // Run the command.
+ cmd(subFlags, args)
+}
diff --git a/go/cmd/vtworker/plugin_grpctabletconn.go b/go/cmd/vtworker/plugin_grpctabletconn.go
index 282f56094ad..e773721e758 100644
--- a/go/cmd/vtworker/plugin_grpctabletconn.go
+++ b/go/cmd/vtworker/plugin_grpctabletconn.go
@@ -7,5 +7,5 @@ package main
// Imports and register the gRPC tabletconn client
import (
- _ "github.com/youtube/vitess/go/vt/tabletserver/grpctabletconn"
+ _ "github.com/youtube/vitess/go/vt/vttablet/grpctabletconn"
)
diff --git a/go/cmd/vtworker/plugin_grpctmclient.go b/go/cmd/vtworker/plugin_grpctmclient.go
index a41f5ccad3e..16db003c134 100644
--- a/go/cmd/vtworker/plugin_grpctmclient.go
+++ b/go/cmd/vtworker/plugin_grpctmclient.go
@@ -7,5 +7,5 @@ package main
// Imports and register the gRPC tabletmanager client
import (
- _ "github.com/youtube/vitess/go/vt/tabletmanager/grpctmclient"
+ _ "github.com/youtube/vitess/go/vt/vttablet/grpctmclient"
)
diff --git a/go/mysqlconn/auth_server.go b/go/mysqlconn/auth_server.go
new file mode 100644
index 00000000000..de837bedd6b
--- /dev/null
+++ b/go/mysqlconn/auth_server.go
@@ -0,0 +1,111 @@
+package mysqlconn
+
+import (
+ "crypto/rand"
+ "crypto/sha1"
+
+ log "github.com/golang/glog"
+)
+
+// AuthServer is the interface that servers must implement to validate
+// users and passwords. It has two modes:
+//
+// 1. using salt the way MySQL native auth does it. In that case, the
+// password is not sent in the clear, but the salt is used to hash the
+// password both on the client and server side, and the result is sent
+// and compared.
+//
+// 2. sending the user / password in the clear (using MySQL Cleartext
+// method). The server then gets access to both user and password, and
+// can authenticate using any method. If SSL is not used, it means the
+// password is sent in the clear. That may not be suitable for some
+// use cases.
+type AuthServer interface {
+ // UseClearText returns true is Cleartext auth is used.
+ // - If it is not set, Salt() and ValidateHash() are called.
+ // The server starts up in mysql_native_password mode.
+ // (but ValidateClearText can also be called, if client
+ // switched to Cleartext).
+ // - If it is set, ValidateClearText() is called.
+ // The server starts up in mysql_clear_password mode.
+ UseClearText() bool
+
+ // Salt returns the salt to use for a connection.
+ // It should be 20 bytes of data.
+ Salt() ([]byte, error)
+
+ // ValidateHash validates the data sent by the client matches
+ // what the server computes. It also returns the user data.
+ ValidateHash(salt []byte, user string, authResponse []byte) (string, error)
+
+ // ValidateClearText validates a user / password is correct.
+ // It also returns the user data.
+ ValidateClearText(user, password string) (string, error)
+}
+
+// authServers is a registry of AuthServer implementations.
+var authServers = make(map[string]AuthServer)
+
+// RegisterAuthServerImpl registers an implementations of AuthServer.
+func RegisterAuthServerImpl(name string, authServer AuthServer) {
+ if _, ok := authServers[name]; ok {
+ log.Fatalf("AuthServer named %v already exists", name)
+ }
+ authServers[name] = authServer
+}
+
+// GetAuthServer returns an AuthServer by name, or log.Fatalf.
+func GetAuthServer(name string) AuthServer {
+ authServer, ok := authServers[name]
+ if !ok {
+ log.Fatalf("no AuthServer name %v registered", name)
+ }
+ return authServer
+}
+
+// newSalt returns a 20 character salt.
+func newSalt() ([]byte, error) {
+ salt := make([]byte, 20)
+ if _, err := rand.Read(salt); err != nil {
+ return nil, err
+ }
+
+ // Salt must be a legal UTF8 string.
+ for i := 0; i < len(salt); i++ {
+ salt[i] &= 0x7f
+ if salt[i] == '\x00' || salt[i] == '$' {
+ salt[i]++
+ }
+ }
+
+ return salt, nil
+}
+
+// scramblePassword computes the hash of the password using 4.1+ method.
+func scramblePassword(salt, password []byte) []byte {
+ if len(password) == 0 {
+ return nil
+ }
+
+ // stage1Hash = SHA1(password)
+ crypt := sha1.New()
+ crypt.Write(password)
+ stage1 := crypt.Sum(nil)
+
+ // scrambleHash = SHA1(salt + SHA1(stage1Hash))
+ // inner Hash
+ crypt.Reset()
+ crypt.Write(stage1)
+ hash := crypt.Sum(nil)
+ // outer Hash
+ crypt.Reset()
+ crypt.Write(salt)
+ crypt.Write(hash)
+ scramble := crypt.Sum(nil)
+
+ // token = scrambleHash XOR stage1Hash
+ for i := range scramble {
+ scramble[i] ^= stage1[i]
+ }
+ return scramble
+}
diff --git a/go/mysqlconn/auth_server_config.go b/go/mysqlconn/auth_server_config.go
new file mode 100644
index 00000000000..3fca5d71608
--- /dev/null
+++ b/go/mysqlconn/auth_server_config.go
@@ -0,0 +1,101 @@
+package mysqlconn
+
+import (
+ "bytes"
+ "encoding/json"
+ "io/ioutil"
+
+ log "github.com/golang/glog"
+
+ "github.com/youtube/vitess/go/sqldb"
+)
+
+// AuthServerConfig implements AuthServer using a static configuration.
+type AuthServerConfig struct {
+ // ClearText can be set to force the use of ClearText auth.
+ ClearText bool
+
+ // Entries contains the users, passwords and user data.
+ Entries map[string]*AuthServerConfigEntry
+}
+
+// AuthServerConfigEntry stores the values for a given user.
+type AuthServerConfigEntry struct {
+ Password string
+ UserData string
+}
+
+// NewAuthServerConfig returns a new empty AuthServerConfig.
+func NewAuthServerConfig() *AuthServerConfig {
+ return &AuthServerConfig{
+ ClearText: false,
+ Entries: make(map[string]*AuthServerConfigEntry),
+ }
+}
+
+// RegisterAuthServerConfigFromParams creates and registers a new
+// AuthServerConfig, loaded for a JSON file or string. If file is set,
+// it uses file. Otherwise, load the string. It log.Fatals out in case
+// of error.
+func RegisterAuthServerConfigFromParams(file, str string) {
+ authServerConfig := NewAuthServerConfig()
+ jsonConfig := []byte(str)
+ if file != "" {
+ data, err := ioutil.ReadFile(file)
+ if err != nil {
+ log.Fatalf("Failed to read mysql_auth_server_config_file file: %v", err)
+ }
+ jsonConfig = data
+ }
+
+ // Parse JSON config.
+ if err := json.Unmarshal(jsonConfig, &authServerConfig.Entries); err != nil {
+ log.Fatalf("Error parsing auth server config: %v", err)
+ }
+
+ // And register the server.
+ RegisterAuthServerImpl("config", authServerConfig)
+}
+
+// UseClearText is part of the AuthServer interface.
+func (a *AuthServerConfig) UseClearText() bool {
+ return a.ClearText
+}
+
+// Salt is part of the AuthServer interface.
+func (a *AuthServerConfig) Salt() ([]byte, error) {
+ return newSalt()
+}
+
+// ValidateHash is part of the AuthServer interface.
+func (a *AuthServerConfig) ValidateHash(salt []byte, user string, authResponse []byte) (string, error) {
+ // Find the entry.
+ entry, ok := a.Entries[user]
+ if !ok {
+ return "", sqldb.NewSQLError(ERAccessDeniedError, SSAccessDeniedError, "Access denied for user '%v'", user)
+ }
+
+ // Validate the password.
+ computedAuthResponse := scramblePassword(salt, []byte(entry.Password))
+ if bytes.Compare(authResponse, computedAuthResponse) != 0 {
+ return "", sqldb.NewSQLError(ERAccessDeniedError, SSAccessDeniedError, "Access denied for user '%v'", user)
+ }
+
+ return entry.UserData, nil
+}
+
+// ValidateClearText is part of the AuthServer interface.
+func (a *AuthServerConfig) ValidateClearText(user, password string) (string, error) {
+ // Find the entry.
+ entry, ok := a.Entries[user]
+ if !ok {
+ return "", sqldb.NewSQLError(ERAccessDeniedError, SSAccessDeniedError, "Access denied for user '%v'", user)
+ }
+
+ // Validate the password.
+ if entry.Password != password {
+ return "", sqldb.NewSQLError(ERAccessDeniedError, SSAccessDeniedError, "Access denied for user '%v'", user)
+ }
+
+ return entry.UserData, nil
+}
diff --git a/go/mysqlconn/client.go b/go/mysqlconn/client.go
index 5365f58d9ba..fe2305bb7c3 100644
--- a/go/mysqlconn/client.go
+++ b/go/mysqlconn/client.go
@@ -1,7 +1,7 @@
package mysqlconn
import (
- "crypto/sha1"
+ "crypto/tls"
"fmt"
"net"
"strconv"
@@ -11,6 +11,7 @@ import (
"golang.org/x/net/context"
"github.com/youtube/vitess/go/sqldb"
+ "github.com/youtube/vitess/go/vt/servenv/grpcutils"
)
// connectResult is used by Connect.
@@ -183,7 +184,7 @@ func (c *Conn) clientHandshake(characterSet uint8, params *sqldb.ConnParams) err
if err != nil {
return sqldb.NewSQLError(CRServerLost, "", "initial packet read failed: %v", err)
}
- capabilities, cipher, err := c.parseInitialHandshakePacket(data)
+ capabilities, salt, err := c.parseInitialHandshakePacket(data)
if err != nil {
return err
}
@@ -193,16 +194,55 @@ func (c *Conn) clientHandshake(characterSet uint8, params *sqldb.ConnParams) err
return sqldb.NewSQLError(CRVersionError, SSUnknownSQLState, "cannot connect to servers earlier than 4.1")
}
- // If client asked for SSL, but server doesn't support it, stop right here.
- if capabilities&CapabilityClientSSL == 0 && params.SslCert != "" && params.SslKey != "" {
- return sqldb.NewSQLError(CRSSLConnectionError, SSUnknownSQLState, "server doesn't support SSL but client asked for it")
- }
-
- // Remember a subset of the capabilities, so we can use them later in the protocol.
+ // Remember a subset of the capabilities, so we can use them
+ // later in the protocol.
c.Capabilities = capabilities & (CapabilityClientDeprecateEOF)
+ // Handle switch to SSL if necessary.
+ if params.Flags&CapabilityClientSSL > 0 {
+ // If client asked for SSL, but server doesn't support it,
+ // stop right here.
+ if capabilities&CapabilityClientSSL == 0 {
+ return sqldb.NewSQLError(CRSSLConnectionError, SSUnknownSQLState, "server doesn't support SSL but client asked for it")
+ }
+
+ // The ServerName to verify depends on what the hostname is.
+ // - If using a socket, we use "localhost".
+ // - If it is an IP address, we need to prefix it with 'IP:'.
+ // - If not, we can just use it as is.
+ // We may need to add a ServerName field to ConnParams to
+ // make this more explicit.
+ serverName := "localhost"
+ if params.Host != "" {
+ if net.ParseIP(params.Host) != nil {
+ serverName = "IP:" + params.Host
+ } else {
+ serverName = params.Host
+ }
+ }
+
+ // Build the TLS config.
+ clientConfig, err := grpcutils.TLSClientConfig(params.SslCert, params.SslKey, params.SslCa, serverName)
+ if err != nil {
+ return sqldb.NewSQLError(CRSSLConnectionError, SSUnknownSQLState, "error loading client cert and ca: %v", err)
+ }
+
+ // Send the SSLRequest packet.
+ if err := c.writeSSLRequest(capabilities, characterSet, params); err != nil {
+ return err
+ }
+
+ // Switch to SSL.
+ conn := tls.Client(c.conn, clientConfig)
+ c.conn = conn
+ c.reader.Reset(conn)
+ c.writer.Reset(conn)
+ c.Capabilities |= CapabilityClientSSL
+ }
+
// Build and send our handshake response 41.
- if err := c.writeHandshakeResponse41(capabilities, cipher, characterSet, params); err != nil {
+ // Note this one will never have SSL flag on.
+ if err := c.writeHandshakeResponse41(capabilities, salt, characterSet, params); err != nil {
return err
}
@@ -213,12 +253,42 @@ func (c *Conn) clientHandshake(characterSet uint8, params *sqldb.ConnParams) err
}
switch response[0] {
case OKPacket:
- // OK packet, we are authenticated. We keep going.
+ // OK packet, we are authenticated. Save the user, keep going.
+ c.User = params.Uname
+ case AuthSwitchRequestPacket:
+ // Server is asking to use a different auth method. We
+ // only support cleartext plugin.
+ pluginName, _, err := parseAuthSwitchRequest(response)
+ if err != nil {
+ return sqldb.NewSQLError(CRServerHandshakeErr, SSUnknownSQLState, "cannot parse auth switch request: %v", err)
+ }
+ if pluginName != mysqlClearPassword {
+ return sqldb.NewSQLError(CRServerHandshakeErr, SSUnknownSQLState, "server asked for unsupported auth method: %v", pluginName)
+ }
+
+ // Write the password packet.
+ if err := c.writeClearTextPassword(params); err != nil {
+ return err
+ }
+
+ // Wait for OK packet.
+ response, err = c.readPacket()
+ if err != nil {
+ return sqldb.NewSQLError(CRServerLost, SSUnknownSQLState, "%v", err)
+ }
+ switch response[0] {
+ case OKPacket:
+ // OK packet, we are authenticated. Save the user, keep going.
+ c.User = params.Uname
+ case ErrPacket:
+ return parseErrorPacket(response)
+ default:
+ return sqldb.NewSQLError(CRServerHandshakeErr, SSUnknownSQLState, "initial server response cannot be parsed: %v", response)
+ }
case ErrPacket:
return parseErrorPacket(response)
default:
- // FIXME(alainjobart) handle extra auth cases and so on.
- return fmt.Errorf("initial server response is asking for more information, not implemented yet: %v", response)
+ return sqldb.NewSQLError(CRServerHandshakeErr, SSUnknownSQLState, "initial server response cannot be parsed: %v", response)
}
// If the server didn't support DbName in its handshake, set
@@ -375,9 +445,58 @@ func (c *Conn) parseInitialHandshakePacket(data []byte) (uint32, []byte, error)
return capabilities, authPluginData, nil
}
+// writeSSLRequest writes the SSLRequest packet. It's just a truncated
+// HandshakeResponse41.
+func (c *Conn) writeSSLRequest(capabilities uint32, characterSet uint8, params *sqldb.ConnParams) error {
+ // Build our flags, with CapabilityClientSSL.
+ var flags uint32 = CapabilityClientLongPassword |
+ CapabilityClientLongFlag |
+ CapabilityClientProtocol41 |
+ CapabilityClientTransactions |
+ CapabilityClientSecureConnection |
+ CapabilityClientPluginAuth |
+ CapabilityClientPluginAuthLenencClientData |
+ CapabilityClientSSL |
+ // If the server supported
+ // CapabilityClientDeprecateEOF, we also support it.
+ c.Capabilities&CapabilityClientDeprecateEOF
+
+ length :=
+ 4 + // Client capability flags.
+ 4 + // Max-packet size.
+ 1 + // Character set.
+ 23 // Reserved.
+
+ // Add the DB name if the server supports it.
+ if params.DbName != "" && (capabilities&CapabilityClientConnectWithDB != 0) {
+ flags |= CapabilityClientConnectWithDB
+ }
+
+ data := make([]byte, length)
+ pos := 0
+
+ // Client capability flags.
+ pos = writeUint32(data, pos, flags)
+
+ // Max-packet size, always 0. See doc.go.
+ pos += 4
+
+ // Character set.
+ pos = writeByte(data, pos, characterSet)
+
+ // And send it as is.
+ if err := c.writePacket(data); err != nil {
+ return sqldb.NewSQLError(CRServerLost, SSUnknownSQLState, "cannot send SSLRequest: %v", err)
+ }
+ if err := c.flush(); err != nil {
+ return sqldb.NewSQLError(CRServerLost, SSUnknownSQLState, "cannot flush SSLRequest: %v", err)
+ }
+ return nil
+}
+
// writeHandshakeResponse41 writes the handshake response.
// Returns a sqldb.SQLError.
-func (c *Conn) writeHandshakeResponse41(capabilities uint32, cipher []byte, characterSet uint8, params *sqldb.ConnParams) error {
+func (c *Conn) writeHandshakeResponse41(capabilities uint32, salt []byte, characterSet uint8, params *sqldb.ConnParams) error {
// Build our flags.
var flags uint32 = CapabilityClientLongPassword |
CapabilityClientLongFlag |
@@ -390,10 +509,10 @@ func (c *Conn) writeHandshakeResponse41(capabilities uint32, cipher []byte, char
// CapabilityClientDeprecateEOF, we also support it.
c.Capabilities&CapabilityClientDeprecateEOF
- // FIXME(alainjobart) add SSL, multi statement, client found rows.
+ // FIXME(alainjobart) add multi statement, client found rows.
// Password encryption.
- scrambledPassword := scramblePassword(cipher, []byte(params.Pass))
+ scrambledPassword := scramblePassword(salt, []byte(params.Pass))
length :=
4 + // Client capability flags.
@@ -430,12 +549,6 @@ func (c *Conn) writeHandshakeResponse41(capabilities uint32, cipher []byte, char
// Character set.
pos = writeByte(data, pos, characterSet)
- // FIXME(alainjobart): With SSL can send this now.
- // For now we don't support it.
- if params.SslCert != "" && params.SslKey != "" {
- return sqldb.NewSQLError(CRSSLConnectionError, SSUnknownSQLState, "SSL support is not implemented yet in this client")
- }
-
// 23 reserved bytes, all 0.
pos += 23
@@ -475,31 +588,29 @@ func (c *Conn) writeHandshakeResponse41(capabilities uint32, cipher []byte, char
return nil
}
-// Encrypt password using 4.1+ method
-func scramblePassword(scramble, password []byte) []byte {
- if len(password) == 0 {
- return nil
- }
-
- // stage1Hash = SHA1(password)
- crypt := sha1.New()
- crypt.Write(password)
- stage1 := crypt.Sum(nil)
-
- // scrambleHash = SHA1(scramble + SHA1(stage1Hash))
- // inner Hash
- crypt.Reset()
- crypt.Write(stage1)
- hash := crypt.Sum(nil)
- // outer Hash
- crypt.Reset()
- crypt.Write(scramble)
- crypt.Write(hash)
- scramble = crypt.Sum(nil)
-
- // token = scrambleHash XOR stage1Hash
- for i := range scramble {
- scramble[i] ^= stage1[i]
- }
- return scramble
+func parseAuthSwitchRequest(data []byte) (string, []byte, error) {
+ pos := 1
+ pluginName, pos, ok := readNullString(data, pos)
+ if !ok {
+ return "", nil, fmt.Errorf("cannot get plugin name from AuthSwitchRequest: %v", data)
+ }
+
+ return pluginName, data[pos:], nil
+}
+
+// writeClearTextPassword writes the clear text password.
+// Returns a sqldb.SQLError.
+func (c *Conn) writeClearTextPassword(params *sqldb.ConnParams) error {
+ length := len(params.Pass) + 1
+ data := c.startEphemeralPacket(length)
+ pos := 0
+ pos = writeNullString(data, pos, params.Pass)
+ // Sanity check.
+ if pos != len(data) {
+ return fmt.Errorf("error building ClearTextPassword packet: got %v bytes expected %v", pos, len(data))
+ }
+ if err := c.writeEphemeralPacket(true); err != nil {
+ return err
+ }
+ return nil
}
diff --git a/go/mysqlconn/client_test.go b/go/mysqlconn/client_test.go
index cb83fc98ca7..f1b3ec259fb 100644
--- a/go/mysqlconn/client_test.go
+++ b/go/mysqlconn/client_test.go
@@ -5,6 +5,7 @@ import (
"io/ioutil"
"net"
"os"
+ "path"
"strings"
"sync"
"testing"
@@ -13,6 +14,7 @@ import (
"golang.org/x/net/context"
"github.com/youtube/vitess/go/sqldb"
+ "github.com/youtube/vitess/go/vt/tlstest"
"github.com/youtube/vitess/go/vt/vttest"
)
@@ -169,13 +171,65 @@ func testDupEntryWithRealDatabase(t *testing.T, params *sqldb.ConnParams) {
assertSQLError(t, err, ERDupEntry, SSDupKey, "Duplicate entry")
}
+// testTLS tests our client can connect via SSL.
+func testTLS(t *testing.T, params *sqldb.ConnParams) {
+ // First make sure the official 'mysql' client can connect.
+ output, ok := runMysql(t, params, "status")
+ if !ok {
+ t.Fatalf("'mysql -e status' failed: %v", output)
+ }
+ if !strings.Contains(output, "Cipher in use is") {
+ t.Fatalf("cannot connect via SSL: %v", output)
+ }
+
+ // Now connect with our client.
+ ctx := context.Background()
+ conn, err := Connect(ctx, params)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer conn.Close()
+
+ result, err := conn.ExecuteFetch("SHOW STATUS LIKE 'Ssl_cipher'", 10, true)
+ if err != nil {
+ t.Fatalf("SHOW STATUS LIKE 'Ssl_cipher' failed: %v", err)
+ }
+ if len(result.Rows) != 1 || result.Rows[0][0].String() != "Ssl_cipher" ||
+ result.Rows[0][1].String() == "" {
+ t.Fatalf("SHOW STATUS LIKE 'Ssl_cipher' returned unexpected result: %v", result)
+ }
+}
+
// TestWithRealDatabase runs a real MySQL database, and runs all kinds
// of tests on it. To minimize overhead, we only run one database, and
// run all the tests on it.
func TestWithRealDatabase(t *testing.T) {
+ // Create the certs.
+ root, err := ioutil.TempDir("", "TestTLSServer")
+ if err != nil {
+ t.Fatalf("TempDir failed: %v", err)
+ }
+ defer os.RemoveAll(root)
+ tlstest.CreateCA(root)
+ tlstest.CreateSignedCert(root, tlstest.CA, "01", "server", "localhost")
+ tlstest.CreateSignedCert(root, tlstest.CA, "02", "client", "Client Cert")
+
+ // Create the extra SSL my.cnf lines.
+ cnf := fmt.Sprintf(`
+ssl-ca=%v/ca-cert.pem
+ssl-cert=%v/server-cert.pem
+ssl-key=%v/server-key.pem
+`, root, root, root)
+ extraMyCnf := path.Join(root, "ssl_my.cnf")
+ if err := ioutil.WriteFile(extraMyCnf, []byte(cnf), os.ModePerm); err != nil {
+ t.Fatalf("ioutil.WriteFile(%v) failed: %v", extraMyCnf, err)
+ }
+
+ // Launch MySQL.
hdl, err := vttest.LaunchVitess(
vttest.MySQLOnly("vttest"),
- vttest.NoStderr())
+ vttest.NoStderr(),
+ vttest.ExtraMyCnf(extraMyCnf))
if err != nil {
t.Fatal(err)
}
@@ -224,4 +278,17 @@ func TestWithRealDatabase(t *testing.T) {
t.Run("Schema", func(t *testing.T) {
testSchema(t, ¶ms)
})
+
+ // Test SSL. First we make sure a real 'mysql' client gets it.
+ params.Flags = CapabilityClientSSL
+ params.SslCa = path.Join(root, "ca-cert.pem")
+ params.SslCert = path.Join(root, "client-cert.pem")
+ params.SslKey = path.Join(root, "client-key.pem")
+ t.Run("TLS", func(t *testing.T) {
+ testTLS(t, ¶ms)
+ })
+
+ // Uncomment to sleep and be able to connect to MySQL
+ // fmt.Printf("Connect to MySQL using parameters: %v\n", params)
+ // time.Sleep(10 * time.Minute)
}
diff --git a/go/mysqlconn/conn.go b/go/mysqlconn/conn.go
index 81b3fdb0658..3757095238a 100644
--- a/go/mysqlconn/conn.go
+++ b/go/mysqlconn/conn.go
@@ -60,7 +60,7 @@ type Conn struct {
// Capabilities is the current set of features this connection
// is using. It is the features that are both supported by
// the client and the server, and currently in use.
- // It is set after the initial handshake.
+ // It is set during the initial handshake.
//
// It is only used for CapabilityClientDeprecateEOF.
Capabilities uint32
@@ -71,6 +71,14 @@ type Conn struct {
// See the values in constants.go.
CharacterSet uint8
+ // User is the name used by the client to connect.
+ // It is set during the initial handshake.
+ User string
+
+ // UserData is custom data returned by the AuthServer module.
+ // It is set during the initial handshake.
+ UserData string
+
// SchemaName is the default database name to use. It is set
// during handshake, and by ComInitDb packets. Both client and
// servers maintain it.
@@ -145,6 +153,46 @@ func newConn(conn net.Conn) *Conn {
}
}
+// readPacketDirect attempts to read a packet from the socket directly.
+// It needs to be used for the first handshake packet the server receives,
+// so we do't buffer the SSL negotiation packet. As a shortcut, only
+// packets smaller than MaxPacketSize can be read here.
+func (c *Conn) readPacketDirect() ([]byte, error) {
+ var header [4]byte
+ if _, err := io.ReadFull(c.conn, header[:]); err != nil {
+ return nil, fmt.Errorf("io.ReadFull(header size) failed: %v", err)
+ }
+
+ sequence := uint8(header[3])
+ if sequence != c.sequence {
+ return nil, fmt.Errorf("invalid sequence, expected %v got %v", c.sequence, sequence)
+ }
+
+ c.sequence++
+
+ length := int(uint32(header[0]) | uint32(header[1])<<8 | uint32(header[2])<<16)
+ if length <= cap(c.buffer) {
+ // Fast path: read into buffer, we're good.
+ c.buffer = c.buffer[:length]
+ if _, err := io.ReadFull(c.conn, c.buffer); err != nil {
+ return nil, fmt.Errorf("io.ReadFull(direct packet body of length %v) failed: %v", length, err)
+ }
+ return c.buffer, nil
+ }
+
+ // Sanity check
+ if length == MaxPacketSize {
+ return nil, fmt.Errorf("readPacketDirect doesn't support more than one packet")
+ }
+
+ // Slow path, revert to allocating.
+ data := make([]byte, length)
+ if _, err := io.ReadFull(c.conn, data); err != nil {
+ return nil, fmt.Errorf("io.ReadFull(packet body of length %v) failed: %v", length, err)
+ }
+ return data, nil
+}
+
// readEphemeralPacket attempts to read a packet into c.buffer. Do
// not use this method if the contents of the packet needs to be kept
// after the next readEphemeralPacket. If the packet is bigger than
@@ -457,6 +505,11 @@ func (c *Conn) writeComQuit() error {
return nil
}
+// RemoteAddr returns the underlying socket RemoteAddr().
+func (c *Conn) RemoteAddr() net.Addr {
+ return c.conn.RemoteAddr()
+}
+
// Close closes the connection. It can be called from a different go
// routine to interrupt the current connection.
func (c *Conn) Close() {
diff --git a/go/mysqlconn/conn_test.go b/go/mysqlconn/conn_test.go
index baa925c1dfa..052a79a2238 100644
--- a/go/mysqlconn/conn_test.go
+++ b/go/mysqlconn/conn_test.go
@@ -121,13 +121,22 @@ func verifyPacketCommsSpecific(t *testing.T, cConn *Conn, data []byte,
// Write a packet on one side, read it on the other, check it's
// correct. We use all possible read and write methods.
func verifyPacketComms(t *testing.T, cConn, sConn *Conn, data []byte) {
+ // All three writes, with ReadPacket.
verifyPacketCommsSpecific(t, cConn, data, useWritePacket, sConn.ReadPacket)
verifyPacketCommsSpecific(t, cConn, data, useWriteEphemeralPacket, sConn.ReadPacket)
verifyPacketCommsSpecific(t, cConn, data, useWriteEphemeralPacketDirect, sConn.ReadPacket)
+ // All three writes, with readEphemeralPacket.
verifyPacketCommsSpecific(t, cConn, data, useWritePacket, sConn.readEphemeralPacket)
verifyPacketCommsSpecific(t, cConn, data, useWriteEphemeralPacket, sConn.readEphemeralPacket)
verifyPacketCommsSpecific(t, cConn, data, useWriteEphemeralPacketDirect, sConn.readEphemeralPacket)
+
+ // All three writes, with readPacketDirect, if size allows it.
+ if len(data) < MaxPacketSize {
+ verifyPacketCommsSpecific(t, cConn, data, useWritePacket, sConn.readPacketDirect)
+ verifyPacketCommsSpecific(t, cConn, data, useWriteEphemeralPacket, sConn.readPacketDirect)
+ verifyPacketCommsSpecific(t, cConn, data, useWriteEphemeralPacketDirect, sConn.readPacketDirect)
+ }
}
func TestPackets(t *testing.T) {
diff --git a/go/mysqlconn/constants.go b/go/mysqlconn/constants.go
index 9c0ce2e96ef..9afe9d47812 100644
--- a/go/mysqlconn/constants.go
+++ b/go/mysqlconn/constants.go
@@ -1,5 +1,7 @@
package mysqlconn
+import "github.com/youtube/vitess/go/sqldb"
+
const (
// MaxPacketSize is the maximum payload length of a packet
// the server supports.
@@ -8,9 +10,15 @@ const (
// protocolVersion is the current version of the protocol.
// Always 10.
protocolVersion = 10
+)
- // mysqlNativePassword is the auth form we use.
+// Supported auth forms.
+const (
+ // mysqlNativePassword uses a salt and transmits a hash on the wire.
mysqlNativePassword = "mysql_native_password"
+
+ // mysqlClearPassword transmits the password in the clear.
+ mysqlClearPassword = "mysql_clear_password"
)
// Capability flags.
@@ -58,7 +66,6 @@ const (
// CapabilityClientSSL is CLIENT_SSL.
// Switch to SSL after handshake.
- // Not supported yet, but checked.
CapabilityClientSSL = 1 << 11
// CLIENT_IGNORE_SIGPIPE 1 << 12
@@ -140,6 +147,9 @@ const (
// EOFPacket is the header of the EOF packet.
EOFPacket = 0xfe
+ // AuthSwitchRequestPacket is used to switch auth method.
+ AuthSwitchRequestPacket = 0xfe
+
// ErrPacket is the header of the error packet.
ErrPacket = 0xff
@@ -183,6 +193,10 @@ const (
// Sent when the streaming calls are not done in the right order.
CRCommandsOutOfSync = 2014
+ // CRNamedPipeStateError is CR_NAMEDPIPESETSTATE_ERROR.
+ // This is the highest possible number for a connection error.
+ CRNamedPipeStateError = 2018
+
// CRCantReadCharset is CR_CANT_READ_CHARSET
CRCantReadCharset = 2019
@@ -340,3 +354,19 @@ var CharacterSetMap = map[string]uint8{
func IsNum(typ uint8) bool {
return ((typ <= 9 /* MYSQL_TYPE_INT24 */ && typ != 7 /* MYSQL_TYPE_TIMESTAMP */) || typ == 13 /* MYSQL_TYPE_YEAR */ || typ == 246 /* MYSQL_TYPE_NEWDECIMAL */)
}
+
+// IsConnErr returns true if the error is a connection error.
+func IsConnErr(err error) bool {
+ if sqlErr, ok := err.(*sqldb.SQLError); ok {
+ num := sqlErr.Number()
+ // ServerLost means that the query has already been
+ // received by MySQL and may have already been executed.
+ // Since we don't know if the query is idempotent, we don't
+ // count this error as connection error which could be retried.
+ if num == CRServerLost {
+ return false
+ }
+ return num >= CRUnknownError && num <= CRNamedPipeStateError
+ }
+ return false
+}
diff --git a/go/mysqlconn/constants_test.go b/go/mysqlconn/constants_test.go
new file mode 100644
index 00000000000..c5c26d5484f
--- /dev/null
+++ b/go/mysqlconn/constants_test.go
@@ -0,0 +1,36 @@
+package mysqlconn
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/youtube/vitess/go/sqldb"
+)
+
+func TestIsConnErr(t *testing.T) {
+ testcases := []struct {
+ in error
+ want bool
+ }{{
+ in: errors.New("t"),
+ want: false,
+ }, {
+ in: sqldb.NewSQLError(5, "", ""),
+ want: false,
+ }, {
+ in: sqldb.NewSQLError(CRServerGone, "", ""),
+ want: true,
+ }, {
+ in: sqldb.NewSQLError(CRServerLost, "", ""),
+ want: false,
+ }, {
+ in: sqldb.NewSQLError(CRCantReadCharset, "", ""),
+ want: false,
+ }}
+ for _, tcase := range testcases {
+ got := IsConnErr(tcase.in)
+ if got != tcase.want {
+ t.Errorf("IsConnErr(%#v): %v, want %v", tcase.in, got, tcase.want)
+ }
+ }
+}
diff --git a/go/mysqlconn/doc.go b/go/mysqlconn/doc.go
index 5b425156cd4..236783ffbc9 100644
--- a/go/mysqlconn/doc.go
+++ b/go/mysqlconn/doc.go
@@ -34,16 +34,29 @@ server should ignore it anyway), and then should send a COM_INIT_DB
message to set the database.
--
-CLIENT_SSL:
+PLUGABLE AUTHENTICATION:
-SSL is not supported yet, in neither client nor server. It is not a lot to add.
+See https://dev.mysql.com/doc/internals/en/authentication-method-mismatch.html
+for more information on this.
---
-PLUGABLE AUTHENTICATION:
+Our server side always starts by using mysql_native_password, like a
+real MySQL server.
+
+Our client will expect the server to always use mysql_native_password
+in its initial handshake. This is what a real server always does, even though
+it's not technically mandatory.
+
+Our server can then use the client's auth methods right away:
+- mysql_native_password
+- mysql_clear_password
-We only support mysql_native_password for now, both client and server
-side. It wouldn't be a lot of work to add SHA256 for instance.
+If our server's AuthServer UseClearText() returns true, and the
+client's auth method is not mysql_clear_password, we will
+re-negotiate.
+If any of these methods doesn't work for the server, it will re-negotiate
+by sending an Authentication Method Switch Request Packet.
+The client will then handle that if it can.
--
Maximum Packet Size:
diff --git a/go/mysqlconn/fakesqldb/server.go b/go/mysqlconn/fakesqldb/server.go
index cdece9830ba..b2c4bc12530 100644
--- a/go/mysqlconn/fakesqldb/server.go
+++ b/go/mysqlconn/fakesqldb/server.go
@@ -73,14 +73,18 @@ func New(t *testing.T) *DB {
connections: make(map[uint32]*mysqlconn.Conn),
}
+ authServer := mysqlconn.NewAuthServerConfig()
+ authServer.Entries["user1"] = &mysqlconn.AuthServerConfigEntry{
+ Password: "password1",
+ }
+
// Start listening.
var err error
- db.listener, err = mysqlconn.NewListener("tcp", ":0", db)
+ db.listener, err = mysqlconn.NewListener("tcp", ":0", authServer, db)
if err != nil {
t.Fatalf("NewListener failed: %v", err)
}
- db.listener.PasswordMap["user1"] = "password1"
db.acceptWG.Add(1)
go func() {
defer db.acceptWG.Done()
diff --git a/go/mysqlconn/handshake_test.go b/go/mysqlconn/handshake_test.go
new file mode 100644
index 00000000000..93ffb260bc8
--- /dev/null
+++ b/go/mysqlconn/handshake_test.go
@@ -0,0 +1,200 @@
+package mysqlconn
+
+import (
+ "context"
+ "io/ioutil"
+ "net"
+ "os"
+ "path"
+ "reflect"
+ "strings"
+ "testing"
+
+ "github.com/youtube/vitess/go/sqldb"
+ "github.com/youtube/vitess/go/vt/servenv/grpcutils"
+ "github.com/youtube/vitess/go/vt/tlstest"
+)
+
+// This file tests the handshake scenarios between our client and our server.
+
+func TestClearTextClientAuth(t *testing.T) {
+ th := &testHandler{}
+
+ authServer := NewAuthServerConfig()
+ authServer.Entries["user1"] = &AuthServerConfigEntry{
+ Password: "password1",
+ }
+ authServer.ClearText = true
+
+ // Create the listener.
+ l, err := NewListener("tcp", ":0", authServer, th)
+ if err != nil {
+ t.Fatalf("NewListener failed: %v", err)
+ }
+ defer l.Close()
+ host := l.Addr().(*net.TCPAddr).IP.String()
+ port := l.Addr().(*net.TCPAddr).Port
+ go func() {
+ l.Accept()
+ }()
+
+ // Setup the right parameters.
+ params := &sqldb.ConnParams{
+ Host: host,
+ Port: port,
+ Uname: "user1",
+ Pass: "password1",
+ }
+
+ // Connection should fail, as server requires SSL for clear text auth.
+ ctx := context.Background()
+ conn, err := Connect(ctx, params)
+ if err == nil || !strings.Contains(err.Error(), "Cannot use clear text authentication over non-SSL connections") {
+ t.Fatalf("unexpected connection error: %v", err)
+ }
+
+ // Change server side to allow clear text without auth.
+ l.AllowClearTextWithoutTLS = true
+ conn, err = Connect(ctx, params)
+ if err != nil {
+ t.Fatalf("unexpected connection error: %v", err)
+ }
+ defer conn.Close()
+
+ // Run a 'select rows' command with results.
+ result, err := conn.ExecuteFetch("select rows", 10000, true)
+ if err != nil {
+ t.Fatalf("ExecuteFetch failed: %v", err)
+ }
+ if !reflect.DeepEqual(result, selectRowsResult) {
+ t.Errorf("Got wrong result from ExecuteFetch(select rows): %v", result)
+ }
+
+ // Send a ComQuit to avoid the error message on the server side.
+ conn.writeComQuit()
+}
+
+// TestSSLConnection creates a server with TLS support, a client that
+// also has SSL support, and connects them.
+func TestSSLConnection(t *testing.T) {
+ th := &testHandler{}
+
+ authServer := NewAuthServerConfig()
+ authServer.Entries["user1"] = &AuthServerConfigEntry{
+ Password: "password1",
+ }
+
+ // Create the listener, so we can get its host.
+ l, err := NewListener("tcp", ":0", authServer, th)
+ if err != nil {
+ t.Fatalf("NewListener failed: %v", err)
+ }
+ defer l.Close()
+ host := l.Addr().(*net.TCPAddr).IP.String()
+ port := l.Addr().(*net.TCPAddr).Port
+
+ // Create the certs.
+ root, err := ioutil.TempDir("", "TestSSLConnection")
+ if err != nil {
+ t.Fatalf("TempDir failed: %v", err)
+ }
+ defer os.RemoveAll(root)
+ tlstest.CreateCA(root)
+ tlstest.CreateSignedCert(root, tlstest.CA, "01", "server", "IP:"+host)
+ tlstest.CreateSignedCert(root, tlstest.CA, "02", "client", "Client Cert")
+
+ // Create the server with TLS config.
+ serverConfig, err := grpcutils.TLSServerConfig(
+ path.Join(root, "server-cert.pem"),
+ path.Join(root, "server-key.pem"),
+ path.Join(root, "ca-cert.pem"))
+ if err != nil {
+ t.Fatalf("TLSServerConfig failed: %v", err)
+ }
+ l.TLSConfig = serverConfig
+ go func() {
+ l.Accept()
+ }()
+
+ // Setup the right parameters.
+ params := &sqldb.ConnParams{
+ Host: host,
+ Port: port,
+ Uname: "user1",
+ Pass: "password1",
+ // SSL flags.
+ Flags: CapabilityClientSSL,
+ SslCa: path.Join(root, "ca-cert.pem"),
+ SslCert: path.Join(root, "client-cert.pem"),
+ SslKey: path.Join(root, "client-key.pem"),
+ }
+
+ t.Run("Basics", func(t *testing.T) {
+ testSSLConnectionBasics(t, params)
+ })
+
+ // Make sure clear text auth works over SSL.
+ t.Run("ClearText", func(t *testing.T) {
+ l.authServer.(*AuthServerConfig).ClearText = true
+ testSSLConnectionClearText(t, params)
+ })
+}
+
+func testSSLConnectionClearText(t *testing.T, params *sqldb.ConnParams) {
+ // Create a client connection, connect.
+ ctx := context.Background()
+ conn, err := Connect(ctx, params)
+ if err != nil {
+ t.Fatalf("Connect failed: %v", err)
+ }
+ defer conn.Close()
+ if conn.User != "user1" {
+ t.Errorf("Invalid conn.User, got %v was expecting user1", conn.User)
+ }
+
+ // Make sure this went through SSL.
+ result, err := conn.ExecuteFetch("ssl echo", 10000, true)
+ if err != nil {
+ t.Fatalf("ExecuteFetch failed: %v", err)
+ }
+ if result.Rows[0][0].String() != "ON" {
+ t.Errorf("Got wrong result from ExecuteFetch(ssl echo): %v", result)
+ }
+
+ // Send a ComQuit to avoid the error message on the server side.
+ conn.writeComQuit()
+}
+
+func testSSLConnectionBasics(t *testing.T, params *sqldb.ConnParams) {
+ // Create a client connection, connect.
+ ctx := context.Background()
+ conn, err := Connect(ctx, params)
+ if err != nil {
+ t.Fatalf("Connect failed: %v", err)
+ }
+ defer conn.Close()
+ if conn.User != "user1" {
+ t.Errorf("Invalid conn.User, got %v was expecting user1", conn.User)
+ }
+
+ // Run a 'select rows' command with results.
+ result, err := conn.ExecuteFetch("select rows", 10000, true)
+ if err != nil {
+ t.Fatalf("ExecuteFetch failed: %v", err)
+ }
+ if !reflect.DeepEqual(result, selectRowsResult) {
+ t.Errorf("Got wrong result from ExecuteFetch(select rows): %v", result)
+ }
+
+ // Make sure this went through SSL.
+ result, err = conn.ExecuteFetch("ssl echo", 10000, true)
+ if err != nil {
+ t.Fatalf("ExecuteFetch failed: %v", err)
+ }
+ if result.Rows[0][0].String() != "ON" {
+ t.Errorf("Got wrong result from ExecuteFetch(ssl echo): %v", result)
+ }
+
+ // Send a ComQuit to avoid the error message on the server side.
+ conn.writeComQuit()
+}
diff --git a/go/mysqlconn/query_benchmark_test.go b/go/mysqlconn/query_benchmark_test.go
index 2c149f30585..46252c48f60 100644
--- a/go/mysqlconn/query_benchmark_test.go
+++ b/go/mysqlconn/query_benchmark_test.go
@@ -128,12 +128,16 @@ func benchmarkOldParallelReads(b *testing.B, params sqldb.ConnParams, parallelCo
func BenchmarkParallelShortQueries(b *testing.B) {
th := &testHandler{}
- l, err := NewListener("tcp", ":0", th)
+ authServer := NewAuthServerConfig()
+ authServer.Entries["user1"] = &AuthServerConfigEntry{
+ Password: "password1",
+ }
+
+ l, err := NewListener("tcp", ":0", authServer, th)
if err != nil {
b.Fatalf("NewListener failed: %v", err)
}
defer l.Close()
- l.PasswordMap["user1"] = "password1"
go func() {
l.Accept()
diff --git a/go/mysqlconn/replication/binlog_event.go b/go/mysqlconn/replication/binlog_event.go
index 82d740bfc39..af89121a7cf 100644
--- a/go/mysqlconn/replication/binlog_event.go
+++ b/go/mysqlconn/replication/binlog_event.go
@@ -154,16 +154,28 @@ func (q Query) String() string {
// TableMap contains data from a TABLE_MAP_EVENT.
type TableMap struct {
- Flags uint16
+ // Flags is the table's flags.
+ Flags uint16
+
+ // Database is the database name.
Database string
- Name string
- Columns []TableMapColumn
-}
-// TableMapColumn describes a table column inside a TABLE_MAP_EVENT.
-type TableMapColumn struct {
- Type byte
- CanBeNull bool
+ // Name is the name of the table.
+ Name string
+
+ // Types is an array of MySQL types for the fields.
+ Types []byte
+
+ // CanBeNull's bits are set if the column can be NULL.
+ CanBeNull Bitmap
+
+ // Metadata is an array of uint16, one per column.
+ // It contains a few extra information about each column,
+ // that is dependent on the type.
+ // - If the metadata is not present, this is zero.
+ // - If the metadata is one byte, only the lower 8 bits are used.
+ // - If the metadata is two bytes, all 16 bits are used.
+ Metadata []uint16
}
// Rows contains data from a {WRITE,UPDATE,DELETE}_ROWS_EVENT.
@@ -179,10 +191,10 @@ type Rows struct {
// DataColumns describes which columns are included. It is
// a bitmap indexed by the TableMap list of columns.
- // Set for WRITE and UPDATE
+ // Set for WRITE and UPDATE.
DataColumns Bitmap
- // Rows is an array of UpdateRow in the event.
+ // Rows is an array of Row in the event.
Rows []Row
}
diff --git a/go/mysqlconn/replication/binlog_event_common.go b/go/mysqlconn/replication/binlog_event_common.go
index d84c18ce8bf..945dee0cd0e 100644
--- a/go/mysqlconn/replication/binlog_event_common.go
+++ b/go/mysqlconn/replication/binlog_event_common.go
@@ -310,305 +310,3 @@ func (ev binlogEvent) TableID(f BinlogFormat) uint64 {
uint64(ev[pos+4])<<32 |
uint64(ev[pos+5])<<40
}
-
-// TableMap implements BinlogEvent.TableMap().
-//
-// Expected format (L = total length of event data):
-// # bytes field
-// 4/6 table id
-// 2 flags
-// 1 schema name length sl
-// sl schema name
-// 1 [00]
-// 1 table name length tl
-// tl table name
-// 1 [00]
-// column count cc (var-len encoded)
-// cc column-def, one byte per column
-// column-meta-def (var-len encoded string)
-// n NULL-bitmask, length: (cc + 7) / 8
-func (ev binlogEvent) TableMap(f BinlogFormat) (*TableMap, error) {
- data := ev.Bytes()[f.HeaderLength:]
-
- result := &TableMap{}
- pos := 6
- if f.HeaderSize(eTableMapEvent) == 6 {
- pos = 4
- }
- result.Flags = binary.LittleEndian.Uint16(data[pos : pos+2])
- pos += 2
-
- l := int(data[pos])
- result.Database = string(data[pos+1 : pos+1+l])
- pos += 1 + l + 1
-
- l = int(data[pos])
- result.Name = string(data[pos+1 : pos+1+l])
- pos += 1 + l + 1
-
- // FIXME(alainjobart) this is varlength encoded.
- columnCount := int(data[pos])
-
- result.Columns = make([]TableMapColumn, columnCount)
- for i := 0; i < columnCount; i++ {
- result.Columns[i].Type = data[pos+1+i]
- }
- pos += 1 + columnCount
-
- // FIXME(alainjobart) this is a var-len-string.
- // These are type-specific meta-data per field. Not sure what's in
- // there.
- l = int(data[pos])
- pos += 1 + l
-
- // A bit array that says if each colum can be NULL.
- nullBitmap, _ := newBitmap(data, pos, columnCount)
- for i := 0; i < columnCount; i++ {
- result.Columns[i].CanBeNull = nullBitmap.Bit(i)
- }
-
- return result, nil
-}
-
-// cellLength returns the new position after the field with the given type is read.
-func cellLength(data []byte, pos int, tmc *TableMapColumn) (int, error) {
- switch tmc.Type {
- case TypeTiny:
- return 1, nil
- case TypeShort, TypeYear:
- return 2, nil
- case TypeLong, TypeInt24:
- return 4, nil
- case TypeLongLong:
- return 8, nil
- case TypeTimestamp, TypeDate, TypeTime, TypeDateTime:
- // first byte has the length.
- l := int(data[pos])
- return 1 + l, nil
- case TypeVarchar:
- // Length is encoded in 2 bytes.
- l := int(uint64(data[pos]) |
- uint64(data[pos+1])<<8)
- return 2 + l, nil
- default:
- return 0, fmt.Errorf("Unsupported type %v (data: %v pos: %v)", tmc.Type, data, pos)
- }
-}
-
-// FIXME(alainjobart) are the ints signed? It seems Tiny is unsigned,
-// but the others are.
-func cellData(data []byte, pos int, tmc *TableMapColumn) (string, int) {
- switch tmc.Type {
- case TypeTiny:
- return fmt.Sprintf("%v", data[pos]), 1
- case TypeShort, TypeYear:
- val := binary.LittleEndian.Uint16(data[pos : pos+2])
- return fmt.Sprintf("%v", val), 2
- case TypeLong, TypeInt24:
- val := binary.LittleEndian.Uint32(data[pos : pos+4])
- return fmt.Sprintf("%v", val), 4
- case TypeLongLong:
- val := binary.LittleEndian.Uint64(data[pos : pos+8])
- return fmt.Sprintf("%v", val), 8
- case TypeTimestamp, TypeDate, TypeTime, TypeDateTime:
- panic(fmt.Errorf("NYI"))
- case TypeVarchar:
- // Varchar length is two bytes here.
- l := int(uint64(data[pos]) |
- uint64(data[pos+1])<<8)
- return string(data[pos+2 : pos+2+l]), 2 + l
- default:
- panic(fmt.Errorf("Unsupported type %v", tmc.Type))
- }
-}
-
-// Rows implements BinlogEvent.TableMap().
-//
-// Expected format (L = total length of event data):
-// # bytes field
-// 4/6 table id
-// 2 flags
-// -- if version == 2
-// 2 extra data length edl
-// edl extra data
-// -- endif
-// number of columns (var-len encoded)
-// identify bitmap
-// data bitmap
-// -- for each row
-// null bitmap for identify for present rows
-// values for each identify field
-// null bitmap for data for present rows
-// values for each data field
-// --
-func (ev binlogEvent) Rows(f BinlogFormat, tm *TableMap) (Rows, error) {
- typ := ev.Type()
- data := ev.Bytes()[f.HeaderLength:]
- hasIdentify := typ == eUpdateRowsEventV1 || typ == eUpdateRowsEventV2 ||
- typ == eDeleteRowsEventV1 || typ == eDeleteRowsEventV2
- hasData := typ == eWriteRowsEventV1 || typ == eWriteRowsEventV2 ||
- typ == eUpdateRowsEventV1 || typ == eUpdateRowsEventV2
-
- result := Rows{}
- pos := 6
- if f.HeaderSize(typ) == 6 {
- pos = 4
- }
- result.Flags = binary.LittleEndian.Uint16(data[pos : pos+2])
- pos += 2
-
- // version=2 have extra data here.
- if typ == eWriteRowsEventV2 || typ == eUpdateRowsEventV2 || typ == eDeleteRowsEventV2 {
- // This extraDataLength contains the 2 bytes length.
- extraDataLength := binary.LittleEndian.Uint16(data[pos : pos+2])
- pos += int(extraDataLength)
- }
-
- // FIXME(alainjobart) this is var len encoded.
- columnCount := int(data[pos])
- pos++
-
- numIdentifyColumns := 0
- numDataColumns := 0
-
- if hasIdentify {
- // Bitmap of the columns used for identify.
- result.IdentifyColumns, pos = newBitmap(data, pos, columnCount)
- numIdentifyColumns = result.IdentifyColumns.BitCount()
- }
-
- if hasData {
- // Bitmap of columns that are present.
- result.DataColumns, pos = newBitmap(data, pos, columnCount)
- numDataColumns = result.DataColumns.BitCount()
- }
-
- // One row at a time.
- for pos < len(data) {
- row := Row{}
-
- if hasIdentify {
- // Bitmap of identify columns that are null (amongst the ones that are present).
- row.NullIdentifyColumns, pos = newBitmap(data, pos, numIdentifyColumns)
-
- // Get the identify values.
- startPos := pos
- valueIndex := 0
- for c := 0; c < columnCount; c++ {
- if !result.IdentifyColumns.Bit(c) {
- // This column is not represented.
- continue
- }
-
- if row.NullIdentifyColumns.Bit(valueIndex) {
- // This column is represented, but its value is NULL.
- valueIndex++
- continue
- }
-
- // This column is represented now. We need to skip its length.
- l, err := cellLength(data, pos, &tm.Columns[c])
- if err != nil {
- return result, err
- }
- pos += l
- valueIndex++
- }
- row.Identify = data[startPos:pos]
- }
-
- if hasData {
- // Bitmap of columns that are null (amongst the ones that are present).
- row.NullColumns, pos = newBitmap(data, pos, numDataColumns)
-
- // Get the values.
- startPos := pos
- valueIndex := 0
- for c := 0; c < columnCount; c++ {
- if !result.DataColumns.Bit(c) {
- // This column is not represented.
- continue
- }
-
- if row.NullColumns.Bit(valueIndex) {
- // This column is represented, but its value is NULL.
- valueIndex++
- continue
- }
-
- // This column is represented now. We need to skip its length.
- l, err := cellLength(data, pos, &tm.Columns[c])
- if err != nil {
- return result, err
- }
- pos += l
- valueIndex++
- }
- row.Data = data[startPos:pos]
- }
-
- result.Rows = append(result.Rows, row)
- }
-
- return result, nil
-}
-
-// StringValues is a helper method to return the string value of all columns in a row in a Row.
-// Will panic if anything goes wrong, this is meant for tests for now.
-func (rs *Rows) StringValues(tm *TableMap, rowIndex int) []string {
- var result []string
-
- valueIndex := 0
- data := rs.Rows[rowIndex].Data
- pos := 0
- for c := 0; c < rs.DataColumns.Count(); c++ {
- if !rs.DataColumns.Bit(c) {
- continue
- }
-
- if rs.Rows[rowIndex].NullColumns.Bit(valueIndex) {
- // This column is represented, but its value is NULL.
- result = append(result, "NULL")
- valueIndex++
- continue
- }
-
- // We have real data
- value, l := cellData(data, pos, &tm.Columns[c])
- result = append(result, value)
- pos += l
- valueIndex++
- }
-
- return result
-}
-
-// StringIdentifies is a helper method to return the string identify of all columns in a row in a Row.
-// Will panic if anything goes wrong, this is meant for tests for now.
-func (rs *Rows) StringIdentifies(tm *TableMap, rowIndex int) []string {
- var result []string
-
- valueIndex := 0
- data := rs.Rows[rowIndex].Identify
- pos := 0
- for c := 0; c < rs.IdentifyColumns.Count(); c++ {
- if !rs.IdentifyColumns.Bit(c) {
- continue
- }
-
- if rs.Rows[rowIndex].NullIdentifyColumns.Bit(valueIndex) {
- // This column is represented, but its value is NULL.
- result = append(result, "NULL")
- valueIndex++
- continue
- }
-
- // We have real data
- value, l := cellData(data, pos, &tm.Columns[c])
- result = append(result, value)
- pos += l
- valueIndex++
- }
-
- return result
-}
diff --git a/go/mysqlconn/replication/binlog_event_make.go b/go/mysqlconn/replication/binlog_event_make.go
index 22bf4b8dd72..bc254441047 100644
--- a/go/mysqlconn/replication/binlog_event_make.go
+++ b/go/mysqlconn/replication/binlog_event_make.go
@@ -268,11 +268,7 @@ func NewTableMapEvent(f BinlogFormat, s *FakeBinlogStream, tableID uint64, tm *T
panic("Not implemented, post_header_length!=8")
}
- // Build the NullBitmap first.
- nullBitmap := NewServerBitmap(len(tm.Columns))
- for i, tmc := range tm.Columns {
- nullBitmap.Set(i, tmc.CanBeNull)
- }
+ metadataLength := metadataTotalLength(tm.Types)
length := 6 + // table_id
2 + // flags
@@ -283,9 +279,10 @@ func NewTableMapEvent(f BinlogFormat, s *FakeBinlogStream, tableID uint64, tm *T
len(tm.Name) +
1 + // [00]
1 + // column-count FIXME(alainjobart) len enc
- len(tm.Columns) +
- 1 + // lenenc-str column-meta-def, see below.
- len(nullBitmap.data)
+ len(tm.Types) +
+ 1 + // lenenc-str column-meta-def FIXME(alainjobart) len enc
+ metadataLength +
+ len(tm.CanBeNull.data)
data := make([]byte, length)
data[0] = byte(tableID)
@@ -305,20 +302,20 @@ func NewTableMapEvent(f BinlogFormat, s *FakeBinlogStream, tableID uint64, tm *T
data[pos] = 0
pos++
- data[pos] = byte(len(tm.Columns)) // FIXME(alainjobart) lenenc
+ data[pos] = byte(len(tm.Types)) // FIXME(alainjobart) lenenc
pos++
- for i, tmc := range tm.Columns {
- data[pos+i] = tmc.Type
- }
- pos += len(tm.Columns)
+ pos += copy(data[pos:], tm.Types)
- // FIXME(alainjobart) per-column meta data. Starting with
- // len-enc length, so 0 for now.
- data[pos] = 0
+ // Per-column meta data. Starting with len-enc length.
+ // FIXME(alainjobart) lenenc
+ data[pos] = byte(metadataLength)
pos++
+ for c, typ := range tm.Types {
+ pos = metadataWrite(data, pos, typ, tm.Metadata[c])
+ }
- pos += copy(data[pos:], nullBitmap.data)
+ pos += copy(data[pos:], tm.CanBeNull.data)
if pos != len(data) {
panic("bad encoding")
}
@@ -381,7 +378,11 @@ func newRowsEvent(f BinlogFormat, s *FakeBinlogStream, typ byte, tableID uint64,
data[8] = 0x02
data[9] = 0x00
- data[10] = byte(rows.IdentifyColumns.Count()) // FIXME(alainjobart) len
+ if hasIdentify {
+ data[10] = byte(rows.IdentifyColumns.Count()) // FIXME(alainjobart) len
+ } else {
+ data[10] = byte(rows.DataColumns.Count()) // FIXME(alainjobart) len
+ }
pos := 11
if hasIdentify {
diff --git a/go/mysqlconn/replication/binlog_event_make_test.go b/go/mysqlconn/replication/binlog_event_make_test.go
index 24fc1bc5ccc..e9023ab217f 100644
--- a/go/mysqlconn/replication/binlog_event_make_test.go
+++ b/go/mysqlconn/replication/binlog_event_make_test.go
@@ -230,19 +230,36 @@ func TestTableMapEvent(t *testing.T) {
Flags: 0x8090,
Database: "my_database",
Name: "my_table",
- Columns: []TableMapColumn{
- {Type: TypeLongLong, CanBeNull: false},
- {Type: TypeLongLong, CanBeNull: true},
- {Type: TypeLongLong, CanBeNull: true},
- {Type: TypeLongLong, CanBeNull: false},
- {Type: TypeLongLong, CanBeNull: false},
- {Type: TypeTime, CanBeNull: true},
- {Type: TypeLongLong, CanBeNull: false},
- {Type: TypeLongLong, CanBeNull: false},
- {Type: TypeLongLong, CanBeNull: false},
- {Type: TypeVarchar, CanBeNull: true},
+ Types: []byte{
+ TypeLongLong,
+ TypeLongLong,
+ TypeLongLong,
+ TypeLongLong,
+ TypeLongLong,
+ TypeTime,
+ TypeLongLong,
+ TypeLongLong,
+ TypeLongLong,
+ TypeVarchar,
+ },
+ CanBeNull: NewServerBitmap(10),
+ Metadata: []uint16{
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 384, // Length of the varchar field.
},
}
+ tm.CanBeNull.Set(1, true)
+ tm.CanBeNull.Set(2, true)
+ tm.CanBeNull.Set(5, true)
+ tm.CanBeNull.Set(9, true)
event := NewTableMapEvent(f, s, 0x102030405060, tm)
if !event.IsValid() {
@@ -280,11 +297,17 @@ func TestRowsEvent(t *testing.T) {
Flags: 0x8090,
Database: "my_database",
Name: "my_table",
- Columns: []TableMapColumn{
- {Type: TypeLong, CanBeNull: false},
- {Type: TypeVarchar, CanBeNull: true},
+ Types: []byte{
+ TypeLong,
+ TypeVarchar,
+ },
+ CanBeNull: NewServerBitmap(2),
+ Metadata: []uint16{
+ 0,
+ 384,
},
}
+ tm.CanBeNull.Set(1, true)
// Do an update packet with all fields set.
rows := Rows{
@@ -317,11 +340,11 @@ func TestRowsEvent(t *testing.T) {
// Test the Rows we just created, to be sure.
// 1076895760 is 0x40302010.
- identifies := rows.StringIdentifies(tm, 0)
+ identifies, err := rows.StringIdentifiesForTests(tm, 0)
if expected := []string{"1076895760", "abc"}; !reflect.DeepEqual(identifies, expected) {
t.Fatalf("bad Rows idenfity, got %v expected %v", identifies, expected)
}
- values := rows.StringValues(tm, 0)
+ values, err := rows.StringValuesForTests(tm, 0)
if expected := []string{"1076895760", "abcd"}; !reflect.DeepEqual(values, expected) {
t.Fatalf("bad Rows data, got %v expected %v", values, expected)
}
@@ -334,7 +357,7 @@ func TestRowsEvent(t *testing.T) {
t.Fatalf("NewRowsEvent().IsUpdateRows() if false")
}
- event, _, err := event.StripChecksum(f)
+ event, _, err = event.StripChecksum(f)
if err != nil {
t.Fatalf("StripChecksum failed: %v", err)
}
diff --git a/go/mysqlconn/replication/binlog_event_rbr.go b/go/mysqlconn/replication/binlog_event_rbr.go
new file mode 100644
index 00000000000..11df0655176
--- /dev/null
+++ b/go/mysqlconn/replication/binlog_event_rbr.go
@@ -0,0 +1,1029 @@
+package replication
+
+import (
+ "encoding/binary"
+ "fmt"
+ "math"
+ "strconv"
+
+ "github.com/youtube/vitess/go/sqltypes"
+
+ querypb "github.com/youtube/vitess/go/vt/proto/query"
+)
+
+// TableMap implements BinlogEvent.TableMap().
+//
+// Expected format (L = total length of event data):
+// # bytes field
+// 4/6 table id
+// 2 flags
+// 1 schema name length sl
+// sl schema name
+// 1 [00]
+// 1 table name length tl
+// tl table name
+// 1 [00]
+// column count cc (var-len encoded)
+// cc column-def, one byte per column
+// column-meta-def (var-len encoded string)
+// n NULL-bitmask, length: (cc + 7) / 8
+func (ev binlogEvent) TableMap(f BinlogFormat) (*TableMap, error) {
+ data := ev.Bytes()[f.HeaderLength:]
+
+ result := &TableMap{}
+ pos := 6
+ if f.HeaderSize(eTableMapEvent) == 6 {
+ pos = 4
+ }
+ result.Flags = binary.LittleEndian.Uint16(data[pos : pos+2])
+ pos += 2
+
+ l := int(data[pos])
+ result.Database = string(data[pos+1 : pos+1+l])
+ pos += 1 + l + 1
+
+ l = int(data[pos])
+ result.Name = string(data[pos+1 : pos+1+l])
+ pos += 1 + l + 1
+
+ // FIXME(alainjobart) this is varlength encoded.
+ columnCount := int(data[pos])
+ pos++
+
+ result.Types = data[pos : pos+columnCount]
+ pos += columnCount
+
+ // FIXME(alainjobart) this is a var-len-string.
+ l = int(data[pos])
+ pos++
+
+ // Allocate and parse / copy Metadata.
+ result.Metadata = make([]uint16, columnCount)
+ expectedEnd := pos + l
+ for c := 0; c < columnCount; c++ {
+ var err error
+ result.Metadata[c], pos, err = metadataRead(data, pos, result.Types[c])
+ if err != nil {
+ return nil, err
+ }
+ }
+ if pos != expectedEnd {
+ return nil, fmt.Errorf("unexpected metadata end: got %v was expecting %v (data=%v)", pos, expectedEnd, data)
+ }
+
+ // A bit array that says if each colum can be NULL.
+ result.CanBeNull, _ = newBitmap(data, pos, columnCount)
+
+ return result, nil
+}
+
+// metadataLength returns how many bytes are used for metadata, based on a type.
+func metadataLength(typ byte) int {
+ switch typ {
+ case TypeDecimal, TypeTiny, TypeShort, TypeLong, TypeNull, TypeTimestamp, TypeLongLong, TypeInt24, TypeDate, TypeTime, TypeDateTime, TypeYear, TypeNewDate:
+ // No data here.
+ return 0
+
+ case TypeFloat, TypeDouble, TypeTimestamp2, TypeDateTime2, TypeTime2, TypeJSON, TypeTinyBlob, TypeMediumBlob, TypeLongBlob, TypeBlob, TypeGeometry:
+ // One byte.
+ return 1
+
+ case TypeNewDecimal, TypeEnum, TypeSet, TypeString:
+ // Two bytes, Big Endian because of crazy encoding.
+ return 2
+
+ case TypeVarchar, TypeBit, TypeVarString:
+ // Two bytes, Little Endian
+ return 2
+
+ default:
+ // Unknown type. This is used in tests only, so panic.
+ panic(fmt.Errorf("metadataLength: unhandled data type: %v", typ))
+ }
+}
+
+// metadataTotalLength returns the total size of the metadata for an
+// array of types.
+func metadataTotalLength(types []byte) int {
+ sum := 0
+ for _, t := range types {
+ sum += metadataLength(t)
+ }
+ return sum
+}
+
+// metadataRead reads a single value from the metadata string.
+func metadataRead(data []byte, pos int, typ byte) (uint16, int, error) {
+ switch typ {
+
+ case TypeDecimal, TypeTiny, TypeShort, TypeLong, TypeNull, TypeTimestamp, TypeLongLong, TypeInt24, TypeDate, TypeTime, TypeDateTime, TypeYear, TypeNewDate:
+ // No data here.
+ return 0, pos, nil
+
+ case TypeFloat, TypeDouble, TypeTimestamp2, TypeDateTime2, TypeTime2, TypeJSON, TypeTinyBlob, TypeMediumBlob, TypeLongBlob, TypeBlob, TypeGeometry:
+ // One byte.
+ return uint16(data[pos]), pos + 1, nil
+
+ case TypeNewDecimal, TypeEnum, TypeSet, TypeString:
+ // Two bytes, Big Endian because of crazy encoding.
+ return uint16(data[pos])<<8 + uint16(data[pos+1]), pos + 2, nil
+
+ case TypeVarchar, TypeBit, TypeVarString:
+ // Two bytes, Little Endian
+ return uint16(data[pos]) + uint16(data[pos+1])<<8, pos + 2, nil
+
+ default:
+ // Unknown types, we can't go on.
+ return 0, 0, fmt.Errorf("metadataRead: unhandled data type: %v", typ)
+ }
+}
+
+// metadataWrite writes a single value into the metadata string.
+func metadataWrite(data []byte, pos int, typ byte, value uint16) int {
+ switch typ {
+
+ case TypeDecimal, TypeTiny, TypeShort, TypeLong, TypeNull, TypeTimestamp, TypeLongLong, TypeInt24, TypeDate, TypeTime, TypeDateTime, TypeYear, TypeNewDate:
+ // No data here.
+ return pos
+
+ case TypeFloat, TypeDouble, TypeTimestamp2, TypeDateTime2, TypeTime2, TypeJSON, TypeTinyBlob, TypeMediumBlob, TypeLongBlob, TypeBlob, TypeGeometry:
+ // One byte.
+ data[pos] = byte(value)
+ return pos + 1
+
+ case TypeNewDecimal, TypeEnum, TypeSet, TypeString:
+ // Two bytes, Big Endian because of crazy encoding.
+ data[pos] = byte(value >> 8)
+ data[pos+1] = byte(value)
+ return pos + 2
+
+ case TypeVarchar, TypeBit, TypeVarString:
+ // Two bytes, Little Endian
+ data[pos] = byte(value)
+ data[pos+1] = byte(value >> 8)
+ return pos + 2
+
+ default:
+ // Unknown type. This is used in tests only, so panic.
+ panic(fmt.Errorf("metadataRead: unhandled data type: %v", typ))
+ }
+}
+
+var dig2bytes = []int{0, 1, 1, 2, 2, 3, 3, 4, 4, 4}
+
+// cellLength returns the new position after the field with the given
+// type is read.
+func cellLength(data []byte, pos int, typ byte, metadata uint16) (int, error) {
+ switch typ {
+ case TypeNull:
+ return 0, nil
+ case TypeTiny, TypeYear:
+ return 1, nil
+ case TypeShort:
+ return 2, nil
+ case TypeInt24:
+ return 3, nil
+ case TypeLong, TypeFloat, TypeTimestamp:
+ return 4, nil
+ case TypeLongLong, TypeDouble:
+ return 8, nil
+ case TypeDate, TypeNewDate:
+ return 3, nil
+ case TypeTime:
+ return 4, nil
+ case TypeDateTime:
+ return 8, nil
+ case TypeVarchar, TypeVarString:
+ // Length is encoded in 1 or 2 bytes.
+ if metadata > 255 {
+ l := int(uint64(data[pos]) |
+ uint64(data[pos+1])<<8)
+ return l + 2, nil
+ }
+ l := int(data[pos])
+ return l + 1, nil
+ case TypeBit:
+ // bitmap length is in metadata, as:
+ // upper 8 bits: bytes length
+ // lower 8 bits: bit length
+ nbits := ((metadata >> 8) * 8) + (metadata & 0xFF)
+ return (int(nbits) + 7) / 8, nil
+ case TypeTimestamp2:
+ // metadata has number of decimals. One byte encodes
+ // two decimals.
+ return 4 + (int(metadata)+1)/2, nil
+ case TypeDateTime2:
+ // metadata has number of decimals. One byte encodes
+ // two decimals.
+ return 5 + (int(metadata)+1)/2, nil
+ case TypeTime2:
+ // metadata has number of decimals. One byte encodes
+ // two decimals.
+ return 3 + (int(metadata)+1)/2, nil
+ case TypeJSON:
+ // length in encoded in 'meta' bytes, but at least 2,
+ // and the value cannot be > 64k, so just read 2 bytes.
+ // (meta also should have '2' as value).
+ // (this weird logic is what event printing does).
+ l := int(uint64(data[pos]) |
+ uint64(data[pos+1])<<8)
+ return l + int(metadata), nil
+ case TypeNewDecimal:
+ precision := int(metadata >> 8)
+ scale := int(metadata & 0xff)
+ // Example:
+ // NNNNNNNNNNNN.MMMMMM
+ // 12 bytes 6 bytes
+ // precision is 18
+ // scale is 6
+ // storage is done by groups of 9 digits:
+ // - 32 bits are used to store groups of 9 digits.
+ // - any leftover digit is stored in:
+ // - 1 byte for 1 and 2 digits
+ // - 2 bytes for 3 and 4 digits
+ // - 3 bytes for 5 and 6 digits
+ // - 4 bytes for 7 and 8 digits (would also work for 9)
+ // both sides of the dot are stored separately.
+ // In this example, we'd have:
+ // - 2 bytes to store the first 3 full digits.
+ // - 4 bytes to store the next 9 full digits.
+ // - 3 bytes to store the 6 fractional digits.
+ intg := precision - scale
+ intg0 := intg / 9
+ frac0 := scale / 9
+ intg0x := intg - intg0*9
+ frac0x := scale - frac0*9
+ return intg0*4 + dig2bytes[intg0x] + frac0*4 + dig2bytes[frac0x], nil
+ case TypeEnum, TypeSet:
+ return int(metadata & 0xff), nil
+ case TypeTinyBlob, TypeMediumBlob, TypeLongBlob, TypeBlob, TypeGeometry:
+ // of the Blobs, only TypeBlob is used in binary logs,
+ // but supports others just in case.
+ switch metadata {
+ case 1:
+ return 1 + int(uint32(data[pos])), nil
+ case 2:
+ return 2 + int(uint32(data[pos])|
+ uint32(data[pos+1])<<8), nil
+ case 3:
+ return 3 + int(uint32(data[pos])|
+ uint32(data[pos+1])<<8|
+ uint32(data[pos+2])<<16), nil
+ case 4:
+ return 4 + int(uint32(data[pos])|
+ uint32(data[pos+1])<<8|
+ uint32(data[pos+2])<<16|
+ uint32(data[pos+3])<<24), nil
+ default:
+ return 0, fmt.Errorf("unsupported blob/geometry metadata value %v (data: %v pos: %v)", metadata, data, pos)
+ }
+ case TypeString:
+ // This may do String, Enum, and Set. The type is in
+ // metadata. If it's a string, then there will be more bits.
+ // This will give us the maximum length of the field.
+ max := 0
+ t := metadata >> 8
+ if t == TypeEnum || t == TypeSet {
+ max = int(metadata & 0xff)
+ } else {
+ max = int((((metadata >> 4) & 0x300) ^ 0x300) + (metadata & 0xff))
+ }
+
+ // Length is encoded in 1 or 2 bytes.
+ if max > 255 {
+ l := int(uint64(data[pos]) |
+ uint64(data[pos+1])<<8)
+ return l + 2, nil
+ }
+ l := int(data[pos])
+ return l + 1, nil
+
+ default:
+ return 0, fmt.Errorf("unsupported type %v (data: %v pos: %v)", typ, data, pos)
+ }
+}
+
+// CellValue returns the data for a cell as a sqltypes.Value, and how
+// many bytes it takes. It only uses the querypb.Type value for the
+// signed flag.
+func CellValue(data []byte, pos int, typ byte, metadata uint16, styp querypb.Type) (sqltypes.Value, int, error) {
+ switch typ {
+ case TypeTiny:
+ if sqltypes.IsSigned(styp) {
+ return sqltypes.MakeTrusted(querypb.Type_INT8,
+ strconv.AppendInt(nil, int64(int8(data[pos])), 10)), 1, nil
+ }
+ return sqltypes.MakeTrusted(querypb.Type_UINT8,
+ strconv.AppendUint(nil, uint64(data[pos]), 10)), 1, nil
+ case TypeYear:
+ return sqltypes.MakeTrusted(querypb.Type_YEAR,
+ strconv.AppendUint(nil, uint64(data[pos])+1900, 10)), 1, nil
+ case TypeShort:
+ val := binary.LittleEndian.Uint16(data[pos : pos+2])
+ if sqltypes.IsSigned(styp) {
+ return sqltypes.MakeTrusted(querypb.Type_INT16,
+ strconv.AppendInt(nil, int64(int16(val)), 10)), 2, nil
+ }
+ return sqltypes.MakeTrusted(querypb.Type_UINT16,
+ strconv.AppendUint(nil, uint64(val), 10)), 2, nil
+ case TypeInt24:
+ if sqltypes.IsSigned(styp) && data[pos+2]&128 > 0 {
+ // Negative number, have to extend the sign.
+ val := int32(uint32(data[pos]) +
+ uint32(data[pos+1])<<8 +
+ uint32(data[pos+2])<<16 +
+ uint32(255)<<24)
+ return sqltypes.MakeTrusted(querypb.Type_INT24,
+ strconv.AppendInt(nil, int64(val), 10)), 3, nil
+ }
+ // Positive number.
+ val := uint64(data[pos]) +
+ uint64(data[pos+1])<<8 +
+ uint64(data[pos+2])<<16
+ return sqltypes.MakeTrusted(querypb.Type_UINT24,
+ strconv.AppendUint(nil, val, 10)), 3, nil
+ case TypeLong:
+ val := binary.LittleEndian.Uint32(data[pos : pos+4])
+ if sqltypes.IsSigned(styp) {
+ return sqltypes.MakeTrusted(querypb.Type_INT32,
+ strconv.AppendInt(nil, int64(int32(val)), 10)), 4, nil
+ }
+ return sqltypes.MakeTrusted(querypb.Type_UINT32,
+ strconv.AppendUint(nil, uint64(val), 10)), 4, nil
+ case TypeFloat:
+ val := binary.LittleEndian.Uint32(data[pos : pos+4])
+ fval := math.Float32frombits(val)
+ return sqltypes.MakeTrusted(querypb.Type_FLOAT32,
+ strconv.AppendFloat(nil, float64(fval), 'E', -1, 32)), 4, nil
+ case TypeDouble:
+ val := binary.LittleEndian.Uint64(data[pos : pos+8])
+ fval := math.Float64frombits(val)
+ return sqltypes.MakeTrusted(querypb.Type_FLOAT64,
+ strconv.AppendFloat(nil, fval, 'E', -1, 64)), 8, nil
+ case TypeTimestamp:
+ val := binary.LittleEndian.Uint32(data[pos : pos+4])
+ return sqltypes.MakeTrusted(querypb.Type_TIMESTAMP,
+ strconv.AppendUint(nil, uint64(val), 10)), 4, nil
+ case TypeLongLong:
+ val := binary.LittleEndian.Uint64(data[pos : pos+8])
+ if sqltypes.IsSigned(styp) {
+ return sqltypes.MakeTrusted(querypb.Type_INT64,
+ strconv.AppendInt(nil, int64(val), 10)), 8, nil
+ }
+ return sqltypes.MakeTrusted(querypb.Type_UINT64,
+ strconv.AppendUint(nil, val, 10)), 8, nil
+ case TypeDate, TypeNewDate:
+ val := uint32(data[pos]) +
+ uint32(data[pos+1])<<8 +
+ uint32(data[pos+2])<<16
+ day := val & 31
+ month := val >> 5 & 15
+ year := val >> 9
+ return sqltypes.MakeTrusted(querypb.Type_DATE,
+ []byte(fmt.Sprintf("%04d-%02d-%02d", year, month, day))), 3, nil
+ case TypeTime:
+ val := binary.LittleEndian.Uint32(data[pos : pos+4])
+ hour := val / 10000
+ minute := (val % 10000) / 100
+ second := val % 100
+ return sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte(fmt.Sprintf("%02d:%02d:%02d", hour, minute, second))), 4, nil
+ case TypeDateTime:
+ val := binary.LittleEndian.Uint64(data[pos : pos+8])
+ d := val / 1000000
+ t := val % 1000000
+ year := d / 10000
+ month := (d % 10000) / 100
+ day := d % 100
+ hour := t / 10000
+ minute := (t % 10000) / 100
+ second := t % 100
+ return sqltypes.MakeTrusted(querypb.Type_DATETIME,
+ []byte(fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second))), 8, nil
+ case TypeVarchar, TypeVarString:
+ // Length is encoded in 1 or 2 bytes.
+ if metadata > 255 {
+ l := int(uint64(data[pos]) |
+ uint64(data[pos+1])<<8)
+ return sqltypes.MakeTrusted(querypb.Type_VARCHAR,
+ data[pos+2:pos+2+l]), l + 2, nil
+ }
+ l := int(data[pos])
+ return sqltypes.MakeTrusted(querypb.Type_VARCHAR,
+ data[pos+1:pos+1+l]), l + 1, nil
+ case TypeBit:
+ // The contents is just the bytes, quoted.
+ nbits := ((metadata >> 8) * 8) + (metadata & 0xFF)
+ l := (int(nbits) + 7) / 8
+ return sqltypes.MakeTrusted(querypb.Type_BIT,
+ data[pos:pos+l]), l, nil
+ case TypeTimestamp2:
+ second := binary.LittleEndian.Uint32(data[pos : pos+4])
+ switch metadata {
+ case 1:
+ decimals := int(data[pos+4])
+ return sqltypes.MakeTrusted(querypb.Type_TIMESTAMP,
+ []byte(fmt.Sprintf("%v.%01d", second, decimals))), 5, nil
+ case 2:
+ decimals := int(data[pos+4])
+ return sqltypes.MakeTrusted(querypb.Type_TIMESTAMP,
+ []byte(fmt.Sprintf("%v.%02d", second, decimals))), 5, nil
+ case 3:
+ decimals := int(data[pos+4]) +
+ int(data[pos+5])<<8
+ return sqltypes.MakeTrusted(querypb.Type_TIMESTAMP,
+ []byte(fmt.Sprintf("%v.%03d", second, decimals))), 6, nil
+ case 4:
+ decimals := int(data[pos+4]) +
+ int(data[pos+5])<<8
+ return sqltypes.MakeTrusted(querypb.Type_TIMESTAMP,
+ []byte(fmt.Sprintf("%v.%04d", second, decimals))), 6, nil
+ case 5:
+ decimals := int(data[pos+4]) +
+ int(data[pos+5])<<8 +
+ int(data[pos+6])<<16
+ return sqltypes.MakeTrusted(querypb.Type_TIMESTAMP,
+ []byte(fmt.Sprintf("%v.%05d", second, decimals))), 7, nil
+ case 6:
+ decimals := int(data[pos+4]) +
+ int(data[pos+5])<<8 +
+ int(data[pos+6])<<16
+ return sqltypes.MakeTrusted(querypb.Type_TIMESTAMP,
+ []byte(fmt.Sprintf("%v.%.6d", second, decimals))), 7, nil
+ }
+ return sqltypes.MakeTrusted(querypb.Type_TIMESTAMP,
+ strconv.AppendUint(nil, uint64(second), 10)), 4, nil
+ case TypeDateTime2:
+ ymdhms := (uint64(data[pos]) |
+ uint64(data[pos+1])<<8 |
+ uint64(data[pos+2])<<16 |
+ uint64(data[pos+3])<<24 |
+ uint64(data[pos+4])<<32) - uint64(0x8000000000)
+ ymd := ymdhms >> 17
+ ym := ymd >> 5
+ hms := ymdhms % (1 << 17)
+
+ day := ymd % (1 << 5)
+ month := ym % 13
+ year := ym / 13
+
+ second := hms % (1 << 6)
+ minute := (hms >> 6) % (1 << 6)
+ hour := hms >> 12
+
+ datetime := fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second)
+
+ switch metadata {
+ case 1:
+ decimals := int(data[pos+5])
+ return sqltypes.MakeTrusted(querypb.Type_DATETIME,
+ []byte(fmt.Sprintf("%v.%01d", datetime, decimals))), 6, nil
+ case 2:
+ decimals := int(data[pos+5])
+ return sqltypes.MakeTrusted(querypb.Type_DATETIME,
+ []byte(fmt.Sprintf("%v.%02d", datetime, decimals))), 6, nil
+ case 3:
+ decimals := int(data[pos+5]) +
+ int(data[pos+6])<<8
+ return sqltypes.MakeTrusted(querypb.Type_DATETIME,
+ []byte(fmt.Sprintf("%v.%03d", datetime, decimals))), 7, nil
+ case 4:
+ decimals := int(data[pos+5]) +
+ int(data[pos+6])<<8
+ return sqltypes.MakeTrusted(querypb.Type_DATETIME,
+ []byte(fmt.Sprintf("%v.%04d", datetime, decimals))), 7, nil
+ case 5:
+ decimals := int(data[pos+5]) +
+ int(data[pos+6])<<8 +
+ int(data[pos+7])<<16
+ return sqltypes.MakeTrusted(querypb.Type_DATETIME,
+ []byte(fmt.Sprintf("%v.%05d", datetime, decimals))), 8, nil
+ case 6:
+ decimals := int(data[pos+5]) +
+ int(data[pos+6])<<8 +
+ int(data[pos+7])<<16
+ return sqltypes.MakeTrusted(querypb.Type_DATETIME,
+ []byte(fmt.Sprintf("%v.%.6d", datetime, decimals))), 8, nil
+ }
+ return sqltypes.MakeTrusted(querypb.Type_DATETIME,
+ []byte(datetime)), 5, nil
+ case TypeTime2:
+ hms := (int64(data[pos]) |
+ int64(data[pos+1])<<8 |
+ int64(data[pos+2])<<16) - 0x800000
+ sign := ""
+ if hms < 0 {
+ hms = -hms
+ sign = "-"
+ }
+
+ fracStr := ""
+ switch metadata {
+ case 1:
+ frac := int(data[pos+3])
+ if sign == "-" && frac != 0 {
+ hms--
+ frac = 0x100 - frac
+ }
+ fracStr = fmt.Sprintf(".%.1d", frac/10)
+ case 2:
+ frac := int(data[pos+3])
+ if sign == "-" && frac != 0 {
+ hms--
+ frac = 0x100 - frac
+ }
+ fracStr = fmt.Sprintf(".%.2d", frac)
+ case 3:
+ frac := int(data[pos+3]) |
+ int(data[pos+4])<<8
+ if sign == "-" && frac != 0 {
+ hms--
+ frac = 0x10000 - frac
+ }
+ fracStr = fmt.Sprintf(".%.3d", frac/10)
+ case 4:
+ frac := int(data[pos+3]) |
+ int(data[pos+4])<<8
+ if sign == "-" && frac != 0 {
+ hms--
+ frac = 0x10000 - frac
+ }
+ fracStr = fmt.Sprintf(".%.4d", frac)
+ case 5:
+ frac := int(data[pos+3]) |
+ int(data[pos+4])<<8 |
+ int(data[pos+5])<<16
+ if sign == "-" && frac != 0 {
+ hms--
+ frac = 0x1000000 - frac
+ }
+ fracStr = fmt.Sprintf(".%.5d", frac/10)
+ case 6:
+ frac := int(data[pos+3]) |
+ int(data[pos+4])<<8 |
+ int(data[pos+5])<<16
+ if sign == "-" && frac != 0 {
+ hms--
+ frac = 0x1000000 - frac
+ }
+ fracStr = fmt.Sprintf(".%.6d", frac)
+ }
+
+ hour := (hms >> 12) % (1 << 10)
+ minute := (hms >> 6) % (1 << 6)
+ second := hms % (1 << 6)
+ return sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte(fmt.Sprintf("%v%02d:%02d:%02d%v", sign, hour, minute, second, fracStr))), 3 + (int(metadata)+1)/2, nil
+
+ case TypeJSON:
+ // length in encoded in 'meta' bytes, but at least 2,
+ // and the value cannot be > 64k, so just read 2 bytes.
+ // (meta also should have '2' as value).
+ // (this weird logic is what event printing does).
+ l := int(uint64(data[pos]) |
+ uint64(data[pos+1])<<8)
+ return sqltypes.MakeTrusted(querypb.Type_JSON,
+ data[pos+int(metadata):pos+int(metadata)+l]), l + int(metadata), nil
+
+ case TypeNewDecimal:
+ precision := int(metadata >> 8) // total digits number
+ scale := int(metadata & 0xff) // number of fractional digits
+ intg := precision - scale // number of full digits
+ intg0 := intg / 9 // number of 32-bits digits
+ intg0x := intg - intg0*9 // leftover full digits
+ frac0 := scale / 9 // number of 32 bits fractionals
+ frac0x := scale - frac0*9 // leftover fractionals
+
+ l := intg0*4 + dig2bytes[intg0x] + frac0*4 + dig2bytes[frac0x]
+
+ // Copy the data so we can change it. Otherwise
+ // decoding is just too hard.
+ d := make([]byte, l)
+ copy(d, data[pos:pos+l])
+
+ result := []byte{}
+ isNegative := (d[0] & 0x80) == 0
+ d[0] ^= 0x80 // First bit is inverted.
+ if isNegative {
+ // Negative numbers are just inverted bytes.
+ result = append(result, '-')
+ for i := range d {
+ d[i] ^= 0xff
+ }
+ }
+
+ // first we have the leftover full digits
+ var val uint32
+ switch dig2bytes[intg0x] {
+ case 0:
+ // nothing to do
+ case 1:
+ // one byte, up to two digits
+ val = uint32(d[0])
+ case 2:
+ // two bytes, up to 4 digits
+ val = uint32(d[0])<<8 +
+ uint32(d[1])
+ case 3:
+ // 3 bytes, up to 6 digits
+ val = uint32(d[0])<<16 +
+ uint32(d[1])<<8 +
+ uint32(d[2])
+ case 4:
+ // 4 bytes, up to 8 digits (9 digits would be a full)
+ val = uint32(d[0])<<24 +
+ uint32(d[1])<<16 +
+ uint32(d[2])<<8 +
+ uint32(d[3])
+ }
+ pos = dig2bytes[intg0x]
+ if val > 0 {
+ result = strconv.AppendUint(result, uint64(val), 10)
+ }
+
+ // now the full digits, 32 bits each, 9 digits
+ for i := 0; i < intg0; i++ {
+ val = binary.BigEndian.Uint32(d[pos : pos+4])
+ t := fmt.Sprintf("%9d", val)
+ result = append(result, []byte(t)...)
+ pos += 4
+ }
+
+ // now see if we have a fraction
+ if scale == 0 {
+ return sqltypes.MakeTrusted(querypb.Type_DECIMAL,
+ result), l, nil
+ }
+ result = append(result, '.')
+
+ // now the full fractional digits
+ for i := 0; i < frac0; i++ {
+ val = binary.BigEndian.Uint32(d[pos : pos+4])
+ t := fmt.Sprintf("%9d", val)
+ result = append(result, []byte(t)...)
+ pos += 4
+ }
+
+ // then the partial fractional digits
+ t := ""
+ switch dig2bytes[frac0x] {
+ case 0:
+ // Nothing to do
+ return sqltypes.MakeTrusted(querypb.Type_DECIMAL,
+ result), l, nil
+ case 1:
+ // one byte, 1 or 2 digits
+ val = uint32(d[pos])
+ if frac0x == 1 {
+ t = fmt.Sprintf("%1d", val)
+ } else {
+ t = fmt.Sprintf("%2d", val)
+ }
+ case 2:
+ // two bytes, 3 or 4 digits
+ val = uint32(d[pos])<<8 +
+ uint32(d[pos+1])
+ if frac0x == 3 {
+ t = fmt.Sprintf("%3d", val)
+ } else {
+ t = fmt.Sprintf("%4d", val)
+ }
+ case 3:
+ // 3 bytes, 5 or 6 digits
+ val = uint32(d[pos])<<16 +
+ uint32(d[pos+1])<<8 +
+ uint32(d[pos+2])
+ if frac0x == 5 {
+ t = fmt.Sprintf("%5d", val)
+ } else {
+ t = fmt.Sprintf("%6d", val)
+ }
+ case 4:
+ // 4 bytes, 7 or 8 digits (9 digits would be a full)
+ val = uint32(d[pos])<<24 +
+ uint32(d[pos+1])<<16 +
+ uint32(d[pos+2])<<8 +
+ uint32(d[pos+3])
+ if frac0x == 7 {
+ t = fmt.Sprintf("%7d", val)
+ } else {
+ t = fmt.Sprintf("%8d", val)
+ }
+ }
+ result = append(result, []byte(t)...)
+
+ return sqltypes.MakeTrusted(querypb.Type_DECIMAL,
+ result), l, nil
+
+ case TypeEnum:
+ switch metadata & 0xff {
+ case 1:
+ // One byte storage.
+ return sqltypes.MakeTrusted(querypb.Type_ENUM,
+ strconv.AppendUint(nil, uint64(data[pos]), 10)), 1, nil
+ case 2:
+ // Two bytes storage.
+ val := binary.LittleEndian.Uint16(data[pos : pos+2])
+ return sqltypes.MakeTrusted(querypb.Type_ENUM,
+ strconv.AppendUint(nil, uint64(val), 10)), 2, nil
+ default:
+ return sqltypes.NULL, 0, fmt.Errorf("unexpected enum size: %v", metadata&0xff)
+ }
+
+ case TypeSet:
+ l := int(metadata & 0xff)
+ return sqltypes.MakeTrusted(querypb.Type_SET,
+ data[pos:pos+l]), l, nil
+
+ case TypeTinyBlob, TypeMediumBlob, TypeLongBlob, TypeBlob:
+ // Only TypeBlob is used in binary logs,
+ // but supports others just in case.
+ l := 0
+ switch metadata {
+ case 1:
+ l = int(uint32(data[pos]))
+ case 2:
+ l = int(uint32(data[pos]) |
+ uint32(data[pos+1])<<8)
+ case 3:
+ l = int(uint32(data[pos]) |
+ uint32(data[pos+1])<<8 |
+ uint32(data[pos+2])<<16)
+ case 4:
+ l = int(uint32(data[pos]) |
+ uint32(data[pos+1])<<8 |
+ uint32(data[pos+2])<<16 |
+ uint32(data[pos+3])<<24)
+ default:
+ return sqltypes.NULL, 0, fmt.Errorf("unsupported blob metadata value %v (data: %v pos: %v)", metadata, data, pos)
+ }
+ pos += int(metadata)
+ return sqltypes.MakeTrusted(querypb.Type_VARBINARY,
+ data[pos:pos+l]), l + int(metadata), nil
+
+ case TypeString:
+ // This may do String, Enum, and Set. The type is in
+ // metadata. If it's a string, then there will be more bits.
+ t := metadata >> 8
+ if t == TypeEnum {
+ switch metadata & 0xff {
+ case 1:
+ // One byte storage.
+ return sqltypes.MakeTrusted(querypb.Type_ENUM,
+ strconv.AppendUint(nil, uint64(data[pos]), 10)), 1, nil
+ case 2:
+ // Two bytes storage.
+ val := binary.LittleEndian.Uint16(data[pos : pos+2])
+ return sqltypes.MakeTrusted(querypb.Type_ENUM,
+ strconv.AppendUint(nil, uint64(val), 10)), 2, nil
+ default:
+ return sqltypes.NULL, 0, fmt.Errorf("unexpected enum size: %v", metadata&0xff)
+ }
+ }
+ if t == TypeSet {
+ l := int(metadata & 0xff)
+ return sqltypes.MakeTrusted(querypb.Type_BIT,
+ data[pos:pos+l]), l, nil
+ }
+ // This is a real string. The length is weird.
+ max := int((((metadata >> 4) & 0x300) ^ 0x300) + (metadata & 0xff))
+ // Length is encoded in 1 or 2 bytes.
+ if max > 255 {
+ l := int(uint64(data[pos]) |
+ uint64(data[pos+1])<<8)
+ return sqltypes.MakeTrusted(querypb.Type_VARCHAR,
+ data[pos+2:pos+2+l]), l + 2, nil
+ }
+ l := int(data[pos])
+ return sqltypes.MakeTrusted(querypb.Type_VARCHAR,
+ data[pos+1:pos+1+l]), l + 1, nil
+
+ case TypeGeometry:
+ l := 0
+ switch metadata {
+ case 1:
+ l = int(uint32(data[pos]))
+ case 2:
+ l = int(uint32(data[pos]) |
+ uint32(data[pos+1])<<8)
+ case 3:
+ l = int(uint32(data[pos]) |
+ uint32(data[pos+1])<<8 |
+ uint32(data[pos+2])<<16)
+ case 4:
+ l = int(uint32(data[pos]) |
+ uint32(data[pos+1])<<8 |
+ uint32(data[pos+2])<<16 |
+ uint32(data[pos+3])<<24)
+ default:
+ return sqltypes.NULL, 0, fmt.Errorf("unsupported geometry metadata value %v (data: %v pos: %v)", metadata, data, pos)
+ }
+ pos += int(metadata)
+ return sqltypes.MakeTrusted(querypb.Type_GEOMETRY,
+ data[pos:pos+l]), l + int(metadata), nil
+
+ default:
+ return sqltypes.NULL, 0, fmt.Errorf("unsupported type %v", typ)
+ }
+}
+
+// Rows implements BinlogEvent.TableMap().
+//
+// Expected format (L = total length of event data):
+// # bytes field
+// 4/6 table id
+// 2 flags
+// -- if version == 2
+// 2 extra data length edl
+// edl extra data
+// -- endif
+// number of columns (var-len encoded)
+// identify bitmap
+// data bitmap
+// -- for each row
+// null bitmap for identify for present rows
+// values for each identify field
+// null bitmap for data for present rows
+// values for each data field
+// --
+func (ev binlogEvent) Rows(f BinlogFormat, tm *TableMap) (Rows, error) {
+ typ := ev.Type()
+ data := ev.Bytes()[f.HeaderLength:]
+ hasIdentify := typ == eUpdateRowsEventV1 || typ == eUpdateRowsEventV2 ||
+ typ == eDeleteRowsEventV1 || typ == eDeleteRowsEventV2
+ hasData := typ == eWriteRowsEventV1 || typ == eWriteRowsEventV2 ||
+ typ == eUpdateRowsEventV1 || typ == eUpdateRowsEventV2
+
+ result := Rows{}
+ pos := 6
+ if f.HeaderSize(typ) == 6 {
+ pos = 4
+ }
+ result.Flags = binary.LittleEndian.Uint16(data[pos : pos+2])
+ pos += 2
+
+ // version=2 have extra data here.
+ if typ == eWriteRowsEventV2 || typ == eUpdateRowsEventV2 || typ == eDeleteRowsEventV2 {
+ // This extraDataLength contains the 2 bytes length.
+ extraDataLength := binary.LittleEndian.Uint16(data[pos : pos+2])
+ pos += int(extraDataLength)
+ }
+
+ // FIXME(alainjobart) this is var len encoded.
+ columnCount := int(data[pos])
+ pos++
+
+ numIdentifyColumns := 0
+ numDataColumns := 0
+
+ if hasIdentify {
+ // Bitmap of the columns used for identify.
+ result.IdentifyColumns, pos = newBitmap(data, pos, columnCount)
+ numIdentifyColumns = result.IdentifyColumns.BitCount()
+ }
+
+ if hasData {
+ // Bitmap of columns that are present.
+ result.DataColumns, pos = newBitmap(data, pos, columnCount)
+ numDataColumns = result.DataColumns.BitCount()
+ }
+
+ // One row at a time.
+ for pos < len(data) {
+ row := Row{}
+
+ if hasIdentify {
+ // Bitmap of identify columns that are null (amongst the ones that are present).
+ row.NullIdentifyColumns, pos = newBitmap(data, pos, numIdentifyColumns)
+
+ // Get the identify values.
+ startPos := pos
+ valueIndex := 0
+ for c := 0; c < columnCount; c++ {
+ if !result.IdentifyColumns.Bit(c) {
+ // This column is not represented.
+ continue
+ }
+
+ if row.NullIdentifyColumns.Bit(valueIndex) {
+ // This column is represented, but its value is NULL.
+ valueIndex++
+ continue
+ }
+
+ // This column is represented now. We need to skip its length.
+ l, err := cellLength(data, pos, tm.Types[c], tm.Metadata[c])
+ if err != nil {
+ return result, err
+ }
+ pos += l
+ valueIndex++
+ }
+ row.Identify = data[startPos:pos]
+ }
+
+ if hasData {
+ // Bitmap of columns that are null (amongst the ones that are present).
+ row.NullColumns, pos = newBitmap(data, pos, numDataColumns)
+
+ // Get the values.
+ startPos := pos
+ valueIndex := 0
+ for c := 0; c < columnCount; c++ {
+ if !result.DataColumns.Bit(c) {
+ // This column is not represented.
+ continue
+ }
+
+ if row.NullColumns.Bit(valueIndex) {
+ // This column is represented, but its value is NULL.
+ valueIndex++
+ continue
+ }
+
+ // This column is represented now. We need to skip its length.
+ l, err := cellLength(data, pos, tm.Types[c], tm.Metadata[c])
+ if err != nil {
+ return result, err
+ }
+ pos += l
+ valueIndex++
+ }
+ row.Data = data[startPos:pos]
+ }
+
+ result.Rows = append(result.Rows, row)
+ }
+
+ return result, nil
+}
+
+// StringValuesForTests is a helper method to return the string value
+// of all columns in a row in a Row. Only use it in tests, as the
+// returned values cannot be interpreted correctly without the schema.
+// We assume everything is unsigned in this method.
+func (rs *Rows) StringValuesForTests(tm *TableMap, rowIndex int) ([]string, error) {
+ var result []string
+
+ valueIndex := 0
+ data := rs.Rows[rowIndex].Data
+ pos := 0
+ for c := 0; c < rs.DataColumns.Count(); c++ {
+ if !rs.DataColumns.Bit(c) {
+ continue
+ }
+
+ if rs.Rows[rowIndex].NullColumns.Bit(valueIndex) {
+ // This column is represented, but its value is NULL.
+ result = append(result, "NULL")
+ valueIndex++
+ continue
+ }
+
+ // We have real data
+ value, l, err := CellValue(data, pos, tm.Types[c], tm.Metadata[c], querypb.Type_UINT64)
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, value.String())
+ pos += l
+ valueIndex++
+ }
+
+ return result, nil
+}
+
+// StringIdentifiesForTests is a helper method to return the string
+// identify of all columns in a row in a Row. Only use it in tests, as the
+// returned values cannot be interpreted correctly without the schema.
+// We assume everything is unsigned in this method.
+func (rs *Rows) StringIdentifiesForTests(tm *TableMap, rowIndex int) ([]string, error) {
+ var result []string
+
+ valueIndex := 0
+ data := rs.Rows[rowIndex].Identify
+ pos := 0
+ for c := 0; c < rs.IdentifyColumns.Count(); c++ {
+ if !rs.IdentifyColumns.Bit(c) {
+ continue
+ }
+
+ if rs.Rows[rowIndex].NullIdentifyColumns.Bit(valueIndex) {
+ // This column is represented, but its value is NULL.
+ result = append(result, "NULL")
+ valueIndex++
+ continue
+ }
+
+ // We have real data
+ value, l, err := CellValue(data, pos, tm.Types[c], tm.Metadata[c], querypb.Type_UINT64)
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, value.String())
+ pos += l
+ valueIndex++
+ }
+
+ return result, nil
+}
diff --git a/go/mysqlconn/replication/binlog_event_rbr_test.go b/go/mysqlconn/replication/binlog_event_rbr_test.go
new file mode 100644
index 00000000000..7c92b69b803
--- /dev/null
+++ b/go/mysqlconn/replication/binlog_event_rbr_test.go
@@ -0,0 +1,519 @@
+package replication
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "github.com/youtube/vitess/go/sqltypes"
+ querypb "github.com/youtube/vitess/go/vt/proto/query"
+)
+
+func TestCellLengthAndData(t *testing.T) {
+ testcases := []struct {
+ typ byte
+ metadata uint16
+ styp querypb.Type
+ data []byte
+ out sqltypes.Value
+ }{{
+ typ: TypeTiny,
+ styp: querypb.Type_UINT8,
+ data: []byte{0x82},
+ out: sqltypes.MakeTrusted(querypb.Type_UINT8,
+ []byte("130")),
+ }, {
+ typ: TypeTiny,
+ styp: querypb.Type_INT8,
+ data: []byte{0xfe},
+ out: sqltypes.MakeTrusted(querypb.Type_INT8,
+ []byte("-2")),
+ }, {
+ typ: TypeYear,
+ data: []byte{0x82},
+ out: sqltypes.MakeTrusted(querypb.Type_YEAR,
+ []byte("2030")),
+ }, {
+ typ: TypeShort,
+ styp: querypb.Type_UINT16,
+ data: []byte{0x82, 0x81},
+ out: sqltypes.MakeTrusted(querypb.Type_UINT16,
+ []byte(fmt.Sprintf("%v", 0x8182))),
+ }, {
+ typ: TypeShort,
+ styp: querypb.Type_INT16,
+ data: []byte{0xfe, 0xff},
+ out: sqltypes.MakeTrusted(querypb.Type_INT16,
+ []byte(fmt.Sprintf("%v", -1-int32(0x0001)))),
+ }, {
+ typ: TypeInt24,
+ styp: querypb.Type_UINT24,
+ data: []byte{0x83, 0x82, 0x81},
+ out: sqltypes.MakeTrusted(querypb.Type_UINT24,
+ []byte(fmt.Sprintf("%v", 0x818283))),
+ }, {
+ typ: TypeInt24,
+ styp: querypb.Type_INT24,
+ data: []byte{0xfd, 0xfe, 0xff},
+ out: sqltypes.MakeTrusted(querypb.Type_INT24,
+ []byte(fmt.Sprintf("%v", -1-int32(0x000102)))),
+ }, {
+ typ: TypeLong,
+ styp: querypb.Type_UINT32,
+ data: []byte{0x84, 0x83, 0x82, 0x81},
+ out: sqltypes.MakeTrusted(querypb.Type_UINT32,
+ []byte(fmt.Sprintf("%v", 0x81828384))),
+ }, {
+ typ: TypeLong,
+ styp: querypb.Type_INT32,
+ data: []byte{0xfc, 0xfd, 0xfe, 0xff},
+ out: sqltypes.MakeTrusted(querypb.Type_INT32,
+ []byte(fmt.Sprintf("%v", -1-int32(0x00010203)))),
+ }, {
+ // 3.1415927E+00 = 0x40490fdb
+ typ: TypeFloat,
+ data: []byte{0xdb, 0x0f, 0x49, 0x40},
+ out: sqltypes.MakeTrusted(querypb.Type_FLOAT32,
+ []byte("3.1415927E+00")),
+ }, {
+ // 3.1415926535E+00 = 0x400921fb54411744
+ typ: TypeDouble,
+ data: []byte{0x44, 0x17, 0x41, 0x54, 0xfb, 0x21, 0x09, 0x40},
+ out: sqltypes.MakeTrusted(querypb.Type_FLOAT64,
+ []byte("3.1415926535E+00")),
+ }, {
+ typ: TypeTimestamp,
+ data: []byte{0x84, 0x83, 0x82, 0x81},
+ out: sqltypes.MakeTrusted(querypb.Type_TIMESTAMP,
+ []byte(fmt.Sprintf("%v", 0x81828384))),
+ }, {
+ typ: TypeLongLong,
+ styp: querypb.Type_UINT64,
+ data: []byte{0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81},
+ out: sqltypes.MakeTrusted(querypb.Type_UINT64,
+ []byte(fmt.Sprintf("%v", uint64(0x8182838485868788)))),
+ }, {
+ typ: TypeLongLong,
+ styp: querypb.Type_INT64,
+ data: []byte{0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff},
+ out: sqltypes.MakeTrusted(querypb.Type_INT64,
+ []byte(fmt.Sprintf("%v", -1-int64(0x0001020304050607)))),
+ }, {
+ typ: TypeDate,
+ // 2010 << 9 + 10 << 5 + 3 = 1029443 = 0x0fb543
+ data: []byte{0x43, 0xb5, 0x0f},
+ out: sqltypes.MakeTrusted(querypb.Type_DATE,
+ []byte("2010-10-03")),
+ }, {
+ typ: TypeNewDate,
+ // 2010 << 9 + 10 << 5 + 3 = 1029443 = 0x0fb543
+ data: []byte{0x43, 0xb5, 0x0f},
+ out: sqltypes.MakeTrusted(querypb.Type_DATE,
+ []byte("2010-10-03")),
+ }, {
+ typ: TypeTime,
+ // 154532 = 0x00025ba4
+ data: []byte{0xa4, 0x5b, 0x02, 0x00},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("15:45:32")),
+ }, {
+ typ: TypeDateTime,
+ // 19840304154532 = 0x120b6e4807a4
+ data: []byte{0xa4, 0x07, 0x48, 0x6e, 0x0b, 0x12, 0x00, 0x00},
+ out: sqltypes.MakeTrusted(querypb.Type_DATETIME,
+ []byte("1984-03-04 15:45:32")),
+ }, {
+ typ: TypeVarchar,
+ metadata: 20, // one byte length encoding
+ data: []byte{3, 'a', 'b', 'c'},
+ out: sqltypes.MakeTrusted(querypb.Type_VARCHAR,
+ []byte("abc")),
+ }, {
+ typ: TypeVarchar,
+ metadata: 384, // two bytes length encoding
+ data: []byte{3, 0, 'a', 'b', 'c'},
+ out: sqltypes.MakeTrusted(querypb.Type_VARCHAR,
+ []byte("abc")),
+ }, {
+ typ: TypeBit,
+ metadata: 0x0107,
+ data: []byte{0x3, 0x1},
+ out: sqltypes.MakeTrusted(querypb.Type_BIT,
+ []byte{3, 1}),
+ }, {
+ typ: TypeTimestamp2,
+ metadata: 0,
+ data: []byte{0x84, 0x83, 0x82, 0x81},
+ out: sqltypes.MakeTrusted(querypb.Type_TIMESTAMP,
+ []byte(fmt.Sprintf("%v", 0x81828384))),
+ }, {
+ typ: TypeTimestamp2,
+ metadata: 1,
+ data: []byte{0x84, 0x83, 0x82, 0x81, 7},
+ out: sqltypes.MakeTrusted(querypb.Type_TIMESTAMP,
+ []byte(fmt.Sprintf("%v.7", 0x81828384))),
+ }, {
+ typ: TypeTimestamp2,
+ metadata: 2,
+ data: []byte{0x84, 0x83, 0x82, 0x81, 76},
+ out: sqltypes.MakeTrusted(querypb.Type_TIMESTAMP,
+ []byte(fmt.Sprintf("%v.76", 0x81828384))),
+ }, {
+ typ: TypeTimestamp2,
+ metadata: 3,
+ // 765 = 0x02fd
+ data: []byte{0x84, 0x83, 0x82, 0x81, 0xfd, 0x02},
+ out: sqltypes.MakeTrusted(querypb.Type_TIMESTAMP,
+ []byte(fmt.Sprintf("%v.765", 0x81828384))),
+ }, {
+ typ: TypeTimestamp2,
+ metadata: 4,
+ // 7654 = 0x1de6
+ data: []byte{0x84, 0x83, 0x82, 0x81, 0xe6, 0x1d},
+ out: sqltypes.MakeTrusted(querypb.Type_TIMESTAMP,
+ []byte(fmt.Sprintf("%v.7654", 0x81828384))),
+ }, {
+ typ: TypeTimestamp2,
+ metadata: 5,
+ // 76543 = 0x012aff
+ data: []byte{0x84, 0x83, 0x82, 0x81, 0xff, 0x2a, 0x01},
+ out: sqltypes.MakeTrusted(querypb.Type_TIMESTAMP,
+ []byte(fmt.Sprintf("%v.76543", 0x81828384))),
+ }, {
+ typ: TypeTimestamp2,
+ metadata: 6,
+ // 765432 = 0x0badf8
+ data: []byte{0x84, 0x83, 0x82, 0x81, 0xf8, 0xad, 0x0b},
+ out: sqltypes.MakeTrusted(querypb.Type_TIMESTAMP,
+ []byte(fmt.Sprintf("%v.765432", 0x81828384))),
+ }, {
+ typ: TypeDateTime2,
+ metadata: 0,
+ // (2012 * 13 + 6) << 22 + 21 << 17 + 15 << 12 + 45 << 6 + 17)
+ // = 109734198097 = 0x198caafb51
+ // Then have to add 0x8000000000 = 0x998caafb51
+ data: []byte{0x51, 0xfb, 0xaa, 0x8c, 0x99},
+ out: sqltypes.MakeTrusted(querypb.Type_DATETIME,
+ []byte("2012-06-21 15:45:17")),
+ }, {
+ typ: TypeDateTime2,
+ metadata: 1,
+ data: []byte{0x51, 0xfb, 0xaa, 0x8c, 0x99, 7},
+ out: sqltypes.MakeTrusted(querypb.Type_DATETIME,
+ []byte("2012-06-21 15:45:17.7")),
+ }, {
+ typ: TypeDateTime2,
+ metadata: 2,
+ data: []byte{0x51, 0xfb, 0xaa, 0x8c, 0x99, 76},
+ out: sqltypes.MakeTrusted(querypb.Type_DATETIME,
+ []byte("2012-06-21 15:45:17.76")),
+ }, {
+ typ: TypeDateTime2,
+ metadata: 3,
+ // 765 = 0x02fd
+ data: []byte{0x51, 0xfb, 0xaa, 0x8c, 0x99, 0xfd, 0x02},
+ out: sqltypes.MakeTrusted(querypb.Type_DATETIME,
+ []byte("2012-06-21 15:45:17.765")),
+ }, {
+ typ: TypeDateTime2,
+ metadata: 4,
+ // 7654 = 0x1de6
+ data: []byte{0x51, 0xfb, 0xaa, 0x8c, 0x99, 0xe6, 0x1d},
+ out: sqltypes.MakeTrusted(querypb.Type_DATETIME,
+ []byte("2012-06-21 15:45:17.7654")),
+ }, {
+ typ: TypeDateTime2,
+ metadata: 5,
+ // 76543 = 0x012aff
+ data: []byte{0x51, 0xfb, 0xaa, 0x8c, 0x99, 0xff, 0x2a, 0x01},
+ out: sqltypes.MakeTrusted(querypb.Type_DATETIME,
+ []byte("2012-06-21 15:45:17.76543")),
+ }, {
+ typ: TypeDateTime2,
+ metadata: 6,
+ // 765432 = 0x0badf8
+ data: []byte{0x51, 0xfb, 0xaa, 0x8c, 0x99, 0xf8, 0xad, 0x0b},
+ out: sqltypes.MakeTrusted(querypb.Type_DATETIME,
+ []byte("2012-06-21 15:45:17.765432")),
+ }, {
+ // This first set of tests is from a comment in
+ // sql-common/my_time.c:
+ //
+ // Disk value intpart frac Time value Memory value
+ // 800000.00 0 0 00:00:00.00 0000000000.000000
+ // 7FFFFF.FF -1 255 -00:00:00.01 FFFFFFFFFF.FFD8F0
+ // 7FFFFF.9D -1 99 -00:00:00.99 FFFFFFFFFF.F0E4D0
+ // 7FFFFF.00 -1 0 -00:00:01.00 FFFFFFFFFF.000000
+ // 7FFFFE.FF -1 255 -00:00:01.01 FFFFFFFFFE.FFD8F0
+ // 7FFFFE.F6 -2 246 -00:00:01.10 FFFFFFFFFE.FE7960
+ typ: TypeTime2,
+ metadata: 2,
+ data: []byte{0x00, 0x00, 0x80, 0x00},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("00:00:00.00")),
+ }, {
+ typ: TypeTime2,
+ metadata: 2,
+ data: []byte{0xff, 0xff, 0x7f, 0xff},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("-00:00:00.01")),
+ }, {
+ typ: TypeTime2,
+ metadata: 2,
+ data: []byte{0xff, 0xff, 0x7f, 0x9d},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("-00:00:00.99")),
+ }, {
+ typ: TypeTime2,
+ metadata: 2,
+ data: []byte{0xff, 0xff, 0x7f, 0x00},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("-00:00:01.00")),
+ }, {
+ typ: TypeTime2,
+ metadata: 2,
+ data: []byte{0xfe, 0xff, 0x7f, 0xff},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("-00:00:01.01")),
+ }, {
+ typ: TypeTime2,
+ metadata: 2,
+ data: []byte{0xfe, 0xff, 0x7f, 0xf6},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("-00:00:01.10")),
+ }, {
+ // Similar tests for 4 decimals.
+ typ: TypeTime2,
+ metadata: 4,
+ data: []byte{0x00, 0x00, 0x80, 0x00, 0x00},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("00:00:00.0000")),
+ }, {
+ typ: TypeTime2,
+ metadata: 4,
+ data: []byte{0xff, 0xff, 0x7f, 0xff, 0xff},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("-00:00:00.0001")),
+ }, {
+ typ: TypeTime2,
+ metadata: 4,
+ data: []byte{0xff, 0xff, 0x7f, 0x9d, 0xff},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("-00:00:00.0099")),
+ }, {
+ typ: TypeTime2,
+ metadata: 4,
+ data: []byte{0xff, 0xff, 0x7f, 0x00, 0x00},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("-00:00:01.0000")),
+ }, {
+ typ: TypeTime2,
+ metadata: 4,
+ data: []byte{0xfe, 0xff, 0x7f, 0xff, 0xff},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("-00:00:01.0001")),
+ }, {
+ typ: TypeTime2,
+ metadata: 4,
+ data: []byte{0xfe, 0xff, 0x7f, 0xf6, 0xff},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("-00:00:01.0010")),
+ }, {
+ // Similar tests for 6 decimals.
+ typ: TypeTime2,
+ metadata: 6,
+ data: []byte{0x00, 0x00, 0x80, 0x00, 0x00, 0x00},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("00:00:00.000000")),
+ }, {
+ typ: TypeTime2,
+ metadata: 6,
+ data: []byte{0xff, 0xff, 0x7f, 0xff, 0xff, 0xff},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("-00:00:00.000001")),
+ }, {
+ typ: TypeTime2,
+ metadata: 6,
+ data: []byte{0xff, 0xff, 0x7f, 0x9d, 0xff, 0xff},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("-00:00:00.000099")),
+ }, {
+ typ: TypeTime2,
+ metadata: 6,
+ data: []byte{0xff, 0xff, 0x7f, 0x00, 0x00, 0x00},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("-00:00:01.000000")),
+ }, {
+ typ: TypeTime2,
+ metadata: 6,
+ data: []byte{0xfe, 0xff, 0x7f, 0xff, 0xff, 0xff},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("-00:00:01.000001")),
+ }, {
+ typ: TypeTime2,
+ metadata: 6,
+ data: []byte{0xfe, 0xff, 0x7f, 0xf6, 0xff, 0xff},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("-00:00:01.000010")),
+ }, {
+ // Few more tests.
+ typ: TypeTime2,
+ metadata: 0,
+ data: []byte{0x00, 0x00, 0x80},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("00:00:00")),
+ }, {
+ typ: TypeTime2,
+ metadata: 1,
+ data: []byte{0x01, 0x00, 0x80, 0x0a},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("00:00:01.1")),
+ }, {
+ typ: TypeTime2,
+ metadata: 2,
+ data: []byte{0x01, 0x00, 0x80, 0x0a},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("00:00:01.10")),
+ }, {
+ typ: TypeTime2,
+ metadata: 0,
+ // 15 << 12 + 34 << 6 + 54 = 63670 = 0x00f8b6
+ // and need to add 0x800000
+ data: []byte{0xb6, 0xf8, 0x80},
+ out: sqltypes.MakeTrusted(querypb.Type_TIME,
+ []byte("15:34:54")),
+ }, {
+ typ: TypeJSON,
+ metadata: 2,
+ data: []byte{0x03, 0x00, 'a', 'b', 'c'},
+ out: sqltypes.MakeTrusted(querypb.Type_JSON,
+ []byte("abc")),
+ }, {
+ typ: TypeEnum,
+ metadata: 1,
+ data: []byte{0x03},
+ out: sqltypes.MakeTrusted(querypb.Type_ENUM,
+ []byte("3")),
+ }, {
+ typ: TypeEnum,
+ metadata: 2,
+ data: []byte{0x01, 0x02},
+ out: sqltypes.MakeTrusted(querypb.Type_ENUM,
+ []byte(fmt.Sprintf("%v", 0x0201))),
+ }, {
+ typ: TypeSet,
+ metadata: 2,
+ data: []byte{0x01, 0x02},
+ out: sqltypes.MakeTrusted(querypb.Type_SET,
+ []byte{0x01, 0x02}),
+ }, {
+ typ: TypeString,
+ metadata: TypeString<<8 | 5, // maximum length = 5
+ data: []byte{0x04, 0x01, 0x02, 0x03, 0x04},
+ out: sqltypes.MakeTrusted(querypb.Type_VARCHAR,
+ []byte{0x01, 0x02, 0x03, 0x04}),
+ }, {
+ // Length is encoded in 10 bits, 2 of them are in a weird place.
+ // In this test, we set the two high bits.
+ // 773 = 512 + 256 + 5
+ // This requires 2 bytes to store the length.
+ typ: TypeString,
+ metadata: (TypeString<<8 ^ 0x3000) | 5, // maximum length = 773
+ data: []byte{0x04, 0x00, 0x01, 0x02, 0x03, 0x04},
+ out: sqltypes.MakeTrusted(querypb.Type_VARCHAR,
+ []byte{0x01, 0x02, 0x03, 0x04}),
+ }, {
+ // See strings/decimal.c function decimal2bin for why these
+ // values are here.
+ typ: TypeNewDecimal,
+ metadata: 14<<8 | 4,
+ data: []byte{0x81, 0x0D, 0xFB, 0x38, 0xD2, 0x04, 0xD2},
+ out: sqltypes.MakeTrusted(querypb.Type_DECIMAL,
+ []byte("1234567890.1234")),
+ }, {
+ typ: TypeNewDecimal,
+ metadata: 14<<8 | 4,
+ data: []byte{0x7E, 0xF2, 0x04, 0xC7, 0x2D, 0xFB, 0x2D},
+ out: sqltypes.MakeTrusted(querypb.Type_DECIMAL,
+ []byte("-1234567890.1234")),
+ }, {
+ typ: TypeBlob,
+ metadata: 1,
+ data: []byte{0x3, 'a', 'b', 'c'},
+ out: sqltypes.MakeTrusted(querypb.Type_VARBINARY,
+ []byte("abc")),
+ }, {
+ typ: TypeBlob,
+ metadata: 2,
+ data: []byte{0x3, 0x00, 'a', 'b', 'c'},
+ out: sqltypes.MakeTrusted(querypb.Type_VARBINARY,
+ []byte("abc")),
+ }, {
+ typ: TypeBlob,
+ metadata: 3,
+ data: []byte{0x3, 0x00, 0x00, 'a', 'b', 'c'},
+ out: sqltypes.MakeTrusted(querypb.Type_VARBINARY,
+ []byte("abc")),
+ }, {
+ typ: TypeBlob,
+ metadata: 4,
+ data: []byte{0x3, 0x00, 0x00, 0x00, 'a', 'b', 'c'},
+ out: sqltypes.MakeTrusted(querypb.Type_VARBINARY,
+ []byte("abc")),
+ }, {
+ typ: TypeVarString,
+ metadata: 20, // one byte length encoding
+ data: []byte{3, 'a', 'b', 'c'},
+ out: sqltypes.MakeTrusted(querypb.Type_VARCHAR,
+ []byte("abc")),
+ }, {
+ typ: TypeVarString,
+ metadata: 384, // two bytes length encoding
+ data: []byte{3, 0, 'a', 'b', 'c'},
+ out: sqltypes.MakeTrusted(querypb.Type_VARCHAR,
+ []byte("abc")),
+ }, {
+ typ: TypeGeometry,
+ metadata: 1,
+ data: []byte{0x3, 'a', 'b', 'c'},
+ out: sqltypes.MakeTrusted(querypb.Type_GEOMETRY,
+ []byte("abc")),
+ }, {
+ typ: TypeGeometry,
+ metadata: 2,
+ data: []byte{0x3, 0x00, 'a', 'b', 'c'},
+ out: sqltypes.MakeTrusted(querypb.Type_GEOMETRY,
+ []byte("abc")),
+ }, {
+ typ: TypeGeometry,
+ metadata: 3,
+ data: []byte{0x3, 0x00, 0x00, 'a', 'b', 'c'},
+ out: sqltypes.MakeTrusted(querypb.Type_GEOMETRY,
+ []byte("abc")),
+ }, {
+ typ: TypeGeometry,
+ metadata: 4,
+ data: []byte{0x3, 0x00, 0x00, 0x00, 'a', 'b', 'c'},
+ out: sqltypes.MakeTrusted(querypb.Type_GEOMETRY,
+ []byte("abc")),
+ }}
+
+ for _, tcase := range testcases {
+ // Copy the data into a larger buffer (one extra byte
+ // on both sides), so we make sure the 'pos' field works.
+ padded := make([]byte, len(tcase.data)+2)
+ copy(padded[1:], tcase.data)
+
+ // Test cellLength.
+ l, err := cellLength(padded, 1, tcase.typ, tcase.metadata)
+ if err != nil || l != len(tcase.data) {
+ t.Errorf("testcase cellLength(%v,%v) returned unexpected result: %v %v was expected %v ", tcase.typ, tcase.data, l, err, len(tcase.data))
+ }
+
+ // Test CellValue.
+ out, l, err := CellValue(padded, 1, tcase.typ, tcase.metadata, tcase.styp)
+ if err != nil || l != len(tcase.data) || out.Type() != tcase.out.Type() || bytes.Compare(out.Raw(), tcase.out.Raw()) != 0 {
+ t.Errorf("testcase cellData(%v,%v) returned unexpected result: %v(%v) %v %v, was expecting %v(%v) %v ", tcase.typ, tcase.data, out, out.Type(), l, err, tcase.out, tcase.out.Type(), len(tcase.data))
+ }
+ }
+}
diff --git a/go/mysqlconn/replication/constants.go b/go/mysqlconn/replication/constants.go
index 009b1f518f1..e941fa26d85 100644
--- a/go/mysqlconn/replication/constants.go
+++ b/go/mysqlconn/replication/constants.go
@@ -59,6 +59,18 @@ const (
// TypeBit is MYSQL_TYPE_BIT
TypeBit = 16
+ // TypeTimestamp2 is MYSQL_TYPE_TIMESTAMP2
+ TypeTimestamp2 = 17
+
+ // TypeDateTime2 is MYSQL_TYPE_DATETIME2
+ TypeDateTime2 = 18
+
+ // TypeTime2 is MYSQL_TYPE_TIME2
+ TypeTime2 = 19
+
+ // TypeJSON is MYSQL_TYPE_JSON
+ TypeJSON = 245
+
// TypeNewDecimal is MYSQL_TYPE_NEWDECIMAL
TypeNewDecimal = 246
diff --git a/go/mysqlconn/replication_test.go b/go/mysqlconn/replication_test.go
index 8cf0370d20e..ae9f0a93a5b 100644
--- a/go/mysqlconn/replication_test.go
+++ b/go/mysqlconn/replication_test.go
@@ -527,9 +527,9 @@ func testRowReplicationWithRealDatabase(t *testing.T, params *sqldb.ConnParams)
t.Logf("Got Table Map event: %v %v", tableID, tableMap)
if tableMap.Database != "vttest" ||
tableMap.Name != "replication" ||
- len(tableMap.Columns) != 2 ||
- tableMap.Columns[0].CanBeNull ||
- !tableMap.Columns[1].CanBeNull {
+ len(tableMap.Types) != 2 ||
+ tableMap.CanBeNull.Bit(0) ||
+ !tableMap.CanBeNull.Bit(1) {
t.Errorf("got wrong TableMap: %v", tableMap)
}
gotTableMapEvent = true
@@ -543,7 +543,7 @@ func testRowReplicationWithRealDatabase(t *testing.T, params *sqldb.ConnParams)
}
// Check it has 2 rows, and first value is '10', second value is 'nice name'.
- values := wr.StringValues(tableMap, 0)
+ values, _ := wr.StringValuesForTests(tableMap, 0)
t.Logf("Got WriteRows event data: %v %v", wr, values)
if expected := []string{"10", "nice name"}; !reflect.DeepEqual(values, expected) {
t.Fatalf("StringValues returned %v, expected %v", values, expected)
@@ -560,14 +560,14 @@ func testRowReplicationWithRealDatabase(t *testing.T, params *sqldb.ConnParams)
}
// Check it has 2 identify rows, and first value is '10', second value is 'nice name'.
- values := ur.StringIdentifies(tableMap, 0)
+ values, _ := ur.StringIdentifiesForTests(tableMap, 0)
t.Logf("Got UpdateRows event identify: %v %v", ur, values)
if expected := []string{"10", "nice name"}; !reflect.DeepEqual(values, expected) {
t.Fatalf("StringIdentifies returned %v, expected %v", values, expected)
}
// Check it has 2 values rows, and first value is '10', second value is 'nicer name'.
- values = ur.StringValues(tableMap, 0)
+ values, _ = ur.StringValuesForTests(tableMap, 0)
t.Logf("Got UpdateRows event data: %v %v", ur, values)
if expected := []string{"10", "nicer name"}; !reflect.DeepEqual(values, expected) {
t.Fatalf("StringValues returned %v, expected %v", values, expected)
@@ -584,7 +584,7 @@ func testRowReplicationWithRealDatabase(t *testing.T, params *sqldb.ConnParams)
}
// Check it has 2 rows, and first value is '10', second value is 'nicer name'.
- values := dr.StringIdentifies(tableMap, 0)
+ values, _ := dr.StringIdentifiesForTests(tableMap, 0)
t.Logf("Got DeleteRows event identify: %v %v", dr, values)
if expected := []string{"10", "nicer name"}; !reflect.DeepEqual(values, expected) {
t.Fatalf("StringIdentifies returned %v, expected %v", values, expected)
diff --git a/go/mysqlconn/server.go b/go/mysqlconn/server.go
index 72098b062ba..824427cef10 100644
--- a/go/mysqlconn/server.go
+++ b/go/mysqlconn/server.go
@@ -1,8 +1,7 @@
package mysqlconn
import (
- "bytes"
- "crypto/rand"
+ "crypto/tls"
"fmt"
"net"
@@ -46,6 +45,9 @@ type Handler interface {
type Listener struct {
// Construction parameters, set by NewListener.
+ // authServer is the AuthServer object to use for authentication.
+ authServer AuthServer
+
// handler is the data handler.
handler Handler
@@ -60,8 +62,14 @@ type Listener struct {
// ServerVersion is the version we will advertise.
ServerVersion string
- // PasswordMap maps users to passwords.
- PasswordMap map[string]string
+ // TLSConfig is the server TLS config. If set, we will advertise
+ // that we support SSL.
+ TLSConfig *tls.Config
+
+ // AllowClearTextWithoutTLS needs to be set for the
+ // mysql_clear_password authentication method to be accepted
+ // by the server when TLS is not in use.
+ AllowClearTextWithoutTLS bool
// The following parameters are changed by the Accept routine.
@@ -70,17 +78,18 @@ type Listener struct {
}
// NewListener creates a new Listener.
-func NewListener(protocol, address string, handler Handler) (*Listener, error) {
+func NewListener(protocol, address string, authServer AuthServer, handler Handler) (*Listener, error) {
listener, err := net.Listen(protocol, address)
if err != nil {
return nil, err
}
return &Listener{
+ authServer: authServer,
+ handler: handler,
+ listener: listener,
+
ServerVersion: DefaultServerVersion,
- handler: handler,
- PasswordMap: make(map[string]string),
- listener: listener,
connectionID: 1,
}, nil
}
@@ -126,38 +135,111 @@ func (l *Listener) handle(conn net.Conn, connectionID uint32) {
defer l.handler.ConnectionClosed(c)
// First build and send the server handshake packet.
- cipher, err := c.writeHandshakeV10(l.ServerVersion)
+ salt, err := c.writeHandshakeV10(l.ServerVersion, l.authServer, l.TLSConfig != nil)
if err != nil {
log.Errorf("Cannot send HandshakeV10 packet: %v", err)
return
}
- // Wait for the client response.
- response, err := c.readEphemeralPacket()
+ // Wait for the client response. This has to be a direct read,
+ // so we don't buffer the TLS negotiation packets.
+ response, err := c.readPacketDirect()
if err != nil {
log.Errorf("Cannot read client handshake response: %v", err)
return
}
- username, authResponse, err := l.parseClientHandshakePacket(c, response)
+ user, authMethod, authResponse, err := l.parseClientHandshakePacket(c, true, response)
if err != nil {
log.Errorf("Cannot parse client handshake response: %v", err)
return
}
+ if c.Capabilities&CapabilityClientSSL > 0 {
+ // SSL was enabled. We need to re-read the auth packet.
+ response, err = c.readEphemeralPacket()
+ if err != nil {
+ log.Errorf("Cannot read post-SSL client handshake response: %v", err)
+ return
+ }
- // Find the user in our map
- password, ok := l.PasswordMap[username]
- if !ok {
- log.Errorf("Invalid user: %v", username)
- c.writeErrorPacket(ERAccessDeniedError, SSAccessDeniedError, "Access denied for user '%v'", username)
- return
+ user, authMethod, authResponse, err = l.parseClientHandshakePacket(c, false, response)
+ if err != nil {
+ log.Errorf("Cannot parse post-SSL client handshake response: %v", err)
+ return
+ }
}
- // Validate the password.
- computedAuthResponse := scramblePassword(cipher, []byte(password))
- if bytes.Compare(authResponse, computedAuthResponse) != 0 {
- log.Errorf("Invalid password for user %v", username)
- c.writeErrorPacket(ERAccessDeniedError, SSAccessDeniedError, "Access denied for user '%v'", username)
- return
+ // See what method the client used.
+ renegotiateWithClearText := false
+ switch authMethod {
+ case mysqlNativePassword:
+ // This is what the server started with. Let's use it if we can.
+ if !l.authServer.UseClearText() {
+ userData, err := l.authServer.ValidateHash(salt, user, authResponse)
+ if err != nil {
+ c.writeErrorPacketFromError(err)
+ return
+ }
+ c.User = user
+ c.UserData = userData
+ // We're good.
+ break
+ }
+
+ // Our AuthServer cannot use mysql_native_password, it
+ // needs the real password. Let's request that.
+ renegotiateWithClearText = true
+ case mysqlClearPassword:
+ // Client sent us a clear text password. Let's use it if we can.
+ if !l.AllowClearTextWithoutTLS && c.Capabilities&CapabilityClientSSL == 0 {
+ c.writeErrorPacket(CRServerHandshakeErr, SSUnknownSQLState, "Cannot use clear text authentication over non-SSL connections.")
+ return
+ }
+ userData, err := l.authServer.ValidateClearText(user, string(authResponse))
+ if err != nil {
+ c.writeErrorPacketFromError(err)
+ return
+ }
+ c.User = user
+ c.UserData = userData
+ break
+ default:
+ // Client decided to use something we don't understand.
+ // Let's try again with clear text password.
+ renegotiateWithClearText = true
+ }
+
+ // If we need to re-negotiate with clear text, do it.
+ if renegotiateWithClearText {
+ // Check error conditions.
+ if !l.AllowClearTextWithoutTLS && c.Capabilities&CapabilityClientSSL == 0 {
+ c.writeErrorPacket(CRServerHandshakeErr, SSUnknownSQLState, "Cannot use clear text authentication over non-SSL connections.")
+ return
+ }
+
+ if err := c.writeAuthSwitchRequest(mysqlClearPassword, nil); err != nil {
+ log.Errorf("Error write auth switch packet for client %v: %v", c.ConnectionID, err)
+ return
+ }
+
+ // The client is supposed to just send the data in a single packet.
+ // It is a zero-terminated string.
+ data, err := c.readEphemeralPacket()
+ if err != nil {
+ log.Warningf("Error reading auth switch response packet from client %v: %v", c.ConnectionID, err)
+ return
+ }
+ password, pos, ok := readNullString(data, 0)
+ if !ok || pos != len(data) {
+ c.writeErrorPacket(CRServerHandshakeErr, SSUnknownSQLState, "Error parsing packet with password: %v", data)
+ return
+ }
+ userData, err := l.authServer.ValidateClearText(user, password)
+ if err != nil {
+ c.writeErrorPacketFromError(err)
+ return
+ }
+ c.User = user
+ c.UserData = userData
}
// Send an OK packet.
@@ -224,8 +306,8 @@ func (l *Listener) Close() {
}
// writeHandshakeV10 writes the Initial Handshake Packet, server side.
-// It returns the cipher data.
-func (c *Conn) writeHandshakeV10(serverVersion string) ([]byte, error) {
+// It returns the salt data.
+func (c *Conn) writeHandshakeV10(serverVersion string, authServer AuthServer, enableTLS bool) ([]byte, error) {
capabilities := CapabilityClientLongPassword |
CapabilityClientLongFlag |
CapabilityClientConnectWithDB |
@@ -235,12 +317,15 @@ func (c *Conn) writeHandshakeV10(serverVersion string) ([]byte, error) {
CapabilityClientPluginAuth |
CapabilityClientPluginAuthLenencClientData |
CapabilityClientDeprecateEOF
+ if enableTLS {
+ capabilities |= CapabilityClientSSL
+ }
length :=
1 + // protocol version
lenNullString(serverVersion) +
4 + // connection ID
- 8 + // first part of cipher data
+ 8 + // first part of salt data
1 + // filler byte
2 + // capability flags (lower 2 bytes)
1 + // character set
@@ -263,12 +348,22 @@ func (c *Conn) writeHandshakeV10(serverVersion string) ([]byte, error) {
// Add connectionID in.
pos = writeUint32(data, pos, c.ConnectionID)
- // Generate the cipher, put 8 bytes in.
- cipher := make([]byte, 20)
- if _, err := rand.Read(cipher); err != nil {
+ // Generate the salt if needed, put 8 bytes in.
+ var salt []byte
+ var err error
+ if authServer.UseClearText() {
+ // salt will end up being unused, but we can't send
+ // just zero, as the client will still use it, and
+ // that may leak crypto information.
+ salt, err = newSalt()
+ } else {
+ salt, err = authServer.Salt()
+ }
+ if err != nil {
return nil, err
}
- pos += copy(data[pos:], cipher[:8])
+
+ pos += copy(data[pos:], salt[:8])
// One filler byte, always 0.
pos = writeByte(data, pos, 0)
@@ -293,11 +388,11 @@ func (c *Conn) writeHandshakeV10(serverVersion string) ([]byte, error) {
pos += 10
// Second part of auth plugin data.
- pos += copy(data[pos:], cipher[8:])
+ pos += copy(data[pos:], salt[8:])
data[pos] = 0
pos++
- // Copy authPluginName.
+ // Copy authPluginName. We always start with mysql_native_password.
pos = writeNullString(data, pos, mysqlNativePassword)
// Sanity check.
@@ -309,48 +404,62 @@ func (c *Conn) writeHandshakeV10(serverVersion string) ([]byte, error) {
return nil, err
}
- return cipher, nil
+ return salt, nil
}
// parseClientHandshakePacket parses the handshake sent by the client.
-// Returns the username, auth-data, error.
-func (l *Listener) parseClientHandshakePacket(c *Conn, data []byte) (string, []byte, error) {
+// Returns the username, auth method, auth data, error.
+func (l *Listener) parseClientHandshakePacket(c *Conn, firstTime bool, data []byte) (string, string, []byte, error) {
pos := 0
// Client flags, 4 bytes.
clientFlags, pos, ok := readUint32(data, pos)
if !ok {
- return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read client flags")
+ return "", "", nil, fmt.Errorf("parseClientHandshakePacket: can't read client flags")
}
if clientFlags&CapabilityClientProtocol41 == 0 {
- return "", nil, fmt.Errorf("parseClientHandshakePacket: only support protocol 4.1")
+ return "", "", nil, fmt.Errorf("parseClientHandshakePacket: only support protocol 4.1")
}
- // Remember a subset of the capabilities, so we can use them later in the protocol.
- c.Capabilities = clientFlags & (CapabilityClientDeprecateEOF)
+ // Remember a subset of the capabilities, so we can use them
+ // later in the protocol. If we re-received the handshake packet
+ // after SSL negotiation, do not overwrite capabilities.
+ if firstTime {
+ c.Capabilities = clientFlags & (CapabilityClientDeprecateEOF)
+ }
// Max packet size. Don't do anything with this now.
// See doc.go for more information.
- /*maxPacketSize*/
_, pos, ok = readUint32(data, pos)
if !ok {
- return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read maxPacketSize")
+ return "", "", nil, fmt.Errorf("parseClientHandshakePacket: can't read maxPacketSize")
}
// Character set. Need to handle it.
characterSet, pos, ok := readByte(data, pos)
if !ok {
- return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read characterSet")
+ return "", "", nil, fmt.Errorf("parseClientHandshakePacket: can't read characterSet")
}
c.CharacterSet = characterSet
// 23x reserved zero bytes.
pos += 23
+ // Check for SSL.
+ if firstTime && l.TLSConfig != nil && clientFlags&CapabilityClientSSL > 0 {
+ // Need to switch to TLS, and then re-read the packet.
+ conn := tls.Server(c.conn, l.TLSConfig)
+ c.conn = conn
+ c.reader.Reset(conn)
+ c.writer.Reset(conn)
+ c.Capabilities |= CapabilityClientSSL
+ return "", "", nil, nil
+ }
+
// username
username, pos, ok := readNullString(data, pos)
if !ok {
- return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read username")
+ return "", "", nil, fmt.Errorf("parseClientHandshakePacket: can't read username")
}
// auth-response can have three forms.
@@ -359,29 +468,29 @@ func (l *Listener) parseClientHandshakePacket(c *Conn, data []byte) (string, []b
var l uint64
l, pos, ok = readLenEncInt(data, pos)
if !ok {
- return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response variable length")
+ return "", "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response variable length")
}
authResponse, pos, ok = readBytes(data, pos, int(l))
if !ok {
- return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response")
+ return "", "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response")
}
} else if clientFlags&CapabilityClientSecureConnection != 0 {
var l byte
l, pos, ok = readByte(data, pos)
if !ok {
- return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response length")
+ return "", "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response length")
}
authResponse, pos, ok = readBytes(data, pos, int(l))
if !ok {
- return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response")
+ return "", "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response")
}
} else {
a := ""
a, pos, ok = readNullString(data, pos)
if !ok {
- return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response")
+ return "", "", nil, fmt.Errorf("parseClientHandshakePacket: can't read auth-response")
}
authResponse = []byte(a)
}
@@ -391,24 +500,49 @@ func (l *Listener) parseClientHandshakePacket(c *Conn, data []byte) (string, []b
dbname := ""
dbname, pos, ok = readNullString(data, pos)
if !ok {
- return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read dbname")
+ return "", "", nil, fmt.Errorf("parseClientHandshakePacket: can't read dbname")
}
c.SchemaName = dbname
}
- // auth plugin name
- authPluginName := "mysql_native_password"
+ // authMethod (with default)
+ authMethod := mysqlNativePassword
if clientFlags&CapabilityClientPluginAuth != 0 {
- authPluginName, pos, ok = readNullString(data, pos)
+ authMethod, pos, ok = readNullString(data, pos)
if !ok {
- return "", nil, fmt.Errorf("parseClientHandshakePacket: can't read authPluginName")
+ return "", "", nil, fmt.Errorf("parseClientHandshakePacket: can't read authMethod")
}
}
- if authPluginName != mysqlNativePassword {
- return "", nil, fmt.Errorf("invalid authPluginName, got %v but only support %v", authPluginName, mysqlNativePassword)
- }
// FIXME(alainjobart) Add CLIENT_CONNECT_ATTRS parsing if we need it.
- return username, authResponse, nil
+ return username, authMethod, authResponse, nil
+}
+
+// writeAuthSwitchRequest writes an auth switch request packet.
+func (c *Conn) writeAuthSwitchRequest(pluginName string, pluginData []byte) error {
+ length := 1 + // AuthSwitchRequestPacket
+ len(pluginName) + 1 + // 0-terminated pluginName
+ len(pluginData)
+
+ data := c.startEphemeralPacket(length)
+ pos := 0
+
+ // Packet header.
+ pos = writeByte(data, pos, AuthSwitchRequestPacket)
+
+ // Copy server version.
+ pos = writeNullString(data, pos, pluginName)
+
+ // Copy auth data.
+ pos += copy(data[pos:], pluginData)
+
+ // Sanity check.
+ if pos != len(data) {
+ return fmt.Errorf("error building AuthSwitchRequestPacket packet: got %v bytes expected %v", pos, len(data))
+ }
+ if err := c.writeEphemeralPacket(true); err != nil {
+ return err
+ }
+ return nil
}
diff --git a/go/mysqlconn/server_test.go b/go/mysqlconn/server_test.go
index 3182632dcc5..abcb527e032 100644
--- a/go/mysqlconn/server_test.go
+++ b/go/mysqlconn/server_test.go
@@ -2,6 +2,7 @@ package mysqlconn
import (
"fmt"
+ "io/ioutil"
"net"
"os"
"os/exec"
@@ -12,6 +13,8 @@ import (
"github.com/youtube/vitess/go/sqldb"
"github.com/youtube/vitess/go/sqltypes"
vtenv "github.com/youtube/vitess/go/vt/env"
+ "github.com/youtube/vitess/go/vt/servenv/grpcutils"
+ "github.com/youtube/vitess/go/vt/tlstest"
querypb "github.com/youtube/vitess/go/vt/proto/query"
)
@@ -37,6 +40,7 @@ var selectRowsResult = &sqltypes.Result{
sqltypes.MakeTrusted(querypb.Type_VARCHAR, []byte("nicer name")),
},
},
+ RowsAffected: 2,
}
type testHandler struct{}
@@ -83,19 +87,63 @@ func (th *testHandler) ComQuery(c *Conn, query string) (*sqltypes.Result, error)
}, nil
}
+ if query == "ssl echo" {
+ value := "OFF"
+ if c.Capabilities&CapabilityClientSSL > 0 {
+ value = "ON"
+ }
+ return &sqltypes.Result{
+ Fields: []*querypb.Field{
+ {
+ Name: "ssl_flag",
+ Type: querypb.Type_VARCHAR,
+ },
+ },
+ Rows: [][]sqltypes.Value{
+ {
+ sqltypes.MakeTrusted(querypb.Type_VARCHAR, []byte(value)),
+ },
+ },
+ }, nil
+ }
+
+ if query == "userData echo" {
+ return &sqltypes.Result{
+ Fields: []*querypb.Field{
+ {
+ Name: "user",
+ Type: querypb.Type_VARCHAR,
+ },
+ {
+ Name: "user_data",
+ Type: querypb.Type_VARCHAR,
+ },
+ },
+ Rows: [][]sqltypes.Value{
+ {
+ sqltypes.MakeTrusted(querypb.Type_VARCHAR, []byte(c.User)),
+ sqltypes.MakeTrusted(querypb.Type_VARCHAR, []byte(c.UserData)),
+ },
+ },
+ }, nil
+ }
+
return &sqltypes.Result{}, nil
}
func TestServer(t *testing.T) {
th := &testHandler{}
- l, err := NewListener("tcp", ":0", th)
+ authServer := NewAuthServerConfig()
+ authServer.Entries["user1"] = &AuthServerConfigEntry{
+ Password: "password1",
+ UserData: "userData1",
+ }
+ l, err := NewListener("tcp", ":0", authServer, th)
if err != nil {
t.Fatalf("NewListener failed: %v", err)
}
defer l.Close()
- l.PasswordMap["user1"] = "password1"
-
go func() {
l.Accept()
}()
@@ -162,11 +210,219 @@ func TestServer(t *testing.T) {
t.Errorf("Unexpected output for 'schema echo'")
}
+ // Sanity check: make sure this didn't go through SSL
+ output, ok = runMysql(t, params, "ssl echo")
+ if !ok {
+ t.Fatalf("mysql failed: %v", output)
+ }
+ if !strings.Contains(output, "ssl_flag") ||
+ !strings.Contains(output, "OFF") ||
+ !strings.Contains(output, "1 row in set") {
+ t.Errorf("Unexpected output for 'ssl echo': %v", output)
+ }
+
+ // UserData check: checks the server user data is correct.
+ output, ok = runMysql(t, params, "userData echo")
+ if !ok {
+ t.Fatalf("mysql failed: %v", output)
+ }
+ if !strings.Contains(output, "user1") ||
+ !strings.Contains(output, "user_data") ||
+ !strings.Contains(output, "userData1") {
+ t.Errorf("Unexpected output for 'userData echo': %v", output)
+ }
+
+ // Permissions check: check a bad password is rejected.
+ params.Pass = "bad"
+ output, ok = runMysql(t, params, "select rows")
+ if ok {
+ t.Fatalf("mysql should have failed: %v", output)
+ }
+ if !strings.Contains(output, "1045") ||
+ !strings.Contains(output, "28000") ||
+ !strings.Contains(output, "Access denied") {
+ t.Errorf("Unexpected output for invalid password: %v", output)
+ }
+
+ // Permissions check: check an unknown user is rejected.
+ params.Pass = "password1"
+ params.Uname = "user2"
+ output, ok = runMysql(t, params, "select rows")
+ if ok {
+ t.Fatalf("mysql should have failed: %v", output)
+ }
+ if !strings.Contains(output, "1045") ||
+ !strings.Contains(output, "28000") ||
+ !strings.Contains(output, "Access denied") {
+ t.Errorf("Unexpected output for invalid password: %v", output)
+ }
+
// Uncomment to leave setup up for a while, to run tests manually.
// fmt.Printf("Listening to server on host '%v' port '%v'.\n", host, port)
// time.Sleep(60 * time.Minute)
}
+// TestClearTextServer creates a Server that needs clear text passwords from the client.
+func TestClearTextServer(t *testing.T) {
+ th := &testHandler{}
+
+ authServer := NewAuthServerConfig()
+ authServer.Entries["user1"] = &AuthServerConfigEntry{
+ Password: "password1",
+ UserData: "userData1",
+ }
+ authServer.ClearText = true
+ l, err := NewListener("tcp", ":0", authServer, th)
+ if err != nil {
+ t.Fatalf("NewListener failed: %v", err)
+ }
+ defer l.Close()
+ go func() {
+ l.Accept()
+ }()
+
+ host := l.Addr().(*net.TCPAddr).IP.String()
+ port := l.Addr().(*net.TCPAddr).Port
+
+ // Setup the right parameters.
+ params := &sqldb.ConnParams{
+ Host: host,
+ Port: port,
+ Uname: "user1",
+ Pass: "password1",
+ }
+
+ // Run a 'select rows' command with results.
+ // This should fail as clear text is not enabled by default on the client.
+ l.AllowClearTextWithoutTLS = true
+ output, ok := runMysql(t, params, "select rows")
+ if ok {
+ t.Fatalf("mysql should have failed but returned: %v", output)
+ }
+ if strings.Contains(output, "No such file or directory") {
+ t.Logf("skipping mysql clear text tests, as the clear text plugin cannot be loaded: %v", err)
+ return
+ }
+ if !strings.Contains(output, "plugin not enabled") {
+ t.Errorf("Unexpected output for 'select rows': %v", output)
+ }
+
+ // Now enable clear text plugin in client, but server requires SSL.
+ l.AllowClearTextWithoutTLS = false
+ output, ok = runMysql(t, params, enableCleartextPluginPrefix+"select rows")
+ if ok {
+ t.Fatalf("mysql should have failed but returned: %v", output)
+ }
+ if !strings.Contains(output, "Cannot use clear text authentication over non-SSL connections") {
+ t.Errorf("Unexpected output for 'select rows': %v", output)
+ }
+
+ // Now enable clear text plugin, it should now work.
+ l.AllowClearTextWithoutTLS = true
+ output, ok = runMysql(t, params, enableCleartextPluginPrefix+"select rows")
+ if !ok {
+ t.Fatalf("mysql failed: %v", output)
+ }
+ if !strings.Contains(output, "nice name") ||
+ !strings.Contains(output, "nicer name") ||
+ !strings.Contains(output, "2 rows in set") {
+ t.Errorf("Unexpected output for 'select rows'")
+ }
+
+ // Change password, make sure server rejects us.
+ params.Pass = ""
+ output, ok = runMysql(t, params, enableCleartextPluginPrefix+"select rows")
+ if ok {
+ t.Fatalf("mysql should have failed but returned: %v", output)
+ }
+ if !strings.Contains(output, "Access denied for user 'user1'") {
+ t.Errorf("Unexpected output for 'select rows': %v", output)
+ }
+}
+
+// TestTLSServer creates a Server with TLS support, then uses mysql
+// client to connect to it.
+func TestTLSServer(t *testing.T) {
+ th := &testHandler{}
+
+ authServer := NewAuthServerConfig()
+ authServer.Entries["user1"] = &AuthServerConfigEntry{
+ Password: "password1",
+ }
+
+ // Create the listener, so we can get its host.
+ // Below, we are enabling --ssl-verify-server-cert, which adds
+ // a check that the common name of the certificate matches the
+ // server host name we connect to.
+ l, err := NewListener("tcp", ":0", authServer, th)
+ if err != nil {
+ t.Fatalf("NewListener failed: %v", err)
+ }
+ defer l.Close()
+ host := l.Addr().(*net.TCPAddr).IP.String()
+ port := l.Addr().(*net.TCPAddr).Port
+
+ // Create the certs.
+ root, err := ioutil.TempDir("", "TestTLSServer")
+ if err != nil {
+ t.Fatalf("TempDir failed: %v", err)
+ }
+ defer os.RemoveAll(root)
+ tlstest.CreateCA(root)
+ tlstest.CreateSignedCert(root, tlstest.CA, "01", "server", host)
+ tlstest.CreateSignedCert(root, tlstest.CA, "02", "client", "Client Cert")
+
+ // Create the server with TLS config.
+ serverConfig, err := grpcutils.TLSServerConfig(
+ path.Join(root, "server-cert.pem"),
+ path.Join(root, "server-key.pem"),
+ path.Join(root, "ca-cert.pem"))
+ if err != nil {
+ t.Fatalf("TLSServerConfig failed: %v", err)
+ }
+ l.TLSConfig = serverConfig
+ go func() {
+ l.Accept()
+ }()
+
+ // Setup the right parameters.
+ params := &sqldb.ConnParams{
+ Host: host,
+ Port: port,
+ Uname: "user1",
+ Pass: "password1",
+ // SSL flags.
+ Flags: CapabilityClientSSL,
+ SslCa: path.Join(root, "ca-cert.pem"),
+ SslCert: path.Join(root, "client-cert.pem"),
+ SslKey: path.Join(root, "client-key.pem"),
+ }
+
+ // Run a 'select rows' command with results.
+ output, ok := runMysql(t, params, "select rows")
+ if !ok {
+ t.Fatalf("mysql failed: %v", output)
+ }
+ if !strings.Contains(output, "nice name") ||
+ !strings.Contains(output, "nicer name") ||
+ !strings.Contains(output, "2 rows in set") {
+ t.Errorf("Unexpected output for 'select rows'")
+ }
+
+ // make sure this went through SSL
+ output, ok = runMysql(t, params, "ssl echo")
+ if !ok {
+ t.Fatalf("mysql failed: %v", output)
+ }
+ if !strings.Contains(output, "ssl_flag") ||
+ !strings.Contains(output, "ON") ||
+ !strings.Contains(output, "1 row in set") {
+ t.Errorf("Unexpected output for 'ssl echo': %v", output)
+ }
+}
+
+const enableCleartextPluginPrefix = "enable-cleartext-plugin: "
+
// runMysql forks a mysql command line process connecting to the provided server.
func runMysql(t *testing.T, params *sqldb.ConnParams, command string) (string, bool) {
dir, err := vtenv.VtMysqlRoot()
@@ -181,31 +437,36 @@ func runMysql(t *testing.T, params *sqldb.ConnParams, command string) (string, b
// In particular, it has the message:
// Query OK, 1 row affected (0.00 sec)
args := []string{
- "-e", command,
"-v", "-v", "-v",
}
+ if strings.HasPrefix(command, enableCleartextPluginPrefix) {
+ command = command[len(enableCleartextPluginPrefix):]
+ args = append(args, "--enable-cleartext-plugin")
+ }
+ args = append(args, "-e", command)
if params.UnixSocket != "" {
- args = append(args, []string{
- "-S", params.UnixSocket,
- }...)
+ args = append(args, "-S", params.UnixSocket)
} else {
- args = append(args, []string{
+ args = append(args,
"-h", params.Host,
- "-P", fmt.Sprintf("%v", params.Port),
- }...)
+ "-P", fmt.Sprintf("%v", params.Port))
}
if params.Uname != "" {
- args = append(args, []string{
- "-u", params.Uname,
- }...)
+ args = append(args, "-u", params.Uname)
}
if params.Pass != "" {
args = append(args, "-p"+params.Pass)
}
if params.DbName != "" {
- args = append(args, []string{
- "-D", params.DbName,
- }...)
+ args = append(args, "-D", params.DbName)
+ }
+ if params.Flags&CapabilityClientSSL > 0 {
+ args = append(args,
+ "--ssl",
+ "--ssl-ca", params.SslCa,
+ "--ssl-cert", params.SslCert,
+ "--ssl-key", params.SslKey,
+ "--ssl-verify-server-cert")
}
env := []string{
"LD_LIBRARY_PATH=" + path.Join(dir, "lib/mysql"),
diff --git a/go/sqltypes/event_token.go b/go/sqltypes/event_token.go
new file mode 100644
index 00000000000..1eef7c08450
--- /dev/null
+++ b/go/sqltypes/event_token.go
@@ -0,0 +1,24 @@
+package sqltypes
+
+import querypb "github.com/youtube/vitess/go/vt/proto/query"
+
+// EventTokenMinimum returns an event token that is guaranteed to
+// happen before both provided EventToken objects. Note it doesn't
+// parse the position, but rather only uses the timestamp. This is
+// meant to be used for EventToken objects coming from different
+// source shard.
+func EventTokenMinimum(ev1, ev2 *querypb.EventToken) *querypb.EventToken {
+ if ev1 == nil || ev2 == nil {
+ // One or the other is not set, we can't do anything.
+ return nil
+ }
+
+ if ev1.Timestamp < ev2.Timestamp {
+ return &querypb.EventToken{
+ Timestamp: ev1.Timestamp,
+ }
+ }
+ return &querypb.EventToken{
+ Timestamp: ev2.Timestamp,
+ }
+}
diff --git a/go/sqltypes/event_token_test.go b/go/sqltypes/event_token_test.go
new file mode 100644
index 00000000000..eda0a93aca9
--- /dev/null
+++ b/go/sqltypes/event_token_test.go
@@ -0,0 +1,63 @@
+package sqltypes
+
+import (
+ "testing"
+
+ "github.com/golang/protobuf/proto"
+ querypb "github.com/youtube/vitess/go/vt/proto/query"
+)
+
+func TestEventTokenMinimum(t *testing.T) {
+ testcases := []struct {
+ ev1 *querypb.EventToken
+ ev2 *querypb.EventToken
+ expected *querypb.EventToken
+ }{{
+ ev1: nil,
+ ev2: nil,
+ expected: nil,
+ }, {
+ ev1: &querypb.EventToken{
+ Timestamp: 123,
+ },
+ ev2: nil,
+ expected: nil,
+ }, {
+ ev1: nil,
+ ev2: &querypb.EventToken{
+ Timestamp: 123,
+ },
+ expected: nil,
+ }, {
+ ev1: &querypb.EventToken{
+ Timestamp: 123,
+ },
+ ev2: &querypb.EventToken{
+ Timestamp: 456,
+ },
+ expected: &querypb.EventToken{
+ Timestamp: 123,
+ },
+ }, {
+ ev1: &querypb.EventToken{
+ Timestamp: 456,
+ },
+ ev2: &querypb.EventToken{
+ Timestamp: 123,
+ },
+ expected: &querypb.EventToken{
+ Timestamp: 123,
+ },
+ }}
+
+ for _, tcase := range testcases {
+ got := EventTokenMinimum(tcase.ev1, tcase.ev2)
+ if tcase.expected == nil && got != nil {
+ t.Errorf("expected nil result for Minimum(%v, %v) but got: %v", tcase.ev1, tcase.ev2, got)
+ continue
+ }
+ if !proto.Equal(got, tcase.expected) {
+ t.Errorf("got %v but expected %v for Minimum(%v, %v)", got, tcase.expected, tcase.ev1, tcase.ev2)
+ }
+ }
+}
diff --git a/go/sqltypes/proto3.go b/go/sqltypes/proto3.go
index fa4f6139d61..146692061a5 100644
--- a/go/sqltypes/proto3.go
+++ b/go/sqltypes/proto3.go
@@ -136,7 +136,7 @@ func QueryResponsesToProto3(qr []QueryResponse) []*querypb.ResultWithError {
for i, q := range qr {
result[i] = &querypb.ResultWithError{
Result: ResultToProto3(q.QueryResult),
- Error: vterrors.VtRPCErrorFromVtError(q.QueryError),
+ Error: vterrors.ToVTRPC(q.QueryError),
}
}
return result
@@ -151,7 +151,7 @@ func Proto3ToQueryReponses(qr []*querypb.ResultWithError) []QueryResponse {
for i, q := range qr {
result[i] = QueryResponse{
QueryResult: Proto3ToResult(q.Result),
- QueryError: vterrors.FromVtRPCError(q.Error),
+ QueryError: vterrors.FromVTRPC(q.Error),
}
}
return result
diff --git a/go/sqltypes/proto3_test.go b/go/sqltypes/proto3_test.go
index 1bf897a676a..e09e4c2b5da 100644
--- a/go/sqltypes/proto3_test.go
+++ b/go/sqltypes/proto3_test.go
@@ -8,10 +8,8 @@ import (
"reflect"
"testing"
- "errors"
-
querypb "github.com/youtube/vitess/go/vt/proto/query"
- "github.com/youtube/vitess/go/vt/proto/vtrpc"
+ vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
"github.com/youtube/vitess/go/vt/vterrors"
)
@@ -244,7 +242,7 @@ func TestQueryReponses(t *testing.T) {
QueryError: nil,
}, {
QueryResult: nil,
- QueryError: vterrors.FromError(vtrpc.ErrorCode_DEADLINE_EXCEEDED, errors.New("deadline exceeded")),
+ QueryError: vterrors.New(vtrpcpb.Code_DEADLINE_EXCEEDED, "deadline exceeded"),
},
}
@@ -286,9 +284,10 @@ func TestQueryReponses(t *testing.T) {
},
},
}, {
- Error: &vtrpc.RPCError{
- Code: vtrpc.ErrorCode_DEADLINE_EXCEEDED,
- Message: "deadline exceeded",
+ Error: &vtrpcpb.RPCError{
+ LegacyCode: vtrpcpb.LegacyErrorCode_DEADLINE_EXCEEDED_LEGACY,
+ Message: "deadline exceeded",
+ Code: vtrpcpb.Code_DEADLINE_EXCEEDED,
},
Result: nil,
},
diff --git a/go/sqltypes/result.go b/go/sqltypes/result.go
index e54896ee196..b509aa87376 100644
--- a/go/sqltypes/result.go
+++ b/go/sqltypes/result.go
@@ -4,10 +4,7 @@
package sqltypes
-import (
- "github.com/youtube/vitess/go/vt/binlog/eventtoken"
- querypb "github.com/youtube/vitess/go/vt/proto/query"
-)
+import querypb "github.com/youtube/vitess/go/vt/proto/query"
// Result represents a query result.
type Result struct {
@@ -167,7 +164,7 @@ func (result *Result) AppendResult(src *Result) {
// discard the new one.
if result.Extras != nil {
// Note if any of the two is nil, we get nil.
- result.Extras.EventToken = eventtoken.Minimum(result.Extras.EventToken, src.Extras.EventToken)
+ result.Extras.EventToken = EventTokenMinimum(result.Extras.EventToken, src.Extras.EventToken)
result.Extras.Fresher = result.Extras.Fresher && src.Extras.Fresher
}
diff --git a/go/sync2/semaphore.go b/go/sync2/semaphore.go
index 6629042e5fe..cd75e72dd1e 100644
--- a/go/sync2/semaphore.go
+++ b/go/sync2/semaphore.go
@@ -66,3 +66,8 @@ func (sem *Semaphore) TryAcquire() bool {
func (sem *Semaphore) Release() {
sem.slots <- struct{}{}
}
+
+// Size returns the current number of available slots.
+func (sem *Semaphore) Size() int {
+ return len(sem.slots)
+}
diff --git a/go/vt/binlog/binlog_streamer.go b/go/vt/binlog/binlog_streamer.go
index 5ea5421aa65..8bc74014553 100644
--- a/go/vt/binlog/binlog_streamer.go
+++ b/go/vt/binlog/binlog_streamer.go
@@ -5,6 +5,7 @@
package binlog
import (
+ "bytes"
"fmt"
"io"
"strings"
@@ -16,6 +17,8 @@ import (
"github.com/youtube/vitess/go/sqldb"
"github.com/youtube/vitess/go/stats"
"github.com/youtube/vitess/go/vt/mysqlctl"
+ "github.com/youtube/vitess/go/vt/sqlparser"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
binlogdatapb "github.com/youtube/vitess/go/vt/proto/binlogdata"
querypb "github.com/youtube/vitess/go/vt/proto/query"
@@ -49,9 +52,20 @@ var (
}
)
+// FullBinlogStatement has all the information we can gather for an event.
+// Some fields are only set if asked for, and if RBR is used.
+// Otherwise we'll revert back to using the SQL comments, for SBR.
+type FullBinlogStatement struct {
+ Statement *binlogdatapb.BinlogTransaction_Statement
+ Table string
+ KeyspaceID []byte
+ PKNames []*querypb.Field
+ PKRow *querypb.Row
+}
+
// sendTransactionFunc is used to send binlog events.
// reply is of type binlogdatapb.BinlogTransaction.
-type sendTransactionFunc func(trans *binlogdatapb.BinlogTransaction) error
+type sendTransactionFunc func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error
// getStatementCategory returns the binlogdatapb.BL_* category for a SQL statement.
func getStatementCategory(sql string) binlogdatapb.BinlogTransaction_Statement_Category {
@@ -65,9 +79,10 @@ func getStatementCategory(sql string) binlogdatapb.BinlogTransaction_Statement_C
// A Streamer should only be used once. To start another stream, call
// NewStreamer() again.
type Streamer struct {
- // dbname and mysqld are set at creation and immutable.
+ // The following fields at set at creation and immutable.
dbname string
mysqld mysqlctl.MysqlDaemon
+ se *schema.Engine
clientCharset *binlogdatapb.Charset
startPos replication.Position
@@ -86,10 +101,11 @@ type Streamer struct {
// startPos is the position to start streaming at. Incompatible with timestamp.
// timestamp is the timestamp to start streaming at. Incompatible with startPos.
// sendTransaction is called each time a transaction is committed or rolled back.
-func NewStreamer(dbname string, mysqld mysqlctl.MysqlDaemon, clientCharset *binlogdatapb.Charset, startPos replication.Position, timestamp int64, sendTransaction sendTransactionFunc) *Streamer {
+func NewStreamer(dbname string, mysqld mysqlctl.MysqlDaemon, se *schema.Engine, clientCharset *binlogdatapb.Charset, startPos replication.Position, timestamp int64, sendTransaction sendTransactionFunc) *Streamer {
return &Streamer{
dbname: dbname,
mysqld: mysqld,
+ se: se,
clientCharset: clientCharset,
startPos: startPos,
timestamp: timestamp,
@@ -168,7 +184,7 @@ func (bls *Streamer) Stream(ctx context.Context) (err error) {
// If the events channel is closed, parseEvents returns ErrServerEOF.
// If the context is done, returns ctx.Err().
func (bls *Streamer) parseEvents(ctx context.Context, events <-chan replication.BinlogEvent) (replication.Position, error) {
- var statements []*binlogdatapb.BinlogTransaction_Statement
+ var statements []FullBinlogStatement
var format replication.BinlogFormat
var gtid replication.GTID
var pos = bls.startPos
@@ -186,21 +202,18 @@ func (bls *Streamer) parseEvents(ctx context.Context, events <-chan replication.
log.Errorf("BEGIN in binlog stream while still in another transaction; dropping %d statements: %v", len(statements), statements)
binlogStreamerErrors.Add("ParseEvents", 1)
}
- statements = make([]*binlogdatapb.BinlogTransaction_Statement, 0, 10)
+ statements = make([]FullBinlogStatement, 0, 10)
autocommit = false
}
// A commit can be triggered either by a COMMIT query, or by an XID_EVENT.
// Statements that aren't wrapped in BEGIN/COMMIT are committed immediately.
commit := func(timestamp uint32) error {
if int64(timestamp) >= bls.timestamp {
- trans := &binlogdatapb.BinlogTransaction{
- Statements: statements,
- EventToken: &querypb.EventToken{
- Timestamp: int64(timestamp),
- Position: replication.EncodePosition(pos),
- },
+ eventToken := &querypb.EventToken{
+ Timestamp: int64(timestamp),
+ Position: replication.EncodePosition(pos),
}
- if err = bls.sendTransaction(trans); err != nil {
+ if err = bls.sendTransaction(eventToken, statements); err != nil {
if err == io.EOF {
return ErrClientEOF
}
@@ -283,18 +296,22 @@ func (bls *Streamer) parseEvents(ctx context.Context, events <-chan replication.
if err != nil {
return pos, fmt.Errorf("can't parse INTVAR_EVENT: %v, event data: %#v", err, ev)
}
- statements = append(statements, &binlogdatapb.BinlogTransaction_Statement{
- Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
- Sql: []byte(fmt.Sprintf("SET %s=%d", replication.IntVarNames[typ], value)),
+ statements = append(statements, FullBinlogStatement{
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
+ Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
+ Sql: []byte(fmt.Sprintf("SET %s=%d", replication.IntVarNames[typ], value)),
+ },
})
case ev.IsRand(): // RAND_EVENT
seed1, seed2, err := ev.Rand(format)
if err != nil {
return pos, fmt.Errorf("can't parse RAND_EVENT: %v, event data: %#v", err, ev)
}
- statements = append(statements, &binlogdatapb.BinlogTransaction_Statement{
- Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
- Sql: []byte(fmt.Sprintf("SET @@RAND_SEED1=%d, @@RAND_SEED2=%d", seed1, seed2)),
+ statements = append(statements, FullBinlogStatement{
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
+ Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
+ Sql: []byte(fmt.Sprintf("SET @@RAND_SEED1=%d, @@RAND_SEED2=%d", seed1, seed2)),
+ },
})
case ev.IsQuery(): // QUERY_EVENT
// Extract the query string and group into transactions.
@@ -336,7 +353,11 @@ func (bls *Streamer) parseEvents(ctx context.Context, events <-chan replication.
setTimestamp.Charset = q.Charset
statement.Charset = q.Charset
}
- statements = append(statements, setTimestamp, statement)
+ statements = append(statements, FullBinlogStatement{
+ Statement: setTimestamp,
+ }, FullBinlogStatement{
+ Statement: statement,
+ })
if autocommit {
if err = commit(ev.Timestamp()); err != nil {
return pos, err
@@ -368,6 +389,40 @@ func (bls *Streamer) parseEvents(ctx context.Context, events <-chan replication.
return pos, err
}
tableMaps[tableID] = tm
+ case ev.IsWriteRows():
+ tableID := ev.TableID(format)
+ tm, ok := tableMaps[tableID]
+ if !ok {
+ return pos, fmt.Errorf("unknown tableID %v in WriteRows event", tableID)
+ }
+ if tm.Database != "" && tm.Database != bls.dbname {
+ // Skip cross-db statements.
+ continue
+ }
+ ti := bls.se.GetTable(sqlparser.NewTableIdent(tm.Name))
+ if ti == nil {
+ return pos, fmt.Errorf("unknown table %v in schema", tm.Name)
+ }
+ setTimestamp := &binlogdatapb.BinlogTransaction_Statement{
+ Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
+ Sql: []byte(fmt.Sprintf("SET TIMESTAMP=%d", ev.Timestamp())),
+ }
+ statements = append(statements, FullBinlogStatement{
+ Statement: setTimestamp,
+ })
+
+ rows, err := ev.Rows(format, tm)
+ if err != nil {
+ return pos, err
+ }
+
+ statements = appendInserts(statements, &rows, tm, ti)
+
+ if autocommit {
+ if err = commit(ev.Timestamp()); err != nil {
+ return pos, err
+ }
+ }
case ev.IsUpdateRows():
tableID := ev.TableID(format)
tm, ok := tableMaps[tableID]
@@ -378,25 +433,59 @@ func (bls *Streamer) parseEvents(ctx context.Context, events <-chan replication.
// Skip cross-db statements.
continue
}
+ ti := bls.se.GetTable(sqlparser.NewTableIdent(tm.Name))
+ if ti == nil {
+ return pos, fmt.Errorf("unknown table %v in schema", tm.Name)
+ }
+ setTimestamp := &binlogdatapb.BinlogTransaction_Statement{
+ Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
+ Sql: []byte(fmt.Sprintf("SET TIMESTAMP=%d", ev.Timestamp())),
+ }
+ statements = append(statements, FullBinlogStatement{
+ Statement: setTimestamp,
+ })
+
rows, err := ev.Rows(format, tm)
if err != nil {
return pos, err
}
+
+ statements = appendUpdates(statements, &rows, tm, ti)
+
+ if autocommit {
+ if err = commit(ev.Timestamp()); err != nil {
+ return pos, err
+ }
+ }
+ case ev.IsDeleteRows():
+ tableID := ev.TableID(format)
+ tm, ok := tableMaps[tableID]
+ if !ok {
+ return pos, fmt.Errorf("unknown tableID %v in DeleteRows event", tableID)
+ }
+ if tm.Database != "" && tm.Database != bls.dbname {
+ // Skip cross-db statements.
+ continue
+ }
+ ti := bls.se.GetTable(sqlparser.NewTableIdent(tm.Name))
+ if ti == nil {
+ return pos, fmt.Errorf("unknown table %v in schema", tm.Name)
+ }
setTimestamp := &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
Sql: []byte(fmt.Sprintf("SET TIMESTAMP=%d", ev.Timestamp())),
}
- statements = append(statements, setTimestamp)
- for i := range rows.Rows {
- identifies := rows.StringIdentifies(tm, i)
- values := rows.StringValues(tm, i)
- update := &binlogdatapb.BinlogTransaction_Statement{
- Category: binlogdatapb.BinlogTransaction_Statement_BL_UPDATE,
- Sql: []byte(fmt.Sprintf("WIP: update table %v set values = %v where identifies = %v", tm.Name, values, identifies)),
- }
- statements = append(statements, update)
+ statements = append(statements, FullBinlogStatement{
+ Statement: setTimestamp,
+ })
+
+ rows, err := ev.Rows(format, tm)
+ if err != nil {
+ return pos, err
}
+ statements = appendDeletes(statements, &rows, tm, ti)
+
if autocommit {
if err = commit(ev.Timestamp()); err != nil {
return pos, err
@@ -405,3 +494,167 @@ func (bls *Streamer) parseEvents(ctx context.Context, events <-chan replication.
}
}
}
+
+func appendInserts(statements []FullBinlogStatement, rows *replication.Rows, tm *replication.TableMap, ti *schema.Table) []FullBinlogStatement {
+ for i := range rows.Rows {
+ var sql bytes.Buffer
+
+ sql.WriteString("INSERT INTO ")
+ sql.WriteString(tm.Name)
+ sql.WriteString(" SET ")
+
+ if err := writeValuesAsSQL(&sql, rows, tm, ti, i); err != nil {
+ log.Warningf("writeValuesAsSQL(%v) failed: %v", i, err)
+ continue
+ }
+
+ statement := &binlogdatapb.BinlogTransaction_Statement{
+ Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
+ Sql: sql.Bytes(),
+ }
+ statements = append(statements, FullBinlogStatement{
+ Statement: statement,
+ Table: tm.Name,
+ })
+ // TODO(alainjobart): fill in keyspaceID, pkNames, pkRows
+ // if necessary.
+ }
+ return statements
+}
+
+func appendUpdates(statements []FullBinlogStatement, rows *replication.Rows, tm *replication.TableMap, ti *schema.Table) []FullBinlogStatement {
+ for i := range rows.Rows {
+ var sql bytes.Buffer
+
+ sql.WriteString("UPDATE ")
+ sql.WriteString(tm.Name)
+ sql.WriteString(" SET ")
+
+ if err := writeValuesAsSQL(&sql, rows, tm, ti, i); err != nil {
+ log.Warningf("writeValuesAsSQL(%v) failed: %v", i, err)
+ continue
+ }
+
+ sql.WriteString(" WHERE ")
+
+ if err := writeIdentifiesAsSQL(&sql, rows, tm, ti, i); err != nil {
+ log.Warningf("writeIdentifiesAsSQL(%v) failed: %v", i, err)
+ continue
+ }
+
+ update := &binlogdatapb.BinlogTransaction_Statement{
+ Category: binlogdatapb.BinlogTransaction_Statement_BL_UPDATE,
+ Sql: sql.Bytes(),
+ }
+ statements = append(statements, FullBinlogStatement{
+ Statement: update,
+ Table: tm.Name,
+ })
+ // TODO(alainjobart): fill in keyspaceID, pkNames, pkRows
+ // if necessary.
+ }
+ return statements
+}
+
+func appendDeletes(statements []FullBinlogStatement, rows *replication.Rows, tm *replication.TableMap, ti *schema.Table) []FullBinlogStatement {
+ for i := range rows.Rows {
+ var sql bytes.Buffer
+
+ sql.WriteString("DELETE FROM ")
+ sql.WriteString(tm.Name)
+ sql.WriteString(" WHERE ")
+
+ if err := writeIdentifiesAsSQL(&sql, rows, tm, ti, i); err != nil {
+ log.Warningf("writeIdentifiesAsSQL(%v) failed: %v", i, err)
+ continue
+ }
+
+ statement := &binlogdatapb.BinlogTransaction_Statement{
+ Category: binlogdatapb.BinlogTransaction_Statement_BL_DELETE,
+ Sql: sql.Bytes(),
+ }
+ statements = append(statements, FullBinlogStatement{
+ Statement: statement,
+ Table: tm.Name,
+ })
+ // TODO(alainjobart): fill in keyspaceID, pkNames, pkRows
+ // if necessary.
+ }
+ return statements
+}
+
+// writeValuesAsSQL is a helper method to print the values as SQL in the
+// provided bytes.Buffer.
+func writeValuesAsSQL(sql *bytes.Buffer, rs *replication.Rows, tm *replication.TableMap, ti *schema.Table, rowIndex int) error {
+ valueIndex := 0
+ data := rs.Rows[rowIndex].Data
+ pos := 0
+ for c := 0; c < rs.DataColumns.Count(); c++ {
+ if !rs.DataColumns.Bit(c) {
+ continue
+ }
+
+ // Print a separator if needed, then print the name.
+ if valueIndex > 0 {
+ sql.WriteString(", ")
+ }
+ sql.WriteString(ti.Columns[c].Name.String())
+ sql.WriteByte('=')
+
+ if rs.Rows[rowIndex].NullColumns.Bit(valueIndex) {
+ // This column is represented, but its value is NULL.
+ sql.WriteString("NULL")
+ valueIndex++
+ continue
+ }
+
+ // We have real data
+ value, l, err := replication.CellValue(data, pos, tm.Types[c], tm.Metadata[c], ti.Columns[c].Type)
+ if err != nil {
+ return err
+ }
+ value.EncodeSQL(sql)
+ pos += l
+ valueIndex++
+ }
+
+ return nil
+}
+
+// writeIdentifiesAsSQL is a helper method to print the identifies as SQL in the
+// provided bytes.Buffer.
+func writeIdentifiesAsSQL(sql *bytes.Buffer, rs *replication.Rows, tm *replication.TableMap, ti *schema.Table, rowIndex int) error {
+ valueIndex := 0
+ data := rs.Rows[rowIndex].Identify
+ pos := 0
+ for c := 0; c < rs.IdentifyColumns.Count(); c++ {
+ if !rs.IdentifyColumns.Bit(c) {
+ continue
+ }
+
+ // Print a separator if needed, then print the name.
+ if valueIndex > 0 {
+ sql.WriteString(" AND ")
+ }
+ sql.WriteString(ti.Columns[c].Name.String())
+ sql.WriteByte('=')
+
+ if rs.Rows[rowIndex].NullIdentifyColumns.Bit(valueIndex) {
+ // This column is represented, but its value is NULL.
+ sql.WriteString("NULL")
+ valueIndex++
+ continue
+ }
+
+ // We have real data
+ value, l, err := replication.CellValue(data, pos, tm.Types[c], tm.Metadata[c], ti.Columns[c].Type)
+ if err != nil {
+ return err
+ }
+ value.EncodeSQL(sql)
+ pos += l
+ valueIndex++
+ }
+
+ return nil
+}
diff --git a/go/vt/binlog/binlog_streamer_rbr_test.go b/go/vt/binlog/binlog_streamer_rbr_test.go
index f810fa610c3..5efc5391167 100644
--- a/go/vt/binlog/binlog_streamer_rbr_test.go
+++ b/go/vt/binlog/binlog_streamer_rbr_test.go
@@ -7,6 +7,8 @@ import (
"golang.org/x/net/context"
"github.com/youtube/vitess/go/mysqlconn/replication"
+ "github.com/youtube/vitess/go/vt/sqlparser"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
binlogdatapb "github.com/youtube/vitess/go/vt/proto/binlogdata"
querypb "github.com/youtube/vitess/go/vt/proto/query"
@@ -19,19 +21,61 @@ func TestStreamerParseRBRUpdateEvent(t *testing.T) {
s := replication.NewFakeBinlogStream()
s.ServerID = 62344
+ // Create a schema.Engine for this test, with just one table.
+ // We only use the Columns.
+ se := schema.NewEngineForTests()
+ se.SetTableForTests(&schema.Table{
+ Name: sqlparser.NewTableIdent("vt_a"),
+ Columns: []schema.TableColumn{
+ {
+ Name: sqlparser.NewColIdent("id"),
+ Type: querypb.Type_INT64,
+ },
+ {
+ Name: sqlparser.NewColIdent("message"),
+ Type: querypb.Type_VARCHAR,
+ },
+ },
+ })
+
+ // Create a tableMap event on the table.
tableID := uint64(0x102030405060)
tm := &replication.TableMap{
Flags: 0x8090,
Database: "vt_test_keyspace",
Name: "vt_a",
- Columns: []replication.TableMapColumn{
- {Type: replication.TypeLong, CanBeNull: false},
- {Type: replication.TypeVarchar, CanBeNull: true},
+ Types: []byte{
+ replication.TypeLong,
+ replication.TypeVarchar,
+ },
+ CanBeNull: replication.NewServerBitmap(2),
+ Metadata: []uint16{
+ 0,
+ 384, // A VARCHAR(128) in utf8 would result in 384.
},
}
+ tm.CanBeNull.Set(1, true)
+
+ // Do an insert packet with all fields set.
+ insertRows := replication.Rows{
+ Flags: 0x1234,
+ DataColumns: replication.NewServerBitmap(2),
+ Rows: []replication.Row{
+ {
+ NullColumns: replication.NewServerBitmap(2),
+ Data: []byte{
+ 0x10, 0x20, 0x30, 0x40, // long
+ 0x04, 0x00, // len('abcd')
+ 'a', 'b', 'c', 'd', // 'abcd'
+ },
+ },
+ },
+ }
+ insertRows.DataColumns.Set(0, true)
+ insertRows.DataColumns.Set(1, true)
// Do an update packet with all fields set.
- rows := replication.Rows{
+ updateRows := replication.Rows{
Flags: 0x1234,
IdentifyColumns: replication.NewServerBitmap(2),
DataColumns: replication.NewServerBitmap(2),
@@ -52,10 +96,28 @@ func TestStreamerParseRBRUpdateEvent(t *testing.T) {
},
},
}
- rows.IdentifyColumns.Set(0, true)
- rows.IdentifyColumns.Set(1, true)
- rows.DataColumns.Set(0, true)
- rows.DataColumns.Set(1, true)
+ updateRows.IdentifyColumns.Set(0, true)
+ updateRows.IdentifyColumns.Set(1, true)
+ updateRows.DataColumns.Set(0, true)
+ updateRows.DataColumns.Set(1, true)
+
+ // Do a delete packet with all fields set.
+ deleteRows := replication.Rows{
+ Flags: 0x1234,
+ IdentifyColumns: replication.NewServerBitmap(2),
+ Rows: []replication.Row{
+ {
+ NullIdentifyColumns: replication.NewServerBitmap(2),
+ Identify: []byte{
+ 0x10, 0x20, 0x30, 0x40, // long
+ 0x03, 0x00, // len('abc')
+ 'a', 'b', 'c', // 'abc'
+ },
+ },
+ },
+ }
+ deleteRows.IdentifyColumns.Set(0, true)
+ deleteRows.IdentifyColumns.Set(1, true)
input := []replication.BinlogEvent{
replication.NewRotateEvent(f, s, 0, ""),
@@ -65,25 +127,58 @@ func TestStreamerParseRBRUpdateEvent(t *testing.T) {
replication.NewQueryEvent(f, s, replication.Query{
Database: "vt_test_keyspace",
SQL: "BEGIN"}),
- replication.NewUpdateRowsEvent(f, s, tableID, rows),
+ replication.NewWriteRowsEvent(f, s, tableID, insertRows),
+ replication.NewUpdateRowsEvent(f, s, tableID, updateRows),
+ replication.NewDeleteRowsEvent(f, s, tableID, deleteRows),
replication.NewXIDEvent(f, s),
}
events := make(chan replication.BinlogEvent)
- want := []binlogdatapb.BinlogTransaction{
+ want := []fullBinlogTransaction{
{
- Statements: []*binlogdatapb.BinlogTransaction_Statement{
+ statements: []FullBinlogStatement{
{
- Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
- Sql: []byte("SET TIMESTAMP=1407805592"),
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
+ Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
+ Sql: []byte("SET TIMESTAMP=1407805592"),
+ },
},
{
- Category: binlogdatapb.BinlogTransaction_Statement_BL_UPDATE,
- Sql: []byte("WIP: update table vt_a set values = [1076895760 abcd] where identifies = [1076895760 abc]"),
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
+ Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
+ Sql: []byte("INSERT INTO vt_a SET id=1076895760, message='abcd'"),
+ },
+ Table: "vt_a",
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
+ Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
+ Sql: []byte("SET TIMESTAMP=1407805592"),
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
+ Category: binlogdatapb.BinlogTransaction_Statement_BL_UPDATE,
+ Sql: []byte("UPDATE vt_a SET id=1076895760, message='abcd' WHERE id=1076895760 AND message='abc'"),
+ },
+ Table: "vt_a",
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
+ Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
+ Sql: []byte("SET TIMESTAMP=1407805592"),
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
+ Category: binlogdatapb.BinlogTransaction_Statement_BL_DELETE,
+ Sql: []byte("DELETE FROM vt_a WHERE id=1076895760 AND message='abc'"),
+ },
+ Table: "vt_a",
},
},
- EventToken: &querypb.EventToken{
+ eventToken: &querypb.EventToken{
Timestamp: 1407805592,
Position: replication.EncodePosition(replication.Position{
GTIDSet: replication.MariadbGTID{
@@ -95,12 +190,15 @@ func TestStreamerParseRBRUpdateEvent(t *testing.T) {
},
},
}
- var got []binlogdatapb.BinlogTransaction
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
- got = append(got, *trans)
+ var got []fullBinlogTransaction
+ sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error {
+ got = append(got, fullBinlogTransaction{
+ eventToken: eventToken,
+ statements: statements,
+ })
return nil
}
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ bls := NewStreamer("vt_test_keyspace", nil, se, nil, replication.Position{}, 0, sendTransaction)
go sendTestEvents(events, input)
_, err := bls.parseEvents(context.Background(), events)
@@ -109,6 +207,12 @@ func TestStreamerParseRBRUpdateEvent(t *testing.T) {
}
if !reflect.DeepEqual(got, want) {
- t.Errorf("binlogConnStreamer.parseEvents(): got:\n%v\nwant:\n%v", got, want)
+ t.Errorf("binlogConnStreamer.parseEvents(): got:\n%+v\nwant:\n%+v", got, want)
+ for i, fbt := range got {
+ t.Errorf("Got (%v)=%v", i, fbt.statements)
+ }
+ for i, fbt := range want {
+ t.Errorf("Want(%v)=%v", i, fbt.statements)
+ }
}
}
diff --git a/go/vt/binlog/binlog_streamer_test.go b/go/vt/binlog/binlog_streamer_test.go
index 1b7dedcfe5a..d3935ad50f3 100644
--- a/go/vt/binlog/binlog_streamer_test.go
+++ b/go/vt/binlog/binlog_streamer_test.go
@@ -20,6 +20,29 @@ import (
querypb "github.com/youtube/vitess/go/vt/proto/query"
)
+// fullBinlogTransaction is a helper type for tests.
+type fullBinlogTransaction struct {
+ eventToken *querypb.EventToken
+ statements []FullBinlogStatement
+}
+
+type binlogStatements []binlogdatapb.BinlogTransaction
+
+func (bs *binlogStatements) sendTransaction(eventToken *querypb.EventToken, statements []FullBinlogStatement) error {
+ var s []*binlogdatapb.BinlogTransaction_Statement
+ if len(statements) > 0 {
+ s = make([]*binlogdatapb.BinlogTransaction_Statement, len(statements))
+ for i, statement := range statements {
+ s[i] = statement.Statement
+ }
+ }
+ *bs = append(*bs, binlogdatapb.BinlogTransaction{
+ Statements: s,
+ EventToken: eventToken,
+ })
+ return nil
+}
+
func sendTestEvents(channel chan<- replication.BinlogEvent, events []replication.BinlogEvent) {
for _, ev := range events {
channel <- ev
@@ -65,12 +88,8 @@ func TestStreamerParseEventsXID(t *testing.T) {
},
},
}
- var got []binlogdatapb.BinlogTransaction
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
- got = append(got, *trans)
- return nil
- }
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ var got binlogStatements
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, (&got).sendTransaction)
go sendTestEvents(events, input)
_, err := bls.parseEvents(context.Background(), events)
@@ -78,7 +97,7 @@ func TestStreamerParseEventsXID(t *testing.T) {
t.Errorf("unexpected error: %v", err)
}
- if !reflect.DeepEqual(got, want) {
+ if !reflect.DeepEqual(got, binlogStatements(want)) {
t.Errorf("binlogConnStreamer.parseEvents(): got:\n%v\nwant:\n%v", got, want)
}
}
@@ -123,12 +142,8 @@ func TestStreamerParseEventsCommit(t *testing.T) {
},
},
}
- var got []binlogdatapb.BinlogTransaction
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
- got = append(got, *trans)
- return nil
- }
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ var got binlogStatements
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, (&got).sendTransaction)
go sendTestEvents(events, input)
_, err := bls.parseEvents(context.Background(), events)
@@ -136,7 +151,7 @@ func TestStreamerParseEventsCommit(t *testing.T) {
t.Errorf("unexpected error: %v", err)
}
- if !reflect.DeepEqual(got, want) {
+ if !reflect.DeepEqual(got, binlogStatements(want)) {
t.Errorf("binlogConnStreamer.parseEvents(): got %v, want %v", got, want)
}
}
@@ -144,10 +159,10 @@ func TestStreamerParseEventsCommit(t *testing.T) {
func TestStreamerStop(t *testing.T) {
events := make(chan replication.BinlogEvent)
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
+ sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error {
return nil
}
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, sendTransaction)
// Start parseEvents(), but don't send it anything, so it just waits.
ctx, cancel := context.WithCancel(context.Background())
@@ -189,10 +204,10 @@ func TestStreamerParseEventsClientEOF(t *testing.T) {
events := make(chan replication.BinlogEvent)
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
+ sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error {
return io.EOF
}
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, sendTransaction)
go sendTestEvents(events, input)
_, err := bls.parseEvents(context.Background(), events)
@@ -207,10 +222,10 @@ func TestStreamerParseEventsServerEOF(t *testing.T) {
events := make(chan replication.BinlogEvent)
close(events)
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
+ sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error {
return nil
}
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, sendTransaction)
_, err := bls.parseEvents(context.Background(), events)
if err != want {
@@ -237,10 +252,10 @@ func TestStreamerParseEventsSendErrorXID(t *testing.T) {
events := make(chan replication.BinlogEvent)
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
+ sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error {
return fmt.Errorf("foobar")
}
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, sendTransaction)
go sendTestEvents(events, input)
@@ -275,10 +290,10 @@ func TestStreamerParseEventsSendErrorCommit(t *testing.T) {
events := make(chan replication.BinlogEvent)
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
+ sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error {
return fmt.Errorf("foobar")
}
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, sendTransaction)
go sendTestEvents(events, input)
_, err := bls.parseEvents(context.Background(), events)
@@ -308,10 +323,10 @@ func TestStreamerParseEventsInvalid(t *testing.T) {
events := make(chan replication.BinlogEvent)
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
+ sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error {
return nil
}
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, sendTransaction)
go sendTestEvents(events, input)
_, err := bls.parseEvents(context.Background(), events)
@@ -343,10 +358,10 @@ func TestStreamerParseEventsInvalidFormat(t *testing.T) {
events := make(chan replication.BinlogEvent)
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
+ sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error {
return nil
}
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, sendTransaction)
go sendTestEvents(events, input)
_, err := bls.parseEvents(context.Background(), events)
@@ -378,10 +393,10 @@ func TestStreamerParseEventsNoFormat(t *testing.T) {
events := make(chan replication.BinlogEvent)
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
+ sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error {
return nil
}
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, sendTransaction)
go sendTestEvents(events, input)
_, err := bls.parseEvents(context.Background(), events)
@@ -411,10 +426,10 @@ func TestStreamerParseEventsInvalidQuery(t *testing.T) {
events := make(chan replication.BinlogEvent)
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
+ sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error {
return nil
}
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, sendTransaction)
go sendTestEvents(events, input)
_, err := bls.parseEvents(context.Background(), events)
@@ -490,20 +505,16 @@ func TestStreamerParseEventsRollback(t *testing.T) {
},
},
}
- var got []binlogdatapb.BinlogTransaction
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
- got = append(got, *trans)
- return nil
- }
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ var got binlogStatements
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, (&got).sendTransaction)
go sendTestEvents(events, input)
if _, err := bls.parseEvents(context.Background(), events); err != ErrServerEOF {
t.Errorf("unexpected error: %v", err)
}
- if !reflect.DeepEqual(got, want) {
- t.Errorf("binlogConnStreamer.parseEvents(): got %v, want %v", got, want)
+ if !reflect.DeepEqual(got, binlogStatements(want)) {
+ t.Errorf("binlogConnStreamer.parseEvents(): got:\n%v\nwant:\n%v", got, want)
}
}
@@ -555,19 +566,15 @@ func TestStreamerParseEventsDMLWithoutBegin(t *testing.T) {
},
},
}
- var got []binlogdatapb.BinlogTransaction
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
- got = append(got, *trans)
- return nil
- }
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ var got binlogStatements
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, (&got).sendTransaction)
go sendTestEvents(events, input)
if _, err := bls.parseEvents(context.Background(), events); err != ErrServerEOF {
t.Errorf("unexpected error: %v", err)
}
- if !reflect.DeepEqual(got, want) {
+ if !reflect.DeepEqual(got, binlogStatements(want)) {
t.Errorf("binlogConnStreamer.parseEvents(): got:\n%v\nwant:\n%v", got, want)
}
}
@@ -610,7 +617,7 @@ func TestStreamerParseEventsBeginWithoutCommit(t *testing.T) {
},
},
{
- Statements: []*binlogdatapb.BinlogTransaction_Statement{},
+ Statements: nil,
EventToken: &querypb.EventToken{
Timestamp: 1407805592,
Position: replication.EncodePosition(replication.Position{
@@ -623,19 +630,15 @@ func TestStreamerParseEventsBeginWithoutCommit(t *testing.T) {
},
},
}
- var got []binlogdatapb.BinlogTransaction
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
- got = append(got, *trans)
- return nil
- }
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ var got binlogStatements
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, (&got).sendTransaction)
go sendTestEvents(events, input)
if _, err := bls.parseEvents(context.Background(), events); err != ErrServerEOF {
t.Errorf("unexpected error: %v", err)
}
- if !reflect.DeepEqual(got, want) {
+ if !reflect.DeepEqual(got, binlogStatements(want)) {
t.Errorf("binlogConnStreamer.parseEvents(): got:\n%v\nwant:\n%v", got, want)
}
}
@@ -680,19 +683,15 @@ func TestStreamerParseEventsSetInsertID(t *testing.T) {
},
},
}
- var got []binlogdatapb.BinlogTransaction
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
- got = append(got, *trans)
- return nil
- }
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ var got binlogStatements
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, (&got).sendTransaction)
go sendTestEvents(events, input)
if _, err := bls.parseEvents(context.Background(), events); err != ErrServerEOF {
t.Errorf("unexpected error: %v", err)
}
- if !reflect.DeepEqual(got, want) {
+ if !reflect.DeepEqual(got, binlogStatements(want)) {
t.Errorf("binlogConnStreamer.parseEvents(): got %v, want %v", got, want)
}
}
@@ -717,10 +716,10 @@ func TestStreamerParseEventsInvalidIntVar(t *testing.T) {
events := make(chan replication.BinlogEvent)
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
+ sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error {
return nil
}
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, sendTransaction)
go sendTestEvents(events, input)
_, err := bls.parseEvents(context.Background(), events)
@@ -774,19 +773,15 @@ func TestStreamerParseEventsOtherDB(t *testing.T) {
},
},
}
- var got []binlogdatapb.BinlogTransaction
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
- got = append(got, *trans)
- return nil
- }
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ var got binlogStatements
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, (&got).sendTransaction)
go sendTestEvents(events, input)
if _, err := bls.parseEvents(context.Background(), events); err != ErrServerEOF {
t.Errorf("unexpected error: %v", err)
}
- if !reflect.DeepEqual(got, want) {
+ if !reflect.DeepEqual(got, binlogStatements(want)) {
t.Errorf("binlogConnStreamer.parseEvents(): got %v, want %v", got, want)
}
}
@@ -832,19 +827,15 @@ func TestStreamerParseEventsOtherDBBegin(t *testing.T) {
},
},
}
- var got []binlogdatapb.BinlogTransaction
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
- got = append(got, *trans)
- return nil
- }
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ var got binlogStatements
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, (&got).sendTransaction)
go sendTestEvents(events, input)
if _, err := bls.parseEvents(context.Background(), events); err != ErrServerEOF {
t.Errorf("unexpected error: %v", err)
}
- if !reflect.DeepEqual(got, want) {
+ if !reflect.DeepEqual(got, binlogStatements(want)) {
t.Errorf("binlogConnStreamer.parseEvents(): got %v, want %v", got, want)
}
}
@@ -869,10 +860,10 @@ func TestStreamerParseEventsBeginAgain(t *testing.T) {
events := make(chan replication.BinlogEvent)
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
+ sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error {
return nil
}
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, sendTransaction)
before := binlogStreamerErrors.Counts()["ParseEvents"]
go sendTestEvents(events, input)
@@ -932,19 +923,15 @@ func TestStreamerParseEventsMariadbBeginGTID(t *testing.T) {
},
},
}
- var got []binlogdatapb.BinlogTransaction
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
- got = append(got, *trans)
- return nil
- }
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ var got binlogStatements
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, (&got).sendTransaction)
go sendTestEvents(events, input)
if _, err := bls.parseEvents(context.Background(), events); err != ErrServerEOF {
t.Errorf("unexpected error: %v", err)
}
- if !reflect.DeepEqual(got, want) {
+ if !reflect.DeepEqual(got, binlogStatements(want)) {
t.Errorf("binlogConnStreamer.parseEvents(): got:\n%v\nwant:\n%v", got, want)
}
}
@@ -987,19 +974,15 @@ func TestStreamerParseEventsMariadbStandaloneGTID(t *testing.T) {
},
},
}
- var got []binlogdatapb.BinlogTransaction
- sendTransaction := func(trans *binlogdatapb.BinlogTransaction) error {
- got = append(got, *trans)
- return nil
- }
- bls := NewStreamer("vt_test_keyspace", nil, nil, replication.Position{}, 0, sendTransaction)
+ var got binlogStatements
+ bls := NewStreamer("vt_test_keyspace", nil, nil, nil, replication.Position{}, 0, (&got).sendTransaction)
go sendTestEvents(events, input)
if _, err := bls.parseEvents(context.Background(), events); err != ErrServerEOF {
t.Errorf("unexpected error: %v", err)
}
- if !reflect.DeepEqual(got, want) {
+ if !reflect.DeepEqual(got, binlogStatements(want)) {
t.Errorf("binlogConnStreamer.parseEvents(): got:\n%v\nwant:\n%v", got, want)
}
}
diff --git a/go/vt/binlog/event_streamer.go b/go/vt/binlog/event_streamer.go
index c16b643dc77..f1da61b447e 100644
--- a/go/vt/binlog/event_streamer.go
+++ b/go/vt/binlog/event_streamer.go
@@ -17,6 +17,7 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/mysqlctl"
"github.com/youtube/vitess/go/vt/sqlparser"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
binlogdatapb "github.com/youtube/vitess/go/vt/proto/binlogdata"
querypb "github.com/youtube/vitess/go/vt/proto/query"
@@ -39,11 +40,11 @@ type EventStreamer struct {
}
// NewEventStreamer returns a new EventStreamer on top of a Streamer
-func NewEventStreamer(dbname string, mysqld mysqlctl.MysqlDaemon, startPos replication.Position, timestamp int64, sendEvent sendEventFunc) *EventStreamer {
+func NewEventStreamer(dbname string, mysqld mysqlctl.MysqlDaemon, se *schema.Engine, startPos replication.Position, timestamp int64, sendEvent sendEventFunc) *EventStreamer {
evs := &EventStreamer{
sendEvent: sendEvent,
}
- evs.bls = NewStreamer(dbname, mysqld, nil, startPos, timestamp, evs.transactionToEvent)
+ evs.bls = NewStreamer(dbname, mysqld, se, nil, startPos, timestamp, evs.transactionToEvent)
return evs
}
@@ -52,16 +53,16 @@ func (evs *EventStreamer) Stream(ctx context.Context) error {
return evs.bls.Stream(ctx)
}
-func (evs *EventStreamer) transactionToEvent(trans *binlogdatapb.BinlogTransaction) error {
+func (evs *EventStreamer) transactionToEvent(eventToken *querypb.EventToken, statements []FullBinlogStatement) error {
event := &querypb.StreamEvent{
- EventToken: trans.EventToken,
+ EventToken: eventToken,
}
var err error
var insertid int64
- for _, stmt := range trans.Statements {
- switch stmt.Category {
+ for _, stmt := range statements {
+ switch stmt.Statement.Category {
case binlogdatapb.BinlogTransaction_Statement_BL_SET:
- sql := string(stmt.Sql)
+ sql := string(stmt.Statement.Sql)
if strings.HasPrefix(sql, binlogSetInsertID) {
insertid, err = strconv.ParseInt(sql[binlogSetInsertIDLen:], 10, 64)
if err != nil {
@@ -73,29 +74,29 @@ func (evs *EventStreamer) transactionToEvent(trans *binlogdatapb.BinlogTransacti
binlogdatapb.BinlogTransaction_Statement_BL_UPDATE,
binlogdatapb.BinlogTransaction_Statement_BL_DELETE:
var dmlStatement *querypb.StreamEvent_Statement
- dmlStatement, insertid, err = evs.buildDMLStatement(string(stmt.Sql), insertid)
+ dmlStatement, insertid, err = evs.buildDMLStatement(string(stmt.Statement.Sql), insertid)
if err != nil {
dmlStatement = &querypb.StreamEvent_Statement{
Category: querypb.StreamEvent_Statement_Error,
- Sql: stmt.Sql,
+ Sql: stmt.Statement.Sql,
}
}
event.Statements = append(event.Statements, dmlStatement)
case binlogdatapb.BinlogTransaction_Statement_BL_DDL:
ddlStatement := &querypb.StreamEvent_Statement{
Category: querypb.StreamEvent_Statement_DDL,
- Sql: stmt.Sql,
+ Sql: stmt.Statement.Sql,
}
event.Statements = append(event.Statements, ddlStatement)
case binlogdatapb.BinlogTransaction_Statement_BL_UNRECOGNIZED:
unrecognized := &querypb.StreamEvent_Statement{
Category: querypb.StreamEvent_Statement_Error,
- Sql: stmt.Sql,
+ Sql: stmt.Statement.Sql,
}
event.Statements = append(event.Statements, unrecognized)
default:
binlogStreamerErrors.Add("EventStreamer", 1)
- log.Errorf("Unrecognized event: %v: %s", stmt.Category, stmt.Sql)
+ log.Errorf("Unrecognized event: %v: %s", stmt.Statement.Category, stmt.Statement.Sql)
}
}
return evs.sendEvent(event)
diff --git a/go/vt/binlog/event_streamer_test.go b/go/vt/binlog/event_streamer_test.go
index b1a2749d348..265b468b3cf 100644
--- a/go/vt/binlog/event_streamer_test.go
+++ b/go/vt/binlog/event_streamer_test.go
@@ -34,15 +34,15 @@ func TestEventErrors(t *testing.T) {
},
}
for _, sql := range dmlErrorCases {
- trans := &binlogdatapb.BinlogTransaction{
- Statements: []*binlogdatapb.BinlogTransaction_Statement{
- {
+ statements := []FullBinlogStatement{
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
Sql: []byte(sql),
},
},
}
- err := evs.transactionToEvent(trans)
+ err := evs.transactionToEvent(nil, statements)
if err != nil {
t.Errorf("%s: %v", sql, err)
continue
@@ -67,16 +67,16 @@ func TestSetErrors(t *testing.T) {
return nil
},
}
- trans := &binlogdatapb.BinlogTransaction{
- Statements: []*binlogdatapb.BinlogTransaction_Statement{
- {
+ statements := []FullBinlogStatement{
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
Sql: []byte("SET INSERT_ID=abcd"),
},
},
}
before := binlogStreamerErrors.Counts()["EventStreamer"]
- err := evs.transactionToEvent(trans)
+ err := evs.transactionToEvent(nil, statements)
if err != nil {
t.Error(err)
}
@@ -87,25 +87,36 @@ func TestSetErrors(t *testing.T) {
}
func TestDMLEvent(t *testing.T) {
- trans := &binlogdatapb.BinlogTransaction{
- Statements: []*binlogdatapb.BinlogTransaction_Statement{{
- Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
- Sql: []byte("SET TIMESTAMP=2"),
- }, {
- Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
- Sql: []byte("SET INSERT_ID=10"),
- }, {
- Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
- Sql: []byte("query /* _stream _table_ (eid id name) (null 1 'bmFtZQ==' ) (null 18446744073709551615 'bmFtZQ==' ); */"),
- }, {
- Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
- Sql: []byte("query"),
- }},
- EventToken: &querypb.EventToken{
- Timestamp: 1,
- Position: "MariaDB/0-41983-20",
+ statements := []FullBinlogStatement{
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
+ Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
+ Sql: []byte("SET TIMESTAMP=2"),
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
+ Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
+ Sql: []byte("SET INSERT_ID=10"),
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
+ Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
+ Sql: []byte("query /* _stream _table_ (eid id name) (null 1 'bmFtZQ==' ) (null 18446744073709551615 'bmFtZQ==' ); */"),
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
+ Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
+ Sql: []byte("query"),
+ },
},
}
+ eventToken := &querypb.EventToken{
+ Timestamp: 1,
+ Position: "MariaDB/0-41983-20",
+ }
evs := &EventStreamer{
sendEvent: func(event *querypb.StreamEvent) error {
for _, statement := range event.Statements {
@@ -135,27 +146,30 @@ func TestDMLEvent(t *testing.T) {
return nil
},
}
- err := evs.transactionToEvent(trans)
+ err := evs.transactionToEvent(eventToken, statements)
if err != nil {
t.Error(err)
}
}
func TestDDLEvent(t *testing.T) {
- trans := &binlogdatapb.BinlogTransaction{
- Statements: []*binlogdatapb.BinlogTransaction_Statement{
- {
+ statements := []FullBinlogStatement{
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
Sql: []byte("SET TIMESTAMP=2"),
- }, {
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_DDL,
Sql: []byte("DDL"),
},
},
- EventToken: &querypb.EventToken{
- Timestamp: 1,
- Position: "MariaDB/0-41983-20",
- },
+ }
+ eventToken := &querypb.EventToken{
+ Timestamp: 1,
+ Position: "MariaDB/0-41983-20",
}
evs := &EventStreamer{
sendEvent: func(event *querypb.StreamEvent) error {
@@ -180,7 +194,7 @@ func TestDDLEvent(t *testing.T) {
return nil
},
}
- err := evs.transactionToEvent(trans)
+ err := evs.transactionToEvent(eventToken, statements)
if err != nil {
t.Error(err)
}
diff --git a/go/vt/binlog/eventtoken/compare.go b/go/vt/binlog/eventtoken/compare.go
index c4f90d27b43..974080e25c4 100644
--- a/go/vt/binlog/eventtoken/compare.go
+++ b/go/vt/binlog/eventtoken/compare.go
@@ -8,29 +8,6 @@ import (
querypb "github.com/youtube/vitess/go/vt/proto/query"
)
-// Minimum returns an event token that is guaranteed to happen before
-// both provided EventToken objects.
-//
-// FIXME(alainjobart) for now, we always strip the shard and position,
-// and only look at timestamp. It is only used across shards so it's
-// not a big deal. When we compare values within a shard, we'll have
-// to fix this.
-func Minimum(ev1, ev2 *querypb.EventToken) *querypb.EventToken {
- if ev1 == nil || ev2 == nil {
- // One or the other is not set, we can't do anything.
- return nil
- }
-
- if ev1.Timestamp < ev2.Timestamp {
- return &querypb.EventToken{
- Timestamp: ev1.Timestamp,
- }
- }
- return &querypb.EventToken{
- Timestamp: ev2.Timestamp,
- }
-}
-
// Fresher compares two event tokens. It returns a negative number if
// ev1ev2. In case of doubt (we don't have enough information to know
diff --git a/go/vt/binlog/eventtoken/compare_test.go b/go/vt/binlog/eventtoken/compare_test.go
index 46f5367c6f9..9f6868e2000 100644
--- a/go/vt/binlog/eventtoken/compare_test.go
+++ b/go/vt/binlog/eventtoken/compare_test.go
@@ -3,65 +3,9 @@ package eventtoken
import (
"testing"
- "github.com/golang/protobuf/proto"
querypb "github.com/youtube/vitess/go/vt/proto/query"
)
-func TestMinimum(t *testing.T) {
- testcases := []struct {
- ev1 *querypb.EventToken
- ev2 *querypb.EventToken
- expected *querypb.EventToken
- }{{
- ev1: nil,
- ev2: nil,
- expected: nil,
- }, {
- ev1: &querypb.EventToken{
- Timestamp: 123,
- },
- ev2: nil,
- expected: nil,
- }, {
- ev1: nil,
- ev2: &querypb.EventToken{
- Timestamp: 123,
- },
- expected: nil,
- }, {
- ev1: &querypb.EventToken{
- Timestamp: 123,
- },
- ev2: &querypb.EventToken{
- Timestamp: 456,
- },
- expected: &querypb.EventToken{
- Timestamp: 123,
- },
- }, {
- ev1: &querypb.EventToken{
- Timestamp: 456,
- },
- ev2: &querypb.EventToken{
- Timestamp: 123,
- },
- expected: &querypb.EventToken{
- Timestamp: 123,
- },
- }}
-
- for _, tcase := range testcases {
- got := Minimum(tcase.ev1, tcase.ev2)
- if tcase.expected == nil && got != nil {
- t.Errorf("expected nil result for Minimum(%v, %v) but got: %v", tcase.ev1, tcase.ev2, got)
- continue
- }
- if !proto.Equal(got, tcase.expected) {
- t.Errorf("got %v but expected %v for Minimum(%v, %v)", got, tcase.expected, tcase.ev1, tcase.ev2)
- }
- }
-}
-
func TestFresher(t *testing.T) {
testcases := []struct {
ev1 *querypb.EventToken
diff --git a/go/vt/binlog/keyrange_filter.go b/go/vt/binlog/keyrange_filter.go
index 5d8e242ca46..614700d9e2a 100644
--- a/go/vt/binlog/keyrange_filter.go
+++ b/go/vt/binlog/keyrange_filter.go
@@ -12,41 +12,43 @@ import (
"errors"
"fmt"
+ "github.com/youtube/vitess/go/vt/sqlparser"
+
binlogdatapb "github.com/youtube/vitess/go/vt/proto/binlogdata"
+ querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
- "github.com/youtube/vitess/go/vt/sqlparser"
)
// KeyRangeFilterFunc returns a function that calls callback only if statements
// in the transaction match the specified keyrange. The resulting function can be
// passed into the Streamer: bls.Stream(file, pos, sendTransaction) ->
// bls.Stream(file, pos, KeyRangeFilterFunc(keyrange, sendTransaction))
-func KeyRangeFilterFunc(keyrange *topodatapb.KeyRange, callback sendTransactionFunc) sendTransactionFunc {
- return func(reply *binlogdatapb.BinlogTransaction) error {
+func KeyRangeFilterFunc(keyrange *topodatapb.KeyRange, callback func(*binlogdatapb.BinlogTransaction) error) sendTransactionFunc {
+ return func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error {
matched := false
- filtered := make([]*binlogdatapb.BinlogTransaction_Statement, 0, len(reply.Statements))
- for _, statement := range reply.Statements {
- switch statement.Category {
+ filtered := make([]*binlogdatapb.BinlogTransaction_Statement, 0, len(statements))
+ for _, statement := range statements {
+ switch statement.Statement.Category {
case binlogdatapb.BinlogTransaction_Statement_BL_SET:
- filtered = append(filtered, statement)
+ filtered = append(filtered, statement.Statement)
case binlogdatapb.BinlogTransaction_Statement_BL_DDL:
- log.Warningf("Not forwarding DDL: %s", statement.Sql)
+ log.Warningf("Not forwarding DDL: %s", statement.Statement.Sql)
continue
case binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
binlogdatapb.BinlogTransaction_Statement_BL_UPDATE,
binlogdatapb.BinlogTransaction_Statement_BL_DELETE:
- keyspaceIDS, err := sqlannotation.ExtractKeyspaceIDS(string(statement.Sql))
+ keyspaceIDS, err := sqlannotation.ExtractKeyspaceIDS(string(statement.Statement.Sql))
if err != nil {
- if statement.Category == binlogdatapb.BinlogTransaction_Statement_BL_INSERT {
+ if statement.Statement.Category == binlogdatapb.BinlogTransaction_Statement_BL_INSERT {
// TODO(erez): Stop filtered-replication here, and alert.
logExtractKeySpaceIDError(err)
continue
}
- // If no keyspace IDs are found, we replicate to all tarrgets.
+ // If no keyspace IDs are found, we replicate to all targets.
// This is safe for UPDATE and DELETE because vttablet rewrites queries to
// include the primary key and the query will only affect the shards that
// have the rows.
- filtered = append(filtered, statement)
+ filtered = append(filtered, statement.Statement)
matched = true
continue
}
@@ -55,37 +57,38 @@ func KeyRangeFilterFunc(keyrange *topodatapb.KeyRange, callback sendTransactionF
// Skip keyspace ids that don't belong to the destination shard.
continue
}
- filtered = append(filtered, statement)
+ filtered = append(filtered, statement.Statement)
matched = true
continue
}
- query, err := getValidRangeQuery(string(statement.Sql), keyspaceIDS, keyrange)
+ query, err := getValidRangeQuery(string(statement.Statement.Sql), keyspaceIDS, keyrange)
if err != nil {
- log.Errorf("Error parsing statement (%s). Got %v", string(statement.Sql), err)
+ log.Errorf("Error parsing statement (%s). Got %v", string(statement.Statement.Sql), err)
continue
}
if query == "" {
continue
}
splitStatement := &binlogdatapb.BinlogTransaction_Statement{
- Category: statement.Category,
- Charset: statement.Charset,
+ Category: statement.Statement.Category,
+ Charset: statement.Statement.Charset,
Sql: []byte(query),
}
filtered = append(filtered, splitStatement)
matched = true
case binlogdatapb.BinlogTransaction_Statement_BL_UNRECOGNIZED:
updateStreamErrors.Add("KeyRangeStream", 1)
- log.Errorf("Error parsing keyspace id: %s", statement.Sql)
+ log.Errorf("Error parsing keyspace id: %s", statement.Statement.Sql)
continue
}
}
+ trans := &binlogdatapb.BinlogTransaction{
+ EventToken: eventToken,
+ }
if matched {
- reply.Statements = filtered
- } else {
- reply.Statements = nil
+ trans.Statements = filtered
}
- return callback(reply)
+ return callback(trans)
}
}
diff --git a/go/vt/binlog/keyrange_filter_test.go b/go/vt/binlog/keyrange_filter_test.go
index 580c15892ab..c4c0550d0e3 100644
--- a/go/vt/binlog/keyrange_filter_test.go
+++ b/go/vt/binlog/keyrange_filter_test.go
@@ -19,41 +19,59 @@ var testKeyRange = &topodatapb.KeyRange{
}
func TestKeyRangeFilterPass(t *testing.T) {
- input := binlogdatapb.BinlogTransaction{
- Statements: []*binlogdatapb.BinlogTransaction_Statement{
- {
+ statements := []FullBinlogStatement{
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
Sql: []byte("set1"),
- }, {
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
Sql: []byte("insert into tbl(col1, col2) values(1, a) /* vtgate:: keyspace_id:02 */"),
- }, {
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
Sql: []byte("insert into tbl(col1, col2, col3) values(1, 2, 3),(4, 5, 6) /* vtgate:: keyspace_id:01,02 *//*trailing_comments */"),
- }, {
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
Sql: []byte("insert into tbl(col1, col2, col3) values(1, 2, 3),(4, 5, 6) /* vtgate:: keyspace_id:01,20 *//*trailing_comments */"),
- }, {
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
Sql: []byte("insert into tbl(col1, col2, col3) values(1, 2, 3),(4, 5, 6) /* vtgate:: keyspace_id:10,20 *//*trailing_comments */"),
- }, {
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_UPDATE,
Sql: []byte("update tbl set col1=1"),
- }, {
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_DELETE,
Sql: []byte("delete from tbl where col1=1"),
},
},
- EventToken: &querypb.EventToken{
- Position: "MariaDB/0-41983-1",
- },
+ }
+ eventToken := &querypb.EventToken{
+ Position: "MariaDB/0-41983-1",
}
var got string
f := KeyRangeFilterFunc(testKeyRange, func(reply *binlogdatapb.BinlogTransaction) error {
got = bltToString(reply)
return nil
})
- f(&input)
+ f(eventToken, statements)
want := `statement: <6, "set1"> statement: <7, "insert into tbl(col1, col2) values(1, a) /* vtgate:: keyspace_id:02 */"> statement: <7, "insert into tbl(col1, col2, col3) values (1, 2, 3), (4, 5, 6) /* vtgate:: keyspace_id:01,02 *//*trailing_comments */"> statement: <7, "insert into tbl(col1, col2, col3) values (1, 2, 3) /* vtgate:: keyspace_id:01,20 *//*trailing_comments */"> statement: <8, "update tbl set col1=1"> statement: <9, "delete from tbl where col1=1"> position: "MariaDB/0-41983-1" `
if want != got {
t.Errorf("want\n%s, got\n%s", want, got)
@@ -61,26 +79,29 @@ func TestKeyRangeFilterPass(t *testing.T) {
}
func TestKeyRangeFilterSkip(t *testing.T) {
- input := binlogdatapb.BinlogTransaction{
- Statements: []*binlogdatapb.BinlogTransaction_Statement{
- {
+ statements := []FullBinlogStatement{
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
Sql: []byte("set1"),
- }, {
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
Sql: []byte("dml1 /* vtgate:: keyspace_id:20 */"),
},
},
- EventToken: &querypb.EventToken{
- Position: "MariaDB/0-41983-1",
- },
+ }
+ eventToken := &querypb.EventToken{
+ Position: "MariaDB/0-41983-1",
}
var got string
f := KeyRangeFilterFunc(testKeyRange, func(reply *binlogdatapb.BinlogTransaction) error {
got = bltToString(reply)
return nil
})
- f(&input)
+ f(eventToken, statements)
want := `position: "MariaDB/0-41983-1" `
if want != got {
t.Errorf("want %s, got %s", want, got)
@@ -88,26 +109,29 @@ func TestKeyRangeFilterSkip(t *testing.T) {
}
func TestKeyRangeFilterDDL(t *testing.T) {
- input := binlogdatapb.BinlogTransaction{
- Statements: []*binlogdatapb.BinlogTransaction_Statement{
- {
+ statements := []FullBinlogStatement{
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
Sql: []byte("set1"),
- }, {
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_DDL,
Sql: []byte("ddl"),
},
},
- EventToken: &querypb.EventToken{
- Position: "MariaDB/0-41983-1",
- },
+ }
+ eventToken := &querypb.EventToken{
+ Position: "MariaDB/0-41983-1",
}
var got string
f := KeyRangeFilterFunc(testKeyRange, func(reply *binlogdatapb.BinlogTransaction) error {
got = bltToString(reply)
return nil
})
- f(&input)
+ f(eventToken, statements)
want := `position: "MariaDB/0-41983-1" `
if want != got {
t.Errorf("want %s, got %s", want, got)
@@ -115,32 +139,41 @@ func TestKeyRangeFilterDDL(t *testing.T) {
}
func TestKeyRangeFilterMalformed(t *testing.T) {
- input := binlogdatapb.BinlogTransaction{
- Statements: []*binlogdatapb.BinlogTransaction_Statement{
- {
+ statements := []FullBinlogStatement{
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
Sql: []byte("set1"),
- }, {
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
Sql: []byte("ddl"),
- }, {
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
Sql: []byte("dml1 /* vtgate:: keyspace_id:20*/"),
- }, {
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
Sql: []byte("dml1 /* vtgate:: keyspace_id:2 */"), // Odd-length hex string.
},
},
- EventToken: &querypb.EventToken{
- Position: "MariaDB/0-41983-1",
- },
+ }
+ eventToken := &querypb.EventToken{
+ Position: "MariaDB/0-41983-1",
}
var got string
f := KeyRangeFilterFunc(testKeyRange, func(reply *binlogdatapb.BinlogTransaction) error {
got = bltToString(reply)
return nil
})
- f(&input)
+ f(eventToken, statements)
want := `position: "MariaDB/0-41983-1" `
if want != got {
t.Errorf("want %s, got %s", want, got)
diff --git a/go/vt/binlog/tables_filter.go b/go/vt/binlog/tables_filter.go
index 8e03c004cd3..3017a67a3dd 100644
--- a/go/vt/binlog/tables_filter.go
+++ b/go/vt/binlog/tables_filter.go
@@ -10,6 +10,7 @@ import (
log "github.com/golang/glog"
binlogdatapb "github.com/youtube/vitess/go/vt/proto/binlogdata"
+ querypb "github.com/youtube/vitess/go/vt/proto/query"
)
const (
@@ -21,53 +22,61 @@ const (
// in the transaction match the specified tables. The resulting function can be
// passed into the Streamer: bls.Stream(file, pos, sendTransaction) ->
// bls.Stream(file, pos, TablesFilterFunc(sendTransaction))
-func TablesFilterFunc(tables []string, callback sendTransactionFunc) sendTransactionFunc {
- return func(reply *binlogdatapb.BinlogTransaction) error {
+func TablesFilterFunc(tables []string, callback func(*binlogdatapb.BinlogTransaction) error) sendTransactionFunc {
+ return func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error {
matched := false
- filtered := make([]*binlogdatapb.BinlogTransaction_Statement, 0, len(reply.Statements))
- for _, statement := range reply.Statements {
- switch statement.Category {
+ filtered := make([]*binlogdatapb.BinlogTransaction_Statement, 0, len(statements))
+ for _, statement := range statements {
+ switch statement.Statement.Category {
case binlogdatapb.BinlogTransaction_Statement_BL_SET:
- filtered = append(filtered, statement)
+ filtered = append(filtered, statement.Statement)
case binlogdatapb.BinlogTransaction_Statement_BL_DDL:
- log.Warningf("Not forwarding DDL: %s", statement.Sql)
+ log.Warningf("Not forwarding DDL: %s", statement.Statement.Sql)
continue
case binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
binlogdatapb.BinlogTransaction_Statement_BL_UPDATE,
binlogdatapb.BinlogTransaction_Statement_BL_DELETE:
- sql := string(statement.Sql)
- tableIndex := strings.LastIndex(sql, streamComment)
- if tableIndex == -1 {
- updateStreamErrors.Add("TablesStream", 1)
- log.Errorf("Error parsing table name: %s", sql)
- continue
- }
- tableStart := tableIndex + len(streamComment)
- tableEnd := strings.Index(sql[tableStart:], space)
- if tableEnd == -1 {
- updateStreamErrors.Add("TablesStream", 1)
- log.Errorf("Error parsing table name: %s", sql)
- continue
+ tableName := statement.Table
+ if tableName == "" {
+ // The statement doesn't
+ // contain the table name (SBR
+ // event), figure it out.
+ sql := string(statement.Statement.Sql)
+ tableIndex := strings.LastIndex(sql, streamComment)
+ if tableIndex == -1 {
+ updateStreamErrors.Add("TablesStream", 1)
+ log.Errorf("Error parsing table name: %s", sql)
+ continue
+ }
+ tableStart := tableIndex + len(streamComment)
+ tableEnd := strings.Index(sql[tableStart:], space)
+ if tableEnd == -1 {
+ updateStreamErrors.Add("TablesStream", 1)
+ log.Errorf("Error parsing table name: %s", sql)
+ continue
+ }
+ tableName = sql[tableStart : tableStart+tableEnd]
}
- tableName := sql[tableStart : tableStart+tableEnd]
for _, t := range tables {
if t == tableName {
- filtered = append(filtered, statement)
+ filtered = append(filtered, statement.Statement)
matched = true
break
}
}
case binlogdatapb.BinlogTransaction_Statement_BL_UNRECOGNIZED:
updateStreamErrors.Add("TablesStream", 1)
- log.Errorf("Error parsing table name: %s", string(statement.Sql))
+ log.Errorf("Error parsing table name: %s", string(statement.Statement.Sql))
continue
}
}
+
+ trans := &binlogdatapb.BinlogTransaction{
+ EventToken: eventToken,
+ }
if matched {
- reply.Statements = filtered
- } else {
- reply.Statements = nil
+ trans.Statements = filtered
}
- return callback(reply)
+ return callback(trans)
}
}
diff --git a/go/vt/binlog/tables_filter_test.go b/go/vt/binlog/tables_filter_test.go
index 5f546336adc..ed1be7033e0 100644
--- a/go/vt/binlog/tables_filter_test.go
+++ b/go/vt/binlog/tables_filter_test.go
@@ -17,29 +17,35 @@ var testTables = []string{
}
func TestTablesFilterPass(t *testing.T) {
- input := binlogdatapb.BinlogTransaction{
- Statements: []*binlogdatapb.BinlogTransaction_Statement{
- {
+ statements := []FullBinlogStatement{
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
Sql: []byte("set1"),
- }, {
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
Sql: []byte("dml1 /* _stream included1 (id ) (500 ); */"),
- }, {
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
Sql: []byte("dml2 /* _stream included2 (id ) (500 ); */"),
},
},
- EventToken: &querypb.EventToken{
- Position: "MariaDB/0-41983-1",
- },
+ }
+ eventToken := &querypb.EventToken{
+ Position: "MariaDB/0-41983-1",
}
var got string
f := TablesFilterFunc(testTables, func(reply *binlogdatapb.BinlogTransaction) error {
got = bltToString(reply)
return nil
})
- f(&input)
+ f(eventToken, statements)
want := `statement: <6, "set1"> statement: <7, "dml1 /* _stream included1 (id ) (500 ); */"> statement: <7, "dml2 /* _stream included2 (id ) (500 ); */"> position: "MariaDB/0-41983-1" `
if want != got {
t.Errorf("want\n%s, got\n%s", want, got)
@@ -47,26 +53,29 @@ func TestTablesFilterPass(t *testing.T) {
}
func TestTablesFilterSkip(t *testing.T) {
- input := binlogdatapb.BinlogTransaction{
- Statements: []*binlogdatapb.BinlogTransaction_Statement{
- {
+ statements := []FullBinlogStatement{
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
Sql: []byte("set1"),
- }, {
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
Sql: []byte("dml1 /* _stream excluded1 (id ) (500 ); */"),
},
},
- EventToken: &querypb.EventToken{
- Position: "MariaDB/0-41983-1",
- },
+ }
+ eventToken := &querypb.EventToken{
+ Position: "MariaDB/0-41983-1",
}
var got string
f := TablesFilterFunc(testTables, func(reply *binlogdatapb.BinlogTransaction) error {
got = bltToString(reply)
return nil
})
- f(&input)
+ f(eventToken, statements)
want := `position: "MariaDB/0-41983-1" `
if want != got {
t.Errorf("want %s, got %s", want, got)
@@ -74,26 +83,29 @@ func TestTablesFilterSkip(t *testing.T) {
}
func TestTablesFilterDDL(t *testing.T) {
- input := binlogdatapb.BinlogTransaction{
- Statements: []*binlogdatapb.BinlogTransaction_Statement{
- {
+ statements := []FullBinlogStatement{
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
Sql: []byte("set1"),
- }, {
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_DDL,
Sql: []byte("ddl"),
},
},
- EventToken: &querypb.EventToken{
- Position: "MariaDB/0-41983-1",
- },
+ }
+ eventToken := &querypb.EventToken{
+ Position: "MariaDB/0-41983-1",
}
var got string
f := TablesFilterFunc(testTables, func(reply *binlogdatapb.BinlogTransaction) error {
got = bltToString(reply)
return nil
})
- f(&input)
+ f(eventToken, statements)
want := `position: "MariaDB/0-41983-1" `
if want != got {
t.Errorf("want %s, got %s", want, got)
@@ -101,29 +113,35 @@ func TestTablesFilterDDL(t *testing.T) {
}
func TestTablesFilterMalformed(t *testing.T) {
- input := binlogdatapb.BinlogTransaction{
- Statements: []*binlogdatapb.BinlogTransaction_Statement{
- {
+ statements := []FullBinlogStatement{
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
Sql: []byte("set1"),
- }, {
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
Sql: []byte("ddl"),
- }, {
+ },
+ },
+ {
+ Statement: &binlogdatapb.BinlogTransaction_Statement{
Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
Sql: []byte("dml1 /* _stream excluded1*/"),
},
},
- EventToken: &querypb.EventToken{
- Position: "MariaDB/0-41983-1",
- },
+ }
+ eventToken := &querypb.EventToken{
+ Position: "MariaDB/0-41983-1",
}
var got string
f := TablesFilterFunc(testTables, func(reply *binlogdatapb.BinlogTransaction) error {
got = bltToString(reply)
return nil
})
- f(&input)
+ f(eventToken, statements)
want := `position: "MariaDB/0-41983-1" `
if want != got {
t.Errorf("want %s, got %s", want, got)
diff --git a/go/vt/binlog/updatestream.go b/go/vt/binlog/updatestream.go
index 9f881630b9f..8a46abfa497 100644
--- a/go/vt/binlog/updatestream.go
+++ b/go/vt/binlog/updatestream.go
@@ -14,10 +14,10 @@ import (
// UpdateStream is the interface for the binlog server
type UpdateStream interface {
// StreamKeyRange streams events related to a KeyRange only
- StreamKeyRange(ctx context.Context, position string, keyRange *topodatapb.KeyRange, charset *binlogdatapb.Charset, callback func(*binlogdatapb.BinlogTransaction) error) error
+ StreamKeyRange(ctx context.Context, position string, keyRange *topodatapb.KeyRange, charset *binlogdatapb.Charset, callback func(trans *binlogdatapb.BinlogTransaction) error) error
// StreamTables streams events related to a set of Tables only
- StreamTables(ctx context.Context, position string, tables []string, charset *binlogdatapb.Charset, callback func(*binlogdatapb.BinlogTransaction) error) error
+ StreamTables(ctx context.Context, position string, tables []string, charset *binlogdatapb.Charset, callback func(trans *binlogdatapb.BinlogTransaction) error) error
// HandlePanic should be called in a defer,
// first thing in the RPC implementation.
diff --git a/go/vt/binlog/updatestreamctl.go b/go/vt/binlog/updatestreamctl.go
index fcd44185b77..11eddf9935c 100644
--- a/go/vt/binlog/updatestreamctl.go
+++ b/go/vt/binlog/updatestreamctl.go
@@ -16,6 +16,7 @@ import (
"github.com/youtube/vitess/go/sync2"
"github.com/youtube/vitess/go/tb"
"github.com/youtube/vitess/go/vt/mysqlctl"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
binlogdatapb "github.com/youtube/vitess/go/vt/proto/binlogdata"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
@@ -92,9 +93,9 @@ func (m *UpdateStreamControlMock) IsEnabled() bool {
// and UpdateStreamControl
type UpdateStreamImpl struct {
// the following variables are set at construction time
-
mysqld mysqlctl.MysqlDaemon
dbname string
+ se *schema.Engine
// actionLock protects the following variables
actionLock sync.Mutex
@@ -154,9 +155,10 @@ type RegisterUpdateStreamServiceFunc func(UpdateStream)
var RegisterUpdateStreamServices []RegisterUpdateStreamServiceFunc
// NewUpdateStream returns a new UpdateStreamImpl object
-func NewUpdateStream(mysqld mysqlctl.MysqlDaemon, dbname string) *UpdateStreamImpl {
+func NewUpdateStream(mysqld mysqlctl.MysqlDaemon, se *schema.Engine, dbname string) *UpdateStreamImpl {
return &UpdateStreamImpl{
mysqld: mysqld,
+ se: se,
dbname: dbname,
}
}
@@ -216,7 +218,7 @@ func (updateStream *UpdateStreamImpl) IsEnabled() bool {
}
// StreamKeyRange is part of the UpdateStream interface
-func (updateStream *UpdateStreamImpl) StreamKeyRange(ctx context.Context, position string, keyRange *topodatapb.KeyRange, charset *binlogdatapb.Charset, callback func(reply *binlogdatapb.BinlogTransaction) error) (err error) {
+func (updateStream *UpdateStreamImpl) StreamKeyRange(ctx context.Context, position string, keyRange *topodatapb.KeyRange, charset *binlogdatapb.Charset, callback func(trans *binlogdatapb.BinlogTransaction) error) (err error) {
pos, err := replication.DecodePosition(position)
if err != nil {
return err
@@ -237,12 +239,12 @@ func (updateStream *UpdateStreamImpl) StreamKeyRange(ctx context.Context, positi
log.Infof("ServeUpdateStream starting @ %#v", pos)
// Calls cascade like this: binlog.Streamer->KeyRangeFilterFunc->func(*binlogdatapb.BinlogTransaction)->callback
- f := KeyRangeFilterFunc(keyRange, func(reply *binlogdatapb.BinlogTransaction) error {
- keyrangeStatements.Add(int64(len(reply.Statements)))
+ f := KeyRangeFilterFunc(keyRange, func(trans *binlogdatapb.BinlogTransaction) error {
+ keyrangeStatements.Add(int64(len(trans.Statements)))
keyrangeTransactions.Add(1)
- return callback(reply)
+ return callback(trans)
})
- bls := NewStreamer(updateStream.dbname, updateStream.mysqld, charset, pos, 0, f)
+ bls := NewStreamer(updateStream.dbname, updateStream.mysqld, updateStream.se, charset, pos, 0, f)
streamCtx, cancel := context.WithCancel(ctx)
i := updateStream.streams.Add(cancel)
@@ -252,7 +254,7 @@ func (updateStream *UpdateStreamImpl) StreamKeyRange(ctx context.Context, positi
}
// StreamTables is part of the UpdateStream interface
-func (updateStream *UpdateStreamImpl) StreamTables(ctx context.Context, position string, tables []string, charset *binlogdatapb.Charset, callback func(reply *binlogdatapb.BinlogTransaction) error) (err error) {
+func (updateStream *UpdateStreamImpl) StreamTables(ctx context.Context, position string, tables []string, charset *binlogdatapb.Charset, callback func(trans *binlogdatapb.BinlogTransaction) error) (err error) {
pos, err := replication.DecodePosition(position)
if err != nil {
return err
@@ -273,12 +275,12 @@ func (updateStream *UpdateStreamImpl) StreamTables(ctx context.Context, position
log.Infof("ServeUpdateStream starting @ %#v", pos)
// Calls cascade like this: binlog.Streamer->TablesFilterFunc->func(*binlogdatapb.BinlogTransaction)->callback
- f := TablesFilterFunc(tables, func(reply *binlogdatapb.BinlogTransaction) error {
- tablesStatements.Add(int64(len(reply.Statements)))
+ f := TablesFilterFunc(tables, func(trans *binlogdatapb.BinlogTransaction) error {
+ tablesStatements.Add(int64(len(trans.Statements)))
tablesTransactions.Add(1)
- return callback(reply)
+ return callback(trans)
})
- bls := NewStreamer(updateStream.dbname, updateStream.mysqld, charset, pos, 0, f)
+ bls := NewStreamer(updateStream.dbname, updateStream.mysqld, updateStream.se, charset, pos, 0, f)
streamCtx, cancel := context.WithCancel(ctx)
i := updateStream.streams.Add(cancel)
diff --git a/go/vt/callerid/callerid.go b/go/vt/callerid/callerid.go
index 799d660369f..d3677ee9310 100644
--- a/go/vt/callerid/callerid.go
+++ b/go/vt/callerid/callerid.go
@@ -18,7 +18,7 @@ type callerIDKey int
var (
// internal Context key for immediate CallerID
- immediateCallerIDKey callerIDKey = 0
+ immediateCallerIDKey callerIDKey
// internal Context key for effective CallerID
effectiveCallerIDKey callerIDKey = 1
)
@@ -65,7 +65,7 @@ func GetComponent(ef *vtrpcpb.CallerID) string {
return ef.Component
}
-// GetSubcomponent returns a component inisde the process of effective caller,
+// GetSubcomponent returns a component inside the process of effective caller,
// which is responsible for generating this request. Suggested values are a
// servlet name or an API endpoint name.
func GetSubcomponent(ef *vtrpcpb.CallerID) string {
diff --git a/go/vt/discovery/fake_healthcheck.go b/go/vt/discovery/fake_healthcheck.go
index d1d82550d05..4c68c1dc6d7 100644
--- a/go/vt/discovery/fake_healthcheck.go
+++ b/go/vt/discovery/fake_healthcheck.go
@@ -3,9 +3,9 @@ package discovery
import (
"sync"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/sandboxconn"
"github.com/youtube/vitess/go/vt/topo"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/sandboxconn"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
diff --git a/go/vt/discovery/healthcheck.go b/go/vt/discovery/healthcheck.go
index 0f51658a911..0255594cd74 100644
--- a/go/vt/discovery/healthcheck.go
+++ b/go/vt/discovery/healthcheck.go
@@ -33,9 +33,9 @@ import (
"github.com/youtube/vitess/go/stats"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
"github.com/youtube/vitess/go/vt/topo/topoproto"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletconn"
"golang.org/x/net/context"
)
diff --git a/go/vt/discovery/healthcheck_test.go b/go/vt/discovery/healthcheck_test.go
index 2411e871138..93c45ea9724 100644
--- a/go/vt/discovery/healthcheck_test.go
+++ b/go/vt/discovery/healthcheck_test.go
@@ -11,10 +11,10 @@ import (
"time"
"github.com/youtube/vitess/go/vt/status"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice/fakes"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
"github.com/youtube/vitess/go/vt/topo"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice/fakes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletconn"
"golang.org/x/net/context"
querypb "github.com/youtube/vitess/go/vt/proto/query"
diff --git a/go/vt/dtids/dtids.go b/go/vt/dtids/dtids.go
index cd387fe9013..03aa48c82cb 100644
--- a/go/vt/dtids/dtids.go
+++ b/go/vt/dtids/dtids.go
@@ -27,7 +27,7 @@ func New(mmShard *vtgatepb.Session_ShardSession) string {
func ShardSession(dtid string) (*vtgatepb.Session_ShardSession, error) {
splits := strings.Split(dtid, ":")
if len(splits) != 3 {
- return nil, vterrors.FromError(vtrpcpb.ErrorCode_BAD_INPUT, fmt.Errorf("invalid parts in dtid: %s", dtid))
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid parts in dtid: %s", dtid)
}
target := &querypb.Target{
Keyspace: splits[0],
@@ -36,7 +36,7 @@ func ShardSession(dtid string) (*vtgatepb.Session_ShardSession, error) {
}
txid, err := strconv.ParseInt(splits[2], 10, 0)
if err != nil {
- return nil, vterrors.FromError(vtrpcpb.ErrorCode_BAD_INPUT, fmt.Errorf("invalid transaction id in dtid: %s", dtid))
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid transaction id in dtid: %s", dtid)
}
return &vtgatepb.Session_ShardSession{
Target: target,
@@ -48,11 +48,11 @@ func ShardSession(dtid string) (*vtgatepb.Session_ShardSession, error) {
func TransactionID(dtid string) (int64, error) {
splits := strings.Split(dtid, ":")
if len(splits) != 3 {
- return 0, vterrors.FromError(vtrpcpb.ErrorCode_BAD_INPUT, fmt.Errorf("invalid parts in dtid: %s", dtid))
+ return 0, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid parts in dtid: %s", dtid)
}
txid, err := strconv.ParseInt(splits[2], 10, 0)
if err != nil {
- return 0, vterrors.FromError(vtrpcpb.ErrorCode_BAD_INPUT, fmt.Errorf("invalid transaction id in dtid: %s", dtid))
+ return 0, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid transaction id in dtid: %s", dtid)
}
return txid, nil
}
diff --git a/go/vt/etcdtopo/watch.go b/go/vt/etcdtopo/watch.go
index a4a09570782..8fdf5b0b8b6 100644
--- a/go/vt/etcdtopo/watch.go
+++ b/go/vt/etcdtopo/watch.go
@@ -76,7 +76,11 @@ func (s *Server) Watch(ctx context.Context, cellName, filePath string) (*topo.Wa
watchChannel := make(chan *etcd.Response)
watchError := make(chan error)
go func(stop chan bool) {
- versionToWatch := initial.Node.ModifiedIndex + 1
+ // We start watching from the etcd version we got
+ // during the get, and not from the ModifiedIndex of
+ // the node, as the node might be older than the
+ // retention period of the server.
+ versionToWatch := initial.EtcdIndex + 1
_, err := cell.Client.Watch(filePath, versionToWatch, false /* recursive */, watchChannel, stop)
// Watch will only return a non-nil error, otherwise
// it keeps on watching. Send the error down.
diff --git a/go/vt/logutil/throttled.go b/go/vt/logutil/throttled.go
index 867071f7b88..9fbbf73e37b 100644
--- a/go/vt/logutil/throttled.go
+++ b/go/vt/logutil/throttled.go
@@ -46,7 +46,7 @@ func (tl *ThrottledLogger) log(logF logFunc, format string, v ...interface{}) {
logWaitTime := tl.maxInterval - (now.Sub(tl.lastlogTime))
if logWaitTime < 0 {
tl.lastlogTime = now
- logF(2, fmt.Sprintf(tl.name+":"+format, v...))
+ logF(2, fmt.Sprintf(tl.name+": "+format, v...))
return
}
// If this is the first message to be skipped, start a goroutine
diff --git a/go/vt/logutil/throttled_test.go b/go/vt/logutil/throttled_test.go
index decfc465e4a..593c6692cda 100644
--- a/go/vt/logutil/throttled_test.go
+++ b/go/vt/logutil/throttled_test.go
@@ -24,7 +24,7 @@ func TestThrottledLogger(t *testing.T) {
start := time.Now()
go tl.Infof("test %v", 1)
- if got, want := <-log, "name:test 1"; got != want {
+ if got, want := <-log, "name: test 1"; got != want {
t.Errorf("got %q, want %q", got, want)
}
@@ -40,7 +40,7 @@ func TestThrottledLogger(t *testing.T) {
}
go tl.Infof("test %v", 3)
- if got, want := <-log, "name:test 3"; got != want {
+ if got, want := <-log, "name: test 3"; got != want {
t.Errorf("got %q, want %q", got, want)
}
if got, want := skippedCount(tl), 0; got != want {
diff --git a/go/vt/mysqlctl/schema.go b/go/vt/mysqlctl/schema.go
index 16e932431de..4a270ec097f 100644
--- a/go/vt/mysqlctl/schema.go
+++ b/go/vt/mysqlctl/schema.go
@@ -189,7 +189,7 @@ func (mysqld *Mysqld) GetPrimaryKeyColumns(dbName, table string) ([]string, erro
}
}
if keyNameIndex == -1 || seqInIndexIndex == -1 || columnNameIndex == -1 {
- return nil, fmt.Errorf("Unknown columns in 'show index' result: %v", qr.Fields)
+ return nil, fmt.Errorf("unknown columns in 'show index' result: %v", qr.Fields)
}
columns := make([]string, 0, 5)
@@ -206,7 +206,7 @@ func (mysqld *Mysqld) GetPrimaryKeyColumns(dbName, table string) ([]string, erro
return nil, err
}
if seqInIndex != expectedIndex {
- return nil, fmt.Errorf("Unexpected index: %v != %v", seqInIndex, expectedIndex)
+ return nil, fmt.Errorf("unexpected index: %v != %v", seqInIndex, expectedIndex)
}
expectedIndex++
diff --git a/go/vt/proto/automation/automation.pb.go b/go/vt/proto/automation/automation.pb.go
index c63e1a8151b..38dbf1e67c9 100644
--- a/go/vt/proto/automation/automation.pb.go
+++ b/go/vt/proto/automation/automation.pb.go
@@ -105,6 +105,13 @@ func (m *ClusterOperation) String() string { return proto.CompactText
func (*ClusterOperation) ProtoMessage() {}
func (*ClusterOperation) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (m *ClusterOperation) GetId() string {
+ if m != nil {
+ return m.Id
+ }
+ return ""
+}
+
func (m *ClusterOperation) GetSerialTasks() []*TaskContainer {
if m != nil {
return m.SerialTasks
@@ -112,6 +119,20 @@ func (m *ClusterOperation) GetSerialTasks() []*TaskContainer {
return nil
}
+func (m *ClusterOperation) GetState() ClusterOperationState {
+ if m != nil {
+ return m.State
+ }
+ return ClusterOperationState_UNKNOWN_CLUSTER_OPERATION_STATE
+}
+
+func (m *ClusterOperation) GetError() string {
+ if m != nil {
+ return m.Error
+ }
+ return ""
+}
+
// TaskContainer holds one or more task which may be executed in parallel.
// "concurrency", if > 0, limits the amount of concurrently executed tasks.
type TaskContainer struct {
@@ -131,6 +152,13 @@ func (m *TaskContainer) GetParallelTasks() []*Task {
return nil
}
+func (m *TaskContainer) GetConcurrency() int32 {
+ if m != nil {
+ return m.Concurrency
+ }
+ return 0
+}
+
// Task represents a specific task which should be automatically executed.
type Task struct {
// Task specification.
@@ -150,6 +178,13 @@ func (m *Task) String() string { return proto.CompactTextString(m) }
func (*Task) ProtoMessage() {}
func (*Task) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+func (m *Task) GetName() string {
+ if m != nil {
+ return m.Name
+ }
+ return ""
+}
+
func (m *Task) GetParameters() map[string]string {
if m != nil {
return m.Parameters
@@ -157,6 +192,34 @@ func (m *Task) GetParameters() map[string]string {
return nil
}
+func (m *Task) GetId() string {
+ if m != nil {
+ return m.Id
+ }
+ return ""
+}
+
+func (m *Task) GetState() TaskState {
+ if m != nil {
+ return m.State
+ }
+ return TaskState_UNKNOWN_TASK_STATE
+}
+
+func (m *Task) GetOutput() string {
+ if m != nil {
+ return m.Output
+ }
+ return ""
+}
+
+func (m *Task) GetError() string {
+ if m != nil {
+ return m.Error
+ }
+ return ""
+}
+
type EnqueueClusterOperationRequest struct {
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
Parameters map[string]string `protobuf:"bytes,2,rep,name=parameters" json:"parameters,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
@@ -167,6 +230,13 @@ func (m *EnqueueClusterOperationRequest) String() string { return pro
func (*EnqueueClusterOperationRequest) ProtoMessage() {}
func (*EnqueueClusterOperationRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
+func (m *EnqueueClusterOperationRequest) GetName() string {
+ if m != nil {
+ return m.Name
+ }
+ return ""
+}
+
func (m *EnqueueClusterOperationRequest) GetParameters() map[string]string {
if m != nil {
return m.Parameters
@@ -183,6 +253,13 @@ func (m *EnqueueClusterOperationResponse) String() string { return pr
func (*EnqueueClusterOperationResponse) ProtoMessage() {}
func (*EnqueueClusterOperationResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
+func (m *EnqueueClusterOperationResponse) GetId() string {
+ if m != nil {
+ return m.Id
+ }
+ return ""
+}
+
type GetClusterOperationStateRequest struct {
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
}
@@ -192,6 +269,13 @@ func (m *GetClusterOperationStateRequest) String() string { return pr
func (*GetClusterOperationStateRequest) ProtoMessage() {}
func (*GetClusterOperationStateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
+func (m *GetClusterOperationStateRequest) GetId() string {
+ if m != nil {
+ return m.Id
+ }
+ return ""
+}
+
type GetClusterOperationStateResponse struct {
State ClusterOperationState `protobuf:"varint,1,opt,name=state,enum=automation.ClusterOperationState" json:"state,omitempty"`
}
@@ -203,6 +287,13 @@ func (*GetClusterOperationStateResponse) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{6}
}
+func (m *GetClusterOperationStateResponse) GetState() ClusterOperationState {
+ if m != nil {
+ return m.State
+ }
+ return ClusterOperationState_UNKNOWN_CLUSTER_OPERATION_STATE
+}
+
type GetClusterOperationDetailsRequest struct {
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
}
@@ -214,6 +305,13 @@ func (*GetClusterOperationDetailsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{7}
}
+func (m *GetClusterOperationDetailsRequest) GetId() string {
+ if m != nil {
+ return m.Id
+ }
+ return ""
+}
+
type GetClusterOperationDetailsResponse struct {
// Full snapshot of the execution e.g. including output of each task.
ClusterOp *ClusterOperation `protobuf:"bytes,2,opt,name=cluster_op,json=clusterOp" json:"cluster_op,omitempty"`
diff --git a/go/vt/proto/binlogdata/binlogdata.pb.go b/go/vt/proto/binlogdata/binlogdata.pb.go
index 57c00efc7ff..c9cbcf14543 100644
--- a/go/vt/proto/binlogdata/binlogdata.pb.go
+++ b/go/vt/proto/binlogdata/binlogdata.pb.go
@@ -98,6 +98,27 @@ func (m *Charset) String() string { return proto.CompactTextString(m)
func (*Charset) ProtoMessage() {}
func (*Charset) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (m *Charset) GetClient() int32 {
+ if m != nil {
+ return m.Client
+ }
+ return 0
+}
+
+func (m *Charset) GetConn() int32 {
+ if m != nil {
+ return m.Conn
+ }
+ return 0
+}
+
+func (m *Charset) GetServer() int32 {
+ if m != nil {
+ return m.Server
+ }
+ return 0
+}
+
// BinlogTransaction describes a transaction inside the binlogs.
// It is streamed by vttablet for filtered replication, used during resharding.
type BinlogTransaction struct {
@@ -140,6 +161,13 @@ func (m *BinlogTransaction_Statement) String() string { return proto.
func (*BinlogTransaction_Statement) ProtoMessage() {}
func (*BinlogTransaction_Statement) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 0} }
+func (m *BinlogTransaction_Statement) GetCategory() BinlogTransaction_Statement_Category {
+ if m != nil {
+ return m.Category
+ }
+ return BinlogTransaction_Statement_BL_UNRECOGNIZED
+}
+
func (m *BinlogTransaction_Statement) GetCharset() *Charset {
if m != nil {
return m.Charset
@@ -147,6 +175,13 @@ func (m *BinlogTransaction_Statement) GetCharset() *Charset {
return nil
}
+func (m *BinlogTransaction_Statement) GetSql() []byte {
+ if m != nil {
+ return m.Sql
+ }
+ return nil
+}
+
// StreamKeyRangeRequest is the payload to StreamKeyRange
type StreamKeyRangeRequest struct {
// where to start
@@ -162,6 +197,13 @@ func (m *StreamKeyRangeRequest) String() string { return proto.Compac
func (*StreamKeyRangeRequest) ProtoMessage() {}
func (*StreamKeyRangeRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+func (m *StreamKeyRangeRequest) GetPosition() string {
+ if m != nil {
+ return m.Position
+ }
+ return ""
+}
+
func (m *StreamKeyRangeRequest) GetKeyRange() *topodata.KeyRange {
if m != nil {
return m.KeyRange
@@ -208,6 +250,20 @@ func (m *StreamTablesRequest) String() string { return proto.CompactT
func (*StreamTablesRequest) ProtoMessage() {}
func (*StreamTablesRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
+func (m *StreamTablesRequest) GetPosition() string {
+ if m != nil {
+ return m.Position
+ }
+ return ""
+}
+
+func (m *StreamTablesRequest) GetTables() []string {
+ if m != nil {
+ return m.Tables
+ }
+ return nil
+}
+
func (m *StreamTablesRequest) GetCharset() *Charset {
if m != nil {
return m.Charset
diff --git a/go/vt/proto/logutil/logutil.pb.go b/go/vt/proto/logutil/logutil.pb.go
index 43dbdd597d3..98596199609 100644
--- a/go/vt/proto/logutil/logutil.pb.go
+++ b/go/vt/proto/logutil/logutil.pb.go
@@ -73,6 +73,20 @@ func (m *Time) String() string { return proto.CompactTextString(m) }
func (*Time) ProtoMessage() {}
func (*Time) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (m *Time) GetSeconds() int64 {
+ if m != nil {
+ return m.Seconds
+ }
+ return 0
+}
+
+func (m *Time) GetNanoseconds() int32 {
+ if m != nil {
+ return m.Nanoseconds
+ }
+ return 0
+}
+
// Event is a single logging event
type Event struct {
Time *Time `protobuf:"bytes,1,opt,name=time" json:"time,omitempty"`
@@ -94,6 +108,34 @@ func (m *Event) GetTime() *Time {
return nil
}
+func (m *Event) GetLevel() Level {
+ if m != nil {
+ return m.Level
+ }
+ return Level_INFO
+}
+
+func (m *Event) GetFile() string {
+ if m != nil {
+ return m.File
+ }
+ return ""
+}
+
+func (m *Event) GetLine() int64 {
+ if m != nil {
+ return m.Line
+ }
+ return 0
+}
+
+func (m *Event) GetValue() string {
+ if m != nil {
+ return m.Value
+ }
+ return ""
+}
+
func init() {
proto.RegisterType((*Time)(nil), "logutil.Time")
proto.RegisterType((*Event)(nil), "logutil.Event")
diff --git a/go/vt/proto/mysqlctl/mysqlctl.pb.go b/go/vt/proto/mysqlctl/mysqlctl.pb.go
index c6b3947c555..68ea55ec20a 100644
--- a/go/vt/proto/mysqlctl/mysqlctl.pb.go
+++ b/go/vt/proto/mysqlctl/mysqlctl.pb.go
@@ -49,6 +49,13 @@ func (m *StartRequest) String() string { return proto.CompactTextStri
func (*StartRequest) ProtoMessage() {}
func (*StartRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (m *StartRequest) GetMysqldArgs() []string {
+ if m != nil {
+ return m.MysqldArgs
+ }
+ return nil
+}
+
type StartResponse struct {
}
@@ -66,6 +73,13 @@ func (m *ShutdownRequest) String() string { return proto.CompactTextS
func (*ShutdownRequest) ProtoMessage() {}
func (*ShutdownRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+func (m *ShutdownRequest) GetWaitForMysqld() bool {
+ if m != nil {
+ return m.WaitForMysqld
+ }
+ return false
+}
+
type ShutdownResponse struct {
}
diff --git a/go/vt/proto/query/query.pb.go b/go/vt/proto/query/query.pb.go
index 1dfb9d76cf4..bc54e6429a9 100644
--- a/go/vt/proto/query/query.pb.go
+++ b/go/vt/proto/query/query.pb.go
@@ -490,6 +490,27 @@ func (m *Target) String() string { return proto.CompactTextString(m)
func (*Target) ProtoMessage() {}
func (*Target) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (m *Target) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
+func (m *Target) GetShard() string {
+ if m != nil {
+ return m.Shard
+ }
+ return ""
+}
+
+func (m *Target) GetTabletType() topodata.TabletType {
+ if m != nil {
+ return m.TabletType
+ }
+ return topodata.TabletType_UNKNOWN
+}
+
// VTGateCallerID is sent by VTGate to VTTablet to describe the
// caller. If possible, this information is secure. For instance,
// if using unique certificates that guarantee that VTGate->VTTablet
@@ -507,6 +528,13 @@ func (m *VTGateCallerID) String() string { return proto.CompactTextSt
func (*VTGateCallerID) ProtoMessage() {}
func (*VTGateCallerID) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+func (m *VTGateCallerID) GetUsername() string {
+ if m != nil {
+ return m.Username
+ }
+ return ""
+}
+
// EventToken is a structure that describes a point in time in a
// replication stream on one shard. The most recent known replication
// position can be retrieved from vttablet when executing a query. It
@@ -527,6 +555,27 @@ func (m *EventToken) String() string { return proto.CompactTextString
func (*EventToken) ProtoMessage() {}
func (*EventToken) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+func (m *EventToken) GetTimestamp() int64 {
+ if m != nil {
+ return m.Timestamp
+ }
+ return 0
+}
+
+func (m *EventToken) GetShard() string {
+ if m != nil {
+ return m.Shard
+ }
+ return ""
+}
+
+func (m *EventToken) GetPosition() string {
+ if m != nil {
+ return m.Position
+ }
+ return ""
+}
+
// Value represents a typed value.
type Value struct {
Type Type `protobuf:"varint,1,opt,name=type,enum=query.Type" json:"type,omitempty"`
@@ -538,6 +587,20 @@ func (m *Value) String() string { return proto.CompactTextString(m) }
func (*Value) ProtoMessage() {}
func (*Value) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
+func (m *Value) GetType() Type {
+ if m != nil {
+ return m.Type
+ }
+ return Type_NULL_TYPE
+}
+
+func (m *Value) GetValue() []byte {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
// BindVariable represents a single bind variable in a Query.
type BindVariable struct {
Type Type `protobuf:"varint,1,opt,name=type,enum=query.Type" json:"type,omitempty"`
@@ -551,6 +614,20 @@ func (m *BindVariable) String() string { return proto.CompactTextStri
func (*BindVariable) ProtoMessage() {}
func (*BindVariable) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
+func (m *BindVariable) GetType() Type {
+ if m != nil {
+ return m.Type
+ }
+ return Type_NULL_TYPE
+}
+
+func (m *BindVariable) GetValue() []byte {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
func (m *BindVariable) GetValues() []*Value {
if m != nil {
return m.Values
@@ -571,6 +648,13 @@ func (m *BoundQuery) String() string { return proto.CompactTextString
func (*BoundQuery) ProtoMessage() {}
func (*BoundQuery) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
+func (m *BoundQuery) GetSql() string {
+ if m != nil {
+ return m.Sql
+ }
+ return ""
+}
+
func (m *BoundQuery) GetBindVariables() map[string]*BindVariable {
if m != nil {
return m.BindVariables
@@ -597,6 +681,13 @@ func (m *ExecuteOptions) String() string { return proto.CompactTextSt
func (*ExecuteOptions) ProtoMessage() {}
func (*ExecuteOptions) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
+func (m *ExecuteOptions) GetIncludeEventToken() bool {
+ if m != nil {
+ return m.IncludeEventToken
+ }
+ return false
+}
+
func (m *ExecuteOptions) GetCompareEventToken() *EventToken {
if m != nil {
return m.CompareEventToken
@@ -604,6 +695,13 @@ func (m *ExecuteOptions) GetCompareEventToken() *EventToken {
return nil
}
+func (m *ExecuteOptions) GetIncludedFields() ExecuteOptions_IncludedFields {
+ if m != nil {
+ return m.IncludedFields
+ }
+ return ExecuteOptions_TYPE_AND_NAME
+}
+
// Field describes a single column returned by a query
type Field struct {
// name of the field as returned by mysql C API
@@ -632,6 +730,76 @@ func (m *Field) String() string { return proto.CompactTextString(m) }
func (*Field) ProtoMessage() {}
func (*Field) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
+func (m *Field) GetName() string {
+ if m != nil {
+ return m.Name
+ }
+ return ""
+}
+
+func (m *Field) GetType() Type {
+ if m != nil {
+ return m.Type
+ }
+ return Type_NULL_TYPE
+}
+
+func (m *Field) GetTable() string {
+ if m != nil {
+ return m.Table
+ }
+ return ""
+}
+
+func (m *Field) GetOrgTable() string {
+ if m != nil {
+ return m.OrgTable
+ }
+ return ""
+}
+
+func (m *Field) GetDatabase() string {
+ if m != nil {
+ return m.Database
+ }
+ return ""
+}
+
+func (m *Field) GetOrgName() string {
+ if m != nil {
+ return m.OrgName
+ }
+ return ""
+}
+
+func (m *Field) GetColumnLength() uint32 {
+ if m != nil {
+ return m.ColumnLength
+ }
+ return 0
+}
+
+func (m *Field) GetCharset() uint32 {
+ if m != nil {
+ return m.Charset
+ }
+ return 0
+}
+
+func (m *Field) GetDecimals() uint32 {
+ if m != nil {
+ return m.Decimals
+ }
+ return 0
+}
+
+func (m *Field) GetFlags() uint32 {
+ if m != nil {
+ return m.Flags
+ }
+ return 0
+}
+
// Row is a database row.
type Row struct {
// lengths contains the length of each value in values.
@@ -648,6 +816,20 @@ func (m *Row) String() string { return proto.CompactTextString(m) }
func (*Row) ProtoMessage() {}
func (*Row) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
+func (m *Row) GetLengths() []int64 {
+ if m != nil {
+ return m.Lengths
+ }
+ return nil
+}
+
+func (m *Row) GetValues() []byte {
+ if m != nil {
+ return m.Values
+ }
+ return nil
+}
+
// ResultExtras contains optional out-of-band information. Usually the
// extras are requested by adding ExecuteOptions flags.
type ResultExtras struct {
@@ -671,6 +853,13 @@ func (m *ResultExtras) GetEventToken() *EventToken {
return nil
}
+func (m *ResultExtras) GetFresher() bool {
+ if m != nil {
+ return m.Fresher
+ }
+ return false
+}
+
// QueryResult is returned by Execute and ExecuteStream.
//
// As returned by Execute, len(fields) is always equal to len(row)
@@ -700,6 +889,20 @@ func (m *QueryResult) GetFields() []*Field {
return nil
}
+func (m *QueryResult) GetRowsAffected() uint64 {
+ if m != nil {
+ return m.RowsAffected
+ }
+ return 0
+}
+
+func (m *QueryResult) GetInsertId() uint64 {
+ if m != nil {
+ return m.InsertId
+ }
+ return 0
+}
+
func (m *QueryResult) GetRows() []*Row {
if m != nil {
return m.Rows
@@ -760,6 +963,20 @@ func (m *StreamEvent_Statement) String() string { return proto.Compac
func (*StreamEvent_Statement) ProtoMessage() {}
func (*StreamEvent_Statement) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11, 0} }
+func (m *StreamEvent_Statement) GetCategory() StreamEvent_Statement_Category {
+ if m != nil {
+ return m.Category
+ }
+ return StreamEvent_Statement_Error
+}
+
+func (m *StreamEvent_Statement) GetTableName() string {
+ if m != nil {
+ return m.TableName
+ }
+ return ""
+}
+
func (m *StreamEvent_Statement) GetPrimaryKeyFields() []*Field {
if m != nil {
return m.PrimaryKeyFields
@@ -774,6 +991,13 @@ func (m *StreamEvent_Statement) GetPrimaryKeyValues() []*Row {
return nil
}
+func (m *StreamEvent_Statement) GetSql() []byte {
+ if m != nil {
+ return m.Sql
+ }
+ return nil
+}
+
// ExecuteRequest is the payload to Execute
type ExecuteRequest struct {
EffectiveCallerId *vtrpc.CallerID `protobuf:"bytes,1,opt,name=effective_caller_id,json=effectiveCallerId" json:"effective_caller_id,omitempty"`
@@ -817,6 +1041,13 @@ func (m *ExecuteRequest) GetQuery() *BoundQuery {
return nil
}
+func (m *ExecuteRequest) GetTransactionId() int64 {
+ if m != nil {
+ return m.TransactionId
+ }
+ return 0
+}
+
func (m *ExecuteRequest) GetOptions() *ExecuteOptions {
if m != nil {
return m.Options
@@ -914,6 +1145,20 @@ func (m *ExecuteBatchRequest) GetQueries() []*BoundQuery {
return nil
}
+func (m *ExecuteBatchRequest) GetAsTransaction() bool {
+ if m != nil {
+ return m.AsTransaction
+ }
+ return false
+}
+
+func (m *ExecuteBatchRequest) GetTransactionId() int64 {
+ if m != nil {
+ return m.TransactionId
+ }
+ return 0
+}
+
func (m *ExecuteBatchRequest) GetOptions() *ExecuteOptions {
if m != nil {
return m.Options
@@ -1047,6 +1292,13 @@ func (m *BeginResponse) String() string { return proto.CompactTextStr
func (*BeginResponse) ProtoMessage() {}
func (*BeginResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} }
+func (m *BeginResponse) GetTransactionId() int64 {
+ if m != nil {
+ return m.TransactionId
+ }
+ return 0
+}
+
// CommitRequest is the payload to Commit
type CommitRequest struct {
EffectiveCallerId *vtrpc.CallerID `protobuf:"bytes,1,opt,name=effective_caller_id,json=effectiveCallerId" json:"effective_caller_id,omitempty"`
@@ -1081,6 +1333,13 @@ func (m *CommitRequest) GetTarget() *Target {
return nil
}
+func (m *CommitRequest) GetTransactionId() int64 {
+ if m != nil {
+ return m.TransactionId
+ }
+ return 0
+}
+
// CommitResponse is the returned value from Commit
type CommitResponse struct {
}
@@ -1124,6 +1383,13 @@ func (m *RollbackRequest) GetTarget() *Target {
return nil
}
+func (m *RollbackRequest) GetTransactionId() int64 {
+ if m != nil {
+ return m.TransactionId
+ }
+ return 0
+}
+
// RollbackResponse is the returned value from Rollback
type RollbackResponse struct {
}
@@ -1168,6 +1434,20 @@ func (m *PrepareRequest) GetTarget() *Target {
return nil
}
+func (m *PrepareRequest) GetTransactionId() int64 {
+ if m != nil {
+ return m.TransactionId
+ }
+ return 0
+}
+
+func (m *PrepareRequest) GetDtid() string {
+ if m != nil {
+ return m.Dtid
+ }
+ return ""
+}
+
// PrepareResponse is the returned value from Prepare
type PrepareResponse struct {
}
@@ -1211,6 +1491,13 @@ func (m *CommitPreparedRequest) GetTarget() *Target {
return nil
}
+func (m *CommitPreparedRequest) GetDtid() string {
+ if m != nil {
+ return m.Dtid
+ }
+ return ""
+}
+
// CommitPreparedResponse is the returned value from CommitPrepared
type CommitPreparedResponse struct {
}
@@ -1255,6 +1542,20 @@ func (m *RollbackPreparedRequest) GetTarget() *Target {
return nil
}
+func (m *RollbackPreparedRequest) GetTransactionId() int64 {
+ if m != nil {
+ return m.TransactionId
+ }
+ return 0
+}
+
+func (m *RollbackPreparedRequest) GetDtid() string {
+ if m != nil {
+ return m.Dtid
+ }
+ return ""
+}
+
// RollbackPreparedResponse is the returned value from RollbackPrepared
type RollbackPreparedResponse struct {
}
@@ -1299,6 +1600,13 @@ func (m *CreateTransactionRequest) GetTarget() *Target {
return nil
}
+func (m *CreateTransactionRequest) GetDtid() string {
+ if m != nil {
+ return m.Dtid
+ }
+ return ""
+}
+
func (m *CreateTransactionRequest) GetParticipants() []*Target {
if m != nil {
return m.Participants
@@ -1350,6 +1658,20 @@ func (m *StartCommitRequest) GetTarget() *Target {
return nil
}
+func (m *StartCommitRequest) GetTransactionId() int64 {
+ if m != nil {
+ return m.TransactionId
+ }
+ return 0
+}
+
+func (m *StartCommitRequest) GetDtid() string {
+ if m != nil {
+ return m.Dtid
+ }
+ return ""
+}
+
// StartCommitResponse is the returned value from StartCommit
type StartCommitResponse struct {
}
@@ -1394,6 +1716,20 @@ func (m *SetRollbackRequest) GetTarget() *Target {
return nil
}
+func (m *SetRollbackRequest) GetTransactionId() int64 {
+ if m != nil {
+ return m.TransactionId
+ }
+ return 0
+}
+
+func (m *SetRollbackRequest) GetDtid() string {
+ if m != nil {
+ return m.Dtid
+ }
+ return ""
+}
+
// SetRollbackResponse is the returned value from SetRollback
type SetRollbackResponse struct {
}
@@ -1437,6 +1773,13 @@ func (m *ConcludeTransactionRequest) GetTarget() *Target {
return nil
}
+func (m *ConcludeTransactionRequest) GetDtid() string {
+ if m != nil {
+ return m.Dtid
+ }
+ return ""
+}
+
// ConcludeTransactionResponse is the returned value from ConcludeTransaction
type ConcludeTransactionResponse struct {
}
@@ -1480,6 +1823,13 @@ func (m *ReadTransactionRequest) GetTarget() *Target {
return nil
}
+func (m *ReadTransactionRequest) GetDtid() string {
+ if m != nil {
+ return m.Dtid
+ }
+ return ""
+}
+
// ReadTransactionResponse is the returned value from ReadTransaction
type ReadTransactionResponse struct {
Metadata *TransactionMetadata `protobuf:"bytes,1,opt,name=metadata" json:"metadata,omitempty"`
@@ -1576,6 +1926,13 @@ func (m *BeginExecuteResponse) GetResult() *QueryResult {
return nil
}
+func (m *BeginExecuteResponse) GetTransactionId() int64 {
+ if m != nil {
+ return m.TransactionId
+ }
+ return 0
+}
+
// BeginExecuteBatchRequest is the payload to BeginExecuteBatch
type BeginExecuteBatchRequest struct {
EffectiveCallerId *vtrpc.CallerID `protobuf:"bytes,1,opt,name=effective_caller_id,json=effectiveCallerId" json:"effective_caller_id,omitempty"`
@@ -1619,6 +1976,13 @@ func (m *BeginExecuteBatchRequest) GetQueries() []*BoundQuery {
return nil
}
+func (m *BeginExecuteBatchRequest) GetAsTransaction() bool {
+ if m != nil {
+ return m.AsTransaction
+ }
+ return false
+}
+
func (m *BeginExecuteBatchRequest) GetOptions() *ExecuteOptions {
if m != nil {
return m.Options
@@ -1656,6 +2020,13 @@ func (m *BeginExecuteBatchResponse) GetResults() []*QueryResult {
return nil
}
+func (m *BeginExecuteBatchResponse) GetTransactionId() int64 {
+ if m != nil {
+ return m.TransactionId
+ }
+ return 0
+}
+
// MessageStreamRequest is the request payload for MessageStream.
type MessageStreamRequest struct {
EffectiveCallerId *vtrpc.CallerID `protobuf:"bytes,1,opt,name=effective_caller_id,json=effectiveCallerId" json:"effective_caller_id,omitempty"`
@@ -1691,6 +2062,13 @@ func (m *MessageStreamRequest) GetTarget() *Target {
return nil
}
+func (m *MessageStreamRequest) GetName() string {
+ if m != nil {
+ return m.Name
+ }
+ return ""
+}
+
// MessageStreamResponse is a response for MessageStream.
type MessageStreamResponse struct {
Result *QueryResult `protobuf:"bytes,1,opt,name=result" json:"result,omitempty"`
@@ -1744,6 +2122,13 @@ func (m *MessageAckRequest) GetTarget() *Target {
return nil
}
+func (m *MessageAckRequest) GetName() string {
+ if m != nil {
+ return m.Name
+ }
+ return ""
+}
+
func (m *MessageAckRequest) GetIds() []*Value {
if m != nil {
return m.Ids
@@ -1818,6 +2203,34 @@ func (m *SplitQueryRequest) GetQuery() *BoundQuery {
return nil
}
+func (m *SplitQueryRequest) GetSplitColumn() []string {
+ if m != nil {
+ return m.SplitColumn
+ }
+ return nil
+}
+
+func (m *SplitQueryRequest) GetSplitCount() int64 {
+ if m != nil {
+ return m.SplitCount
+ }
+ return 0
+}
+
+func (m *SplitQueryRequest) GetNumRowsPerQueryPart() int64 {
+ if m != nil {
+ return m.NumRowsPerQueryPart
+ }
+ return 0
+}
+
+func (m *SplitQueryRequest) GetAlgorithm() SplitQueryRequest_Algorithm {
+ if m != nil {
+ return m.Algorithm
+ }
+ return SplitQueryRequest_EQUAL_SPLITS
+}
+
// QuerySplit represents one query to execute on the tablet
type QuerySplit struct {
// query is the query to execute
@@ -1838,6 +2251,13 @@ func (m *QuerySplit) GetQuery() *BoundQuery {
return nil
}
+func (m *QuerySplit) GetRowCount() int64 {
+ if m != nil {
+ return m.RowCount
+ }
+ return 0
+}
+
// SplitQueryResponse is returned by SplitQuery and represents all the queries
// to execute in order to get the entire data set.
type SplitQueryResponse struct {
@@ -1901,6 +2321,48 @@ func (m *RealtimeStats) String() string { return proto.CompactTextStr
func (*RealtimeStats) ProtoMessage() {}
func (*RealtimeStats) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{53} }
+func (m *RealtimeStats) GetHealthError() string {
+ if m != nil {
+ return m.HealthError
+ }
+ return ""
+}
+
+func (m *RealtimeStats) GetSecondsBehindMaster() uint32 {
+ if m != nil {
+ return m.SecondsBehindMaster
+ }
+ return 0
+}
+
+func (m *RealtimeStats) GetBinlogPlayersCount() int32 {
+ if m != nil {
+ return m.BinlogPlayersCount
+ }
+ return 0
+}
+
+func (m *RealtimeStats) GetSecondsBehindMasterFilteredReplication() int64 {
+ if m != nil {
+ return m.SecondsBehindMasterFilteredReplication
+ }
+ return 0
+}
+
+func (m *RealtimeStats) GetCpuUsage() float64 {
+ if m != nil {
+ return m.CpuUsage
+ }
+ return 0
+}
+
+func (m *RealtimeStats) GetQps() float64 {
+ if m != nil {
+ return m.Qps
+ }
+ return 0
+}
+
// StreamHealthResponse is streamed by StreamHealth on a regular basis
type StreamHealthResponse struct {
// target is the current server type. Only queries with that exact Target
@@ -1932,6 +2394,20 @@ func (m *StreamHealthResponse) GetTarget() *Target {
return nil
}
+func (m *StreamHealthResponse) GetServing() bool {
+ if m != nil {
+ return m.Serving
+ }
+ return false
+}
+
+func (m *StreamHealthResponse) GetTabletExternallyReparentedTimestamp() int64 {
+ if m != nil {
+ return m.TabletExternallyReparentedTimestamp
+ }
+ return 0
+}
+
func (m *StreamHealthResponse) GetRealtimeStats() *RealtimeStats {
if m != nil {
return m.RealtimeStats
@@ -1980,6 +2456,20 @@ func (m *UpdateStreamRequest) GetTarget() *Target {
return nil
}
+func (m *UpdateStreamRequest) GetPosition() string {
+ if m != nil {
+ return m.Position
+ }
+ return ""
+}
+
+func (m *UpdateStreamRequest) GetTimestamp() int64 {
+ if m != nil {
+ return m.Timestamp
+ }
+ return 0
+}
+
// UpdateStreamResponse is returned by UpdateStream
type UpdateStreamResponse struct {
Event *StreamEvent `protobuf:"bytes,1,opt,name=event" json:"event,omitempty"`
@@ -2010,6 +2500,27 @@ func (m *TransactionMetadata) String() string { return proto.CompactT
func (*TransactionMetadata) ProtoMessage() {}
func (*TransactionMetadata) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{57} }
+func (m *TransactionMetadata) GetDtid() string {
+ if m != nil {
+ return m.Dtid
+ }
+ return ""
+}
+
+func (m *TransactionMetadata) GetState() TransactionState {
+ if m != nil {
+ return m.State
+ }
+ return TransactionState_UNKNOWN
+}
+
+func (m *TransactionMetadata) GetTimeCreated() int64 {
+ if m != nil {
+ return m.TimeCreated
+ }
+ return 0
+}
+
func (m *TransactionMetadata) GetParticipants() []*Target {
if m != nil {
return m.Participants
diff --git a/go/vt/proto/replicationdata/replicationdata.pb.go b/go/vt/proto/replicationdata/replicationdata.pb.go
index 6733adef566..667bd10090c 100644
--- a/go/vt/proto/replicationdata/replicationdata.pb.go
+++ b/go/vt/proto/replicationdata/replicationdata.pb.go
@@ -45,6 +45,55 @@ func (m *Status) String() string { return proto.CompactTextString(m)
func (*Status) ProtoMessage() {}
func (*Status) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (m *Status) GetPosition() string {
+ if m != nil {
+ return m.Position
+ }
+ return ""
+}
+
+func (m *Status) GetSlaveIoRunning() bool {
+ if m != nil {
+ return m.SlaveIoRunning
+ }
+ return false
+}
+
+func (m *Status) GetSlaveSqlRunning() bool {
+ if m != nil {
+ return m.SlaveSqlRunning
+ }
+ return false
+}
+
+func (m *Status) GetSecondsBehindMaster() uint32 {
+ if m != nil {
+ return m.SecondsBehindMaster
+ }
+ return 0
+}
+
+func (m *Status) GetMasterHost() string {
+ if m != nil {
+ return m.MasterHost
+ }
+ return ""
+}
+
+func (m *Status) GetMasterPort() int32 {
+ if m != nil {
+ return m.MasterPort
+ }
+ return 0
+}
+
+func (m *Status) GetMasterConnectRetry() int32 {
+ if m != nil {
+ return m.MasterConnectRetry
+ }
+ return 0
+}
+
func init() {
proto.RegisterType((*Status)(nil), "replicationdata.Status")
}
diff --git a/go/vt/proto/tableacl/tableacl.pb.go b/go/vt/proto/tableacl/tableacl.pb.go
index 5840705e279..2baa4ecbf7c 100644
--- a/go/vt/proto/tableacl/tableacl.pb.go
+++ b/go/vt/proto/tableacl/tableacl.pb.go
@@ -44,6 +44,41 @@ func (m *TableGroupSpec) String() string { return proto.CompactTextSt
func (*TableGroupSpec) ProtoMessage() {}
func (*TableGroupSpec) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (m *TableGroupSpec) GetName() string {
+ if m != nil {
+ return m.Name
+ }
+ return ""
+}
+
+func (m *TableGroupSpec) GetTableNamesOrPrefixes() []string {
+ if m != nil {
+ return m.TableNamesOrPrefixes
+ }
+ return nil
+}
+
+func (m *TableGroupSpec) GetReaders() []string {
+ if m != nil {
+ return m.Readers
+ }
+ return nil
+}
+
+func (m *TableGroupSpec) GetWriters() []string {
+ if m != nil {
+ return m.Writers
+ }
+ return nil
+}
+
+func (m *TableGroupSpec) GetAdmins() []string {
+ if m != nil {
+ return m.Admins
+ }
+ return nil
+}
+
type Config struct {
TableGroups []*TableGroupSpec `protobuf:"bytes,1,rep,name=table_groups,json=tableGroups" json:"table_groups,omitempty"`
}
diff --git a/go/vt/proto/tabletmanagerdata/tabletmanagerdata.pb.go b/go/vt/proto/tabletmanagerdata/tabletmanagerdata.pb.go
index b0faa63f945..280956aa4cd 100644
--- a/go/vt/proto/tabletmanagerdata/tabletmanagerdata.pb.go
+++ b/go/vt/proto/tabletmanagerdata/tabletmanagerdata.pb.go
@@ -144,6 +144,55 @@ func (m *TableDefinition) String() string { return proto.CompactTextS
func (*TableDefinition) ProtoMessage() {}
func (*TableDefinition) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (m *TableDefinition) GetName() string {
+ if m != nil {
+ return m.Name
+ }
+ return ""
+}
+
+func (m *TableDefinition) GetSchema() string {
+ if m != nil {
+ return m.Schema
+ }
+ return ""
+}
+
+func (m *TableDefinition) GetColumns() []string {
+ if m != nil {
+ return m.Columns
+ }
+ return nil
+}
+
+func (m *TableDefinition) GetPrimaryKeyColumns() []string {
+ if m != nil {
+ return m.PrimaryKeyColumns
+ }
+ return nil
+}
+
+func (m *TableDefinition) GetType() string {
+ if m != nil {
+ return m.Type
+ }
+ return ""
+}
+
+func (m *TableDefinition) GetDataLength() uint64 {
+ if m != nil {
+ return m.DataLength
+ }
+ return 0
+}
+
+func (m *TableDefinition) GetRowCount() uint64 {
+ if m != nil {
+ return m.RowCount
+ }
+ return 0
+}
+
type SchemaDefinition struct {
DatabaseSchema string `protobuf:"bytes,1,opt,name=database_schema,json=databaseSchema" json:"database_schema,omitempty"`
TableDefinitions []*TableDefinition `protobuf:"bytes,2,rep,name=table_definitions,json=tableDefinitions" json:"table_definitions,omitempty"`
@@ -155,6 +204,13 @@ func (m *SchemaDefinition) String() string { return proto.CompactText
func (*SchemaDefinition) ProtoMessage() {}
func (*SchemaDefinition) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+func (m *SchemaDefinition) GetDatabaseSchema() string {
+ if m != nil {
+ return m.DatabaseSchema
+ }
+ return ""
+}
+
func (m *SchemaDefinition) GetTableDefinitions() []*TableDefinition {
if m != nil {
return m.TableDefinitions
@@ -162,6 +218,13 @@ func (m *SchemaDefinition) GetTableDefinitions() []*TableDefinition {
return nil
}
+func (m *SchemaDefinition) GetVersion() string {
+ if m != nil {
+ return m.Version
+ }
+ return ""
+}
+
type SchemaChangeResult struct {
// before_schema holds the schema before each change.
BeforeSchema *SchemaDefinition `protobuf:"bytes,1,opt,name=before_schema,json=beforeSchema" json:"before_schema,omitempty"`
@@ -203,6 +266,27 @@ func (m *UserPermission) String() string { return proto.CompactTextSt
func (*UserPermission) ProtoMessage() {}
func (*UserPermission) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
+func (m *UserPermission) GetHost() string {
+ if m != nil {
+ return m.Host
+ }
+ return ""
+}
+
+func (m *UserPermission) GetUser() string {
+ if m != nil {
+ return m.User
+ }
+ return ""
+}
+
+func (m *UserPermission) GetPasswordChecksum() uint64 {
+ if m != nil {
+ return m.PasswordChecksum
+ }
+ return 0
+}
+
func (m *UserPermission) GetPrivileges() map[string]string {
if m != nil {
return m.Privileges
@@ -224,6 +308,27 @@ func (m *DbPermission) String() string { return proto.CompactTextStri
func (*DbPermission) ProtoMessage() {}
func (*DbPermission) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
+func (m *DbPermission) GetHost() string {
+ if m != nil {
+ return m.Host
+ }
+ return ""
+}
+
+func (m *DbPermission) GetDb() string {
+ if m != nil {
+ return m.Db
+ }
+ return ""
+}
+
+func (m *DbPermission) GetUser() string {
+ if m != nil {
+ return m.User
+ }
+ return ""
+}
+
func (m *DbPermission) GetPrivileges() map[string]string {
if m != nil {
return m.Privileges
@@ -268,6 +373,20 @@ func (m *BlpPosition) String() string { return proto.CompactTextStrin
func (*BlpPosition) ProtoMessage() {}
func (*BlpPosition) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
+func (m *BlpPosition) GetUid() uint32 {
+ if m != nil {
+ return m.Uid
+ }
+ return 0
+}
+
+func (m *BlpPosition) GetPosition() string {
+ if m != nil {
+ return m.Position
+ }
+ return ""
+}
+
type PingRequest struct {
Payload string `protobuf:"bytes,1,opt,name=payload" json:"payload,omitempty"`
}
@@ -277,6 +396,13 @@ func (m *PingRequest) String() string { return proto.CompactTextStrin
func (*PingRequest) ProtoMessage() {}
func (*PingRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
+func (m *PingRequest) GetPayload() string {
+ if m != nil {
+ return m.Payload
+ }
+ return ""
+}
+
type PingResponse struct {
Payload string `protobuf:"bytes,1,opt,name=payload" json:"payload,omitempty"`
}
@@ -286,6 +412,13 @@ func (m *PingResponse) String() string { return proto.CompactTextStri
func (*PingResponse) ProtoMessage() {}
func (*PingResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
+func (m *PingResponse) GetPayload() string {
+ if m != nil {
+ return m.Payload
+ }
+ return ""
+}
+
type SleepRequest struct {
// duration is in nanoseconds
Duration int64 `protobuf:"varint,1,opt,name=duration" json:"duration,omitempty"`
@@ -296,6 +429,13 @@ func (m *SleepRequest) String() string { return proto.CompactTextStri
func (*SleepRequest) ProtoMessage() {}
func (*SleepRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} }
+func (m *SleepRequest) GetDuration() int64 {
+ if m != nil {
+ return m.Duration
+ }
+ return 0
+}
+
type SleepResponse struct {
}
@@ -315,6 +455,20 @@ func (m *ExecuteHookRequest) String() string { return proto.CompactTe
func (*ExecuteHookRequest) ProtoMessage() {}
func (*ExecuteHookRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
+func (m *ExecuteHookRequest) GetName() string {
+ if m != nil {
+ return m.Name
+ }
+ return ""
+}
+
+func (m *ExecuteHookRequest) GetParameters() []string {
+ if m != nil {
+ return m.Parameters
+ }
+ return nil
+}
+
func (m *ExecuteHookRequest) GetExtraEnv() map[string]string {
if m != nil {
return m.ExtraEnv
@@ -333,6 +487,27 @@ func (m *ExecuteHookResponse) String() string { return proto.CompactT
func (*ExecuteHookResponse) ProtoMessage() {}
func (*ExecuteHookResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} }
+func (m *ExecuteHookResponse) GetExitStatus() int64 {
+ if m != nil {
+ return m.ExitStatus
+ }
+ return 0
+}
+
+func (m *ExecuteHookResponse) GetStdout() string {
+ if m != nil {
+ return m.Stdout
+ }
+ return ""
+}
+
+func (m *ExecuteHookResponse) GetStderr() string {
+ if m != nil {
+ return m.Stderr
+ }
+ return ""
+}
+
type GetSchemaRequest struct {
Tables []string `protobuf:"bytes,1,rep,name=tables" json:"tables,omitempty"`
IncludeViews bool `protobuf:"varint,2,opt,name=include_views,json=includeViews" json:"include_views,omitempty"`
@@ -344,6 +519,27 @@ func (m *GetSchemaRequest) String() string { return proto.CompactText
func (*GetSchemaRequest) ProtoMessage() {}
func (*GetSchemaRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} }
+func (m *GetSchemaRequest) GetTables() []string {
+ if m != nil {
+ return m.Tables
+ }
+ return nil
+}
+
+func (m *GetSchemaRequest) GetIncludeViews() bool {
+ if m != nil {
+ return m.IncludeViews
+ }
+ return false
+}
+
+func (m *GetSchemaRequest) GetExcludeTables() []string {
+ if m != nil {
+ return m.ExcludeTables
+ }
+ return nil
+}
+
type GetSchemaResponse struct {
SchemaDefinition *SchemaDefinition `protobuf:"bytes,1,opt,name=schema_definition,json=schemaDefinition" json:"schema_definition,omitempty"`
}
@@ -425,6 +621,13 @@ func (m *ChangeTypeRequest) String() string { return proto.CompactTex
func (*ChangeTypeRequest) ProtoMessage() {}
func (*ChangeTypeRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} }
+func (m *ChangeTypeRequest) GetTabletType() topodata.TabletType {
+ if m != nil {
+ return m.TabletType
+ }
+ return topodata.TabletType_UNKNOWN
+}
+
type ChangeTypeResponse struct {
}
@@ -474,6 +677,13 @@ func (m *IgnoreHealthErrorRequest) String() string { return proto.Com
func (*IgnoreHealthErrorRequest) ProtoMessage() {}
func (*IgnoreHealthErrorRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{27} }
+func (m *IgnoreHealthErrorRequest) GetPattern() string {
+ if m != nil {
+ return m.Pattern
+ }
+ return ""
+}
+
type IgnoreHealthErrorResponse struct {
}
@@ -494,6 +704,13 @@ func (m *ReloadSchemaRequest) String() string { return proto.CompactT
func (*ReloadSchemaRequest) ProtoMessage() {}
func (*ReloadSchemaRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{29} }
+func (m *ReloadSchemaRequest) GetWaitPosition() string {
+ if m != nil {
+ return m.WaitPosition
+ }
+ return ""
+}
+
type ReloadSchemaResponse struct {
}
@@ -511,6 +728,13 @@ func (m *PreflightSchemaRequest) String() string { return proto.Compa
func (*PreflightSchemaRequest) ProtoMessage() {}
func (*PreflightSchemaRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{31} }
+func (m *PreflightSchemaRequest) GetChanges() []string {
+ if m != nil {
+ return m.Changes
+ }
+ return nil
+}
+
type PreflightSchemaResponse struct {
// change_results has for each change the schema before and after it.
// The number of elements is identical to the length of "changes" in the request.
@@ -542,6 +766,27 @@ func (m *ApplySchemaRequest) String() string { return proto.CompactTe
func (*ApplySchemaRequest) ProtoMessage() {}
func (*ApplySchemaRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{33} }
+func (m *ApplySchemaRequest) GetSql() string {
+ if m != nil {
+ return m.Sql
+ }
+ return ""
+}
+
+func (m *ApplySchemaRequest) GetForce() bool {
+ if m != nil {
+ return m.Force
+ }
+ return false
+}
+
+func (m *ApplySchemaRequest) GetAllowReplication() bool {
+ if m != nil {
+ return m.AllowReplication
+ }
+ return false
+}
+
func (m *ApplySchemaRequest) GetBeforeSchema() *SchemaDefinition {
if m != nil {
return m.BeforeSchema
@@ -593,6 +838,41 @@ func (m *ExecuteFetchAsDbaRequest) String() string { return proto.Com
func (*ExecuteFetchAsDbaRequest) ProtoMessage() {}
func (*ExecuteFetchAsDbaRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{35} }
+func (m *ExecuteFetchAsDbaRequest) GetQuery() []byte {
+ if m != nil {
+ return m.Query
+ }
+ return nil
+}
+
+func (m *ExecuteFetchAsDbaRequest) GetDbName() string {
+ if m != nil {
+ return m.DbName
+ }
+ return ""
+}
+
+func (m *ExecuteFetchAsDbaRequest) GetMaxRows() uint64 {
+ if m != nil {
+ return m.MaxRows
+ }
+ return 0
+}
+
+func (m *ExecuteFetchAsDbaRequest) GetDisableBinlogs() bool {
+ if m != nil {
+ return m.DisableBinlogs
+ }
+ return false
+}
+
+func (m *ExecuteFetchAsDbaRequest) GetReloadSchema() bool {
+ if m != nil {
+ return m.ReloadSchema
+ }
+ return false
+}
+
type ExecuteFetchAsDbaResponse struct {
Result *query.QueryResult `protobuf:"bytes,1,opt,name=result" json:"result,omitempty"`
}
@@ -621,6 +901,34 @@ func (m *ExecuteFetchAsAllPrivsRequest) String() string { return prot
func (*ExecuteFetchAsAllPrivsRequest) ProtoMessage() {}
func (*ExecuteFetchAsAllPrivsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{37} }
+func (m *ExecuteFetchAsAllPrivsRequest) GetQuery() []byte {
+ if m != nil {
+ return m.Query
+ }
+ return nil
+}
+
+func (m *ExecuteFetchAsAllPrivsRequest) GetDbName() string {
+ if m != nil {
+ return m.DbName
+ }
+ return ""
+}
+
+func (m *ExecuteFetchAsAllPrivsRequest) GetMaxRows() uint64 {
+ if m != nil {
+ return m.MaxRows
+ }
+ return 0
+}
+
+func (m *ExecuteFetchAsAllPrivsRequest) GetReloadSchema() bool {
+ if m != nil {
+ return m.ReloadSchema
+ }
+ return false
+}
+
type ExecuteFetchAsAllPrivsResponse struct {
Result *query.QueryResult `protobuf:"bytes,1,opt,name=result" json:"result,omitempty"`
}
@@ -647,6 +955,20 @@ func (m *ExecuteFetchAsAppRequest) String() string { return proto.Com
func (*ExecuteFetchAsAppRequest) ProtoMessage() {}
func (*ExecuteFetchAsAppRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{39} }
+func (m *ExecuteFetchAsAppRequest) GetQuery() []byte {
+ if m != nil {
+ return m.Query
+ }
+ return nil
+}
+
+func (m *ExecuteFetchAsAppRequest) GetMaxRows() uint64 {
+ if m != nil {
+ return m.MaxRows
+ }
+ return 0
+}
+
type ExecuteFetchAsAppResponse struct {
Result *query.QueryResult `protobuf:"bytes,1,opt,name=result" json:"result,omitempty"`
}
@@ -704,6 +1026,13 @@ func (m *MasterPositionResponse) String() string { return proto.Compa
func (*MasterPositionResponse) ProtoMessage() {}
func (*MasterPositionResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{44} }
+func (m *MasterPositionResponse) GetPosition() string {
+ if m != nil {
+ return m.Position
+ }
+ return ""
+}
+
type StopSlaveRequest struct {
}
@@ -730,6 +1059,20 @@ func (m *StopSlaveMinimumRequest) String() string { return proto.Comp
func (*StopSlaveMinimumRequest) ProtoMessage() {}
func (*StopSlaveMinimumRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{47} }
+func (m *StopSlaveMinimumRequest) GetPosition() string {
+ if m != nil {
+ return m.Position
+ }
+ return ""
+}
+
+func (m *StopSlaveMinimumRequest) GetWaitTimeout() int64 {
+ if m != nil {
+ return m.WaitTimeout
+ }
+ return 0
+}
+
type StopSlaveMinimumResponse struct {
Position string `protobuf:"bytes,1,opt,name=position" json:"position,omitempty"`
}
@@ -739,6 +1082,13 @@ func (m *StopSlaveMinimumResponse) String() string { return proto.Com
func (*StopSlaveMinimumResponse) ProtoMessage() {}
func (*StopSlaveMinimumResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{48} }
+func (m *StopSlaveMinimumResponse) GetPosition() string {
+ if m != nil {
+ return m.Position
+ }
+ return ""
+}
+
type StartSlaveRequest struct {
}
@@ -769,6 +1119,13 @@ func (*TabletExternallyReparentedRequest) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{51}
}
+func (m *TabletExternallyReparentedRequest) GetExternalId() string {
+ if m != nil {
+ return m.ExternalId
+ }
+ return ""
+}
+
type TabletExternallyReparentedResponse struct {
}
@@ -814,6 +1171,13 @@ func (m *GetSlavesResponse) String() string { return proto.CompactTex
func (*GetSlavesResponse) ProtoMessage() {}
func (*GetSlavesResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{56} }
+func (m *GetSlavesResponse) GetAddrs() []string {
+ if m != nil {
+ return m.Addrs
+ }
+ return nil
+}
+
type WaitBlpPositionRequest struct {
BlpPosition *BlpPosition `protobuf:"bytes,1,opt,name=blp_position,json=blpPosition" json:"blp_position,omitempty"`
WaitTimeout int64 `protobuf:"varint,2,opt,name=wait_timeout,json=waitTimeout" json:"wait_timeout,omitempty"`
@@ -831,6 +1195,13 @@ func (m *WaitBlpPositionRequest) GetBlpPosition() *BlpPosition {
return nil
}
+func (m *WaitBlpPositionRequest) GetWaitTimeout() int64 {
+ if m != nil {
+ return m.WaitTimeout
+ }
+ return 0
+}
+
type WaitBlpPositionResponse struct {
}
@@ -896,6 +1267,13 @@ func (m *RunBlpUntilRequest) GetBlpPositions() []*BlpPosition {
return nil
}
+func (m *RunBlpUntilRequest) GetWaitTimeout() int64 {
+ if m != nil {
+ return m.WaitTimeout
+ }
+ return 0
+}
+
type RunBlpUntilResponse struct {
Position string `protobuf:"bytes,1,opt,name=position" json:"position,omitempty"`
}
@@ -905,6 +1283,13 @@ func (m *RunBlpUntilResponse) String() string { return proto.CompactT
func (*RunBlpUntilResponse) ProtoMessage() {}
func (*RunBlpUntilResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{64} }
+func (m *RunBlpUntilResponse) GetPosition() string {
+ if m != nil {
+ return m.Position
+ }
+ return ""
+}
+
type ResetReplicationRequest struct {
}
@@ -938,6 +1323,13 @@ func (m *InitMasterResponse) String() string { return proto.CompactTe
func (*InitMasterResponse) ProtoMessage() {}
func (*InitMasterResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{68} }
+func (m *InitMasterResponse) GetPosition() string {
+ if m != nil {
+ return m.Position
+ }
+ return ""
+}
+
type PopulateReparentJournalRequest struct {
TimeCreatedNs int64 `protobuf:"varint,1,opt,name=time_created_ns,json=timeCreatedNs" json:"time_created_ns,omitempty"`
ActionName string `protobuf:"bytes,2,opt,name=action_name,json=actionName" json:"action_name,omitempty"`
@@ -950,6 +1342,20 @@ func (m *PopulateReparentJournalRequest) String() string { return pro
func (*PopulateReparentJournalRequest) ProtoMessage() {}
func (*PopulateReparentJournalRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{69} }
+func (m *PopulateReparentJournalRequest) GetTimeCreatedNs() int64 {
+ if m != nil {
+ return m.TimeCreatedNs
+ }
+ return 0
+}
+
+func (m *PopulateReparentJournalRequest) GetActionName() string {
+ if m != nil {
+ return m.ActionName
+ }
+ return ""
+}
+
func (m *PopulateReparentJournalRequest) GetMasterAlias() *topodata.TabletAlias {
if m != nil {
return m.MasterAlias
@@ -957,6 +1363,13 @@ func (m *PopulateReparentJournalRequest) GetMasterAlias() *topodata.TabletAlias
return nil
}
+func (m *PopulateReparentJournalRequest) GetReplicationPosition() string {
+ if m != nil {
+ return m.ReplicationPosition
+ }
+ return ""
+}
+
type PopulateReparentJournalResponse struct {
}
@@ -985,6 +1398,20 @@ func (m *InitSlaveRequest) GetParent() *topodata.TabletAlias {
return nil
}
+func (m *InitSlaveRequest) GetReplicationPosition() string {
+ if m != nil {
+ return m.ReplicationPosition
+ }
+ return ""
+}
+
+func (m *InitSlaveRequest) GetTimeCreatedNs() int64 {
+ if m != nil {
+ return m.TimeCreatedNs
+ }
+ return 0
+}
+
type InitSlaveResponse struct {
}
@@ -1010,6 +1437,13 @@ func (m *DemoteMasterResponse) String() string { return proto.Compact
func (*DemoteMasterResponse) ProtoMessage() {}
func (*DemoteMasterResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{74} }
+func (m *DemoteMasterResponse) GetPosition() string {
+ if m != nil {
+ return m.Position
+ }
+ return ""
+}
+
type PromoteSlaveWhenCaughtUpRequest struct {
Position string `protobuf:"bytes,1,opt,name=position" json:"position,omitempty"`
}
@@ -1021,6 +1455,13 @@ func (*PromoteSlaveWhenCaughtUpRequest) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{75}
}
+func (m *PromoteSlaveWhenCaughtUpRequest) GetPosition() string {
+ if m != nil {
+ return m.Position
+ }
+ return ""
+}
+
type PromoteSlaveWhenCaughtUpResponse struct {
Position string `protobuf:"bytes,1,opt,name=position" json:"position,omitempty"`
}
@@ -1032,6 +1473,13 @@ func (*PromoteSlaveWhenCaughtUpResponse) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{76}
}
+func (m *PromoteSlaveWhenCaughtUpResponse) GetPosition() string {
+ if m != nil {
+ return m.Position
+ }
+ return ""
+}
+
type SlaveWasPromotedRequest struct {
}
@@ -1066,6 +1514,20 @@ func (m *SetMasterRequest) GetParent() *topodata.TabletAlias {
return nil
}
+func (m *SetMasterRequest) GetTimeCreatedNs() int64 {
+ if m != nil {
+ return m.TimeCreatedNs
+ }
+ return 0
+}
+
+func (m *SetMasterRequest) GetForceStartSlave() bool {
+ if m != nil {
+ return m.ForceStartSlave
+ }
+ return false
+}
+
type SetMasterResponse struct {
}
@@ -1144,6 +1606,13 @@ func (m *PromoteSlaveResponse) String() string { return proto.Compact
func (*PromoteSlaveResponse) ProtoMessage() {}
func (*PromoteSlaveResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{86} }
+func (m *PromoteSlaveResponse) GetPosition() string {
+ if m != nil {
+ return m.Position
+ }
+ return ""
+}
+
type BackupRequest struct {
Concurrency int64 `protobuf:"varint,1,opt,name=concurrency" json:"concurrency,omitempty"`
}
@@ -1153,6 +1622,13 @@ func (m *BackupRequest) String() string { return proto.CompactTextStr
func (*BackupRequest) ProtoMessage() {}
func (*BackupRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{87} }
+func (m *BackupRequest) GetConcurrency() int64 {
+ if m != nil {
+ return m.Concurrency
+ }
+ return 0
+}
+
type BackupResponse struct {
Event *logutil.Event `protobuf:"bytes,1,opt,name=event" json:"event,omitempty"`
}
diff --git a/go/vt/proto/throttlerdata/throttlerdata.pb.go b/go/vt/proto/throttlerdata/throttlerdata.pb.go
index c0e9a9fbf43..3e7c25463c1 100644
--- a/go/vt/proto/throttlerdata/throttlerdata.pb.go
+++ b/go/vt/proto/throttlerdata/throttlerdata.pb.go
@@ -76,6 +76,13 @@ func (m *SetMaxRateRequest) String() string { return proto.CompactTex
func (*SetMaxRateRequest) ProtoMessage() {}
func (*SetMaxRateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+func (m *SetMaxRateRequest) GetRate() int64 {
+ if m != nil {
+ return m.Rate
+ }
+ return 0
+}
+
// SetMaxRateResponse is returned by the SetMaxRate RPC.
type SetMaxRateResponse struct {
// names is the list of throttler names which were updated.
@@ -87,6 +94,13 @@ func (m *SetMaxRateResponse) String() string { return proto.CompactTe
func (*SetMaxRateResponse) ProtoMessage() {}
func (*SetMaxRateResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
+func (m *SetMaxRateResponse) GetNames() []string {
+ if m != nil {
+ return m.Names
+ }
+ return nil
+}
+
// Configuration holds the configuration parameters for the
// MaxReplicationLagModule which adaptively adjusts the throttling rate based on
// the observed replication lag across all replicas.
@@ -175,6 +189,104 @@ func (m *Configuration) String() string { return proto.CompactTextStr
func (*Configuration) ProtoMessage() {}
func (*Configuration) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
+func (m *Configuration) GetTargetReplicationLagSec() int64 {
+ if m != nil {
+ return m.TargetReplicationLagSec
+ }
+ return 0
+}
+
+func (m *Configuration) GetMaxReplicationLagSec() int64 {
+ if m != nil {
+ return m.MaxReplicationLagSec
+ }
+ return 0
+}
+
+func (m *Configuration) GetInitialRate() int64 {
+ if m != nil {
+ return m.InitialRate
+ }
+ return 0
+}
+
+func (m *Configuration) GetMaxIncrease() float64 {
+ if m != nil {
+ return m.MaxIncrease
+ }
+ return 0
+}
+
+func (m *Configuration) GetEmergencyDecrease() float64 {
+ if m != nil {
+ return m.EmergencyDecrease
+ }
+ return 0
+}
+
+func (m *Configuration) GetMinDurationBetweenIncreasesSec() int64 {
+ if m != nil {
+ return m.MinDurationBetweenIncreasesSec
+ }
+ return 0
+}
+
+func (m *Configuration) GetMaxDurationBetweenIncreasesSec() int64 {
+ if m != nil {
+ return m.MaxDurationBetweenIncreasesSec
+ }
+ return 0
+}
+
+func (m *Configuration) GetMinDurationBetweenDecreasesSec() int64 {
+ if m != nil {
+ return m.MinDurationBetweenDecreasesSec
+ }
+ return 0
+}
+
+func (m *Configuration) GetSpreadBacklogAcrossSec() int64 {
+ if m != nil {
+ return m.SpreadBacklogAcrossSec
+ }
+ return 0
+}
+
+func (m *Configuration) GetIgnoreNSlowestReplicas() int32 {
+ if m != nil {
+ return m.IgnoreNSlowestReplicas
+ }
+ return 0
+}
+
+func (m *Configuration) GetIgnoreNSlowestRdonlys() int32 {
+ if m != nil {
+ return m.IgnoreNSlowestRdonlys
+ }
+ return 0
+}
+
+func (m *Configuration) GetAgeBadRateAfterSec() int64 {
+ if m != nil {
+ return m.AgeBadRateAfterSec
+ }
+ return 0
+}
+
+func (m *Configuration) GetBadRateIncrease() float64 {
+ if m != nil {
+ return m.BadRateIncrease
+ }
+ return 0
+}
+
+func (m *Configuration) GetMaxRateApproachThreshold() float64 {
+ if m != nil {
+ return m.MaxRateApproachThreshold
+ }
+ return 0
+}
+
// GetConfigurationRequest is the payload for the GetConfiguration RPC.
type GetConfigurationRequest struct {
// throttler_name specifies which throttler to select. If empty, all active
@@ -187,6 +299,13 @@ func (m *GetConfigurationRequest) String() string { return proto.Comp
func (*GetConfigurationRequest) ProtoMessage() {}
func (*GetConfigurationRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
+func (m *GetConfigurationRequest) GetThrottlerName() string {
+ if m != nil {
+ return m.ThrottlerName
+ }
+ return ""
+}
+
// GetConfigurationResponse is returned by the GetConfiguration RPC.
type GetConfigurationResponse struct {
// max_rates returns the configurations for each throttler.
@@ -223,6 +342,13 @@ func (m *UpdateConfigurationRequest) String() string { return proto.C
func (*UpdateConfigurationRequest) ProtoMessage() {}
func (*UpdateConfigurationRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
+func (m *UpdateConfigurationRequest) GetThrottlerName() string {
+ if m != nil {
+ return m.ThrottlerName
+ }
+ return ""
+}
+
func (m *UpdateConfigurationRequest) GetConfiguration() *Configuration {
if m != nil {
return m.Configuration
@@ -230,6 +356,13 @@ func (m *UpdateConfigurationRequest) GetConfiguration() *Configuration {
return nil
}
+func (m *UpdateConfigurationRequest) GetCopyZeroValues() bool {
+ if m != nil {
+ return m.CopyZeroValues
+ }
+ return false
+}
+
// UpdateConfigurationResponse is returned by the UpdateConfiguration RPC.
type UpdateConfigurationResponse struct {
// names is the list of throttler names which were updated.
@@ -241,6 +374,13 @@ func (m *UpdateConfigurationResponse) String() string { return proto.
func (*UpdateConfigurationResponse) ProtoMessage() {}
func (*UpdateConfigurationResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
+func (m *UpdateConfigurationResponse) GetNames() []string {
+ if m != nil {
+ return m.Names
+ }
+ return nil
+}
+
// ResetConfigurationRequest is the payload for the ResetConfiguration RPC.
type ResetConfigurationRequest struct {
// throttler_name specifies which throttler to reset. If empty, all active
@@ -253,6 +393,13 @@ func (m *ResetConfigurationRequest) String() string { return proto.Co
func (*ResetConfigurationRequest) ProtoMessage() {}
func (*ResetConfigurationRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} }
+func (m *ResetConfigurationRequest) GetThrottlerName() string {
+ if m != nil {
+ return m.ThrottlerName
+ }
+ return ""
+}
+
// ResetConfigurationResponse is returned by the ResetConfiguration RPC.
type ResetConfigurationResponse struct {
// names is the list of throttler names which were updated.
@@ -264,6 +411,13 @@ func (m *ResetConfigurationResponse) String() string { return proto.C
func (*ResetConfigurationResponse) ProtoMessage() {}
func (*ResetConfigurationResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} }
+func (m *ResetConfigurationResponse) GetNames() []string {
+ if m != nil {
+ return m.Names
+ }
+ return nil
+}
+
func init() {
proto.RegisterType((*MaxRatesRequest)(nil), "throttlerdata.MaxRatesRequest")
proto.RegisterType((*MaxRatesResponse)(nil), "throttlerdata.MaxRatesResponse")
diff --git a/go/vt/proto/topodata/topodata.pb.go b/go/vt/proto/topodata/topodata.pb.go
index fe4098946c3..38ba2c01d78 100644
--- a/go/vt/proto/topodata/topodata.pb.go
+++ b/go/vt/proto/topodata/topodata.pb.go
@@ -146,6 +146,20 @@ func (m *KeyRange) String() string { return proto.CompactTextString(m
func (*KeyRange) ProtoMessage() {}
func (*KeyRange) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (m *KeyRange) GetStart() []byte {
+ if m != nil {
+ return m.Start
+ }
+ return nil
+}
+
+func (m *KeyRange) GetEnd() []byte {
+ if m != nil {
+ return m.End
+ }
+ return nil
+}
+
// TabletAlias is a globally unique tablet identifier.
type TabletAlias struct {
// cell is the cell (or datacenter) the tablet is in
@@ -160,6 +174,20 @@ func (m *TabletAlias) String() string { return proto.CompactTextStrin
func (*TabletAlias) ProtoMessage() {}
func (*TabletAlias) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+func (m *TabletAlias) GetCell() string {
+ if m != nil {
+ return m.Cell
+ }
+ return ""
+}
+
+func (m *TabletAlias) GetUid() uint32 {
+ if m != nil {
+ return m.Uid
+ }
+ return 0
+}
+
// Tablet represents information about a running instance of vttablet.
type Tablet struct {
// alias is the unique name of the tablet.
@@ -198,6 +226,20 @@ func (m *Tablet) GetAlias() *TabletAlias {
return nil
}
+func (m *Tablet) GetHostname() string {
+ if m != nil {
+ return m.Hostname
+ }
+ return ""
+}
+
+func (m *Tablet) GetIp() string {
+ if m != nil {
+ return m.Ip
+ }
+ return ""
+}
+
func (m *Tablet) GetPortMap() map[string]int32 {
if m != nil {
return m.PortMap
@@ -205,6 +247,20 @@ func (m *Tablet) GetPortMap() map[string]int32 {
return nil
}
+func (m *Tablet) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
+func (m *Tablet) GetShard() string {
+ if m != nil {
+ return m.Shard
+ }
+ return ""
+}
+
func (m *Tablet) GetKeyRange() *KeyRange {
if m != nil {
return m.KeyRange
@@ -212,6 +268,20 @@ func (m *Tablet) GetKeyRange() *KeyRange {
return nil
}
+func (m *Tablet) GetType() TabletType {
+ if m != nil {
+ return m.Type
+ }
+ return TabletType_UNKNOWN
+}
+
+func (m *Tablet) GetDbNameOverride() string {
+ if m != nil {
+ return m.DbNameOverride
+ }
+ return ""
+}
+
func (m *Tablet) GetTags() map[string]string {
if m != nil {
return m.Tags
@@ -282,6 +352,13 @@ func (m *Shard) GetSourceShards() []*Shard_SourceShard {
return nil
}
+func (m *Shard) GetCells() []string {
+ if m != nil {
+ return m.Cells
+ }
+ return nil
+}
+
func (m *Shard) GetTabletControls() []*Shard_TabletControl {
if m != nil {
return m.TabletControls
@@ -300,6 +377,20 @@ func (m *Shard_ServedType) String() string { return proto.CompactText
func (*Shard_ServedType) ProtoMessage() {}
func (*Shard_ServedType) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3, 0} }
+func (m *Shard_ServedType) GetTabletType() TabletType {
+ if m != nil {
+ return m.TabletType
+ }
+ return TabletType_UNKNOWN
+}
+
+func (m *Shard_ServedType) GetCells() []string {
+ if m != nil {
+ return m.Cells
+ }
+ return nil
+}
+
// SourceShard represents a data source for filtered replication
// accross shards. When this is used in a destination shard, the master
// of that shard will run filtered replication.
@@ -321,6 +412,27 @@ func (m *Shard_SourceShard) String() string { return proto.CompactTex
func (*Shard_SourceShard) ProtoMessage() {}
func (*Shard_SourceShard) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3, 1} }
+func (m *Shard_SourceShard) GetUid() uint32 {
+ if m != nil {
+ return m.Uid
+ }
+ return 0
+}
+
+func (m *Shard_SourceShard) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
+func (m *Shard_SourceShard) GetShard() string {
+ if m != nil {
+ return m.Shard
+ }
+ return ""
+}
+
func (m *Shard_SourceShard) GetKeyRange() *KeyRange {
if m != nil {
return m.KeyRange
@@ -328,6 +440,13 @@ func (m *Shard_SourceShard) GetKeyRange() *KeyRange {
return nil
}
+func (m *Shard_SourceShard) GetTables() []string {
+ if m != nil {
+ return m.Tables
+ }
+ return nil
+}
+
// TabletControl controls tablet's behavior
type Shard_TabletControl struct {
// which tablet type is affected
@@ -343,6 +462,34 @@ func (m *Shard_TabletControl) String() string { return proto.CompactT
func (*Shard_TabletControl) ProtoMessage() {}
func (*Shard_TabletControl) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3, 2} }
+func (m *Shard_TabletControl) GetTabletType() TabletType {
+ if m != nil {
+ return m.TabletType
+ }
+ return TabletType_UNKNOWN
+}
+
+func (m *Shard_TabletControl) GetCells() []string {
+ if m != nil {
+ return m.Cells
+ }
+ return nil
+}
+
+func (m *Shard_TabletControl) GetDisableQueryService() bool {
+ if m != nil {
+ return m.DisableQueryService
+ }
+ return false
+}
+
+func (m *Shard_TabletControl) GetBlacklistedTables() []string {
+ if m != nil {
+ return m.BlacklistedTables
+ }
+ return nil
+}
+
// A Keyspace contains data about a keyspace.
type Keyspace struct {
// name of the column used for sharding
@@ -361,6 +508,20 @@ func (m *Keyspace) String() string { return proto.CompactTextString(m
func (*Keyspace) ProtoMessage() {}
func (*Keyspace) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
+func (m *Keyspace) GetShardingColumnName() string {
+ if m != nil {
+ return m.ShardingColumnName
+ }
+ return ""
+}
+
+func (m *Keyspace) GetShardingColumnType() KeyspaceIdType {
+ if m != nil {
+ return m.ShardingColumnType
+ }
+ return KeyspaceIdType_UNSET
+}
+
func (m *Keyspace) GetServedFroms() []*Keyspace_ServedFrom {
if m != nil {
return m.ServedFroms
@@ -384,6 +545,27 @@ func (m *Keyspace_ServedFrom) String() string { return proto.CompactT
func (*Keyspace_ServedFrom) ProtoMessage() {}
func (*Keyspace_ServedFrom) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4, 0} }
+func (m *Keyspace_ServedFrom) GetTabletType() TabletType {
+ if m != nil {
+ return m.TabletType
+ }
+ return TabletType_UNKNOWN
+}
+
+func (m *Keyspace_ServedFrom) GetCells() []string {
+ if m != nil {
+ return m.Cells
+ }
+ return nil
+}
+
+func (m *Keyspace_ServedFrom) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
// ShardReplication describes the MySQL replication relationships
// whithin a cell.
type ShardReplication struct {
@@ -433,6 +615,13 @@ func (m *ShardReference) String() string { return proto.CompactTextSt
func (*ShardReference) ProtoMessage() {}
func (*ShardReference) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
+func (m *ShardReference) GetName() string {
+ if m != nil {
+ return m.Name
+ }
+ return ""
+}
+
func (m *ShardReference) GetKeyRange() *KeyRange {
if m != nil {
return m.KeyRange
@@ -462,6 +651,20 @@ func (m *SrvKeyspace) GetPartitions() []*SrvKeyspace_KeyspacePartition {
return nil
}
+func (m *SrvKeyspace) GetShardingColumnName() string {
+ if m != nil {
+ return m.ShardingColumnName
+ }
+ return ""
+}
+
+func (m *SrvKeyspace) GetShardingColumnType() KeyspaceIdType {
+ if m != nil {
+ return m.ShardingColumnType
+ }
+ return KeyspaceIdType_UNSET
+}
+
func (m *SrvKeyspace) GetServedFrom() []*SrvKeyspace_ServedFrom {
if m != nil {
return m.ServedFrom
@@ -483,6 +686,13 @@ func (*SrvKeyspace_KeyspacePartition) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{7, 0}
}
+func (m *SrvKeyspace_KeyspacePartition) GetServedType() TabletType {
+ if m != nil {
+ return m.ServedType
+ }
+ return TabletType_UNKNOWN
+}
+
func (m *SrvKeyspace_KeyspacePartition) GetShardReferences() []*ShardReference {
if m != nil {
return m.ShardReferences
@@ -504,6 +714,20 @@ func (m *SrvKeyspace_ServedFrom) String() string { return proto.Compa
func (*SrvKeyspace_ServedFrom) ProtoMessage() {}
func (*SrvKeyspace_ServedFrom) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7, 1} }
+func (m *SrvKeyspace_ServedFrom) GetTabletType() TabletType {
+ if m != nil {
+ return m.TabletType
+ }
+ return TabletType_UNKNOWN
+}
+
+func (m *SrvKeyspace_ServedFrom) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
// CellInfo contains information about a cell. CellInfo objects are
// stored in the global topology server, and describe how to reach
// local topology servers.
@@ -523,6 +747,20 @@ func (m *CellInfo) String() string { return proto.CompactTextString(m
func (*CellInfo) ProtoMessage() {}
func (*CellInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
+func (m *CellInfo) GetServerAddress() string {
+ if m != nil {
+ return m.ServerAddress
+ }
+ return ""
+}
+
+func (m *CellInfo) GetRoot() string {
+ if m != nil {
+ return m.Root
+ }
+ return ""
+}
+
func init() {
proto.RegisterType((*KeyRange)(nil), "topodata.KeyRange")
proto.RegisterType((*TabletAlias)(nil), "topodata.TabletAlias")
diff --git a/go/vt/proto/vschema/vschema.pb.go b/go/vt/proto/vschema/vschema.pb.go
index dc15d87b4e9..7e269a2bd35 100644
--- a/go/vt/proto/vschema/vschema.pb.go
+++ b/go/vt/proto/vschema/vschema.pb.go
@@ -46,6 +46,13 @@ func (m *Keyspace) String() string { return proto.CompactTextString(m
func (*Keyspace) ProtoMessage() {}
func (*Keyspace) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (m *Keyspace) GetSharded() bool {
+ if m != nil {
+ return m.Sharded
+ }
+ return false
+}
+
func (m *Keyspace) GetVindexes() map[string]*Vindex {
if m != nil {
return m.Vindexes
@@ -82,6 +89,13 @@ func (m *Vindex) String() string { return proto.CompactTextString(m)
func (*Vindex) ProtoMessage() {}
func (*Vindex) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+func (m *Vindex) GetType() string {
+ if m != nil {
+ return m.Type
+ }
+ return ""
+}
+
func (m *Vindex) GetParams() map[string]string {
if m != nil {
return m.Params
@@ -89,6 +103,13 @@ func (m *Vindex) GetParams() map[string]string {
return nil
}
+func (m *Vindex) GetOwner() string {
+ if m != nil {
+ return m.Owner
+ }
+ return ""
+}
+
// Table is the table info for a Keyspace.
type Table struct {
// If the table is a sequence, type must be
@@ -106,6 +127,13 @@ func (m *Table) String() string { return proto.CompactTextString(m) }
func (*Table) ProtoMessage() {}
func (*Table) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+func (m *Table) GetType() string {
+ if m != nil {
+ return m.Type
+ }
+ return ""
+}
+
func (m *Table) GetColumnVindexes() []*ColumnVindex {
if m != nil {
return m.ColumnVindexes
@@ -132,6 +160,20 @@ func (m *ColumnVindex) String() string { return proto.CompactTextStri
func (*ColumnVindex) ProtoMessage() {}
func (*ColumnVindex) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
+func (m *ColumnVindex) GetColumn() string {
+ if m != nil {
+ return m.Column
+ }
+ return ""
+}
+
+func (m *ColumnVindex) GetName() string {
+ if m != nil {
+ return m.Name
+ }
+ return ""
+}
+
// Autoincrement is used to designate a column as auto-inc.
type AutoIncrement struct {
Column string `protobuf:"bytes,1,opt,name=column" json:"column,omitempty"`
@@ -144,6 +186,20 @@ func (m *AutoIncrement) String() string { return proto.CompactTextStr
func (*AutoIncrement) ProtoMessage() {}
func (*AutoIncrement) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
+func (m *AutoIncrement) GetColumn() string {
+ if m != nil {
+ return m.Column
+ }
+ return ""
+}
+
+func (m *AutoIncrement) GetSequence() string {
+ if m != nil {
+ return m.Sequence
+ }
+ return ""
+}
+
// SrvVSchema is the roll-up of all the Keyspace schema for a cell.
type SrvVSchema struct {
// keyspaces is a map of keyspace name -> Keyspace object.
diff --git a/go/vt/proto/vtctldata/vtctldata.pb.go b/go/vt/proto/vtctldata/vtctldata.pb.go
index a112ca33c52..3a9025a0954 100644
--- a/go/vt/proto/vtctldata/vtctldata.pb.go
+++ b/go/vt/proto/vtctldata/vtctldata.pb.go
@@ -42,6 +42,20 @@ func (m *ExecuteVtctlCommandRequest) String() string { return proto.C
func (*ExecuteVtctlCommandRequest) ProtoMessage() {}
func (*ExecuteVtctlCommandRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (m *ExecuteVtctlCommandRequest) GetArgs() []string {
+ if m != nil {
+ return m.Args
+ }
+ return nil
+}
+
+func (m *ExecuteVtctlCommandRequest) GetActionTimeout() int64 {
+ if m != nil {
+ return m.ActionTimeout
+ }
+ return 0
+}
+
// ExecuteVtctlCommandResponse is streamed back by ExecuteVtctlCommand.
type ExecuteVtctlCommandResponse struct {
Event *logutil.Event `protobuf:"bytes,1,opt,name=event" json:"event,omitempty"`
diff --git a/go/vt/proto/vtgate/vtgate.pb.go b/go/vt/proto/vtgate/vtgate.pb.go
index c0a6a28df6e..179b0591340 100644
--- a/go/vt/proto/vtgate/vtgate.pb.go
+++ b/go/vt/proto/vtgate/vtgate.pb.go
@@ -89,6 +89,13 @@ func (m *Session) String() string { return proto.CompactTextString(m)
func (*Session) ProtoMessage() {}
func (*Session) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (m *Session) GetInTransaction() bool {
+ if m != nil {
+ return m.InTransaction
+ }
+ return false
+}
+
func (m *Session) GetShardSessions() []*Session_ShardSession {
if m != nil {
return m.ShardSessions
@@ -96,6 +103,13 @@ func (m *Session) GetShardSessions() []*Session_ShardSession {
return nil
}
+func (m *Session) GetSingleDb() bool {
+ if m != nil {
+ return m.SingleDb
+ }
+ return false
+}
+
type Session_ShardSession struct {
Target *query.Target `protobuf:"bytes,1,opt,name=target" json:"target,omitempty"`
TransactionId int64 `protobuf:"varint,2,opt,name=transaction_id,json=transactionId" json:"transaction_id,omitempty"`
@@ -113,6 +127,13 @@ func (m *Session_ShardSession) GetTarget() *query.Target {
return nil
}
+func (m *Session_ShardSession) GetTransactionId() int64 {
+ if m != nil {
+ return m.TransactionId
+ }
+ return 0
+}
+
// ExecuteRequest is the payload to Execute.
type ExecuteRequest struct {
// caller_id identifies the caller. This is the effective caller ID,
@@ -159,6 +180,27 @@ func (m *ExecuteRequest) GetQuery() *query.BoundQuery {
return nil
}
+func (m *ExecuteRequest) GetTabletType() topodata.TabletType {
+ if m != nil {
+ return m.TabletType
+ }
+ return topodata.TabletType_UNKNOWN
+}
+
+func (m *ExecuteRequest) GetNotInTransaction() bool {
+ if m != nil {
+ return m.NotInTransaction
+ }
+ return false
+}
+
+func (m *ExecuteRequest) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
func (m *ExecuteRequest) GetOptions() *query.ExecuteOptions {
if m != nil {
return m.Options
@@ -252,6 +294,34 @@ func (m *ExecuteShardsRequest) GetQuery() *query.BoundQuery {
return nil
}
+func (m *ExecuteShardsRequest) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
+func (m *ExecuteShardsRequest) GetShards() []string {
+ if m != nil {
+ return m.Shards
+ }
+ return nil
+}
+
+func (m *ExecuteShardsRequest) GetTabletType() topodata.TabletType {
+ if m != nil {
+ return m.TabletType
+ }
+ return topodata.TabletType_UNKNOWN
+}
+
+func (m *ExecuteShardsRequest) GetNotInTransaction() bool {
+ if m != nil {
+ return m.NotInTransaction
+ }
+ return false
+}
+
func (m *ExecuteShardsRequest) GetOptions() *query.ExecuteOptions {
if m != nil {
return m.Options
@@ -346,6 +416,34 @@ func (m *ExecuteKeyspaceIdsRequest) GetQuery() *query.BoundQuery {
return nil
}
+func (m *ExecuteKeyspaceIdsRequest) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
+func (m *ExecuteKeyspaceIdsRequest) GetKeyspaceIds() [][]byte {
+ if m != nil {
+ return m.KeyspaceIds
+ }
+ return nil
+}
+
+func (m *ExecuteKeyspaceIdsRequest) GetTabletType() topodata.TabletType {
+ if m != nil {
+ return m.TabletType
+ }
+ return topodata.TabletType_UNKNOWN
+}
+
+func (m *ExecuteKeyspaceIdsRequest) GetNotInTransaction() bool {
+ if m != nil {
+ return m.NotInTransaction
+ }
+ return false
+}
+
func (m *ExecuteKeyspaceIdsRequest) GetOptions() *query.ExecuteOptions {
if m != nil {
return m.Options
@@ -440,6 +538,13 @@ func (m *ExecuteKeyRangesRequest) GetQuery() *query.BoundQuery {
return nil
}
+func (m *ExecuteKeyRangesRequest) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
func (m *ExecuteKeyRangesRequest) GetKeyRanges() []*topodata.KeyRange {
if m != nil {
return m.KeyRanges
@@ -447,6 +552,20 @@ func (m *ExecuteKeyRangesRequest) GetKeyRanges() []*topodata.KeyRange {
return nil
}
+func (m *ExecuteKeyRangesRequest) GetTabletType() topodata.TabletType {
+ if m != nil {
+ return m.TabletType
+ }
+ return topodata.TabletType_UNKNOWN
+}
+
+func (m *ExecuteKeyRangesRequest) GetNotInTransaction() bool {
+ if m != nil {
+ return m.NotInTransaction
+ }
+ return false
+}
+
func (m *ExecuteKeyRangesRequest) GetOptions() *query.ExecuteOptions {
if m != nil {
return m.Options
@@ -543,6 +662,20 @@ func (m *ExecuteEntityIdsRequest) GetQuery() *query.BoundQuery {
return nil
}
+func (m *ExecuteEntityIdsRequest) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
+func (m *ExecuteEntityIdsRequest) GetEntityColumnName() string {
+ if m != nil {
+ return m.EntityColumnName
+ }
+ return ""
+}
+
func (m *ExecuteEntityIdsRequest) GetEntityKeyspaceIds() []*ExecuteEntityIdsRequest_EntityId {
if m != nil {
return m.EntityKeyspaceIds
@@ -550,6 +683,20 @@ func (m *ExecuteEntityIdsRequest) GetEntityKeyspaceIds() []*ExecuteEntityIdsRequ
return nil
}
+func (m *ExecuteEntityIdsRequest) GetTabletType() topodata.TabletType {
+ if m != nil {
+ return m.TabletType
+ }
+ return topodata.TabletType_UNKNOWN
+}
+
+func (m *ExecuteEntityIdsRequest) GetNotInTransaction() bool {
+ if m != nil {
+ return m.NotInTransaction
+ }
+ return false
+}
+
func (m *ExecuteEntityIdsRequest) GetOptions() *query.ExecuteOptions {
if m != nil {
return m.Options
@@ -573,6 +720,27 @@ func (*ExecuteEntityIdsRequest_EntityId) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{9, 0}
}
+func (m *ExecuteEntityIdsRequest_EntityId) GetType() query.Type {
+ if m != nil {
+ return m.Type
+ }
+ return query.Type_NULL_TYPE
+}
+
+func (m *ExecuteEntityIdsRequest_EntityId) GetValue() []byte {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
+func (m *ExecuteEntityIdsRequest_EntityId) GetKeyspaceId() []byte {
+ if m != nil {
+ return m.KeyspaceId
+ }
+ return nil
+}
+
// ExecuteEntityIdsResponse is the returned value from ExecuteEntityIds.
type ExecuteEntityIdsResponse struct {
// error contains an application level error if necessary. Note the
@@ -660,6 +828,27 @@ func (m *ExecuteBatchRequest) GetQueries() []*query.BoundQuery {
return nil
}
+func (m *ExecuteBatchRequest) GetTabletType() topodata.TabletType {
+ if m != nil {
+ return m.TabletType
+ }
+ return topodata.TabletType_UNKNOWN
+}
+
+func (m *ExecuteBatchRequest) GetAsTransaction() bool {
+ if m != nil {
+ return m.AsTransaction
+ }
+ return false
+}
+
+func (m *ExecuteBatchRequest) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
func (m *ExecuteBatchRequest) GetOptions() *query.ExecuteOptions {
if m != nil {
return m.Options
@@ -729,6 +918,20 @@ func (m *BoundShardQuery) GetQuery() *query.BoundQuery {
return nil
}
+func (m *BoundShardQuery) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
+func (m *BoundShardQuery) GetShards() []string {
+ if m != nil {
+ return m.Shards
+ }
+ return nil
+}
+
// ExecuteBatchShardsRequest is the payload to ExecuteBatchShards
type ExecuteBatchShardsRequest struct {
// caller_id identifies the caller. This is the effective caller ID,
@@ -775,6 +978,20 @@ func (m *ExecuteBatchShardsRequest) GetQueries() []*BoundShardQuery {
return nil
}
+func (m *ExecuteBatchShardsRequest) GetTabletType() topodata.TabletType {
+ if m != nil {
+ return m.TabletType
+ }
+ return topodata.TabletType_UNKNOWN
+}
+
+func (m *ExecuteBatchShardsRequest) GetAsTransaction() bool {
+ if m != nil {
+ return m.AsTransaction
+ }
+ return false
+}
+
func (m *ExecuteBatchShardsRequest) GetOptions() *query.ExecuteOptions {
if m != nil {
return m.Options
@@ -845,6 +1062,20 @@ func (m *BoundKeyspaceIdQuery) GetQuery() *query.BoundQuery {
return nil
}
+func (m *BoundKeyspaceIdQuery) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
+func (m *BoundKeyspaceIdQuery) GetKeyspaceIds() [][]byte {
+ if m != nil {
+ return m.KeyspaceIds
+ }
+ return nil
+}
+
// ExecuteBatchKeyspaceIdsRequest is the payload to ExecuteBatchKeyspaceId.
type ExecuteBatchKeyspaceIdsRequest struct {
// caller_id identifies the caller. This is the effective caller ID,
@@ -890,6 +1121,20 @@ func (m *ExecuteBatchKeyspaceIdsRequest) GetQueries() []*BoundKeyspaceIdQuery {
return nil
}
+func (m *ExecuteBatchKeyspaceIdsRequest) GetTabletType() topodata.TabletType {
+ if m != nil {
+ return m.TabletType
+ }
+ return topodata.TabletType_UNKNOWN
+}
+
+func (m *ExecuteBatchKeyspaceIdsRequest) GetAsTransaction() bool {
+ if m != nil {
+ return m.AsTransaction
+ }
+ return false
+}
+
func (m *ExecuteBatchKeyspaceIdsRequest) GetOptions() *query.ExecuteOptions {
if m != nil {
return m.Options
@@ -971,6 +1216,20 @@ func (m *StreamExecuteRequest) GetQuery() *query.BoundQuery {
return nil
}
+func (m *StreamExecuteRequest) GetTabletType() topodata.TabletType {
+ if m != nil {
+ return m.TabletType
+ }
+ return topodata.TabletType_UNKNOWN
+}
+
+func (m *StreamExecuteRequest) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
func (m *StreamExecuteRequest) GetOptions() *query.ExecuteOptions {
if m != nil {
return m.Options
@@ -1034,6 +1293,27 @@ func (m *StreamExecuteShardsRequest) GetQuery() *query.BoundQuery {
return nil
}
+func (m *StreamExecuteShardsRequest) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
+func (m *StreamExecuteShardsRequest) GetShards() []string {
+ if m != nil {
+ return m.Shards
+ }
+ return nil
+}
+
+func (m *StreamExecuteShardsRequest) GetTabletType() topodata.TabletType {
+ if m != nil {
+ return m.TabletType
+ }
+ return topodata.TabletType_UNKNOWN
+}
+
func (m *StreamExecuteShardsRequest) GetOptions() *query.ExecuteOptions {
if m != nil {
return m.Options
@@ -1100,6 +1380,27 @@ func (m *StreamExecuteKeyspaceIdsRequest) GetQuery() *query.BoundQuery {
return nil
}
+func (m *StreamExecuteKeyspaceIdsRequest) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
+func (m *StreamExecuteKeyspaceIdsRequest) GetKeyspaceIds() [][]byte {
+ if m != nil {
+ return m.KeyspaceIds
+ }
+ return nil
+}
+
+func (m *StreamExecuteKeyspaceIdsRequest) GetTabletType() topodata.TabletType {
+ if m != nil {
+ return m.TabletType
+ }
+ return topodata.TabletType_UNKNOWN
+}
+
func (m *StreamExecuteKeyspaceIdsRequest) GetOptions() *query.ExecuteOptions {
if m != nil {
return m.Options
@@ -1166,6 +1467,13 @@ func (m *StreamExecuteKeyRangesRequest) GetQuery() *query.BoundQuery {
return nil
}
+func (m *StreamExecuteKeyRangesRequest) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
func (m *StreamExecuteKeyRangesRequest) GetKeyRanges() []*topodata.KeyRange {
if m != nil {
return m.KeyRanges
@@ -1173,6 +1481,13 @@ func (m *StreamExecuteKeyRangesRequest) GetKeyRanges() []*topodata.KeyRange {
return nil
}
+func (m *StreamExecuteKeyRangesRequest) GetTabletType() topodata.TabletType {
+ if m != nil {
+ return m.TabletType
+ }
+ return topodata.TabletType_UNKNOWN
+}
+
func (m *StreamExecuteKeyRangesRequest) GetOptions() *query.ExecuteOptions {
if m != nil {
return m.Options
@@ -1222,6 +1537,13 @@ func (m *BeginRequest) GetCallerId() *vtrpc.CallerID {
return nil
}
+func (m *BeginRequest) GetSingleDb() bool {
+ if m != nil {
+ return m.SingleDb
+ }
+ return false
+}
+
// BeginResponse is the returned value from Begin.
type BeginResponse struct {
// session is the initial session information to use for subsequent queries.
@@ -1271,6 +1593,13 @@ func (m *CommitRequest) GetSession() *Session {
return nil
}
+func (m *CommitRequest) GetAtomic() bool {
+ if m != nil {
+ return m.Atomic
+ }
+ return false
+}
+
// CommitResponse is the returned value from Commit.
type CommitResponse struct {
}
@@ -1338,6 +1667,13 @@ func (m *ResolveTransactionRequest) GetCallerId() *vtrpc.CallerID {
return nil
}
+func (m *ResolveTransactionRequest) GetDtid() string {
+ if m != nil {
+ return m.Dtid
+ }
+ return ""
+}
+
// MessageStreamRequest is the request payload for MessageStream.
type MessageStreamRequest struct {
// caller_id identifies the caller. This is the effective caller ID,
@@ -1365,6 +1701,20 @@ func (m *MessageStreamRequest) GetCallerId() *vtrpc.CallerID {
return nil
}
+func (m *MessageStreamRequest) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
+func (m *MessageStreamRequest) GetShard() string {
+ if m != nil {
+ return m.Shard
+ }
+ return ""
+}
+
func (m *MessageStreamRequest) GetKeyRange() *topodata.KeyRange {
if m != nil {
return m.KeyRange
@@ -1372,6 +1722,13 @@ func (m *MessageStreamRequest) GetKeyRange() *topodata.KeyRange {
return nil
}
+func (m *MessageStreamRequest) GetName() string {
+ if m != nil {
+ return m.Name
+ }
+ return ""
+}
+
// MessageAckRequest is the request payload for MessageAck.
type MessageAckRequest struct {
// caller_id identifies the caller. This is the effective caller ID,
@@ -1397,6 +1754,20 @@ func (m *MessageAckRequest) GetCallerId() *vtrpc.CallerID {
return nil
}
+func (m *MessageAckRequest) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
+func (m *MessageAckRequest) GetName() string {
+ if m != nil {
+ return m.Name
+ }
+ return ""
+}
+
func (m *MessageAckRequest) GetIds() []*query.Value {
if m != nil {
return m.Ids
@@ -1516,6 +1887,13 @@ func (m *SplitQueryRequest) GetCallerId() *vtrpc.CallerID {
return nil
}
+func (m *SplitQueryRequest) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
func (m *SplitQueryRequest) GetQuery() *query.BoundQuery {
if m != nil {
return m.Query
@@ -1523,6 +1901,41 @@ func (m *SplitQueryRequest) GetQuery() *query.BoundQuery {
return nil
}
+func (m *SplitQueryRequest) GetSplitColumn() []string {
+ if m != nil {
+ return m.SplitColumn
+ }
+ return nil
+}
+
+func (m *SplitQueryRequest) GetSplitCount() int64 {
+ if m != nil {
+ return m.SplitCount
+ }
+ return 0
+}
+
+func (m *SplitQueryRequest) GetNumRowsPerQueryPart() int64 {
+ if m != nil {
+ return m.NumRowsPerQueryPart
+ }
+ return 0
+}
+
+func (m *SplitQueryRequest) GetAlgorithm() query.SplitQueryRequest_Algorithm {
+ if m != nil {
+ return m.Algorithm
+ }
+ return query.SplitQueryRequest_EQUAL_SPLITS
+}
+
+func (m *SplitQueryRequest) GetUseSplitQueryV2() bool {
+ if m != nil {
+ return m.UseSplitQueryV2
+ }
+ return false
+}
+
// SplitQueryResponse is the returned value from SplitQuery.
type SplitQueryResponse struct {
// splits contains the queries to run to fetch the entire data set.
@@ -1555,6 +1968,13 @@ func (*SplitQueryResponse_KeyRangePart) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{38, 0}
}
+func (m *SplitQueryResponse_KeyRangePart) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
func (m *SplitQueryResponse_KeyRangePart) GetKeyRanges() []*topodata.KeyRange {
if m != nil {
return m.KeyRanges
@@ -1576,6 +1996,20 @@ func (*SplitQueryResponse_ShardPart) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{38, 1}
}
+func (m *SplitQueryResponse_ShardPart) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
+func (m *SplitQueryResponse_ShardPart) GetShards() []string {
+ if m != nil {
+ return m.Shards
+ }
+ return nil
+}
+
type SplitQueryResponse_Part struct {
// query is the query and bind variables to execute.
Query *query.BoundQuery `protobuf:"bytes,1,opt,name=query" json:"query,omitempty"`
@@ -1614,6 +2048,13 @@ func (m *SplitQueryResponse_Part) GetShardPart() *SplitQueryResponse_ShardPart {
return nil
}
+func (m *SplitQueryResponse_Part) GetSize() int64 {
+ if m != nil {
+ return m.Size
+ }
+ return 0
+}
+
// GetSrvKeyspaceRequest is the payload to GetSrvKeyspace.
type GetSrvKeyspaceRequest struct {
// keyspace name to fetch.
@@ -1625,6 +2066,13 @@ func (m *GetSrvKeyspaceRequest) String() string { return proto.Compac
func (*GetSrvKeyspaceRequest) ProtoMessage() {}
func (*GetSrvKeyspaceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{39} }
+func (m *GetSrvKeyspaceRequest) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
// GetSrvKeyspaceResponse is the returned value from GetSrvKeyspace.
type GetSrvKeyspaceResponse struct {
// srv_keyspace is the topology object for the SrvKeyspace.
@@ -1679,6 +2127,20 @@ func (m *UpdateStreamRequest) GetCallerId() *vtrpc.CallerID {
return nil
}
+func (m *UpdateStreamRequest) GetKeyspace() string {
+ if m != nil {
+ return m.Keyspace
+ }
+ return ""
+}
+
+func (m *UpdateStreamRequest) GetShard() string {
+ if m != nil {
+ return m.Shard
+ }
+ return ""
+}
+
func (m *UpdateStreamRequest) GetKeyRange() *topodata.KeyRange {
if m != nil {
return m.KeyRange
@@ -1686,6 +2148,20 @@ func (m *UpdateStreamRequest) GetKeyRange() *topodata.KeyRange {
return nil
}
+func (m *UpdateStreamRequest) GetTabletType() topodata.TabletType {
+ if m != nil {
+ return m.TabletType
+ }
+ return topodata.TabletType_UNKNOWN
+}
+
+func (m *UpdateStreamRequest) GetTimestamp() int64 {
+ if m != nil {
+ return m.Timestamp
+ }
+ return 0
+}
+
func (m *UpdateStreamRequest) GetEvent() *query.EventToken {
if m != nil {
return m.Event
@@ -1717,6 +2193,13 @@ func (m *UpdateStreamResponse) GetEvent() *query.StreamEvent {
return nil
}
+func (m *UpdateStreamResponse) GetResumeTimestamp() int64 {
+ if m != nil {
+ return m.ResumeTimestamp
+ }
+ return 0
+}
+
func init() {
proto.RegisterType((*Session)(nil), "vtgate.Session")
proto.RegisterType((*Session_ShardSession)(nil), "vtgate.Session.ShardSession")
diff --git a/go/vt/proto/vtrpc/vtrpc.pb.go b/go/vt/proto/vtrpc/vtrpc.pb.go
index f69f21b5912..f88edca9057 100644
--- a/go/vt/proto/vtrpc/vtrpc.pb.go
+++ b/go/vt/proto/vtrpc/vtrpc.pb.go
@@ -29,43 +29,200 @@ var _ = math.Inf
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
-// ErrorCode is the enum values for Errors. Internally, errors should
-// be created with one of these codes. These will then be translated over the wire
-// by various RPC frameworks.
-type ErrorCode int32
+// Code represents canonical error codes. The names, numbers and comments
+// must match the ones defined by grpc:
+// https://godoc.org/google.golang.org/grpc/codes.
+type Code int32
const (
- // SUCCESS is returned from a successful call.
- ErrorCode_SUCCESS ErrorCode = 0
- // CANCELLED means that the context was cancelled (and noticed in the app layer,
+ // OK is returned on success.
+ Code_OK Code = 0
+ // CANCELED indicates the operation was cancelled (typically by the caller).
+ Code_CANCELED Code = 1
+ // UNKNOWN error. An example of where this error may be returned is
+ // if a Status value received from another address space belongs to
+ // an error-space that is not known in this address space. Also
+ // errors raised by APIs that do not return enough error information
+ // may be converted to this error.
+ Code_UNKNOWN Code = 2
+ // INVALID_ARGUMENT indicates client specified an invalid argument.
+ // Note that this differs from FAILED_PRECONDITION. It indicates arguments
+ // that are problematic regardless of the state of the system
+ // (e.g., a malformed file name).
+ Code_INVALID_ARGUMENT Code = 3
+ // DEADLINE_EXCEEDED means operation expired before completion.
+ // For operations that change the state of the system, this error may be
+ // returned even if the operation has completed successfully. For
+ // example, a successful response from a server could have been delayed
+ // long enough for the deadline to expire.
+ Code_DEADLINE_EXCEEDED Code = 4
+ // NOT_FOUND means some requested entity (e.g., file or directory) was
+ // not found.
+ Code_NOT_FOUND Code = 5
+ // ALREADY_EXISTS means an attempt to create an entity failed because one
+ // already exists.
+ Code_ALREADY_EXISTS Code = 6
+ // PERMISSION_DENIED indicates the caller does not have permission to
+ // execute the specified operation. It must not be used for rejections
+ // caused by exhausting some resource (use RESOURCE_EXHAUSTED
+ // instead for those errors). It must not be
+ // used if the caller cannot be identified (use Unauthenticated
+ // instead for those errors).
+ Code_PERMISSION_DENIED Code = 7
+ // UNAUTHENTICATED indicates the request does not have valid
+ // authentication credentials for the operation.
+ Code_UNAUTHENTICATED Code = 16
+ // RESOURCE_EXHAUSTED indicates some resource has been exhausted, perhaps
+ // a per-user quota, or perhaps the entire file system is out of space.
+ Code_RESOURCE_EXHAUSTED Code = 8
+ // FAILED_PRECONDITION indicates operation was rejected because the
+ // system is not in a state required for the operation's execution.
+ // For example, directory to be deleted may be non-empty, an rmdir
+ // operation is applied to a non-directory, etc.
+ //
+ // A litmus test that may help a service implementor in deciding
+ // between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE:
+ // (a) Use UNAVAILABLE if the client can retry just the failing call.
+ // (b) Use ABORTED if the client should retry at a higher-level
+ // (e.g., restarting a read-modify-write sequence).
+ // (c) Use FAILED_PRECONDITION if the client should not retry until
+ // the system state has been explicitly fixed. E.g., if an "rmdir"
+ // fails because the directory is non-empty, FAILED_PRECONDITION
+ // should be returned since the client should not retry unless
+ // they have first fixed up the directory by deleting files from it.
+ // (d) Use FAILED_PRECONDITION if the client performs conditional
+ // REST Get/Update/Delete on a resource and the resource on the
+ // server does not match the condition. E.g., conflicting
+ // read-modify-write on the same resource.
+ Code_FAILED_PRECONDITION Code = 9
+ // ABORTED indicates the operation was aborted, typically due to a
+ // concurrency issue like sequencer check failures, transaction aborts,
+ // etc.
+ //
+ // See litmus test above for deciding between FAILED_PRECONDITION,
+ // ABORTED, and UNAVAILABLE.
+ Code_ABORTED Code = 10
+ // OUT_OF_RANGE means operation was attempted past the valid range.
+ // E.g., seeking or reading past end of file.
+ //
+ // Unlike INVALID_ARGUMENT, this error indicates a problem that may
+ // be fixed if the system state changes. For example, a 32-bit file
+ // system will generate INVALID_ARGUMENT if asked to read at an
+ // offset that is not in the range [0,2^32-1], but it will generate
+ // OUT_OF_RANGE if asked to read from an offset past the current
+ // file size.
+ //
+ // There is a fair bit of overlap between FAILED_PRECONDITION and
+ // OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific
+ // error) when it applies so that callers who are iterating through
+ // a space can easily look for an OUT_OF_RANGE error to detect when
+ // they are done.
+ Code_OUT_OF_RANGE Code = 11
+ // UNIMPLEMENTED indicates operation is not implemented or not
+ // supported/enabled in this service.
+ Code_UNIMPLEMENTED Code = 12
+ // INTERNAL errors. Means some invariants expected by underlying
+ // system has been broken. If you see one of these errors,
+ // something is very broken.
+ Code_INTERNAL Code = 13
+ // UNAVAILABLE indicates the service is currently unavailable.
+ // This is a most likely a transient condition and may be corrected
+ // by retrying with a backoff.
+ //
+ // See litmus test above for deciding between FAILED_PRECONDITION,
+ // ABORTED, and UNAVAILABLE.
+ Code_UNAVAILABLE Code = 14
+ // DATA_LOSS indicates unrecoverable data loss or corruption.
+ Code_DATA_LOSS Code = 15
+)
+
+var Code_name = map[int32]string{
+ 0: "OK",
+ 1: "CANCELED",
+ 2: "UNKNOWN",
+ 3: "INVALID_ARGUMENT",
+ 4: "DEADLINE_EXCEEDED",
+ 5: "NOT_FOUND",
+ 6: "ALREADY_EXISTS",
+ 7: "PERMISSION_DENIED",
+ 16: "UNAUTHENTICATED",
+ 8: "RESOURCE_EXHAUSTED",
+ 9: "FAILED_PRECONDITION",
+ 10: "ABORTED",
+ 11: "OUT_OF_RANGE",
+ 12: "UNIMPLEMENTED",
+ 13: "INTERNAL",
+ 14: "UNAVAILABLE",
+ 15: "DATA_LOSS",
+}
+var Code_value = map[string]int32{
+ "OK": 0,
+ "CANCELED": 1,
+ "UNKNOWN": 2,
+ "INVALID_ARGUMENT": 3,
+ "DEADLINE_EXCEEDED": 4,
+ "NOT_FOUND": 5,
+ "ALREADY_EXISTS": 6,
+ "PERMISSION_DENIED": 7,
+ "UNAUTHENTICATED": 16,
+ "RESOURCE_EXHAUSTED": 8,
+ "FAILED_PRECONDITION": 9,
+ "ABORTED": 10,
+ "OUT_OF_RANGE": 11,
+ "UNIMPLEMENTED": 12,
+ "INTERNAL": 13,
+ "UNAVAILABLE": 14,
+ "DATA_LOSS": 15,
+}
+
+func (x Code) String() string {
+ return proto.EnumName(Code_name, int32(x))
+}
+func (Code) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+
+// LegacyErrorCode is the enum values for Errors. This type is deprecated.
+// Use Code instead. Background: In the initial design, we thought
+// that we may end up with a different list of canonical error codes
+// than the ones defined by grpc. In hindisght, we realize that
+// the grpc error codes are fairly generic and mostly sufficient.
+// In order to avoid confusion, this type will be deprecated in
+// favor of the new Code that matches exactly what grpc defines.
+// Some names below have a _LEGACY suffix. This is to prevent
+// name collisions with Code.
+type LegacyErrorCode int32
+
+const (
+ // SUCCESS_LEGACY is returned from a successful call.
+ LegacyErrorCode_SUCCESS_LEGACY LegacyErrorCode = 0
+ // CANCELLED_LEGACY means that the context was cancelled (and noticed in the app layer,
// as opposed to the RPC layer).
- ErrorCode_CANCELLED ErrorCode = 1
- // UNKNOWN_ERROR includes:
+ LegacyErrorCode_CANCELLED_LEGACY LegacyErrorCode = 1
+ // UNKNOWN_ERROR_LEGACY includes:
// 1. MySQL error codes that we don't explicitly handle.
// 2. MySQL response that wasn't as expected. For example, we might expect a MySQL
// timestamp to be returned in a particular way, but it wasn't.
// 3. Anything else that doesn't fall into a different bucket.
- ErrorCode_UNKNOWN_ERROR ErrorCode = 2
- // BAD_INPUT is returned when an end-user either sends SQL that couldn't be parsed correctly,
+ LegacyErrorCode_UNKNOWN_ERROR_LEGACY LegacyErrorCode = 2
+ // BAD_INPUT_LEGACY is returned when an end-user either sends SQL that couldn't be parsed correctly,
// or tries a query that isn't supported by Vitess.
- ErrorCode_BAD_INPUT ErrorCode = 3
- // DEADLINE_EXCEEDED is returned when an action is taking longer than a given timeout.
- ErrorCode_DEADLINE_EXCEEDED ErrorCode = 4
- // INTEGRITY_ERROR is returned on integrity error from MySQL, usually due to
+ LegacyErrorCode_BAD_INPUT_LEGACY LegacyErrorCode = 3
+ // DEADLINE_EXCEEDED_LEGACY is returned when an action is taking longer than a given timeout.
+ LegacyErrorCode_DEADLINE_EXCEEDED_LEGACY LegacyErrorCode = 4
+ // INTEGRITY_ERROR_LEGACY is returned on integrity error from MySQL, usually due to
// duplicate primary keys.
- ErrorCode_INTEGRITY_ERROR ErrorCode = 5
- // PERMISSION_DENIED errors are returned when a user requests access to something
+ LegacyErrorCode_INTEGRITY_ERROR_LEGACY LegacyErrorCode = 5
+ // PERMISSION_DENIED_LEGACY errors are returned when a user requests access to something
// that they don't have permissions for.
- ErrorCode_PERMISSION_DENIED ErrorCode = 6
- // RESOURCE_EXHAUSTED is returned when a query exceeds its quota in some dimension
+ LegacyErrorCode_PERMISSION_DENIED_LEGACY LegacyErrorCode = 6
+ // RESOURCE_EXHAUSTED_LEGACY is returned when a query exceeds its quota in some dimension
// and can't be completed due to that. Queries that return RESOURCE_EXHAUSTED
// should not be retried, as it could be detrimental to the server's health.
// Examples of errors that will cause the RESOURCE_EXHAUSTED code:
// 1. TxPoolFull: this is retried server-side, and is only returned as an error
// if the server-side retries failed.
// 2. Query is killed due to it taking too long.
- ErrorCode_RESOURCE_EXHAUSTED ErrorCode = 7
- // QUERY_NOT_SERVED means that a query could not be served right now.
+ LegacyErrorCode_RESOURCE_EXHAUSTED_LEGACY LegacyErrorCode = 7
+ // QUERY_NOT_SERVED_LEGACY means that a query could not be served right now.
// Client can interpret it as: "the tablet that you sent this query to cannot
// serve the query right now, try a different tablet or try again later."
// This could be due to various reasons: QueryService is not serving, should
@@ -73,71 +230,61 @@ const (
// Clients that receive this error should usually retry the query, but after taking
// the appropriate steps to make sure that the query will get sent to the correct
// tablet.
- ErrorCode_QUERY_NOT_SERVED ErrorCode = 8
- // NOT_IN_TX means that we're not currently in a transaction, but we should be.
- ErrorCode_NOT_IN_TX ErrorCode = 9
- // INTERNAL_ERRORs are problems that only the server can fix, not the client.
- // These errors are not due to a query itself, but rather due to the state of
- // the system.
- // Generally, we don't expect the errors to go away by themselves, but they
- // may go away after human intervention.
- // Examples of scenarios where INTERNAL_ERROR is returned:
- // 1. Something is not configured correctly internally.
- // 2. A necessary resource is not available, and we don't expect it to become available by itself.
- // 3. A sanity check fails.
- // 4. Some other internal error occurs.
- // Clients should not retry immediately, as there is little chance of success.
- // However, it's acceptable for retries to happen internally, for example to
- // multiple backends, in case only a subset of backend are not functional.
- ErrorCode_INTERNAL_ERROR ErrorCode = 10
- // TRANSIENT_ERROR is used for when there is some error that we expect we can
+ LegacyErrorCode_QUERY_NOT_SERVED_LEGACY LegacyErrorCode = 8
+ // NOT_IN_TX_LEGACY means that we're not currently in a transaction, but we should be.
+ LegacyErrorCode_NOT_IN_TX_LEGACY LegacyErrorCode = 9
+ // INTERNAL_ERROR_LEGACY means some invariants expected by underlying
+ // system has been broken. If you see one of these errors,
+ // something is very broken.
+ LegacyErrorCode_INTERNAL_ERROR_LEGACY LegacyErrorCode = 10
+ // TRANSIENT_ERROR_LEGACY is used for when there is some error that we expect we can
// recover from automatically - often due to a resource limit temporarily being
// reached. Retrying this error, with an exponential backoff, should succeed.
// Clients should be able to successfully retry the query on the same backends.
// Examples of things that can trigger this error:
// 1. Query has been throttled
// 2. VtGate could have request backlog
- ErrorCode_TRANSIENT_ERROR ErrorCode = 11
- // UNAUTHENTICATED errors are returned when a user requests access to something,
+ LegacyErrorCode_TRANSIENT_ERROR_LEGACY LegacyErrorCode = 11
+ // UNAUTHENTICATED_LEGACY errors are returned when a user requests access to something,
// and we're unable to verify the user's authentication.
- ErrorCode_UNAUTHENTICATED ErrorCode = 12
+ LegacyErrorCode_UNAUTHENTICATED_LEGACY LegacyErrorCode = 12
)
-var ErrorCode_name = map[int32]string{
- 0: "SUCCESS",
- 1: "CANCELLED",
- 2: "UNKNOWN_ERROR",
- 3: "BAD_INPUT",
- 4: "DEADLINE_EXCEEDED",
- 5: "INTEGRITY_ERROR",
- 6: "PERMISSION_DENIED",
- 7: "RESOURCE_EXHAUSTED",
- 8: "QUERY_NOT_SERVED",
- 9: "NOT_IN_TX",
- 10: "INTERNAL_ERROR",
- 11: "TRANSIENT_ERROR",
- 12: "UNAUTHENTICATED",
-}
-var ErrorCode_value = map[string]int32{
- "SUCCESS": 0,
- "CANCELLED": 1,
- "UNKNOWN_ERROR": 2,
- "BAD_INPUT": 3,
- "DEADLINE_EXCEEDED": 4,
- "INTEGRITY_ERROR": 5,
- "PERMISSION_DENIED": 6,
- "RESOURCE_EXHAUSTED": 7,
- "QUERY_NOT_SERVED": 8,
- "NOT_IN_TX": 9,
- "INTERNAL_ERROR": 10,
- "TRANSIENT_ERROR": 11,
- "UNAUTHENTICATED": 12,
-}
-
-func (x ErrorCode) String() string {
- return proto.EnumName(ErrorCode_name, int32(x))
-}
-func (ErrorCode) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+var LegacyErrorCode_name = map[int32]string{
+ 0: "SUCCESS_LEGACY",
+ 1: "CANCELLED_LEGACY",
+ 2: "UNKNOWN_ERROR_LEGACY",
+ 3: "BAD_INPUT_LEGACY",
+ 4: "DEADLINE_EXCEEDED_LEGACY",
+ 5: "INTEGRITY_ERROR_LEGACY",
+ 6: "PERMISSION_DENIED_LEGACY",
+ 7: "RESOURCE_EXHAUSTED_LEGACY",
+ 8: "QUERY_NOT_SERVED_LEGACY",
+ 9: "NOT_IN_TX_LEGACY",
+ 10: "INTERNAL_ERROR_LEGACY",
+ 11: "TRANSIENT_ERROR_LEGACY",
+ 12: "UNAUTHENTICATED_LEGACY",
+}
+var LegacyErrorCode_value = map[string]int32{
+ "SUCCESS_LEGACY": 0,
+ "CANCELLED_LEGACY": 1,
+ "UNKNOWN_ERROR_LEGACY": 2,
+ "BAD_INPUT_LEGACY": 3,
+ "DEADLINE_EXCEEDED_LEGACY": 4,
+ "INTEGRITY_ERROR_LEGACY": 5,
+ "PERMISSION_DENIED_LEGACY": 6,
+ "RESOURCE_EXHAUSTED_LEGACY": 7,
+ "QUERY_NOT_SERVED_LEGACY": 8,
+ "NOT_IN_TX_LEGACY": 9,
+ "INTERNAL_ERROR_LEGACY": 10,
+ "TRANSIENT_ERROR_LEGACY": 11,
+ "UNAUTHENTICATED_LEGACY": 12,
+}
+
+func (x LegacyErrorCode) String() string {
+ return proto.EnumName(LegacyErrorCode_name, int32(x))
+}
+func (LegacyErrorCode) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
// CallerID is passed along RPCs to identify the originating client
// for a request. It is not meant to be secure, but only
@@ -169,13 +316,35 @@ func (m *CallerID) String() string { return proto.CompactTextString(m
func (*CallerID) ProtoMessage() {}
func (*CallerID) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (m *CallerID) GetPrincipal() string {
+ if m != nil {
+ return m.Principal
+ }
+ return ""
+}
+
+func (m *CallerID) GetComponent() string {
+ if m != nil {
+ return m.Component
+ }
+ return ""
+}
+
+func (m *CallerID) GetSubcomponent() string {
+ if m != nil {
+ return m.Subcomponent
+ }
+ return ""
+}
+
// RPCError is an application-level error structure returned by
// VtTablet (and passed along by VtGate if appropriate).
// We use this so the clients don't have to parse the error messages,
// but instead can depend on the value of the code.
type RPCError struct {
- Code ErrorCode `protobuf:"varint,1,opt,name=code,enum=vtrpc.ErrorCode" json:"code,omitempty"`
- Message string `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
+ LegacyCode LegacyErrorCode `protobuf:"varint,1,opt,name=legacy_code,json=legacyCode,enum=vtrpc.LegacyErrorCode" json:"legacy_code,omitempty"`
+ Message string `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
+ Code Code `protobuf:"varint,3,opt,name=code,enum=vtrpc.Code" json:"code,omitempty"`
}
func (m *RPCError) Reset() { *m = RPCError{} }
@@ -183,38 +352,73 @@ func (m *RPCError) String() string { return proto.CompactTextString(m
func (*RPCError) ProtoMessage() {}
func (*RPCError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+func (m *RPCError) GetLegacyCode() LegacyErrorCode {
+ if m != nil {
+ return m.LegacyCode
+ }
+ return LegacyErrorCode_SUCCESS_LEGACY
+}
+
+func (m *RPCError) GetMessage() string {
+ if m != nil {
+ return m.Message
+ }
+ return ""
+}
+
+func (m *RPCError) GetCode() Code {
+ if m != nil {
+ return m.Code
+ }
+ return Code_OK
+}
+
func init() {
proto.RegisterType((*CallerID)(nil), "vtrpc.CallerID")
proto.RegisterType((*RPCError)(nil), "vtrpc.RPCError")
- proto.RegisterEnum("vtrpc.ErrorCode", ErrorCode_name, ErrorCode_value)
+ proto.RegisterEnum("vtrpc.Code", Code_name, Code_value)
+ proto.RegisterEnum("vtrpc.LegacyErrorCode", LegacyErrorCode_name, LegacyErrorCode_value)
}
func init() { proto.RegisterFile("vtrpc.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
- // 376 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x54, 0x91, 0xcb, 0x6e, 0x13, 0x31,
- 0x14, 0x86, 0x49, 0x7a, 0x49, 0xe6, 0xa4, 0x2d, 0xae, 0xb9, 0x28, 0x42, 0x2c, 0x50, 0xc4, 0x02,
- 0xb1, 0xc8, 0x02, 0x9e, 0xc0, 0xb5, 0x8f, 0xa8, 0x21, 0x9c, 0x09, 0xbe, 0x40, 0xbb, 0x1a, 0x25,
- 0x53, 0x0b, 0x05, 0x25, 0xf1, 0x68, 0x66, 0x52, 0x89, 0x27, 0xe0, 0xb5, 0x91, 0x27, 0xa1, 0x15,
- 0xab, 0xd1, 0xff, 0xff, 0xdf, 0xf8, 0xb3, 0x64, 0x18, 0xdd, 0xb7, 0x75, 0x55, 0x4e, 0xab, 0x3a,
- 0xb6, 0x91, 0x9f, 0x74, 0x61, 0xf2, 0x0b, 0x86, 0x72, 0xb1, 0x5e, 0x87, 0x5a, 0x2b, 0xfe, 0x1a,
- 0xb2, 0xaa, 0x5e, 0x6d, 0xcb, 0x55, 0xb5, 0x58, 0x8f, 0x7b, 0x6f, 0x7a, 0xef, 0x32, 0xf3, 0x58,
- 0xa4, 0xb5, 0x8c, 0x9b, 0x2a, 0x6e, 0xc3, 0xb6, 0x1d, 0xf7, 0xf7, 0xeb, 0x43, 0xc1, 0x27, 0x70,
- 0xd6, 0xec, 0x96, 0x8f, 0xc0, 0x51, 0x07, 0xfc, 0xd7, 0x4d, 0x3e, 0xc3, 0xd0, 0xcc, 0x25, 0xd6,
- 0x75, 0xac, 0xf9, 0x5b, 0x38, 0x2e, 0xe3, 0x5d, 0xe8, 0x34, 0x17, 0x1f, 0xd8, 0x74, 0x7f, 0xb5,
- 0x6e, 0x93, 0xf1, 0x2e, 0x98, 0x6e, 0xe5, 0x63, 0x18, 0x6c, 0x42, 0xd3, 0x2c, 0x7e, 0x86, 0x83,
- 0xf1, 0x5f, 0x7c, 0xff, 0xa7, 0x0f, 0xd9, 0x03, 0xcd, 0x47, 0x30, 0xb0, 0x5e, 0x4a, 0xb4, 0x96,
- 0x3d, 0xe1, 0xe7, 0x90, 0x49, 0x41, 0x12, 0x67, 0x33, 0x54, 0xac, 0xc7, 0x2f, 0xe1, 0xdc, 0xd3,
- 0x17, 0xca, 0x7f, 0x50, 0x81, 0xc6, 0xe4, 0x86, 0xf5, 0x13, 0x71, 0x25, 0x54, 0xa1, 0x69, 0xee,
- 0x1d, 0x3b, 0xe2, 0x2f, 0xe0, 0x52, 0xa1, 0x50, 0x33, 0x4d, 0x58, 0xe0, 0x8d, 0x44, 0x54, 0xa8,
- 0xd8, 0x31, 0x7f, 0x06, 0x4f, 0x35, 0x39, 0xfc, 0x64, 0xb4, 0xbb, 0x3d, 0xfc, 0x7a, 0x92, 0xd8,
- 0x39, 0x9a, 0xaf, 0xda, 0x5a, 0x9d, 0x53, 0xa1, 0x90, 0x34, 0x2a, 0x76, 0xca, 0x5f, 0x02, 0x37,
- 0x68, 0x73, 0x6f, 0x64, 0x3a, 0xe2, 0x5a, 0x78, 0xeb, 0x50, 0xb1, 0x01, 0x7f, 0x0e, 0xec, 0x9b,
- 0x47, 0x73, 0x5b, 0x50, 0xee, 0x0a, 0x8b, 0xe6, 0x3b, 0x2a, 0x36, 0x4c, 0xfe, 0x94, 0x35, 0x15,
- 0xee, 0x86, 0x65, 0x9c, 0xc3, 0x45, 0x12, 0x19, 0x12, 0xb3, 0x83, 0x07, 0x92, 0xdc, 0x19, 0x41,
- 0x56, 0x23, 0xb9, 0x43, 0x39, 0x4a, 0xa5, 0x27, 0xe1, 0xdd, 0x35, 0x92, 0xd3, 0x52, 0x24, 0xc5,
- 0xd9, 0xd5, 0x2b, 0x18, 0x97, 0x71, 0x33, 0xfd, 0x1d, 0x77, 0xed, 0x6e, 0x19, 0xa6, 0xf7, 0xab,
- 0x36, 0x34, 0xcd, 0xfe, 0x91, 0x97, 0xa7, 0xdd, 0xe7, 0xe3, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff,
- 0x7c, 0x77, 0xfd, 0x16, 0xfa, 0x01, 0x00, 0x00,
+ // 590 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x64, 0x93, 0x4d, 0x4f, 0xdb, 0x40,
+ 0x10, 0x86, 0xc9, 0x07, 0xf9, 0x18, 0x07, 0xb2, 0x0c, 0x5f, 0x81, 0x52, 0xb5, 0xe2, 0x54, 0x71,
+ 0xc8, 0xa1, 0x3d, 0xf4, 0xbc, 0xf1, 0x0e, 0x61, 0x85, 0x59, 0xa7, 0xeb, 0x35, 0x25, 0xa7, 0x55,
+ 0x08, 0x16, 0xa2, 0x0a, 0x38, 0x72, 0x02, 0x12, 0x97, 0xfe, 0xac, 0xfe, 0xa6, 0xfe, 0x8c, 0x6a,
+ 0x9d, 0xb8, 0x28, 0xe4, 0x94, 0xec, 0xfb, 0xcc, 0xce, 0xbe, 0xf3, 0x8e, 0x0c, 0xde, 0xcb, 0x3c,
+ 0x9b, 0x8e, 0xbb, 0xd3, 0x2c, 0x9d, 0xa7, 0xb8, 0x99, 0x1f, 0x4e, 0x7f, 0x41, 0xc3, 0x1f, 0x4d,
+ 0x26, 0x49, 0x26, 0x05, 0x9e, 0x40, 0x73, 0x9a, 0x3d, 0x3c, 0x8d, 0x1f, 0xa6, 0xa3, 0x49, 0xa7,
+ 0xf4, 0xb9, 0xf4, 0xa5, 0xa9, 0xdf, 0x04, 0x47, 0xc7, 0xe9, 0xe3, 0x34, 0x7d, 0x4a, 0x9e, 0xe6,
+ 0x9d, 0xf2, 0x82, 0xfe, 0x17, 0xf0, 0x14, 0x5a, 0xb3, 0xe7, 0xdb, 0xb7, 0x82, 0x4a, 0x5e, 0xb0,
+ 0xa2, 0x9d, 0xfe, 0x86, 0x86, 0x1e, 0xf8, 0x94, 0x65, 0x69, 0x86, 0xdf, 0xc1, 0x9b, 0x24, 0xf7,
+ 0xa3, 0xf1, 0xab, 0x1d, 0xa7, 0x77, 0x49, 0xfe, 0xda, 0xf6, 0xd7, 0x83, 0xee, 0xc2, 0x61, 0x90,
+ 0x93, 0xbc, 0xd0, 0x4f, 0xef, 0x12, 0x0d, 0x8b, 0x52, 0xf7, 0x1f, 0x3b, 0x50, 0x7f, 0x4c, 0x66,
+ 0xb3, 0xd1, 0x7d, 0xb2, 0x34, 0x51, 0x1c, 0xf1, 0x13, 0x54, 0xf3, 0x5e, 0x95, 0xbc, 0x97, 0xb7,
+ 0xec, 0x95, 0x37, 0xc8, 0xc1, 0xd9, 0x9f, 0x32, 0x54, 0xf3, 0x1e, 0x35, 0x28, 0x87, 0x97, 0x6c,
+ 0x03, 0x5b, 0xd0, 0xf0, 0xb9, 0xf2, 0x29, 0x20, 0xc1, 0x4a, 0xe8, 0x41, 0x3d, 0x56, 0x97, 0x2a,
+ 0xfc, 0xa9, 0x58, 0x19, 0xf7, 0x80, 0x49, 0x75, 0xcd, 0x03, 0x29, 0x2c, 0xd7, 0xfd, 0xf8, 0x8a,
+ 0x94, 0x61, 0x15, 0xdc, 0x87, 0x1d, 0x41, 0x5c, 0x04, 0x52, 0x91, 0xa5, 0x1b, 0x9f, 0x48, 0x90,
+ 0x60, 0x55, 0xdc, 0x82, 0xa6, 0x0a, 0x8d, 0x3d, 0x0f, 0x63, 0x25, 0xd8, 0x26, 0x22, 0x6c, 0xf3,
+ 0x40, 0x13, 0x17, 0x43, 0x4b, 0x37, 0x32, 0x32, 0x11, 0xab, 0xb9, 0x9b, 0x03, 0xd2, 0x57, 0x32,
+ 0x8a, 0x64, 0xa8, 0xac, 0x20, 0x25, 0x49, 0xb0, 0x3a, 0xee, 0x42, 0x3b, 0x56, 0x3c, 0x36, 0x17,
+ 0xa4, 0x8c, 0xf4, 0xb9, 0x21, 0xc1, 0x18, 0x1e, 0x00, 0x6a, 0x8a, 0xc2, 0x58, 0xfb, 0xee, 0x95,
+ 0x0b, 0x1e, 0x47, 0x4e, 0x6f, 0xe0, 0x21, 0xec, 0x9e, 0x73, 0x19, 0x90, 0xb0, 0x03, 0x4d, 0x7e,
+ 0xa8, 0x84, 0x34, 0x32, 0x54, 0xac, 0xe9, 0x9c, 0xf3, 0x5e, 0xa8, 0x5d, 0x15, 0x20, 0x83, 0x56,
+ 0x18, 0x1b, 0x1b, 0x9e, 0x5b, 0xcd, 0x55, 0x9f, 0x98, 0x87, 0x3b, 0xb0, 0x15, 0x2b, 0x79, 0x35,
+ 0x08, 0xc8, 0x8d, 0x41, 0x82, 0xb5, 0xdc, 0xe4, 0x52, 0x19, 0xd2, 0x8a, 0x07, 0x6c, 0x0b, 0xdb,
+ 0xe0, 0xc5, 0x8a, 0x5f, 0x73, 0x19, 0xf0, 0x5e, 0x40, 0x6c, 0xdb, 0x0d, 0x24, 0xb8, 0xe1, 0x36,
+ 0x08, 0xa3, 0x88, 0xb5, 0xcf, 0xfe, 0x96, 0xa1, 0xfd, 0x6e, 0x27, 0x6e, 0xc8, 0x28, 0xf6, 0x7d,
+ 0x8a, 0x22, 0x1b, 0x50, 0x9f, 0xfb, 0x43, 0xb6, 0xe1, 0x42, 0x5b, 0xe4, 0xe9, 0x3c, 0x2e, 0xd5,
+ 0x12, 0x76, 0x60, 0x6f, 0x99, 0xab, 0x25, 0xad, 0x43, 0x5d, 0x90, 0x3c, 0xe4, 0x1e, 0x17, 0x56,
+ 0xaa, 0x41, 0x6c, 0x0a, 0xb5, 0x82, 0x27, 0xd0, 0x59, 0x0b, 0xb9, 0xa0, 0x55, 0x3c, 0x86, 0x03,
+ 0xe7, 0xbc, 0xaf, 0xa5, 0x19, 0xae, 0xf6, 0xdb, 0x74, 0x37, 0xd7, 0x42, 0x2e, 0x68, 0x0d, 0x3f,
+ 0xc2, 0xd1, 0x7a, 0xac, 0x05, 0xae, 0xe3, 0x07, 0x38, 0xfc, 0x11, 0x93, 0x1e, 0x5a, 0xb7, 0xca,
+ 0x88, 0xf4, 0xf5, 0x1b, 0x6c, 0x38, 0xa7, 0x4e, 0x96, 0xca, 0x9a, 0x9b, 0x42, 0x6d, 0xe2, 0x11,
+ 0xec, 0x17, 0x29, 0xae, 0x5a, 0x01, 0x67, 0xd3, 0x68, 0xae, 0x22, 0x49, 0xca, 0xac, 0x32, 0xcf,
+ 0xb1, 0x77, 0x4b, 0x2f, 0x58, 0xab, 0x77, 0x0c, 0x9d, 0x71, 0xfa, 0xd8, 0x7d, 0x4d, 0x9f, 0xe7,
+ 0xcf, 0xb7, 0x49, 0xf7, 0xe5, 0x61, 0x9e, 0xcc, 0x66, 0x8b, 0x4f, 0xf6, 0xb6, 0x96, 0xff, 0x7c,
+ 0xfb, 0x17, 0x00, 0x00, 0xff, 0xff, 0x5c, 0x6b, 0x47, 0xf6, 0xc8, 0x03, 0x00, 0x00,
}
diff --git a/go/vt/proto/vttest/vttest.pb.go b/go/vt/proto/vttest/vttest.pb.go
index 4457ce4edb4..18bb04df568 100644
--- a/go/vt/proto/vttest/vttest.pb.go
+++ b/go/vt/proto/vttest/vttest.pb.go
@@ -47,6 +47,20 @@ func (m *Shard) String() string { return proto.CompactTextString(m) }
func (*Shard) ProtoMessage() {}
func (*Shard) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (m *Shard) GetName() string {
+ if m != nil {
+ return m.Name
+ }
+ return ""
+}
+
+func (m *Shard) GetDbNameOverride() string {
+ if m != nil {
+ return m.DbNameOverride
+ }
+ return ""
+}
+
// Keyspace describes a single keyspace.
type Keyspace struct {
// name has to be unique in a VTTestTopology.
@@ -70,6 +84,13 @@ func (m *Keyspace) String() string { return proto.CompactTextString(m
func (*Keyspace) ProtoMessage() {}
func (*Keyspace) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+func (m *Keyspace) GetName() string {
+ if m != nil {
+ return m.Name
+ }
+ return ""
+}
+
func (m *Keyspace) GetShards() []*Shard {
if m != nil {
return m.Shards
@@ -77,6 +98,41 @@ func (m *Keyspace) GetShards() []*Shard {
return nil
}
+func (m *Keyspace) GetShardingColumnName() string {
+ if m != nil {
+ return m.ShardingColumnName
+ }
+ return ""
+}
+
+func (m *Keyspace) GetShardingColumnType() string {
+ if m != nil {
+ return m.ShardingColumnType
+ }
+ return ""
+}
+
+func (m *Keyspace) GetServedFrom() string {
+ if m != nil {
+ return m.ServedFrom
+ }
+ return ""
+}
+
+func (m *Keyspace) GetReplicaCount() int32 {
+ if m != nil {
+ return m.ReplicaCount
+ }
+ return 0
+}
+
+func (m *Keyspace) GetRdonlyCount() int32 {
+ if m != nil {
+ return m.RdonlyCount
+ }
+ return 0
+}
+
// VTTestTopology describes the keyspaces in the topology.
type VTTestTopology struct {
// all keyspaces in the topology.
@@ -97,6 +153,13 @@ func (m *VTTestTopology) GetKeyspaces() []*Keyspace {
return nil
}
+func (m *VTTestTopology) GetCells() []string {
+ if m != nil {
+ return m.Cells
+ }
+ return nil
+}
+
func init() {
proto.RegisterType((*Shard)(nil), "vttest.Shard")
proto.RegisterType((*Keyspace)(nil), "vttest.Keyspace")
diff --git a/go/vt/proto/vtworkerdata/vtworkerdata.pb.go b/go/vt/proto/vtworkerdata/vtworkerdata.pb.go
index 71559a88fbe..c15c4236931 100644
--- a/go/vt/proto/vtworkerdata/vtworkerdata.pb.go
+++ b/go/vt/proto/vtworkerdata/vtworkerdata.pb.go
@@ -40,6 +40,13 @@ func (m *ExecuteVtworkerCommandRequest) String() string { return prot
func (*ExecuteVtworkerCommandRequest) ProtoMessage() {}
func (*ExecuteVtworkerCommandRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (m *ExecuteVtworkerCommandRequest) GetArgs() []string {
+ if m != nil {
+ return m.Args
+ }
+ return nil
+}
+
// ExecuteVtworkerCommandResponse is streamed back by ExecuteVtworkerCommand.
type ExecuteVtworkerCommandResponse struct {
Event *logutil.Event `protobuf:"bytes,1,opt,name=event" json:"event,omitempty"`
diff --git a/go/vt/proto/workflow/workflow.pb.go b/go/vt/proto/workflow/workflow.pb.go
index 70ce2abe855..cb3cf7a64d6 100644
--- a/go/vt/proto/workflow/workflow.pb.go
+++ b/go/vt/proto/workflow/workflow.pb.go
@@ -10,6 +10,8 @@ It is generated from these files:
It has these top-level messages:
Workflow
+ WorkflowCheckpoint
+ Task
*/
package workflow
@@ -56,6 +58,30 @@ func (x WorkflowState) String() string {
}
func (WorkflowState) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+type TaskState int32
+
+const (
+ TaskState_TaskNotStarted TaskState = 0
+ TaskState_TaskRunning TaskState = 1
+ TaskState_TaskDone TaskState = 2
+)
+
+var TaskState_name = map[int32]string{
+ 0: "TaskNotStarted",
+ 1: "TaskRunning",
+ 2: "TaskDone",
+}
+var TaskState_value = map[string]int32{
+ "TaskNotStarted": 0,
+ "TaskRunning": 1,
+ "TaskDone": 2,
+}
+
+func (x TaskState) String() string {
+ return proto.EnumName(TaskState_name, int32(x))
+}
+func (TaskState) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+
// Workflow is the persisted state of a long-running workflow.
type Workflow struct {
// uuid is set when the workflow is created, and immutable after
@@ -97,29 +123,183 @@ func (m *Workflow) String() string { return proto.CompactTextString(m
func (*Workflow) ProtoMessage() {}
func (*Workflow) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (m *Workflow) GetUuid() string {
+ if m != nil {
+ return m.Uuid
+ }
+ return ""
+}
+
+func (m *Workflow) GetFactoryName() string {
+ if m != nil {
+ return m.FactoryName
+ }
+ return ""
+}
+
+func (m *Workflow) GetName() string {
+ if m != nil {
+ return m.Name
+ }
+ return ""
+}
+
+func (m *Workflow) GetState() WorkflowState {
+ if m != nil {
+ return m.State
+ }
+ return WorkflowState_NotStarted
+}
+
+func (m *Workflow) GetData() []byte {
+ if m != nil {
+ return m.Data
+ }
+ return nil
+}
+
+func (m *Workflow) GetError() string {
+ if m != nil {
+ return m.Error
+ }
+ return ""
+}
+
+func (m *Workflow) GetStartTime() int64 {
+ if m != nil {
+ return m.StartTime
+ }
+ return 0
+}
+
+func (m *Workflow) GetEndTime() int64 {
+ if m != nil {
+ return m.EndTime
+ }
+ return 0
+}
+
+type WorkflowCheckpoint struct {
+ // code_version is used to detect incompabilities between the version of the
+ // running workflow and the one which wrote the checkpoint. If they don't
+ // match, the workflow must not continue. The author of workflow must update
+ // this variable in their implementation when incompabilities are introduced.
+ CodeVersion int32 `protobuf:"varint,1,opt,name=code_version,json=codeVersion" json:"code_version,omitempty"`
+ // Task is the data structure that stores the execution status and the
+ // attributes of a task.
+ Tasks map[string]*Task `protobuf:"bytes,2,rep,name=tasks" json:"tasks,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ // settings includes workflow specific data, e.g. the resharding workflow
+ // would store the source shards and destination shards.
+ Settings map[string]string `protobuf:"bytes,3,rep,name=settings" json:"settings,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+}
+
+func (m *WorkflowCheckpoint) Reset() { *m = WorkflowCheckpoint{} }
+func (m *WorkflowCheckpoint) String() string { return proto.CompactTextString(m) }
+func (*WorkflowCheckpoint) ProtoMessage() {}
+func (*WorkflowCheckpoint) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+
+func (m *WorkflowCheckpoint) GetCodeVersion() int32 {
+ if m != nil {
+ return m.CodeVersion
+ }
+ return 0
+}
+
+func (m *WorkflowCheckpoint) GetTasks() map[string]*Task {
+ if m != nil {
+ return m.Tasks
+ }
+ return nil
+}
+
+func (m *WorkflowCheckpoint) GetSettings() map[string]string {
+ if m != nil {
+ return m.Settings
+ }
+ return nil
+}
+
+type Task struct {
+ Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
+ State TaskState `protobuf:"varint,2,opt,name=state,enum=workflow.TaskState" json:"state,omitempty"`
+ // attributes includes the parameters the task needs.
+ Attributes map[string]string `protobuf:"bytes,3,rep,name=attributes" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ Error string `protobuf:"bytes,4,opt,name=error" json:"error,omitempty"`
+}
+
+func (m *Task) Reset() { *m = Task{} }
+func (m *Task) String() string { return proto.CompactTextString(m) }
+func (*Task) ProtoMessage() {}
+func (*Task) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+
+func (m *Task) GetId() string {
+ if m != nil {
+ return m.Id
+ }
+ return ""
+}
+
+func (m *Task) GetState() TaskState {
+ if m != nil {
+ return m.State
+ }
+ return TaskState_TaskNotStarted
+}
+
+func (m *Task) GetAttributes() map[string]string {
+ if m != nil {
+ return m.Attributes
+ }
+ return nil
+}
+
+func (m *Task) GetError() string {
+ if m != nil {
+ return m.Error
+ }
+ return ""
+}
+
func init() {
proto.RegisterType((*Workflow)(nil), "workflow.Workflow")
+ proto.RegisterType((*WorkflowCheckpoint)(nil), "workflow.WorkflowCheckpoint")
+ proto.RegisterType((*Task)(nil), "workflow.Task")
proto.RegisterEnum("workflow.WorkflowState", WorkflowState_name, WorkflowState_value)
+ proto.RegisterEnum("workflow.TaskState", TaskState_name, TaskState_value)
}
func init() { proto.RegisterFile("workflow.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
- // 246 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x54, 0x90, 0x41, 0x4b, 0x03, 0x31,
- 0x10, 0x85, 0x4d, 0xbb, 0xdb, 0x4d, 0xa7, 0x75, 0x59, 0x06, 0xc1, 0x78, 0x10, 0x56, 0x4f, 0x8b,
- 0x60, 0x0f, 0x0a, 0xfe, 0x02, 0xcf, 0x3d, 0xa4, 0x82, 0xc7, 0x12, 0xcd, 0x54, 0x16, 0xdd, 0x44,
- 0xd2, 0x59, 0x8a, 0xff, 0xd8, 0x9f, 0x21, 0xc9, 0x76, 0x85, 0xde, 0xde, 0x9b, 0x2f, 0x6f, 0x5e,
- 0x18, 0x28, 0x0f, 0x3e, 0x7c, 0xee, 0xbe, 0xfc, 0x61, 0xf5, 0x1d, 0x3c, 0x7b, 0x94, 0xa3, 0xbf,
- 0xfd, 0x15, 0x20, 0x5f, 0x8f, 0x06, 0x11, 0xb2, 0xbe, 0x6f, 0xad, 0x12, 0xb5, 0x68, 0xe6, 0x3a,
- 0x69, 0xbc, 0x81, 0xe5, 0xce, 0xbc, 0xb3, 0x0f, 0x3f, 0x5b, 0x67, 0x3a, 0x52, 0x93, 0xc4, 0x16,
- 0xc7, 0xd9, 0xda, 0x74, 0x14, 0x63, 0x09, 0x4d, 0x87, 0x58, 0xd4, 0x78, 0x0f, 0xf9, 0x9e, 0x0d,
- 0x93, 0xca, 0x6a, 0xd1, 0x94, 0x0f, 0x97, 0xab, 0xff, 0x1f, 0x8c, 0x6d, 0x9b, 0x88, 0xf5, 0xf0,
- 0x2a, 0xae, 0xb0, 0x86, 0x8d, 0xca, 0x6b, 0xd1, 0x2c, 0x75, 0xd2, 0x78, 0x01, 0x39, 0x85, 0xe0,
- 0x83, 0x9a, 0xa5, 0xbd, 0x83, 0xc1, 0x6b, 0x80, 0x3d, 0x9b, 0xc0, 0x5b, 0x6e, 0x3b, 0x52, 0x45,
- 0x2d, 0x9a, 0xa9, 0x9e, 0xa7, 0xc9, 0x4b, 0xdb, 0x11, 0x5e, 0x81, 0x24, 0x67, 0x07, 0x28, 0x13,
- 0x2c, 0xc8, 0xd9, 0x88, 0xee, 0x9e, 0xe0, 0xfc, 0xa4, 0x1b, 0x4b, 0x80, 0xb5, 0xe7, 0x4d, 0xcc,
- 0x92, 0xad, 0xce, 0x70, 0x01, 0x85, 0xee, 0x9d, 0x6b, 0xdd, 0x47, 0x25, 0x50, 0x42, 0xf6, 0xec,
- 0x1d, 0x55, 0x93, 0xb7, 0x59, 0xba, 0xd9, 0xe3, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe2, 0x1f,
- 0x18, 0x22, 0x45, 0x01, 0x00, 0x00,
+ // 477 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x53, 0xdb, 0x6e, 0xd3, 0x40,
+ 0x10, 0x65, 0x7d, 0x69, 0x9c, 0x71, 0xea, 0x46, 0x43, 0x25, 0x4c, 0x24, 0x90, 0x89, 0x90, 0x30,
+ 0x91, 0xc8, 0x43, 0x90, 0x10, 0x02, 0xb5, 0x12, 0xe2, 0x22, 0x9e, 0xfa, 0xe0, 0x54, 0xf0, 0x18,
+ 0x6d, 0xe3, 0x6d, 0x59, 0xa5, 0xd9, 0xad, 0xd6, 0xeb, 0x56, 0xf9, 0x40, 0x7e, 0x81, 0x6f, 0xe0,
+ 0x33, 0xd0, 0xee, 0xc6, 0x4e, 0x0c, 0x08, 0x89, 0xb7, 0x99, 0x39, 0x73, 0xce, 0x78, 0xf6, 0x8c,
+ 0x21, 0xb9, 0x93, 0x6a, 0x75, 0x79, 0x2d, 0xef, 0xa6, 0x37, 0x4a, 0x6a, 0x89, 0x51, 0x93, 0x8f,
+ 0x7f, 0x12, 0x88, 0xbe, 0x6e, 0x13, 0x44, 0x08, 0xea, 0x9a, 0x97, 0x29, 0xc9, 0x48, 0xde, 0x2f,
+ 0x6c, 0x8c, 0x4f, 0x60, 0x70, 0x49, 0x97, 0x5a, 0xaa, 0xcd, 0x42, 0xd0, 0x35, 0x4b, 0x3d, 0x8b,
+ 0xc5, 0xdb, 0xda, 0x19, 0x5d, 0x33, 0x43, 0xb3, 0x90, 0xef, 0x68, 0x26, 0xc6, 0x17, 0x10, 0x56,
+ 0x9a, 0x6a, 0x96, 0x06, 0x19, 0xc9, 0x93, 0xd9, 0x83, 0x69, 0xfb, 0x05, 0xcd, 0xb4, 0xb9, 0x81,
+ 0x0b, 0xd7, 0x65, 0x24, 0x4a, 0xaa, 0x69, 0x1a, 0x66, 0x24, 0x1f, 0x14, 0x36, 0xc6, 0x63, 0x08,
+ 0x99, 0x52, 0x52, 0xa5, 0x07, 0x56, 0xd7, 0x25, 0xf8, 0x08, 0xa0, 0xd2, 0x54, 0xe9, 0x85, 0xe6,
+ 0x6b, 0x96, 0xf6, 0x32, 0x92, 0xfb, 0x45, 0xdf, 0x56, 0xce, 0xf9, 0x9a, 0xe1, 0x43, 0x88, 0x98,
+ 0x28, 0x1d, 0x18, 0x59, 0xb0, 0xc7, 0x44, 0x69, 0xa0, 0xf1, 0x77, 0x0f, 0xb0, 0x19, 0xfe, 0xfe,
+ 0x1b, 0x5b, 0xae, 0x6e, 0x24, 0x17, 0xda, 0x2c, 0xb8, 0x94, 0x25, 0x5b, 0xdc, 0x32, 0x55, 0x71,
+ 0x29, 0xec, 0xf2, 0x61, 0x11, 0x9b, 0xda, 0x17, 0x57, 0xc2, 0x13, 0x08, 0x35, 0xad, 0x56, 0x55,
+ 0xea, 0x65, 0x7e, 0x1e, 0xcf, 0x9e, 0xfd, 0xb9, 0xcc, 0x4e, 0x6f, 0x7a, 0x6e, 0x3a, 0x3f, 0x0a,
+ 0xad, 0x36, 0x85, 0x63, 0xe1, 0x27, 0x88, 0x2a, 0xa6, 0x35, 0x17, 0x57, 0x55, 0xea, 0x5b, 0x85,
+ 0xc9, 0x3f, 0x15, 0xe6, 0xdb, 0x66, 0x27, 0xd2, 0x72, 0x47, 0x9f, 0x01, 0x76, 0xe2, 0x38, 0x04,
+ 0x7f, 0xc5, 0x36, 0x5b, 0xaf, 0x4c, 0x88, 0x4f, 0x21, 0xbc, 0xa5, 0xd7, 0xb5, 0xf3, 0x28, 0x9e,
+ 0x25, 0xbb, 0x21, 0x86, 0x56, 0x38, 0xf0, 0x8d, 0xf7, 0x9a, 0x8c, 0xde, 0xc2, 0x61, 0x67, 0xc8,
+ 0x5f, 0xc4, 0x8e, 0xf7, 0xc5, 0xfa, 0x7b, 0xe4, 0xf1, 0x0f, 0x02, 0x81, 0x11, 0xc4, 0x04, 0xbc,
+ 0xf6, 0x58, 0x3c, 0x5e, 0xe2, 0xf3, 0xc6, 0x73, 0xcf, 0x7a, 0x7e, 0xbf, 0x3b, 0xbf, 0xe3, 0xf7,
+ 0x29, 0x00, 0xd5, 0x5a, 0xf1, 0x8b, 0x5a, 0xb3, 0xe6, 0x51, 0x1e, 0x77, 0xfb, 0xa7, 0xef, 0xda,
+ 0x06, 0xf7, 0x10, 0x7b, 0x8c, 0xdd, 0x6d, 0x04, 0x7b, 0xb7, 0x31, 0x3a, 0x81, 0xa3, 0xdf, 0x48,
+ 0xff, 0xb3, 0xd8, 0xe4, 0x15, 0x1c, 0x76, 0x8e, 0x13, 0x13, 0x80, 0x33, 0xa9, 0xe7, 0xe6, 0xb8,
+ 0x58, 0x39, 0xbc, 0x87, 0x31, 0xf4, 0x8a, 0x5a, 0x08, 0x2e, 0xae, 0x86, 0x04, 0x23, 0x08, 0x3e,
+ 0x48, 0xc1, 0x86, 0xde, 0xe4, 0x14, 0xfa, 0xed, 0x82, 0x88, 0x90, 0x98, 0xa4, 0xc3, 0x3b, 0x82,
+ 0xd8, 0x3a, 0xd0, 0x72, 0x07, 0x10, 0x99, 0x82, 0xe3, 0x5f, 0x1c, 0xd8, 0x9f, 0xf2, 0xe5, 0xaf,
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x72, 0x5c, 0x6d, 0x7f, 0xa6, 0x03, 0x00, 0x00,
}
diff --git a/go/vt/schemamanager/schemamanager_test.go b/go/vt/schemamanager/schemamanager_test.go
index 1131473c7bb..c6fe1bc31e7 100644
--- a/go/vt/schemamanager/schemamanager_test.go
+++ b/go/vt/schemamanager/schemamanager_test.go
@@ -11,11 +11,11 @@ import (
"github.com/youtube/vitess/go/vt/logutil"
"github.com/youtube/vitess/go/vt/mysqlctl/tmutils"
- "github.com/youtube/vitess/go/vt/tabletmanager/faketmclient"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/test/faketopo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
+ "github.com/youtube/vitess/go/vt/vttablet/faketmclient"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
"golang.org/x/net/context"
@@ -24,7 +24,7 @@ import (
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
// import the gRPC client implementation for tablet manager
- _ "github.com/youtube/vitess/go/vt/tabletmanager/grpctmclient"
+ _ "github.com/youtube/vitess/go/vt/vttablet/grpctmclient"
)
var (
diff --git a/go/vt/schemamanager/schemaswap/schema_swap.go b/go/vt/schemamanager/schemaswap/schema_swap.go
index 9a750af896f..5afc20d046f 100644
--- a/go/vt/schemamanager/schemaswap/schema_swap.go
+++ b/go/vt/schemamanager/schemaswap/schema_swap.go
@@ -21,9 +21,9 @@ import (
"github.com/youtube/vitess/go/vt/logutil"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
workflowpb "github.com/youtube/vitess/go/vt/proto/workflow"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/vtctl"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/workflow"
"github.com/youtube/vitess/go/vt/wrangler"
)
@@ -161,7 +161,7 @@ func RegisterWorkflowFactory() {
}
// Init is a part of workflow.Factory interface. It initializes a Workflow protobuf object.
-func (*SwapWorkflowFactory) Init(workflowProto *workflowpb.Workflow, args []string) error {
+func (*SwapWorkflowFactory) Init(_ *workflow.Manager, workflowProto *workflowpb.Workflow, args []string) error {
subFlags := flag.NewFlagSet(workflowFactoryName, flag.ContinueOnError)
keyspace := subFlags.String("keyspace", "", "Name of a keyspace to perform schema swap on")
@@ -188,7 +188,7 @@ func (*SwapWorkflowFactory) Init(workflowProto *workflowpb.Workflow, args []stri
// Instantiate is a part of workflow.Factory interface. It instantiates workflow.Workflow object from
// workflowpb.Workflow protobuf object.
-func (*SwapWorkflowFactory) Instantiate(workflowProto *workflowpb.Workflow, rootNode *workflow.Node) (workflow.Workflow, error) {
+func (*SwapWorkflowFactory) Instantiate(_ *workflow.Manager, workflowProto *workflowpb.Workflow, rootNode *workflow.Node) (workflow.Workflow, error) {
data := &swapWorkflowData{}
if err := json.Unmarshal(workflowProto.Data, data); err != nil {
return nil, err
diff --git a/go/vt/schemamanager/tablet_executor.go b/go/vt/schemamanager/tablet_executor.go
index eb0f9e91092..1705feee24a 100644
--- a/go/vt/schemamanager/tablet_executor.go
+++ b/go/vt/schemamanager/tablet_executor.go
@@ -140,10 +140,11 @@ func (exec *TabletExecutor) detectBigSchemaChanges(ctx context.Context, parsedDD
tableWithCount[tableSchema.Name] = tableSchema.RowCount
}
for _, ddl := range parsedDDLs {
- if ddl.Action == sqlparser.DropStr {
+ switch ddl.Action {
+ case sqlparser.DropStr, sqlparser.CreateStr:
continue
}
- tableName := ddl.Table.String()
+ tableName := ddl.Table.Name.String()
if rowCount, ok := tableWithCount[tableName]; ok {
if rowCount > 100000 && ddl.Action == sqlparser.AlterStr {
return true, fmt.Errorf(
diff --git a/go/vt/servenv/grpc_server.go b/go/vt/servenv/grpc_server.go
index f8ae96bb23b..8193459b3c4 100644
--- a/go/vt/servenv/grpc_server.go
+++ b/go/vt/servenv/grpc_server.go
@@ -5,17 +5,15 @@
package servenv
import (
- "crypto/tls"
- "crypto/x509"
"flag"
"fmt"
- "io/ioutil"
"net"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
log "github.com/golang/glog"
+ "github.com/youtube/vitess/go/vt/servenv/grpcutils"
)
// This file handles gRPC server, on its own port.
@@ -76,28 +74,9 @@ func createGRPCServer() {
var opts []grpc.ServerOption
if GRPCPort != nil && *GRPCCert != "" && *GRPCKey != "" {
- config := &tls.Config{}
-
- // load the server cert and key
- cert, err := tls.LoadX509KeyPair(*GRPCCert, *GRPCKey)
+ config, err := grpcutils.TLSServerConfig(*GRPCCert, *GRPCKey, *GRPCCA)
if err != nil {
- log.Fatalf("Failed to load cert/key: %v", err)
- }
- config.Certificates = []tls.Certificate{cert}
-
- // if specified, load ca to validate client,
- // and enforce clients present valid certs.
- if *GRPCCA != "" {
- b, err := ioutil.ReadFile(*GRPCCA)
- if err != nil {
- log.Fatalf("Failed to read ca file: %v", err)
- }
- cp := x509.NewCertPool()
- if !cp.AppendCertsFromPEM(b) {
- log.Fatalf("Failed to append certificates")
- }
- config.ClientCAs = cp
- config.ClientAuth = tls.RequireAndVerifyClientCert
+ log.Fatalf("Failed to log gRPC cert/key/ca: %v", err)
}
// create the creds server options
diff --git a/go/vt/servenv/grpcutils/client_tls.go b/go/vt/servenv/grpcutils/client_tls.go
index 7fc8f33f454..61802525780 100644
--- a/go/vt/servenv/grpcutils/client_tls.go
+++ b/go/vt/servenv/grpcutils/client_tls.go
@@ -10,17 +10,12 @@ import (
"google.golang.org/grpc/credentials"
)
-// ClientSecureDialOption returns the gRPC dial option to use for the given client
-// connection. It is either using TLS, or Insecure if nothing is set.
-func ClientSecureDialOption(cert, key, ca, name string) (grpc.DialOption, error) {
- // no secuirty options set, just return
- if (cert == "" || key == "") && ca == "" {
- return grpc.WithInsecure(), nil
- }
-
+// TLSClientConfig returns the TLS config to use for a client to
+// connect to a server with the provided parameters.
+func TLSClientConfig(cert, key, ca, name string) (*tls.Config, error) {
config := &tls.Config{}
- // load the client-side cert & key if any
+ // Load the client-side cert & key if any.
if cert != "" && key != "" {
crt, err := tls.LoadX509KeyPair(cert, key)
if err != nil {
@@ -29,7 +24,7 @@ func ClientSecureDialOption(cert, key, ca, name string) (grpc.DialOption, error)
config.Certificates = []tls.Certificate{crt}
}
- // load the server ca if any
+ // Load the server CA if any.
if ca != "" {
b, err := ioutil.ReadFile(ca)
if err != nil {
@@ -42,12 +37,30 @@ func ClientSecureDialOption(cert, key, ca, name string) (grpc.DialOption, error)
config.RootCAs = cp
}
- // set the server name if any
+ // Set the server name if any.
if name != "" {
config.ServerName = name
}
- // create the creds server options
+ return config, nil
+}
+
+// ClientSecureDialOption returns the gRPC dial option to use for the
+// given client connection. It is either using TLS, or Insecure if
+// nothing is set.
+func ClientSecureDialOption(cert, key, ca, name string) (grpc.DialOption, error) {
+ // No security options set, just return.
+ if (cert == "" || key == "") && ca == "" {
+ return grpc.WithInsecure(), nil
+ }
+
+ // Load the config.
+ config, err := TLSClientConfig(cert, key, ca, name)
+ if err != nil {
+ return nil, err
+ }
+
+ // Create the creds server options.
creds := credentials.NewTLS(config)
return grpc.WithTransportCredentials(creds), nil
}
diff --git a/go/vt/servenv/grpcutils/server_tls.go b/go/vt/servenv/grpcutils/server_tls.go
new file mode 100644
index 00000000000..c7704144706
--- /dev/null
+++ b/go/vt/servenv/grpcutils/server_tls.go
@@ -0,0 +1,38 @@
+package grpcutils
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "io/ioutil"
+)
+
+// TLSServerConfig returns the TLS config to use for a server to
+// accept client connections.
+func TLSServerConfig(cert, key, ca string) (*tls.Config, error) {
+ config := &tls.Config{}
+
+ // Load the server cert and key.
+ crt, err := tls.LoadX509KeyPair(cert, key)
+ if err != nil {
+ return nil, fmt.Errorf("failed to load cert/key: %v", err)
+ }
+ config.Certificates = []tls.Certificate{crt}
+
+ // if specified, load ca to validate client,
+ // and enforce clients present valid certs.
+ if ca != "" {
+ b, err := ioutil.ReadFile(ca)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to read ca file: %v", err)
+ }
+ cp := x509.NewCertPool()
+ if !cp.AppendCertsFromPEM(b) {
+ return nil, fmt.Errorf("Failed to append certificates")
+ }
+ config.ClientCAs = cp
+ config.ClientAuth = tls.RequireAndVerifyClientCert
+ }
+
+ return config, nil
+}
diff --git a/go/vt/sqlparser/ast.go b/go/vt/sqlparser/ast.go
index 75424c310fa..33da94ff4c9 100644
--- a/go/vt/sqlparser/ast.go
+++ b/go/vt/sqlparser/ast.go
@@ -392,8 +392,8 @@ func (node *Set) WalkSubtree(visit Visit) error {
// NewName is set for AlterStr, CreateStr, RenameStr.
type DDL struct {
Action string
- Table TableIdent
- NewName TableIdent
+ Table *TableName
+ NewName *TableName
IfExists bool
}
@@ -725,6 +725,16 @@ func (node *TableName) Equal(t *TableName) bool {
return node.Name == t.Name && node.Qualifier == t.Qualifier
}
+// ToViewName returns a TableName acceptable for use as a VIEW. VIEW names are
+// always lowercase, so ToViewName lowercasese the name. Databases are case-sensitive
+// so Qualifier is left untouched.
+func (node *TableName) ToViewName() *TableName {
+ return &TableName{
+ Qualifier: node.Qualifier,
+ Name: NewTableIdent(strings.ToLower(node.Name.v)),
+ }
+}
+
// ParenTableExpr represents a parenthesized list of TableExpr.
type ParenTableExpr struct {
Exprs TableExprs
diff --git a/go/vt/sqlparser/sql.go b/go/vt/sqlparser/sql.go
index d20462ad18c..db61dff4665 100644
--- a/go/vt/sqlparser/sql.go
+++ b/go/vt/sqlparser/sql.go
@@ -363,333 +363,350 @@ var yyExca = [...]int{
-1, 85,
103, 307,
-2, 303,
- -1, 292,
+ -1, 295,
103, 309,
-2, 305,
}
-const yyNprod = 410
+const yyNprod = 413
const yyPrivate = 57344
var yyTokenNames []string
var yyStates []string
-const yyLast = 2931
+const yyLast = 3133
var yyAct = [...]int{
- 421, 429, 561, 243, 594, 79, 292, 523, 475, 532,
- 472, 593, 417, 328, 374, 267, 503, 487, 311, 501,
- 441, 233, 290, 309, 242, 502, 517, 408, 49, 100,
- 240, 414, 28, 61, 685, 385, 474, 678, 684, 671,
- 81, 683, 677, 597, 638, 266, 296, 670, 373, 3,
- 37, 96, 39, 87, 50, 51, 40, 43, 660, 342,
- 341, 351, 352, 344, 345, 346, 347, 348, 349, 350,
- 343, 80, 403, 353, 138, 124, 327, 465, 52, 466,
- 62, 326, 83, 465, 325, 466, 14, 88, 92, 465,
- 94, 466, 48, 106, 99, 302, 42, 72, 43, 142,
- 144, 45, 46, 47, 44, 62, 67, 111, 576, 300,
- 573, 81, 114, 140, 541, 74, 71, 661, 121, 81,
- 148, 109, 123, 245, 62, 467, 289, 353, 332, 331,
- 138, 467, 118, 63, 304, 236, 146, 467, 122, 139,
- 81, 125, 321, 669, 424, 333, 468, 293, 630, 633,
- 412, 62, 468, 83, 398, 666, 111, 307, 468, 147,
- 372, 83, 324, 147, 363, 364, 308, 143, 117, 113,
- 305, 343, 673, 306, 353, 312, 333, 116, 600, 488,
- 119, 542, 83, 488, 320, 549, 103, 235, 299, 301,
- 298, 667, 448, 76, 543, 346, 347, 348, 349, 350,
- 343, 72, 66, 353, 69, 265, 446, 447, 445, 648,
- 67, 652, 138, 120, 332, 331, 97, 331, 423, 74,
- 71, 65, 444, 70, 77, 73, 68, 75, 303, 332,
- 331, 333, 145, 333, 657, 423, 602, 90, 332, 331,
- 585, 586, 82, 559, 423, 89, 333, 115, 93, 435,
- 437, 438, 477, 330, 436, 333, 30, 334, 344, 345,
- 346, 347, 348, 349, 350, 343, 405, 423, 353, 583,
- 342, 341, 351, 352, 344, 345, 346, 347, 348, 349,
- 350, 343, 332, 331, 353, 329, 559, 375, 259, 258,
- 260, 261, 262, 263, 383, 405, 264, 76, 512, 333,
- 525, 526, 527, 423, 128, 138, 66, 132, 69, 578,
- 423, 643, 582, 82, 387, 388, 389, 390, 391, 392,
- 393, 82, 405, 291, 234, 65, 401, 70, 77, 73,
- 68, 75, 545, 423, 130, 418, 477, 423, 425, 422,
- 423, 415, 82, 323, 95, 406, 581, 413, 136, 399,
- 432, 433, 442, 420, 415, 127, 115, 646, 464, 62,
- 332, 331, 239, 614, 115, 361, 439, 612, 615, 138,
- 645, 611, 613, 610, 476, 478, 682, 333, 563, 566,
- 567, 568, 564, 681, 565, 569, 405, 490, 469, 470,
- 676, 616, 98, 567, 568, 56, 57, 481, 482, 679,
- 14, 505, 102, 317, 663, 496, 492, 495, 316, 674,
- 41, 485, 649, 479, 480, 101, 664, 312, 147, 107,
- 513, 511, 312, 312, 574, 319, 499, 516, 294, 500,
- 126, 491, 571, 493, 494, 510, 60, 508, 375, 102,
- 312, 312, 312, 312, 59, 497, 31, 430, 498, 521,
- 524, 312, 62, 605, 519, 520, 53, 54, 147, 315,
- 443, 442, 33, 34, 35, 36, 528, 314, 563, 566,
- 567, 568, 564, 535, 565, 569, 431, 329, 644, 604,
- 539, 540, 558, 234, 544, 78, 672, 268, 621, 14,
- 551, 30, 552, 553, 554, 555, 538, 32, 27, 1,
- 570, 108, 400, 407, 548, 295, 38, 550, 402, 297,
- 536, 86, 313, 135, 322, 483, 662, 410, 577, 312,
- 579, 580, 546, 584, 522, 575, 556, 572, 418, 603,
- 506, 312, 557, 547, 382, 486, 244, 591, 416, 587,
- 434, 255, 252, 14, 15, 16, 17, 254, 592, 253,
- 131, 318, 335, 62, 595, 596, 237, 141, 104, 562,
- 599, 560, 504, 404, 637, 659, 18, 55, 601, 443,
- 129, 29, 617, 619, 58, 13, 471, 607, 291, 609,
- 496, 12, 495, 312, 11, 606, 484, 608, 10, 9,
- 489, 626, 137, 628, 629, 8, 634, 635, 7, 632,
- 624, 625, 524, 6, 291, 62, 62, 62, 62, 365,
- 366, 367, 368, 369, 370, 410, 618, 640, 291, 5,
- 642, 639, 4, 641, 375, 81, 2, 647, 514, 0,
- 515, 506, 518, 518, 518, 0, 0, 0, 651, 0,
- 0, 0, 653, 19, 20, 22, 21, 23, 0, 656,
- 0, 0, 0, 0, 658, 0, 24, 25, 26, 0,
- 668, 665, 654, 655, 0, 0, 0, 83, 675, 351,
- 352, 344, 345, 346, 347, 348, 349, 350, 343, 680,
- 0, 353, 0, 506, 506, 506, 506, 341, 351, 352,
- 344, 345, 346, 347, 348, 349, 350, 343, 440, 0,
- 353, 449, 450, 451, 452, 453, 454, 455, 456, 457,
- 458, 459, 460, 461, 462, 463, 0, 342, 341, 351,
- 352, 344, 345, 346, 347, 348, 349, 350, 343, 362,
- 0, 353, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 85, 0, 0, 0, 0, 0, 0,
- 0, 598, 0, 371, 533, 0, 0, 376, 377, 378,
- 379, 380, 381, 0, 384, 386, 386, 386, 386, 386,
- 386, 386, 386, 394, 395, 396, 397, 0, 64, 291,
- 0, 0, 622, 91, 623, 0, 91, 0, 64, 0,
- 0, 0, 64, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 64, 0, 64, 0, 419, 0, 529,
- 530, 531, 0, 426, 427, 428, 0, 0, 0, 0,
- 0, 0, 64, 0, 0, 0, 0, 82, 0, 0,
- 64, 0, 0, 0, 0, 0, 64, 0, 0, 64,
- 0, 0, 91, 0, 0, 91, 0, 0, 0, 64,
- 0, 0, 0, 0, 64, 0, 0, 64, 0, 0,
- 0, 0, 0, 0, 72, 0, 0, 0, 64, 0,
- 0, 64, 0, 67, 0, 0, 0, 0, 0, 0,
- 0, 91, 74, 71, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 507, 0, 589, 590, 0, 0,
- 90, 0, 72, 133, 0, 473, 134, 241, 0, 0,
- 0, 67, 0, 0, 0, 0, 276, 0, 0, 0,
- 74, 71, 0, 0, 0, 0, 269, 270, 0, 0,
- 0, 0, 0, 0, 0, 138, 0, 0, 84, 259,
- 258, 260, 261, 262, 263, 0, 0, 264, 256, 257,
- 0, 627, 238, 250, 0, 275, 0, 0, 0, 0,
- 76, 0, 0, 537, 0, 0, 0, 0, 0, 66,
- 0, 69, 0, 0, 0, 247, 248, 310, 0, 0,
- 0, 287, 0, 249, 0, 0, 246, 251, 65, 0,
- 70, 77, 73, 68, 75, 507, 0, 0, 76, 650,
- 419, 285, 0, 0, 0, 0, 0, 66, 0, 69,
- 0, 277, 286, 283, 284, 281, 282, 280, 279, 278,
- 288, 271, 272, 274, 0, 273, 65, 0, 70, 77,
- 73, 68, 75, 72, 0, 0, 0, 110, 0, 0,
- 91, 0, 67, 0, 0, 0, 0, 507, 507, 507,
- 507, 74, 71, 0, 0, 91, 0, 64, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 63,
- 0, 112, 0, 0, 0, 0, 0, 0, 337, 0,
- 340, 0, 631, 0, 0, 636, 354, 355, 356, 357,
- 358, 359, 360, 0, 338, 339, 336, 342, 341, 351,
- 352, 344, 345, 346, 347, 348, 349, 350, 343, 0,
- 0, 353, 0, 0, 91, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 91, 0, 0, 0, 91, 76,
- 0, 0, 0, 0, 0, 0, 0, 0, 66, 0,
- 69, 0, 91, 64, 0, 0, 64, 0, 0, 0,
- 64, 0, 0, 91, 0, 0, 0, 65, 0, 70,
- 77, 73, 68, 75, 72, 0, 91, 0, 91, 241,
- 91, 91, 91, 67, 0, 0, 0, 0, 276, 0,
- 0, 0, 74, 71, 0, 0, 0, 0, 269, 270,
- 0, 0, 0, 0, 0, 0, 0, 138, 588, 0,
- 84, 259, 258, 260, 261, 262, 263, 0, 0, 264,
- 256, 257, 0, 0, 238, 250, 0, 275, 342, 341,
- 351, 352, 344, 345, 346, 347, 348, 349, 350, 343,
- 0, 0, 353, 0, 0, 0, 0, 247, 248, 310,
- 0, 0, 0, 287, 0, 249, 0, 0, 246, 251,
- 0, 64, 0, 0, 0, 0, 0, 0, 0, 0,
- 76, 0, 0, 285, 0, 0, 0, 0, 0, 66,
- 0, 69, 0, 277, 286, 283, 284, 281, 282, 280,
- 279, 278, 288, 271, 272, 274, 0, 273, 65, 91,
- 70, 77, 73, 68, 75, 0, 0, 0, 0, 0,
- 0, 0, 0, 64, 64, 64, 64, 0, 0, 0,
- 0, 0, 0, 0, 64, 0, 0, 91, 0, 0,
- 91, 0, 91, 222, 213, 187, 224, 165, 179, 232,
- 180, 181, 207, 154, 195, 72, 177, 0, 168, 150,
- 174, 151, 166, 189, 67, 192, 164, 215, 198, 230,
- 0, 202, 0, 74, 71, 0, 0, 191, 217, 193,
- 212, 186, 208, 159, 201, 225, 178, 205, 0, 0,
- 0, 90, 0, 0, 0, 0, 0, 0, 0, 0,
- 204, 221, 176, 206, 149, 203, 0, 152, 155, 231,
- 219, 171, 172, 0, 0, 0, 0, 0, 0, 0,
- 190, 194, 209, 184, 0, 0, 0, 0, 0, 0,
- 620, 0, 169, 0, 200, 0, 0, 0, 156, 153,
- 188, 0, 0, 0, 158, 0, 170, 210, 0, 218,
- 185, 76, 220, 183, 182, 223, 226, 216, 167, 175,
- 66, 173, 69, 0, 162, 163, 160, 161, 196, 197,
- 227, 228, 229, 211, 157, 0, 0, 214, 199, 65,
- 0, 70, 77, 73, 68, 75, 222, 213, 187, 224,
- 165, 179, 232, 180, 181, 207, 154, 195, 72, 177,
- 0, 168, 150, 174, 151, 166, 189, 67, 192, 164,
- 215, 198, 230, 0, 202, 0, 74, 71, 0, 0,
- 191, 217, 193, 212, 186, 208, 159, 201, 225, 178,
- 205, 0, 0, 0, 84, 0, 0, 0, 0, 0,
- 0, 0, 0, 204, 221, 176, 206, 149, 203, 0,
- 152, 155, 231, 219, 171, 172, 0, 0, 0, 0,
- 0, 0, 0, 190, 194, 209, 184, 0, 0, 0,
- 0, 0, 0, 509, 0, 169, 0, 200, 0, 0,
- 0, 156, 153, 188, 0, 0, 0, 158, 0, 170,
- 210, 0, 218, 185, 76, 220, 183, 182, 223, 226,
- 216, 167, 175, 66, 173, 69, 0, 162, 163, 160,
- 161, 196, 197, 227, 228, 229, 211, 157, 0, 0,
- 214, 199, 65, 0, 70, 77, 73, 68, 75, 222,
- 213, 187, 224, 165, 179, 232, 180, 181, 207, 154,
- 195, 72, 177, 0, 168, 150, 174, 151, 166, 189,
- 67, 192, 164, 215, 198, 230, 0, 202, 0, 74,
- 71, 0, 0, 191, 217, 193, 212, 186, 208, 159,
- 201, 225, 178, 205, 0, 0, 0, 90, 0, 0,
- 0, 0, 0, 0, 0, 0, 204, 221, 176, 206,
- 149, 203, 0, 152, 155, 231, 219, 171, 172, 0,
- 0, 0, 0, 0, 0, 0, 190, 194, 209, 184,
- 0, 0, 0, 0, 0, 0, 0, 0, 169, 0,
- 200, 0, 0, 0, 156, 153, 188, 0, 0, 0,
- 158, 0, 170, 210, 0, 218, 185, 76, 220, 183,
- 182, 223, 226, 216, 167, 175, 66, 173, 69, 0,
- 162, 163, 160, 161, 196, 197, 227, 228, 229, 211,
- 157, 0, 0, 214, 199, 65, 0, 70, 77, 73,
- 68, 75, 222, 213, 187, 224, 165, 179, 232, 180,
- 181, 207, 154, 195, 72, 177, 0, 168, 150, 174,
- 151, 166, 189, 67, 192, 164, 215, 198, 230, 0,
- 202, 0, 74, 71, 0, 0, 191, 217, 193, 212,
- 186, 208, 159, 201, 225, 178, 205, 0, 0, 0,
- 84, 0, 0, 0, 0, 0, 0, 0, 0, 204,
- 221, 176, 206, 149, 203, 0, 152, 155, 231, 219,
- 171, 172, 0, 0, 0, 0, 0, 0, 0, 190,
- 194, 209, 184, 0, 0, 0, 0, 0, 0, 0,
- 0, 169, 0, 200, 0, 0, 0, 156, 153, 188,
- 0, 0, 0, 158, 0, 170, 210, 0, 218, 185,
- 76, 220, 183, 182, 223, 226, 216, 167, 175, 66,
- 173, 69, 0, 162, 163, 160, 161, 196, 197, 227,
- 228, 229, 211, 157, 0, 0, 214, 199, 65, 0,
- 70, 77, 73, 68, 75, 222, 213, 187, 224, 165,
- 179, 232, 180, 181, 207, 154, 195, 72, 177, 0,
- 168, 150, 174, 151, 166, 189, 67, 192, 164, 215,
- 198, 230, 0, 202, 0, 74, 71, 0, 0, 191,
- 217, 193, 212, 186, 208, 159, 201, 225, 178, 205,
- 0, 0, 0, 63, 0, 0, 0, 0, 0, 0,
- 0, 0, 204, 221, 176, 206, 149, 203, 0, 152,
- 155, 231, 219, 171, 172, 0, 0, 0, 0, 0,
- 0, 0, 190, 194, 209, 184, 0, 0, 0, 0,
- 0, 0, 0, 0, 169, 0, 200, 0, 0, 0,
- 156, 153, 188, 0, 0, 0, 158, 0, 170, 210,
- 0, 218, 185, 76, 220, 183, 182, 223, 226, 216,
- 167, 175, 66, 173, 69, 0, 162, 163, 160, 161,
- 196, 197, 227, 228, 229, 211, 157, 0, 0, 214,
- 199, 65, 0, 70, 77, 73, 68, 75, 72, 0,
- 0, 0, 0, 241, 0, 0, 0, 67, 0, 0,
- 0, 0, 276, 0, 0, 0, 74, 71, 0, 0,
- 0, 0, 269, 270, 0, 0, 0, 0, 0, 0,
- 0, 138, 534, 423, 84, 259, 258, 260, 261, 262,
- 263, 0, 0, 264, 256, 257, 0, 0, 238, 250,
- 0, 275, 342, 341, 351, 352, 344, 345, 346, 347,
- 348, 349, 350, 343, 0, 0, 353, 0, 0, 0,
- 0, 247, 248, 0, 0, 0, 0, 287, 0, 249,
- 0, 0, 246, 251, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 76, 0, 0, 285, 0, 0,
- 0, 0, 0, 66, 0, 69, 0, 277, 286, 283,
- 284, 281, 282, 280, 279, 278, 288, 271, 272, 274,
- 14, 273, 65, 0, 70, 77, 73, 68, 75, 0,
- 0, 72, 0, 0, 0, 0, 241, 0, 0, 0,
- 67, 0, 0, 0, 0, 276, 0, 0, 0, 74,
- 71, 0, 0, 0, 0, 269, 270, 0, 0, 0,
- 0, 0, 0, 0, 138, 0, 0, 84, 259, 258,
- 260, 261, 262, 263, 0, 0, 264, 256, 257, 0,
- 0, 238, 250, 0, 275, 342, 341, 351, 352, 344,
- 345, 346, 347, 348, 349, 350, 343, 0, 0, 353,
- 0, 0, 0, 0, 247, 248, 0, 0, 0, 0,
- 287, 0, 249, 0, 0, 246, 251, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 76, 0, 0,
- 285, 0, 0, 0, 0, 0, 66, 0, 69, 0,
- 277, 286, 283, 284, 281, 282, 280, 279, 278, 288,
- 271, 272, 274, 0, 273, 65, 0, 70, 77, 73,
- 68, 75, 72, 0, 0, 0, 0, 241, 0, 0,
- 0, 67, 0, 0, 0, 0, 276, 0, 0, 0,
- 74, 71, 0, 0, 0, 0, 269, 270, 0, 0,
- 0, 0, 0, 0, 0, 138, 0, 0, 84, 259,
- 258, 260, 261, 262, 263, 0, 0, 264, 256, 257,
- 0, 0, 238, 250, 0, 275, 0, 0, 0, 0,
+ 424, 247, 432, 331, 596, 270, 79, 377, 121, 505,
+ 475, 525, 534, 563, 595, 420, 271, 249, 503, 314,
+ 243, 411, 490, 417, 504, 444, 119, 294, 312, 61,
+ 519, 98, 246, 28, 687, 49, 124, 305, 81, 237,
+ 62, 680, 83, 244, 686, 62, 673, 685, 62, 679,
+ 62, 303, 376, 3, 62, 672, 599, 640, 468, 477,
+ 469, 50, 51, 299, 37, 62, 39, 109, 94, 89,
+ 40, 87, 91, 42, 92, 43, 307, 468, 97, 469,
+ 43, 406, 52, 213, 62, 330, 72, 329, 328, 104,
+ 88, 90, 62, 80, 48, 67, 62, 44, 578, 62,
+ 366, 367, 62, 575, 74, 71, 470, 81, 112, 62,
+ 427, 83, 228, 415, 109, 81, 116, 235, 212, 83,
+ 211, 235, 125, 214, 293, 470, 217, 471, 356, 401,
+ 302, 304, 301, 227, 240, 375, 45, 46, 47, 234,
+ 230, 232, 107, 296, 115, 111, 471, 346, 236, 668,
+ 356, 671, 295, 543, 336, 345, 344, 354, 355, 347,
+ 348, 349, 350, 351, 352, 353, 346, 335, 334, 356,
+ 306, 675, 451, 335, 334, 122, 334, 491, 468, 114,
+ 469, 602, 76, 491, 336, 551, 449, 450, 448, 117,
+ 336, 66, 336, 69, 662, 345, 344, 354, 355, 347,
+ 348, 349, 350, 351, 352, 353, 346, 239, 231, 356,
+ 65, 122, 70, 77, 73, 68, 75, 101, 269, 62,
+ 544, 447, 62, 669, 315, 650, 470, 81, 527, 528,
+ 529, 83, 324, 354, 355, 347, 348, 349, 350, 351,
+ 352, 353, 346, 308, 310, 356, 309, 471, 118, 632,
+ 635, 233, 426, 663, 654, 82, 659, 426, 333, 438,
+ 440, 441, 337, 364, 439, 561, 426, 238, 327, 587,
+ 588, 368, 369, 370, 371, 372, 373, 323, 311, 345,
+ 344, 354, 355, 347, 348, 349, 350, 351, 352, 353,
+ 346, 95, 378, 356, 113, 584, 332, 545, 30, 386,
+ 480, 349, 350, 351, 352, 353, 346, 113, 123, 356,
+ 123, 335, 334, 335, 334, 418, 535, 113, 221, 585,
+ 604, 561, 62, 408, 82, 408, 426, 122, 336, 583,
+ 336, 428, 82, 408, 123, 123, 404, 580, 426, 421,
+ 514, 335, 334, 335, 334, 426, 416, 93, 423, 547,
+ 426, 480, 426, 409, 435, 436, 445, 446, 336, 443,
+ 336, 467, 452, 453, 454, 455, 456, 457, 458, 459,
+ 460, 461, 462, 463, 464, 465, 466, 479, 481, 645,
+ 315, 235, 442, 418, 478, 315, 315, 402, 388, 219,
+ 493, 425, 426, 216, 408, 96, 225, 648, 647, 472,
+ 473, 484, 485, 315, 315, 315, 315, 613, 62, 495,
+ 498, 62, 295, 612, 315, 62, 488, 122, 272, 684,
+ 616, 235, 683, 515, 478, 617, 678, 508, 614, 56,
+ 57, 518, 499, 615, 510, 500, 123, 502, 681, 482,
+ 483, 41, 378, 512, 82, 326, 618, 501, 569, 570,
+ 507, 100, 295, 523, 526, 14, 665, 494, 320, 496,
+ 497, 521, 522, 319, 99, 445, 446, 513, 666, 676,
+ 531, 532, 533, 651, 530, 59, 537, 105, 576, 297,
+ 322, 215, 315, 541, 542, 60, 573, 546, 53, 54,
+ 100, 318, 433, 553, 315, 554, 555, 556, 557, 317,
+ 540, 263, 262, 264, 265, 266, 267, 607, 120, 268,
+ 120, 552, 550, 434, 332, 62, 606, 560, 403, 238,
+ 579, 226, 581, 582, 31, 78, 558, 508, 574, 674,
+ 577, 421, 623, 413, 14, 120, 538, 30, 32, 593,
+ 33, 34, 35, 36, 589, 315, 27, 1, 548, 572,
+ 594, 106, 601, 410, 298, 38, 591, 592, 405, 597,
+ 598, 300, 86, 316, 224, 325, 486, 62, 62, 62,
+ 62, 603, 609, 664, 611, 621, 586, 524, 620, 508,
+ 508, 508, 508, 605, 498, 619, 608, 559, 610, 549,
+ 385, 489, 474, 248, 123, 630, 631, 628, 636, 637,
+ 419, 634, 487, 626, 627, 526, 492, 437, 259, 642,
+ 256, 629, 258, 257, 220, 644, 321, 338, 241, 229,
+ 123, 102, 564, 562, 641, 81, 643, 378, 506, 83,
+ 649, 413, 407, 639, 123, 661, 120, 55, 218, 29,
+ 653, 58, 13, 12, 516, 655, 517, 11, 520, 520,
+ 520, 658, 10, 9, 8, 7, 660, 6, 5, 652,
+ 4, 2, 670, 667, 365, 656, 657, 0, 0, 0,
+ 677, 390, 391, 392, 393, 394, 395, 396, 0, 0,
+ 0, 682, 0, 0, 85, 0, 0, 0, 374, 0,
+ 0, 0, 379, 380, 381, 382, 383, 384, 0, 387,
+ 389, 389, 389, 389, 389, 389, 389, 389, 397, 398,
+ 399, 400, 14, 15, 16, 17, 0, 0, 123, 64,
+ 0, 0, 0, 0, 64, 0, 0, 64, 0, 64,
+ 590, 0, 0, 64, 0, 18, 565, 568, 569, 570,
+ 566, 422, 567, 571, 64, 0, 64, 429, 430, 431,
+ 345, 344, 354, 355, 347, 348, 349, 350, 351, 352,
+ 353, 346, 0, 64, 356, 0, 600, 0, 0, 0,
+ 0, 64, 0, 0, 126, 64, 126, 0, 64, 0,
+ 0, 64, 0, 0, 126, 0, 0, 0, 64, 0,
+ 0, 0, 0, 64, 123, 0, 64, 624, 0, 625,
+ 0, 126, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 19, 20, 22, 21, 23, 0, 0, 0,
+ 0, 0, 0, 0, 0, 24, 25, 26, 509, 0,
+ 0, 72, 0, 0, 476, 0, 245, 0, 0, 0,
+ 67, 0, 82, 0, 0, 280, 0, 0, 0, 74,
+ 71, 0, 0, 0, 0, 273, 274, 0, 0, 0,
+ 0, 0, 0, 0, 122, 536, 0, 84, 263, 262,
+ 264, 265, 266, 267, 0, 0, 268, 260, 261, 0,
+ 0, 242, 254, 0, 279, 345, 344, 354, 355, 347,
+ 348, 349, 350, 351, 352, 353, 346, 539, 64, 356,
+ 0, 64, 126, 0, 251, 252, 313, 0, 0, 0,
+ 291, 126, 253, 0, 0, 250, 255, 0, 120, 347,
+ 348, 349, 350, 351, 352, 353, 346, 76, 509, 356,
+ 289, 0, 0, 422, 0, 0, 66, 0, 69, 0,
+ 281, 290, 287, 288, 285, 286, 284, 283, 282, 292,
+ 275, 276, 278, 0, 277, 65, 0, 70, 77, 73,
+ 68, 75, 345, 344, 354, 355, 347, 348, 349, 350,
+ 351, 352, 353, 346, 0, 0, 356, 0, 0, 0,
+ 509, 509, 509, 509, 126, 344, 354, 355, 347, 348,
+ 349, 350, 351, 352, 353, 346, 0, 0, 356, 126,
+ 0, 64, 72, 0, 0, 0, 412, 0, 0, 0,
+ 0, 67, 0, 0, 0, 633, 0, 14, 638, 0,
+ 74, 71, 0, 565, 568, 569, 570, 566, 72, 567,
+ 571, 0, 0, 646, 0, 0, 0, 67, 125, 0,
+ 414, 0, 0, 0, 72, 0, 74, 71, 108, 0,
+ 335, 334, 0, 67, 0, 0, 0, 0, 126, 0,
+ 0, 122, 74, 71, 63, 0, 0, 336, 126, 0,
+ 0, 0, 126, 0, 0, 0, 0, 0, 0, 0,
+ 63, 0, 110, 72, 0, 0, 126, 64, 0, 0,
+ 64, 0, 67, 0, 64, 0, 0, 126, 76, 0,
+ 0, 74, 71, 0, 0, 0, 0, 66, 0, 69,
+ 126, 0, 126, 0, 126, 126, 126, 0, 0, 125,
+ 0, 0, 222, 0, 76, 223, 65, 0, 70, 77,
+ 73, 68, 75, 66, 0, 69, 0, 0, 0, 0,
+ 76, 0, 0, 0, 0, 0, 0, 0, 0, 66,
+ 0, 69, 65, 0, 70, 77, 73, 68, 75, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 65, 0,
+ 70, 77, 73, 68, 75, 0, 0, 0, 0, 76,
+ 0, 0, 0, 0, 126, 0, 0, 0, 66, 0,
+ 69, 0, 0, 0, 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 65, 0, 70,
+ 77, 73, 68, 75, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 126, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 64, 64, 64, 64,
+ 0, 0, 0, 0, 0, 0, 0, 64, 0, 0,
+ 126, 0, 0, 126, 0, 126, 200, 191, 165, 202,
+ 143, 157, 210, 158, 159, 185, 132, 173, 72, 155,
+ 0, 146, 128, 152, 129, 144, 167, 67, 170, 142,
+ 193, 176, 208, 0, 180, 0, 74, 71, 0, 0,
+ 169, 195, 171, 190, 164, 186, 137, 179, 203, 156,
+ 183, 0, 0, 0, 125, 0, 0, 0, 0, 0,
+ 0, 0, 0, 182, 199, 154, 184, 127, 181, 0,
+ 130, 133, 209, 197, 149, 150, 0, 0, 0, 0,
+ 0, 0, 0, 168, 172, 187, 162, 0, 0, 0,
+ 0, 0, 0, 622, 0, 147, 0, 178, 0, 0,
+ 0, 134, 131, 166, 0, 0, 0, 136, 0, 148,
+ 188, 0, 196, 163, 76, 198, 161, 160, 201, 204,
+ 194, 145, 153, 66, 151, 69, 0, 140, 141, 138,
+ 139, 174, 175, 205, 206, 207, 189, 135, 0, 0,
+ 192, 177, 65, 0, 70, 77, 73, 68, 75, 200,
+ 191, 165, 202, 143, 157, 210, 158, 159, 185, 132,
+ 173, 72, 155, 0, 146, 128, 152, 129, 144, 167,
+ 67, 170, 142, 193, 176, 208, 0, 180, 0, 74,
+ 71, 0, 0, 169, 195, 171, 190, 164, 186, 137,
+ 179, 203, 156, 183, 122, 0, 0, 125, 0, 0,
+ 0, 0, 0, 0, 0, 0, 182, 199, 154, 184,
+ 127, 181, 0, 130, 133, 209, 197, 149, 150, 0,
+ 0, 0, 0, 0, 0, 0, 168, 172, 187, 162,
+ 0, 0, 0, 0, 0, 0, 0, 0, 147, 0,
+ 178, 0, 0, 0, 134, 131, 166, 0, 0, 0,
+ 136, 0, 148, 188, 0, 196, 163, 76, 198, 161,
+ 160, 201, 204, 194, 145, 153, 66, 151, 69, 0,
+ 140, 141, 138, 139, 174, 175, 205, 206, 207, 189,
+ 135, 0, 0, 192, 177, 65, 0, 70, 77, 73,
+ 68, 75, 200, 191, 165, 202, 143, 157, 210, 158,
+ 159, 185, 132, 173, 72, 155, 0, 146, 128, 152,
+ 129, 144, 167, 67, 170, 142, 193, 176, 208, 0,
+ 180, 0, 74, 71, 0, 0, 169, 195, 171, 190,
+ 164, 186, 137, 179, 203, 156, 183, 0, 0, 0,
+ 84, 0, 0, 0, 0, 0, 0, 0, 0, 182,
+ 199, 154, 184, 127, 181, 0, 130, 133, 209, 197,
+ 149, 150, 0, 0, 0, 0, 0, 0, 0, 168,
+ 172, 187, 162, 0, 0, 0, 0, 0, 0, 511,
+ 0, 147, 0, 178, 0, 0, 0, 134, 131, 166,
+ 0, 0, 0, 136, 0, 148, 188, 0, 196, 163,
+ 76, 198, 161, 160, 201, 204, 194, 145, 153, 66,
+ 151, 69, 0, 140, 141, 138, 139, 174, 175, 205,
+ 206, 207, 189, 135, 0, 0, 192, 177, 65, 0,
+ 70, 77, 73, 68, 75, 200, 191, 165, 202, 143,
+ 157, 210, 158, 159, 185, 132, 173, 72, 155, 0,
+ 146, 128, 152, 129, 144, 167, 67, 170, 142, 193,
+ 176, 208, 0, 180, 0, 74, 71, 0, 0, 169,
+ 195, 171, 190, 164, 186, 137, 179, 203, 156, 183,
+ 0, 0, 0, 125, 0, 0, 0, 0, 0, 0,
+ 0, 0, 182, 199, 154, 184, 127, 181, 0, 130,
+ 133, 209, 197, 149, 150, 0, 0, 0, 0, 0,
+ 0, 0, 168, 172, 187, 162, 0, 0, 0, 0,
+ 0, 0, 0, 0, 147, 0, 178, 0, 0, 0,
+ 134, 131, 166, 0, 0, 0, 136, 0, 148, 188,
+ 0, 196, 163, 76, 198, 161, 160, 201, 204, 194,
+ 145, 153, 66, 151, 69, 0, 140, 141, 138, 139,
+ 174, 175, 205, 206, 207, 189, 135, 0, 0, 192,
+ 177, 65, 0, 70, 77, 73, 68, 75, 200, 191,
+ 165, 202, 143, 157, 210, 158, 159, 185, 132, 173,
+ 72, 155, 0, 146, 128, 152, 129, 144, 167, 67,
+ 170, 142, 193, 176, 208, 0, 180, 0, 74, 71,
+ 0, 0, 169, 195, 171, 190, 164, 186, 137, 179,
+ 203, 156, 183, 0, 0, 0, 84, 0, 0, 0,
+ 0, 0, 0, 0, 0, 182, 199, 154, 184, 127,
+ 181, 0, 130, 133, 209, 197, 149, 150, 0, 0,
+ 0, 0, 0, 0, 0, 168, 172, 187, 162, 0,
+ 0, 0, 0, 0, 0, 0, 0, 147, 0, 178,
+ 0, 0, 0, 134, 131, 166, 0, 0, 0, 136,
+ 0, 148, 188, 0, 196, 163, 76, 198, 161, 160,
+ 201, 204, 194, 145, 153, 66, 151, 69, 0, 140,
+ 141, 138, 139, 174, 175, 205, 206, 207, 189, 135,
+ 0, 0, 192, 177, 65, 0, 70, 77, 73, 68,
+ 75, 200, 191, 165, 202, 143, 157, 210, 158, 159,
+ 185, 132, 173, 72, 155, 0, 146, 128, 152, 129,
+ 144, 167, 67, 170, 142, 193, 176, 208, 0, 180,
+ 0, 74, 71, 0, 0, 169, 195, 171, 190, 164,
+ 186, 137, 179, 203, 156, 183, 0, 0, 0, 63,
+ 0, 0, 0, 0, 0, 0, 0, 0, 182, 199,
+ 154, 184, 127, 181, 0, 130, 133, 209, 197, 149,
+ 150, 0, 0, 0, 0, 0, 0, 0, 168, 172,
+ 187, 162, 0, 0, 0, 0, 0, 0, 0, 0,
+ 147, 0, 178, 0, 0, 0, 134, 131, 166, 0,
+ 0, 0, 136, 0, 148, 188, 0, 196, 163, 76,
+ 198, 161, 160, 201, 204, 194, 145, 153, 66, 151,
+ 69, 0, 140, 141, 138, 139, 174, 175, 205, 206,
+ 207, 189, 135, 0, 0, 192, 177, 65, 0, 70,
+ 77, 73, 68, 75, 72, 0, 0, 0, 0, 245,
+ 0, 0, 0, 67, 0, 0, 0, 0, 280, 0,
+ 0, 0, 74, 71, 0, 0, 0, 0, 273, 274,
+ 0, 0, 0, 0, 0, 0, 0, 122, 0, 0,
+ 84, 263, 262, 264, 265, 266, 267, 0, 0, 268,
+ 260, 261, 0, 0, 242, 254, 0, 279, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 251, 252, 313,
+ 0, 0, 0, 291, 0, 253, 0, 0, 250, 255,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 247, 248, 0, 0, 0,
- 0, 287, 0, 249, 0, 0, 246, 251, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 76, 0,
- 0, 285, 0, 0, 0, 0, 0, 66, 0, 69,
- 0, 277, 286, 283, 284, 281, 282, 280, 279, 278,
- 288, 271, 272, 274, 72, 273, 65, 0, 70, 77,
- 73, 68, 75, 67, 0, 0, 0, 0, 276, 0,
- 0, 0, 74, 71, 0, 0, 0, 0, 269, 270,
- 0, 0, 0, 0, 0, 0, 0, 138, 0, 0,
- 84, 259, 258, 260, 261, 262, 263, 0, 0, 264,
- 256, 257, 0, 0, 0, 250, 0, 275, 0, 0,
+ 76, 0, 0, 289, 0, 0, 0, 0, 0, 66,
+ 0, 69, 0, 281, 290, 287, 288, 285, 286, 284,
+ 283, 282, 292, 275, 276, 278, 0, 277, 65, 0,
+ 70, 77, 73, 68, 75, 72, 0, 0, 0, 0,
+ 245, 0, 0, 0, 67, 0, 0, 0, 0, 280,
+ 0, 0, 0, 74, 71, 0, 0, 0, 0, 273,
+ 274, 0, 0, 0, 0, 0, 0, 0, 122, 0,
+ 426, 84, 263, 262, 264, 265, 266, 267, 0, 0,
+ 268, 260, 261, 0, 0, 242, 254, 0, 279, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 247, 248, 0,
- 0, 0, 0, 287, 0, 249, 0, 0, 246, 251,
+ 0, 0, 0, 0, 0, 0, 0, 0, 251, 252,
+ 0, 0, 0, 0, 291, 0, 253, 0, 0, 250,
+ 255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 76, 0, 0, 289, 0, 0, 0, 0, 0,
+ 66, 0, 69, 0, 281, 290, 287, 288, 285, 286,
+ 284, 283, 282, 292, 275, 276, 278, 14, 277, 65,
+ 0, 70, 77, 73, 68, 75, 0, 0, 72, 0,
+ 0, 0, 0, 245, 0, 0, 0, 67, 0, 0,
+ 0, 0, 280, 0, 0, 0, 74, 71, 0, 0,
+ 0, 0, 273, 274, 0, 0, 0, 0, 0, 0,
+ 0, 122, 0, 0, 84, 263, 262, 264, 265, 266,
+ 267, 0, 0, 268, 260, 261, 0, 0, 242, 254,
+ 0, 279, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 76, 0, 0, 285, 0, 0, 0, 0, 0, 66,
- 0, 69, 0, 277, 286, 283, 284, 281, 282, 280,
- 279, 278, 288, 271, 272, 274, 72, 273, 65, 0,
- 70, 77, 73, 68, 75, 67, 0, 0, 0, 0,
- 276, 0, 0, 0, 74, 71, 0, 0, 0, 0,
- 269, 270, 0, 0, 0, 0, 0, 0, 0, 138,
- 0, 0, 84, 259, 258, 260, 261, 262, 263, 0,
- 0, 264, 0, 72, 0, 0, 0, 250, 0, 275,
- 0, 0, 67, 0, 0, 72, 0, 0, 0, 0,
- 0, 74, 71, 0, 67, 0, 0, 0, 0, 247,
- 248, 0, 0, 74, 71, 287, 138, 249, 0, 63,
- 246, 251, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 90, 76, 411, 0, 285, 0, 0, 0, 0,
- 0, 66, 0, 69, 0, 277, 286, 283, 284, 281,
- 282, 280, 279, 278, 288, 271, 272, 274, 0, 273,
- 65, 0, 70, 77, 73, 68, 75, 72, 0, 0,
- 0, 409, 0, 0, 0, 0, 67, 0, 0, 76,
- 0, 0, 0, 0, 0, 74, 71, 0, 66, 0,
- 69, 76, 0, 0, 0, 0, 0, 0, 0, 0,
- 66, 0, 69, 90, 0, 411, 0, 65, 72, 70,
- 77, 73, 68, 75, 0, 332, 331, 67, 0, 65,
- 72, 70, 77, 73, 68, 75, 74, 71, 105, 67,
- 0, 0, 333, 0, 72, 0, 0, 0, 74, 71,
- 0, 0, 0, 67, 63, 0, 112, 0, 0, 72,
- 0, 0, 74, 71, 0, 0, 63, 0, 67, 0,
- 0, 0, 0, 76, 0, 0, 0, 74, 71, 0,
- 84, 0, 66, 0, 69, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 90, 0, 0, 0, 0,
- 72, 65, 0, 70, 77, 73, 68, 75, 0, 67,
- 0, 0, 0, 0, 76, 0, 0, 0, 74, 71,
- 0, 0, 0, 66, 0, 69, 76, 0, 0, 0,
- 0, 0, 0, 0, 0, 66, 63, 69, 0, 0,
- 76, 0, 65, 0, 70, 77, 73, 68, 75, 66,
- 0, 69, 0, 0, 65, 76, 70, 77, 73, 68,
- 75, 0, 0, 0, 66, 0, 69, 0, 65, 0,
- 70, 77, 73, 68, 75, 0, 0, 0, 0, 0,
- 0, 0, 0, 65, 0, 70, 77, 73, 68, 75,
- 0, 0, 0, 0, 0, 0, 76, 0, 0, 0,
- 0, 0, 0, 0, 0, 66, 0, 69, 0, 0,
+ 0, 251, 252, 0, 0, 0, 0, 291, 0, 253,
+ 0, 0, 250, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 76, 0, 0, 289, 0, 0,
+ 0, 0, 0, 66, 0, 69, 0, 281, 290, 287,
+ 288, 285, 286, 284, 283, 282, 292, 275, 276, 278,
+ 0, 277, 65, 0, 70, 77, 73, 68, 75, 72,
+ 0, 0, 0, 0, 245, 0, 0, 0, 67, 0,
+ 0, 0, 0, 280, 0, 0, 0, 74, 71, 0,
+ 0, 0, 0, 273, 274, 0, 0, 0, 0, 0,
+ 0, 0, 122, 0, 0, 84, 263, 262, 264, 265,
+ 266, 267, 0, 0, 268, 260, 261, 0, 0, 242,
+ 254, 0, 279, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 65, 0, 70, 77, 73, 68,
- 75,
+ 0, 0, 251, 252, 0, 0, 0, 0, 291, 0,
+ 253, 0, 0, 250, 255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 76, 0, 0, 289, 0,
+ 0, 0, 0, 0, 66, 0, 69, 0, 281, 290,
+ 287, 288, 285, 286, 284, 283, 282, 292, 275, 276,
+ 278, 72, 277, 65, 0, 70, 77, 73, 68, 75,
+ 67, 0, 0, 0, 0, 280, 0, 0, 0, 74,
+ 71, 0, 0, 0, 0, 273, 274, 0, 0, 0,
+ 0, 0, 0, 0, 122, 0, 0, 84, 263, 262,
+ 264, 265, 266, 267, 0, 0, 268, 260, 261, 0,
+ 0, 0, 254, 0, 279, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 251, 252, 0, 0, 0, 0,
+ 291, 0, 253, 0, 0, 250, 255, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 76, 0, 0,
+ 289, 0, 0, 0, 0, 0, 66, 0, 69, 0,
+ 281, 290, 287, 288, 285, 286, 284, 283, 282, 292,
+ 275, 276, 278, 72, 277, 65, 0, 70, 77, 73,
+ 68, 75, 67, 0, 0, 0, 0, 280, 0, 0,
+ 0, 74, 71, 0, 0, 0, 0, 273, 274, 0,
+ 0, 0, 0, 0, 0, 0, 122, 0, 0, 84,
+ 263, 262, 264, 265, 266, 267, 0, 0, 268, 0,
+ 72, 0, 0, 0, 254, 0, 279, 0, 0, 67,
+ 0, 0, 72, 0, 0, 0, 0, 0, 74, 71,
+ 0, 67, 0, 0, 0, 0, 251, 252, 0, 0,
+ 74, 71, 291, 122, 253, 0, 63, 250, 255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 125, 76,
+ 414, 0, 289, 0, 0, 0, 0, 0, 66, 0,
+ 69, 0, 281, 290, 287, 288, 285, 286, 284, 283,
+ 282, 292, 275, 276, 278, 72, 277, 65, 0, 70,
+ 77, 73, 68, 75, 67, 0, 0, 0, 0, 0,
+ 0, 0, 0, 74, 71, 0, 76, 0, 0, 0,
+ 0, 0, 0, 0, 0, 66, 0, 69, 76, 0,
+ 0, 63, 0, 110, 72, 0, 0, 66, 0, 69,
+ 0, 0, 103, 67, 65, 0, 70, 77, 73, 68,
+ 75, 0, 74, 71, 0, 0, 65, 0, 70, 77,
+ 73, 68, 75, 72, 0, 0, 0, 0, 0, 0,
+ 63, 72, 67, 0, 0, 0, 0, 0, 72, 0,
+ 67, 74, 71, 0, 0, 0, 0, 67, 0, 74,
+ 71, 76, 0, 0, 0, 0, 74, 71, 0, 84,
+ 66, 0, 69, 0, 0, 0, 0, 125, 0, 0,
+ 0, 0, 0, 0, 63, 0, 0, 0, 0, 65,
+ 0, 70, 77, 73, 68, 75, 0, 0, 0, 0,
+ 76, 0, 0, 0, 0, 0, 0, 0, 0, 66,
+ 0, 69, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 65, 76,
+ 70, 77, 73, 68, 75, 0, 0, 76, 66, 0,
+ 69, 0, 0, 0, 76, 0, 66, 0, 69, 0,
+ 0, 0, 0, 66, 0, 69, 0, 65, 0, 70,
+ 77, 73, 68, 75, 0, 65, 0, 70, 77, 73,
+ 68, 75, 65, 0, 70, 77, 73, 68, 75, 340,
+ 0, 343, 0, 0, 0, 0, 0, 357, 358, 359,
+ 360, 361, 362, 363, 0, 341, 342, 339, 345, 344,
+ 354, 355, 347, 348, 349, 350, 351, 352, 353, 346,
+ 0, 0, 356,
}
var yyPact = [...]int{
- 537, -1000, -116, 486, -1000, -1000, -1000, -1000, -1000, -1000,
- -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -61,
- -17, -7, -10, -19, -1000, -1000, -1000, -1000, -1000, 483,
- 437, 358, -1000, -58, 2783, 475, 2727, -63, -25, 2742,
- -1000, -23, 2742, -1000, 2783, -65, 163, -65, 2783, -1000,
- -1000, -1000, -1000, -1000, -1000, 382, -1000, -1000, 125, 2713,
- 390, 1016, 66, -1000, -1000, -1000, -1000, -1000, -1000, -1000,
- -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2783, 196,
- -1000, 102, -1000, 65, -1000, -1000, 2783, 113, 160, -1000,
- -1000, -1000, 2783, -1000, -39, 2783, 408, 306, 2742, -1000,
- 294, 847, -1000, -1000, 319, 2783, -1000, 2727, 52, -1000,
- 2701, -1000, -1000, 1890, 472, 2727, 2285, 1747, -1000, 406,
- -72, -1000, 81, -1000, 2783, -1000, -1000, 2783, -1000, 1147,
- -1000, 449, -1000, 377, 372, 394, 2727, 2742, -1000, -1000,
- 313, -1000, -28, -31, -36, -1000, -1000, -1000, -1000, -1000,
+ 706, -1000, -115, 532, -1000, -1000, -1000, -1000, -1000, -1000,
+ -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -47,
+ -40, -14, 25, -17, -1000, -1000, -1000, -1000, -1000, 528,
+ 469, 392, -1000, -35, 2951, 515, 2936, -45, -22, 2951,
+ -1000, -20, 2951, -1000, 2951, -48, 238, -48, 2951, -1000,
+ -1000, -1000, -1000, -1000, -1000, 431, -1000, -1000, 156, 2907,
+ 448, 1027, 42, -1000, -1000, -1000, -1000, -1000, -1000, -1000,
+ -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2951, 243,
+ -1000, 104, -1000, 41, -1000, -1000, 2951, 122, 195, 1404,
+ 2951, 1404, -31, 2951, 459, 344, 2951, -1000, 349, 1066,
+ -1000, -1000, 367, 2951, -1000, 2936, 93, -1000, 2868, -1000,
+ -1000, 1976, 508, 2936, 2502, 1833, 1404, 457, -55, -1000,
-1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000,
-1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000,
-1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000,
@@ -698,73 +715,76 @@ var yyPact = [...]int{
-1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000,
-1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000,
-1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000,
- -1000, -1000, -1000, 463, 2285, -1000, 149, -1000, 2285, 1011,
- -1000, 255, -1000, 60, -1000, -1000, 2529, 2529, 2529, 2529,
- 2529, 2529, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000,
- -1000, -1000, -1000, -1000, -1000, 255, 57, -1000, 2154, 255,
- 255, 255, 255, 255, 255, 2285, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, -1000,
- 51, -1000, -1000, -1000, -1000, 300, 2742, -1000, -42, -1000,
- -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 335, -1000,
- -1000, 2660, 47, 2783, -1000, -1000, -1000, -1000, 292, 255,
- 486, 305, 288, 41, 463, 255, 255, 255, 431, 461,
- 149, 2285, 2285, 187, 94, 2407, 162, 123, 2529, 2529,
- 2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529,
- 2529, 2529, 2529, 36, -1000, -1000, -1000, -1000, -1000, -1000,
- -1000, -1000, 483, 234, 234, 27, 27, 27, 27, 27,
- 184, 885, 1747, 251, 285, 149, 1147, 1147, 2285, 2285,
- 2742, 419, 108, 149, 2742, -1000, 166, -1000, -1000, -1000,
- -1000, -1000, -1000, -1000, 1147, 1147, 1147, 1147, 1604, 2783,
- -1000, -1000, 2783, -1000, 472, 1147, 2576, -1000, -1000, 2588,
- -1000, -1000, 1461, -1000, -1000, 395, 247, -1000, -1000, 2021,
- -1000, -1000, 2742, -1000, 2742, 431, 2742, 2742, 2742, -1000,
- 2285, 2285, 94, 151, -1000, -1000, 238, -1000, -1000, -1000,
- 2139, -1000, -1000, -1000, -1000, 162, 2529, 2529, 2529, 631,
- 2139, 2006, 581, 600, 27, 103, 103, 74, 74, 74,
- 74, 74, 168, 168, -1000, -1000, -1000, -1000, -1000, -1000,
- -1000, -1000, 166, 1147, 244, 255, -1000, 2285, -1000, 215,
- 215, 63, 173, 281, -1000, 1147, 112, -1000, 2285, 166,
- -1000, 215, 166, 215, 215, -1000, -1000, -1000, -1000, 470,
- -1000, 235, 339, -1000, -1000, -1000, 411, 80, -1000, -1000,
- 7, 397, 255, -1000, 5, -1000, -1000, 258, -1000, 258,
- 258, 295, 218, -1000, 217, -1000, -1000, -1000, -1000, 631,
- 2139, 1132, -1000, 2529, 2529, -1000, 215, 1147, 149, -1000,
- -1000, 36, 36, 36, -98, 2742, 271, 104, -1000, 2285,
- 164, -1000, -1000, -1000, -1000, -1000, -1000, 466, 438, 2576,
- 2576, 2576, 2576, -1000, 334, 332, -1000, 328, 324, 352,
- 2783, -1000, 192, 1318, 480, -1000, 2742, -1000, 2742, -1000,
- -1000, 2285, 2285, 2285, -1000, -1000, -1000, -1000, 2529, 2139,
- 2139, -1000, 166, 166, 24, 166, 166, 255, -1000, -95,
- -1000, 149, 2285, 463, 2285, 2285, 339, 262, 429, -1000,
- -1000, -1000, -1000, 331, -1000, 318, -1000, -1000, -1000, -1000,
- -1000, 2727, -1000, -1000, 149, 149, -1000, 2139, -1000, -1000,
- -1000, 153, -1000, 383, -1000, -1000, 2529, 166, 156, 149,
- 431, 149, 201, 2285, 2285, -1000, -1000, 196, 183, 36,
- -27, -1000, -1000, 386, 149, 149, 30, 135, -1000, 166,
- 1, -106, -1000, 478, 87, -1000, 380, 166, -1000, 354,
- -101, -109, -1000, 364, 36, -1000, -1000, 347, -1000, 340,
- -1000, -103, -1000, -107, -112, -1000,
+ -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000,
+ -1000, 23, -1000, 2951, -1000, -1000, 2951, 1404, 2107, -1000,
+ 481, -1000, 432, 427, 449, 2936, 2944, -1000, 256, -1000,
+ -24, -25, -27, -1000, -1000, -1000, -1000, 500, 2502, -1000,
+ 108, -1000, 2502, 3032, -1000, 277, -1000, -4, -1000, -1000,
+ 2746, 2746, 2746, 2746, 2746, 2746, -1000, -1000, -1000, -1000,
+ -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 277,
+ 32, -1000, 2371, 277, 277, 277, 277, 277, 277, 2502,
+ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277,
+ 277, 277, 277, -1000, 26, -1000, -1000, -1000, 338, 2944,
+ -1000, -33, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000,
+ -1000, 343, -1000, -1000, 985, 10, 2951, -1000, -1000, -1000,
+ -1000, 334, 277, 532, 266, 340, 7, 500, 277, 277,
+ 277, 476, 498, 108, 2502, 2502, 197, 72, 2624, 161,
+ 103, 2746, 2746, 2746, 2746, 2746, 2746, 2746, 2746, 2746,
+ 2746, 2746, 2746, 2746, 2746, 2746, 5, -1000, -1000, -1000,
+ -1000, -1000, -1000, -1000, -1000, 528, 447, 447, 28, 28,
+ 28, 28, 28, 69, 814, 1833, 293, 300, 108, 2107,
+ 2107, 2502, 2502, 2944, 470, 106, 108, 2944, -1000, 200,
+ -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2107, 2107, 2107,
+ 2107, 1690, 2951, -1000, -1000, 2951, -1000, 508, 2107, 2793,
+ -1000, -1000, 2805, -1000, -1000, 1547, -1000, -1000, 441, 289,
+ -1000, -1000, 2238, -1000, -1000, 2944, -1000, 2944, 476, 2944,
+ 2944, 2944, -1000, 2502, 2502, 72, 110, -1000, -1000, 166,
+ -1000, -1000, -1000, 876, -1000, -1000, -1000, -1000, 161, 2746,
+ 2746, 2746, 193, 876, 799, 145, 898, 28, 209, 209,
+ 50, 50, 50, 50, 50, 829, 829, -1000, -1000, -1000,
+ -1000, -1000, -1000, -1000, -1000, 200, 2107, 272, 277, -1000,
+ 2502, -1000, 274, 274, 102, 276, 298, -1000, 2107, 112,
+ -1000, 2502, 200, -1000, 274, 200, 274, 274, -1000, 1404,
+ -1000, 505, -1000, 270, 697, -1000, -1000, -1000, 465, 1011,
+ -1000, -1000, 0, 451, 277, -1000, -5, -1000, -1000, 286,
+ -1000, 286, 286, 278, 268, -1000, 246, -1000, -1000, -1000,
+ -1000, 193, 876, 664, -1000, 2746, 2746, -1000, 274, 2107,
+ 108, -1000, -1000, 5, 5, 5, -85, 2944, 282, 107,
+ -1000, 2502, 248, -1000, -1000, -1000, -1000, -1000, -1000, 503,
+ 492, 2793, 2793, 2793, 2793, -1000, 374, 368, -1000, 389,
+ 381, 407, 2951, -1000, 214, 1261, 524, -1000, 2944, -1000,
+ 2944, -1000, -1000, 2502, 2502, 2502, -1000, -1000, -1000, -1000,
+ 2746, 876, 876, -1000, 200, 200, 125, 200, 200, 277,
+ -1000, -82, -1000, 108, 2502, 500, 2502, 2502, 697, 330,
+ 984, -1000, -1000, -1000, -1000, 359, -1000, 358, -1000, -1000,
+ -1000, -1000, -1000, 2936, -1000, -1000, 108, 108, -1000, 876,
+ -1000, -1000, -1000, 169, -1000, 444, -1000, -1000, 2746, 200,
+ 199, 108, 476, 108, 249, 2502, 2502, -1000, -1000, 243,
+ 205, 5, 109, -1000, -1000, 438, 108, 108, 24, 167,
+ -1000, 200, 9, -99, -1000, 521, 86, -1000, 440, 200,
+ -1000, 390, -94, -105, -1000, 403, 5, -1000, -1000, 386,
+ -1000, 383, -1000, -97, -1000, -101, -112, -1000,
}
var yyPgo = [...]int{
- 0, 626, 48, 622, 619, 603, 598, 595, 589, 588,
- 584, 581, 575, 446, 574, 571, 29, 570, 567, 565,
- 564, 9, 36, 10, 23, 18, 563, 19, 25, 16,
- 562, 561, 2, 559, 33, 558, 401, 557, 26, 21,
- 556, 30, 552, 551, 24, 362, 550, 549, 547, 542,
- 541, 540, 20, 14, 538, 15, 12, 536, 123, 3,
- 535, 17, 534, 533, 532, 529, 13, 524, 7, 523,
- 1, 516, 515, 514, 513, 31, 5, 71, 512, 410,
- 344, 511, 509, 508, 506, 505, 6, 743, 205, 8,
- 27, 503, 45, 22, 121, 501, 500, 28, 4, 11,
- 499, 498, 497, 487, 0, 35,
+ 0, 661, 52, 660, 658, 657, 655, 654, 653, 652,
+ 647, 643, 642, 524, 641, 639, 31, 638, 637, 635,
+ 633, 12, 59, 10, 28, 19, 632, 18, 24, 9,
+ 628, 623, 13, 622, 29, 621, 450, 619, 30, 39,
+ 618, 43, 617, 616, 32, 20, 614, 613, 612, 610,
+ 608, 607, 25, 7, 600, 16, 15, 593, 17, 1,
+ 591, 22, 590, 589, 587, 583, 3, 577, 11, 576,
+ 2, 573, 566, 565, 564, 23, 6, 93, 563, 441,
+ 347, 562, 561, 558, 555, 554, 36, 684, 218, 8,
+ 21, 553, 5, 27, 142, 551, 549, 35, 26, 4,
+ 14, 547, 546, 538, 418, 0, 388,
}
var yyR1 = [...]int{
- 0, 100, 101, 101, 1, 1, 1, 1, 1, 1,
+ 0, 101, 102, 102, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 2, 2, 2, 3, 3,
4, 5, 6, 7, 7, 7, 8, 8, 8, 9,
- 10, 10, 10, 11, 12, 12, 12, 102, 13, 14,
+ 10, 10, 10, 11, 12, 12, 12, 103, 13, 14,
14, 15, 15, 15, 18, 18, 18, 16, 16, 17,
17, 23, 23, 22, 22, 24, 24, 24, 24, 91,
91, 91, 90, 90, 26, 26, 27, 27, 28, 28,
@@ -775,14 +795,14 @@ var yyR1 = [...]int{
25, 25, 25, 41, 41, 40, 40, 40, 40, 40,
40, 40, 40, 40, 40, 40, 40, 51, 51, 51,
51, 51, 51, 42, 42, 42, 42, 42, 42, 42,
- 21, 21, 52, 52, 52, 58, 53, 53, 98, 98,
- 98, 98, 45, 45, 45, 45, 45, 45, 45, 45,
+ 21, 21, 52, 52, 52, 58, 53, 53, 99, 99,
+ 99, 99, 45, 45, 45, 45, 45, 45, 45, 45,
45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
45, 49, 49, 49, 47, 47, 47, 47, 47, 47,
47, 47, 47, 48, 48, 48, 48, 48, 48, 48,
- 48, 105, 105, 50, 50, 50, 50, 19, 19, 19,
- 19, 19, 99, 99, 99, 99, 99, 99, 99, 99,
+ 48, 106, 106, 50, 50, 50, 50, 19, 19, 19,
+ 19, 19, 100, 100, 100, 100, 100, 100, 100, 100,
62, 62, 20, 20, 60, 60, 61, 63, 63, 59,
59, 59, 44, 44, 44, 44, 44, 44, 44, 46,
46, 46, 64, 64, 65, 65, 66, 66, 67, 67,
@@ -801,7 +821,8 @@ var yyR1 = [...]int{
86, 86, 86, 86, 86, 86, 86, 86, 86, 86,
86, 86, 86, 86, 86, 86, 86, 86, 86, 86,
86, 86, 86, 86, 87, 87, 87, 87, 87, 87,
- 87, 87, 87, 87, 87, 87, 87, 103, 104, 97,
+ 87, 87, 87, 87, 87, 87, 87, 104, 105, 97,
+ 98, 98, 98,
}
var yyR2 = [...]int{
@@ -846,78 +867,79 @@ var yyR2 = [...]int{
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+ 0, 1, 1,
}
var yyChk = [...]int{
- -1000, -100, -1, -2, -3, -4, -5, -6, -7, -8,
+ -1000, -101, -1, -2, -3, -4, -5, -6, -7, -8,
-9, -10, -11, -12, 6, 7, 8, 9, 29, 106,
- 107, 109, 108, 110, 119, 120, 121, -101, 148, -15,
- 5, -13, -102, -13, -13, -13, -13, 111, -84, 113,
+ 107, 109, 108, 110, 119, 120, 121, -102, 148, -15,
+ 5, -13, -103, -13, -13, -13, -13, 111, -84, 113,
117, -79, 113, 115, 111, 111, 112, 113, 111, -97,
-97, -97, -2, 19, 20, -18, 37, 38, -14, -79,
-36, -34, -92, 53, -87, 141, 122, 26, 146, 124,
143, 36, 17, 145, 35, 147, 113, 144, 10, -76,
- -77, -59, -88, -92, 53, -87, -81, 116, 112, -88,
- 53, -87, 111, -88, -92, -80, 116, 53, -80, -92,
- -16, 33, 20, 61, -35, 25, -34, 29, -95, -94,
- 21, -92, 55, 103, -34, 51, 75, 103, -92, 67,
- 53, -97, -92, -97, 114, -92, 22, 49, -88, -17,
- 40, -46, -88, 56, 59, -74, 29, -103, 50, -34,
- -76, -37, 47, 115, 48, -94, -93, -92, -86, 66,
- 21, 23, 69, 101, 15, 70, 100, 136, 106, 45,
- 128, 129, 126, 127, 28, 9, 24, 120, 20, 94,
- 108, 73, 74, 123, 22, 121, 64, 18, 48, 10,
- 12, 13, 116, 115, 85, 112, 43, 7, 102, 25,
- 82, 39, 27, 41, 83, 16, 130, 131, 30, 140,
- 96, 46, 33, 67, 62, 49, 65, 14, 44, 84,
- 109, 135, 42, 6, 139, 29, 119, 40, 111, 72,
- 114, 63, 5, 117, 8, 47, 118, 132, 133, 134,
- 31, 71, 11, -39, 11, -77, -25, -40, 67, -45,
- -41, 22, -44, -59, -57, -58, 101, 90, 91, 98,
- 68, 102, -49, -47, -48, -50, 63, 64, 55, 54,
- 56, 57, 58, 59, 62, -88, -92, -55, -103, 41,
- 42, 136, 137, 140, 138, 70, 31, 126, 134, 133,
- 132, 130, 131, 128, 129, 116, 127, 96, 135, -89,
- -93, -88, -86, -97, 22, -85, 118, -82, 109, 107,
- 28, 108, 14, 147, 53, -92, -92, -97, -22, -24,
- 92, -25, -92, -78, 18, 10, 31, 31, -43, 31,
- -2, -76, -73, -88, -39, 112, 112, 112, -66, 14,
- -25, 66, 65, 82, -25, -42, 85, 67, 83, 84,
- 69, 87, 86, 97, 90, 91, 92, 93, 94, 95,
- 96, 88, 89, 100, 75, 76, 77, 78, 79, 80,
- 81, -58, -103, 104, 105, -45, -45, -45, -45, -45,
- -45, -103, 103, -2, -53, -25, -103, -103, -103, -103,
- -103, -103, -62, -25, -103, -105, -103, -105, -105, -105,
- -105, -105, -105, -105, -103, -103, -103, -103, 103, 49,
- -88, -97, -83, 114, -26, 51, 10, -91, -90, 21,
- -88, 55, 103, -34, -75, 49, -54, -56, -55, -103,
- -75, -104, 51, 52, 103, -66, -103, -103, -103, -70,
- 16, 15, -25, -25, -51, 62, 67, 63, 64, -41,
- -45, -52, -55, -58, 60, 85, 83, 84, 69, -45,
- -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
- -45, -45, -45, -45, -98, 53, 55, 101, 122, -44,
- -44, -88, -23, 20, -22, -89, -104, 51, -104, -22,
- -22, -25, -25, -72, -88, -16, -60, -61, 71, -88,
- -104, -22, -23, -22, -22, -89, -86, -92, -92, -39,
- -24, -27, -28, -29, -30, -36, -58, -103, -90, 92,
- -93, 26, 51, -104, -88, -88, -70, -38, -88, -38,
- -38, -25, -67, -68, -25, 62, 63, 64, -52, -45,
- -45, -45, -21, 123, 66, -104, -22, -103, -25, -104,
- -104, 51, 118, 21, -104, 51, -22, -63, -61, 73,
- -25, -104, -104, -104, -104, -104, -97, -64, 12, 51,
- -31, -32, -33, 39, 43, 45, 40, 41, 42, 46,
- -96, 21, -27, 103, 27, -56, 103, -104, 51, -104,
- -104, 51, 17, 51, -69, 23, 24, -21, 66, -45,
- -45, -104, -23, -99, -98, -99, -99, 141, -88, -66,
- 74, -25, 72, -65, 13, 15, -28, -29, -28, -29,
- 39, 39, 39, 44, 39, 44, 39, -32, -92, -104,
- 92, 8, -88, -88, -25, -25, -68, -45, -104, -104,
- 124, -103, -98, 125, -104, -104, -103, -20, 139, -25,
- -66, -25, -53, 49, 49, 39, 39, -76, 56, 29,
- -45, -104, 55, -70, -25, -25, -104, 51, -98, -19,
- 85, 144, -71, 18, 30, -98, 125, 56, -104, 142,
- 46, 145, 8, 85, 29, -104, 36, 143, 146, 35,
- -98, 36, 36, 144, 145, 146,
+ -77, -59, -88, -92, 53, -87, -81, 116, 112, -34,
+ 111, -34, -34, -80, 116, 53, -80, -34, -16, 33,
+ 20, 61, -35, 25, -34, 29, -95, -94, 21, -92,
+ 55, 103, -34, 51, 75, 103, -34, 67, 53, -98,
+ -104, -89, 50, -88, -86, 53, -87, 66, 21, 23,
+ 69, 101, 15, 70, 100, 136, 106, 45, 128, 129,
+ 126, 127, 28, 9, 24, 120, 20, 94, 108, 73,
+ 74, 123, 22, 121, 64, 18, 48, 10, 12, 13,
+ 116, 115, 85, 112, 43, 7, 102, 25, 82, 39,
+ 27, 41, 83, 16, 130, 131, 30, 140, 96, 46,
+ 33, 67, 62, 49, 65, 14, 44, 84, 109, 135,
+ 42, 6, 139, 29, 119, 40, 111, 72, 114, 63,
+ 5, 117, 8, 47, 118, 132, 133, 134, 31, 71,
+ 11, -34, -98, 114, -34, 22, 49, -34, -17, 40,
+ -46, -88, 56, 59, -74, 29, -104, -34, -76, -37,
+ 47, 115, 48, -94, -93, -92, -86, -39, 11, -77,
+ -25, -40, 67, -45, -41, 22, -44, -59, -57, -58,
+ 101, 90, 91, 98, 68, 102, -49, -47, -48, -50,
+ 63, 64, 55, 54, 56, 57, 58, 59, 62, -88,
+ -92, -55, -104, 41, 42, 136, 137, 140, 138, 70,
+ 31, 126, 134, 133, 132, 130, 131, 128, 129, 116,
+ 127, 96, 135, -89, -93, -86, -98, 22, -85, 118,
+ -82, 109, 107, 28, 108, 14, 147, 53, -34, -34,
+ -98, -22, -24, 92, -25, -92, -78, 18, 10, 31,
+ 31, -43, 31, -2, -76, -73, -88, -39, 112, 112,
+ 112, -66, 14, -25, 66, 65, 82, -25, -42, 85,
+ 67, 83, 84, 69, 87, 86, 97, 90, 91, 92,
+ 93, 94, 95, 96, 88, 89, 100, 75, 76, 77,
+ 78, 79, 80, 81, -58, -104, 104, 105, -45, -45,
+ -45, -45, -45, -45, -104, 103, -2, -53, -25, -104,
+ -104, -104, -104, -104, -104, -62, -25, -104, -106, -104,
+ -106, -106, -106, -106, -106, -106, -106, -104, -104, -104,
+ -104, 103, 49, -88, -97, -83, 114, -26, 51, 10,
+ -91, -90, 21, -88, 55, 103, -34, -75, 49, -54,
+ -56, -55, -104, -75, -105, 51, 52, 103, -66, -104,
+ -104, -104, -70, 16, 15, -25, -25, -51, 62, 67,
+ 63, 64, -41, -45, -52, -55, -58, 60, 85, 83,
+ 84, 69, -45, -45, -45, -45, -45, -45, -45, -45,
+ -45, -45, -45, -45, -45, -45, -45, -99, 53, 55,
+ 101, 122, -44, -44, -88, -23, 20, -22, -89, -105,
+ 51, -105, -22, -22, -25, -25, -72, -88, -16, -60,
+ -61, 71, -88, -105, -22, -23, -22, -22, -89, -34,
+ -34, -39, -24, -27, -28, -29, -30, -36, -58, -104,
+ -90, 92, -93, 26, 51, -105, -88, -88, -70, -38,
+ -88, -38, -38, -25, -67, -68, -25, 62, 63, 64,
+ -52, -45, -45, -45, -21, 123, 66, -105, -22, -104,
+ -25, -105, -105, 51, 118, 21, -105, 51, -22, -63,
+ -61, 73, -25, -105, -105, -105, -105, -105, -98, -64,
+ 12, 51, -31, -32, -33, 39, 43, 45, 40, 41,
+ 42, 46, -96, 21, -27, 103, 27, -56, 103, -105,
+ 51, -105, -105, 51, 17, 51, -69, 23, 24, -21,
+ 66, -45, -45, -105, -23, -100, -99, -100, -100, 141,
+ -88, -66, 74, -25, 72, -65, 13, 15, -28, -29,
+ -28, -29, 39, 39, 39, 44, 39, 44, 39, -32,
+ -92, -105, 92, 8, -88, -88, -25, -25, -68, -45,
+ -105, -105, 124, -104, -99, 125, -105, -105, -104, -20,
+ 139, -25, -66, -25, -53, 49, 49, 39, 39, -76,
+ 56, 29, -45, -105, 55, -70, -25, -25, -105, 51,
+ -99, -19, 85, 144, -71, 18, 30, -99, 125, 56,
+ -105, 142, 46, 145, 8, 85, 29, -105, 36, 143,
+ 146, 35, -99, 36, 36, 144, 145, 146,
}
var yyDef = [...]int{
@@ -929,67 +951,67 @@ var yyDef = [...]int{
35, 36, 17, 42, 43, 47, 45, 46, 38, 0,
0, 80, 97, 306, 307, 394, 395, 396, 397, 398,
399, 400, 401, 402, 403, 404, 405, 406, 0, 22,
- 279, 0, 229, 0, -2, -2, 0, 0, 0, 409,
- 302, 303, 0, 409, 0, 0, 0, 0, 0, 33,
- 49, 0, 48, 40, 263, 0, 96, 0, 99, 81,
- 0, 83, 84, 0, 105, 0, 0, 0, 409, 0,
- 300, 25, 0, 28, 0, 30, 285, 0, 409, 0,
- 50, 0, 239, 0, 0, 0, 0, 0, 407, 95,
- 105, 73, 0, 0, 0, 82, 98, 308, 309, 310,
- 311, 312, 313, 314, 315, 316, 317, 318, 319, 320,
- 321, 322, 323, 324, 325, 326, 327, 328, 329, 330,
- 331, 332, 333, 334, 335, 336, 337, 338, 339, 340,
- 341, 342, 343, 344, 345, 346, 347, 348, 349, 350,
- 351, 352, 353, 354, 355, 356, 357, 358, 359, 360,
- 361, 362, 363, 364, 365, 366, 367, 368, 369, 370,
- 371, 372, 373, 374, 375, 376, 377, 378, 379, 380,
- 381, 382, 383, 384, 385, 386, 387, 388, 389, 390,
- 391, 392, 393, 246, 0, 280, 281, 107, 0, 112,
- 115, 0, 152, 153, 154, 155, 0, 0, 0, 0,
- 0, 0, 177, 178, 179, 180, 113, 114, 232, 233,
- 234, 235, 236, 237, 238, 229, 0, 278, 0, 0,
- 0, 0, 0, 0, 0, 220, 0, 201, 201, 201,
- 201, 201, 201, 201, 201, 0, 0, 0, 0, 230,
- 0, 304, -2, 23, 287, 0, 0, 409, 296, 290,
- 291, 292, 293, 294, 295, 29, 31, 32, 64, 53,
- 55, 59, 0, 0, 282, 283, 240, 241, 269, 0,
- 272, 269, 0, 265, 246, 0, 0, 0, 254, 0,
- 106, 0, 0, 0, 110, 0, 0, 0, 0, 0,
+ 279, 0, 229, 0, -2, -2, 0, 0, 0, 410,
+ 0, 410, 0, 0, 0, 0, 0, 33, 49, 0,
+ 48, 40, 263, 0, 96, 0, 99, 81, 0, 83,
+ 84, 0, 105, 0, 0, 0, 410, 0, 300, 25,
+ 411, 412, 407, 304, 305, 302, 303, 310, 311, 312,
+ 313, 314, 315, 316, 317, 318, 319, 320, 321, 322,
+ 323, 324, 325, 326, 327, 328, 329, 330, 331, 332,
+ 333, 334, 335, 336, 337, 338, 339, 340, 341, 342,
+ 343, 344, 345, 346, 347, 348, 349, 350, 351, 352,
+ 353, 354, 355, 356, 357, 358, 359, 360, 361, 362,
+ 363, 364, 365, 366, 367, 368, 369, 370, 371, 372,
+ 373, 374, 375, 376, 377, 378, 379, 380, 381, 382,
+ 383, 384, 385, 386, 387, 388, 389, 390, 391, 392,
+ 393, 0, 28, 0, 30, 285, 0, 410, 0, 50,
+ 0, 239, 0, 0, 0, 0, 0, 95, 105, 73,
+ 0, 0, 0, 82, 98, 308, 309, 246, 0, 280,
+ 281, 107, 0, 112, 115, 0, 152, 153, 154, 155,
+ 0, 0, 0, 0, 0, 0, 177, 178, 179, 180,
+ 113, 114, 232, 233, 234, 235, 236, 237, 238, 229,
+ 0, 278, 0, 0, 0, 0, 0, 0, 0, 220,
+ 0, 201, 201, 201, 201, 201, 201, 201, 201, 0,
+ 0, 0, 0, 230, 0, -2, 23, 287, 0, 0,
+ 409, 296, 290, 291, 292, 293, 294, 295, 29, 31,
+ 32, 64, 53, 55, 59, 0, 0, 282, 283, 240,
+ 241, 269, 0, 272, 269, 0, 265, 246, 0, 0,
+ 0, 254, 0, 106, 0, 0, 0, 110, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 133, 134, 135, 136, 137, 138,
- 139, 126, 0, 0, 0, 171, 172, 173, 174, 175,
- 0, 51, 0, 0, 0, 146, 0, 0, 0, 0,
- 0, 47, 0, 221, 0, 193, 0, 194, 195, 196,
- 197, 198, 199, 200, 0, 51, 0, 0, 0, 0,
- 301, 26, 0, 297, 105, 0, 0, 56, 60, 0,
- 62, 63, 0, 16, 18, 0, 271, 273, 275, 0,
- 19, 264, 0, 408, 0, 254, 0, 0, 0, 21,
- 0, 0, 108, 109, 111, 127, 0, 129, 131, 116,
- 117, 118, 142, 143, 144, 0, 0, 0, 0, 140,
- 122, 0, 156, 157, 158, 159, 160, 161, 162, 163,
- 164, 165, 166, 167, 170, 148, 149, 150, 151, 168,
- 169, 176, 0, 0, 52, 230, 145, 0, 277, 0,
- 0, 0, 0, 0, 261, 0, 227, 224, 0, 0,
- 202, 0, 0, 0, 0, 231, 305, 409, 27, 242,
- 54, 65, 66, 68, 69, 70, 78, 0, 61, 57,
- 0, 0, 0, 276, 267, 266, 20, 0, 103, 0,
- 0, 255, 247, 248, 251, 128, 130, 132, 119, 140,
- 123, 0, 120, 0, 0, 181, 0, 51, 147, 184,
- 185, 0, 0, 0, 0, 0, 246, 0, 225, 0,
- 0, 192, 203, 204, 205, 206, 24, 244, 0, 0,
- 0, 0, 0, 85, 0, 0, 88, 0, 0, 0,
- 0, 79, 0, 0, 0, 274, 0, 100, 0, 101,
- 102, 0, 0, 0, 250, 252, 253, 121, 0, 141,
- 124, 182, 0, 0, 212, 0, 0, 0, 262, 222,
- 191, 228, 0, 246, 0, 0, 67, 74, 0, 77,
- 86, 87, 89, 0, 91, 0, 93, 94, 71, 72,
- 58, 0, 268, 104, 256, 257, 249, 125, 183, 186,
- 213, 0, 217, 0, 187, 188, 0, 0, 0, 226,
- 254, 245, 243, 0, 0, 90, 92, 270, 0, 0,
- 207, 190, 223, 258, 75, 76, 214, 0, 218, 0,
- 0, 0, 15, 0, 0, 215, 0, 0, 189, 0,
- 0, 0, 259, 0, 0, 219, 208, 0, 211, 0,
- 216, 209, 260, 0, 0, 210,
+ 0, 0, 0, 0, 0, 0, 0, 133, 134, 135,
+ 136, 137, 138, 139, 126, 0, 0, 0, 171, 172,
+ 173, 174, 175, 0, 51, 0, 0, 0, 146, 0,
+ 0, 0, 0, 0, 47, 0, 221, 0, 193, 0,
+ 194, 195, 196, 197, 198, 199, 200, 0, 51, 0,
+ 0, 0, 0, 301, 26, 0, 297, 105, 0, 0,
+ 56, 60, 0, 62, 63, 0, 16, 18, 0, 271,
+ 273, 275, 0, 19, 264, 0, 408, 0, 254, 0,
+ 0, 0, 21, 0, 0, 108, 109, 111, 127, 0,
+ 129, 131, 116, 117, 118, 142, 143, 144, 0, 0,
+ 0, 0, 140, 122, 0, 156, 157, 158, 159, 160,
+ 161, 162, 163, 164, 165, 166, 167, 170, 148, 149,
+ 150, 151, 168, 169, 176, 0, 0, 52, 230, 145,
+ 0, 277, 0, 0, 0, 0, 0, 261, 0, 227,
+ 224, 0, 0, 202, 0, 0, 0, 0, 231, 410,
+ 27, 242, 54, 65, 66, 68, 69, 70, 78, 0,
+ 61, 57, 0, 0, 0, 276, 267, 266, 20, 0,
+ 103, 0, 0, 255, 247, 248, 251, 128, 130, 132,
+ 119, 140, 123, 0, 120, 0, 0, 181, 0, 51,
+ 147, 184, 185, 0, 0, 0, 0, 0, 246, 0,
+ 225, 0, 0, 192, 203, 204, 205, 206, 24, 244,
+ 0, 0, 0, 0, 0, 85, 0, 0, 88, 0,
+ 0, 0, 0, 79, 0, 0, 0, 274, 0, 100,
+ 0, 101, 102, 0, 0, 0, 250, 252, 253, 121,
+ 0, 141, 124, 182, 0, 0, 212, 0, 0, 0,
+ 262, 222, 191, 228, 0, 246, 0, 0, 67, 74,
+ 0, 77, 86, 87, 89, 0, 91, 0, 93, 94,
+ 71, 72, 58, 0, 268, 104, 256, 257, 249, 125,
+ 183, 186, 213, 0, 217, 0, 187, 188, 0, 0,
+ 0, 226, 254, 245, 243, 0, 0, 90, 92, 270,
+ 0, 0, 207, 190, 223, 258, 75, 76, 214, 0,
+ 218, 0, 0, 0, 15, 0, 0, 215, 0, 0,
+ 189, 0, 0, 0, 259, 0, 0, 219, 208, 0,
+ 211, 0, 216, 209, 260, 0, 0, 210,
}
var yyTok1 = [...]int{
@@ -1444,45 +1466,45 @@ yydefault:
yyDollar = yyS[yypt-5 : yypt+1]
//line ./go/vt/sqlparser/sql.y:273
{
- yyVAL.statement = &DDL{Action: CreateStr, NewName: yyDollar[4].tableIdent}
+ yyVAL.statement = &DDL{Action: CreateStr, NewName: yyDollar[4].tableName}
}
case 24:
yyDollar = yyS[yypt-8 : yypt+1]
//line ./go/vt/sqlparser/sql.y:277
{
// Change this to an alter statement
- yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[7].tableIdent, NewName: yyDollar[7].tableIdent}
+ yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[7].tableName, NewName: yyDollar[7].tableName}
}
case 25:
yyDollar = yyS[yypt-4 : yypt+1]
//line ./go/vt/sqlparser/sql.y:282
{
- yyVAL.statement = &DDL{Action: CreateStr, NewName: NewTableIdent(yyDollar[3].colIdent.Lowered())}
+ yyVAL.statement = &DDL{Action: CreateStr, NewName: yyDollar[3].tableName.ToViewName()}
}
case 26:
yyDollar = yyS[yypt-6 : yypt+1]
//line ./go/vt/sqlparser/sql.y:288
{
- yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[4].tableIdent, NewName: yyDollar[4].tableIdent}
+ yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[4].tableName, NewName: yyDollar[4].tableName}
}
case 27:
yyDollar = yyS[yypt-7 : yypt+1]
//line ./go/vt/sqlparser/sql.y:292
{
// Change this to a rename statement
- yyVAL.statement = &DDL{Action: RenameStr, Table: yyDollar[4].tableIdent, NewName: yyDollar[7].tableIdent}
+ yyVAL.statement = &DDL{Action: RenameStr, Table: yyDollar[4].tableName, NewName: yyDollar[7].tableName}
}
case 28:
yyDollar = yyS[yypt-4 : yypt+1]
//line ./go/vt/sqlparser/sql.y:297
{
- yyVAL.statement = &DDL{Action: AlterStr, Table: NewTableIdent(yyDollar[3].colIdent.Lowered()), NewName: NewTableIdent(yyDollar[3].colIdent.Lowered())}
+ yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[3].tableName.ToViewName(), NewName: yyDollar[3].tableName.ToViewName()}
}
case 29:
yyDollar = yyS[yypt-5 : yypt+1]
//line ./go/vt/sqlparser/sql.y:303
{
- yyVAL.statement = &DDL{Action: RenameStr, Table: yyDollar[3].tableIdent, NewName: yyDollar[5].tableIdent}
+ yyVAL.statement = &DDL{Action: RenameStr, Table: yyDollar[3].tableName, NewName: yyDollar[5].tableName}
}
case 30:
yyDollar = yyS[yypt-4 : yypt+1]
@@ -1492,14 +1514,14 @@ yydefault:
if yyDollar[3].byt != 0 {
exists = true
}
- yyVAL.statement = &DDL{Action: DropStr, Table: yyDollar[4].tableIdent, IfExists: exists}
+ yyVAL.statement = &DDL{Action: DropStr, Table: yyDollar[4].tableName, IfExists: exists}
}
case 31:
yyDollar = yyS[yypt-5 : yypt+1]
//line ./go/vt/sqlparser/sql.y:317
{
// Change this to an alter statement
- yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[5].tableIdent, NewName: yyDollar[5].tableIdent}
+ yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[5].tableName, NewName: yyDollar[5].tableName}
}
case 32:
yyDollar = yyS[yypt-5 : yypt+1]
@@ -1509,13 +1531,13 @@ yydefault:
if yyDollar[3].byt != 0 {
exists = true
}
- yyVAL.statement = &DDL{Action: DropStr, Table: NewTableIdent(yyDollar[4].colIdent.Lowered()), IfExists: exists}
+ yyVAL.statement = &DDL{Action: DropStr, Table: yyDollar[4].tableName.ToViewName(), IfExists: exists}
}
case 33:
yyDollar = yyS[yypt-3 : yypt+1]
//line ./go/vt/sqlparser/sql.y:332
{
- yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[3].tableIdent, NewName: yyDollar[3].tableIdent}
+ yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[3].tableName, NewName: yyDollar[3].tableName}
}
case 34:
yyDollar = yyS[yypt-2 : yypt+1]
@@ -3142,6 +3164,24 @@ yydefault:
{
forceEOF(yylex)
}
+ case 410:
+ yyDollar = yyS[yypt-0 : yypt+1]
+ //line ./go/vt/sqlparser/sql.y:1697
+ {
+ forceEOF(yylex)
+ }
+ case 411:
+ yyDollar = yyS[yypt-1 : yypt+1]
+ //line ./go/vt/sqlparser/sql.y:1701
+ {
+ forceEOF(yylex)
+ }
+ case 412:
+ yyDollar = yyS[yypt-1 : yypt+1]
+ //line ./go/vt/sqlparser/sql.y:1705
+ {
+ forceEOF(yylex)
+ }
}
goto yystack /* stack new state and value */
}
diff --git a/go/vt/sqlparser/sql.y b/go/vt/sqlparser/sql.y
index 7e62f5e792d..1e60726055a 100644
--- a/go/vt/sqlparser/sql.y
+++ b/go/vt/sqlparser/sql.y
@@ -186,7 +186,7 @@ func forceEOF(yylex interface{}) {
%type sql_id reserved_sql_id col_alias as_ci_opt
%type table_id reserved_table_id table_alias as_opt_id
%type as_opt
-%type force_eof
+%type force_eof ddl_force_eof
%type charset
%type convert_type
@@ -269,43 +269,43 @@ set_statement:
}
create_statement:
- CREATE TABLE not_exists_opt table_id force_eof
+ CREATE TABLE not_exists_opt table_name ddl_force_eof
{
$$ = &DDL{Action: CreateStr, NewName: $4}
}
-| CREATE constraint_opt INDEX ID using_opt ON table_id force_eof
+| CREATE constraint_opt INDEX ID using_opt ON table_name ddl_force_eof
{
// Change this to an alter statement
- $$ = &DDL{Action: AlterStr, Table: $7, NewName: $7}
+ $$ = &DDL{Action: AlterStr, Table: $7, NewName:$7}
}
-| CREATE VIEW sql_id force_eof
+| CREATE VIEW table_name ddl_force_eof
{
- $$ = &DDL{Action: CreateStr, NewName: NewTableIdent($3.Lowered())}
+ $$ = &DDL{Action: CreateStr, NewName: $3.ToViewName()}
}
alter_statement:
- ALTER ignore_opt TABLE table_id non_rename_operation force_eof
+ ALTER ignore_opt TABLE table_name non_rename_operation force_eof
{
$$ = &DDL{Action: AlterStr, Table: $4, NewName: $4}
}
-| ALTER ignore_opt TABLE table_id RENAME to_opt table_id
+| ALTER ignore_opt TABLE table_name RENAME to_opt table_name
{
// Change this to a rename statement
$$ = &DDL{Action: RenameStr, Table: $4, NewName: $7}
}
-| ALTER VIEW sql_id force_eof
+| ALTER VIEW table_name ddl_force_eof
{
- $$ = &DDL{Action: AlterStr, Table: NewTableIdent($3.Lowered()), NewName: NewTableIdent($3.Lowered())}
+ $$ = &DDL{Action: AlterStr, Table: $3.ToViewName(), NewName: $3.ToViewName()}
}
rename_statement:
- RENAME TABLE table_id TO table_id
+ RENAME TABLE table_name TO table_name
{
$$ = &DDL{Action: RenameStr, Table: $3, NewName: $5}
}
drop_statement:
- DROP TABLE exists_opt table_id
+ DROP TABLE exists_opt table_name
{
var exists bool
if $3 != 0 {
@@ -313,22 +313,22 @@ drop_statement:
}
$$ = &DDL{Action: DropStr, Table: $4, IfExists: exists}
}
-| DROP INDEX ID ON table_id
+| DROP INDEX ID ON table_name
{
// Change this to an alter statement
$$ = &DDL{Action: AlterStr, Table: $5, NewName: $5}
}
-| DROP VIEW exists_opt sql_id force_eof
+| DROP VIEW exists_opt table_name ddl_force_eof
{
var exists bool
if $3 != 0 {
exists = true
}
- $$ = &DDL{Action: DropStr, Table: NewTableIdent($4.Lowered()), IfExists: exists}
+ $$ = &DDL{Action: DropStr, Table: $4.ToViewName(), IfExists: exists}
}
analyze_statement:
- ANALYZE TABLE table_id
+ ANALYZE TABLE table_name
{
$$ = &DDL{Action: AlterStr, Table: $3, NewName: $3}
}
@@ -1692,3 +1692,16 @@ force_eof:
{
forceEOF(yylex)
}
+
+ddl_force_eof:
+ {
+ forceEOF(yylex)
+ }
+| openb
+ {
+ forceEOF(yylex)
+ }
+| reserved_sql_id
+ {
+ forceEOF(yylex)
+ }
diff --git a/go/vt/sqlparser/tracked_buffer.go b/go/vt/sqlparser/tracked_buffer.go
index e0ea35a25ce..0a47c810c5a 100644
--- a/go/vt/sqlparser/tracked_buffer.go
+++ b/go/vt/sqlparser/tracked_buffer.go
@@ -115,3 +115,10 @@ func (buf *TrackedBuffer) ParsedQuery() *ParsedQuery {
func (buf *TrackedBuffer) HasBindVars() bool {
return len(buf.bindLocations) != 0
}
+
+// BuildParsedQuery builds a ParsedQuery from the input.
+func BuildParsedQuery(in string, vars ...interface{}) *ParsedQuery {
+ buf := NewTrackedBuffer(nil)
+ buf.Myprintf(in, vars...)
+ return buf.ParsedQuery()
+}
diff --git a/go/vt/tabletserver/messager_engine_test.go b/go/vt/tabletserver/messager_engine_test.go
deleted file mode 100644
index 6bbdfa9f737..00000000000
--- a/go/vt/tabletserver/messager_engine_test.go
+++ /dev/null
@@ -1,290 +0,0 @@
-// Copyright 2017, Google Inc. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package tabletserver
-
-import (
- "reflect"
- "runtime"
- "testing"
- "time"
-
- "github.com/youtube/vitess/go/sqltypes"
-
- querypb "github.com/youtube/vitess/go/vt/proto/query"
- topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema"
-)
-
-var meTable = &schema.Table{
- Type: schema.Message,
- MessageInfo: mmTable.MessageInfo,
-}
-
-func TestMESchemaChanged(t *testing.T) {
- db := setUpTabletServerTest(t)
- defer db.Close()
- testUtils := newTestUtils()
- config := testUtils.newQueryServiceConfig()
- config.TransactionCap = 1
- tsv := NewTabletServer(config)
- dbconfigs := testUtils.newDBConfigs(db)
- target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
- err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
- if err != nil {
- t.Fatalf("StartService failed: %v", err)
- }
- defer tsv.StopService()
-
- me := tsv.messager
- tables := map[string]*schema.Table{
- "t1": meTable,
- "t2": {
- Type: schema.NoType,
- },
- }
- me.schemaChanged(tables, []string{"t1", "t2"}, nil, nil)
- got := extractManagerNames(me.managers)
- want := map[string]bool{"msg": true, "t1": true}
- if !reflect.DeepEqual(got, want) {
- t.Errorf("got: %+v, want %+v", got, want)
- }
- tables = map[string]*schema.Table{
- "t1": meTable,
- "t2": {
- Type: schema.NoType,
- },
- "t3": meTable,
- }
- me.schemaChanged(tables, []string{"t3"}, nil, nil)
- got = extractManagerNames(me.managers)
- want = map[string]bool{"msg": true, "t1": true, "t3": true}
- if !reflect.DeepEqual(got, want) {
- t.Errorf("got: %+v, want %+v", got, want)
- }
- tables = map[string]*schema.Table{
- "t1": meTable,
- "t2": {
- Type: schema.NoType,
- },
- "t4": meTable,
- }
- me.schemaChanged(tables, []string{"t4"}, nil, []string{"t3"})
- got = extractManagerNames(me.managers)
- // schemaChanged is only additive.
- want = map[string]bool{"msg": true, "t1": true, "t4": true}
- if !reflect.DeepEqual(got, want) {
- t.Errorf("got: %+v, want %+v", got, want)
- }
-}
-
-func extractManagerNames(in map[string]*MessageManager) map[string]bool {
- out := make(map[string]bool)
- for k := range in {
- out[k] = true
- }
- return out
-}
-
-func TestSubscribe(t *testing.T) {
- db := setUpTabletServerTest(t)
- defer db.Close()
- testUtils := newTestUtils()
- config := testUtils.newQueryServiceConfig()
- config.TransactionCap = 1
- tsv := NewTabletServer(config)
- dbconfigs := testUtils.newDBConfigs(db)
- target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
- err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
- if err != nil {
- t.Fatalf("StartService failed: %v", err)
- }
- defer tsv.StopService()
-
- me := tsv.messager
- tables := map[string]*schema.Table{
- "t1": meTable,
- "t2": meTable,
- }
- me.schemaChanged(tables, []string{"t1", "t2"}, nil, nil)
- r1 := newTestReceiver(1)
- r2 := newTestReceiver(1)
- // Each receiver is subscribed to different managers.
- me.Subscribe("t1", r1.rcv)
- <-r1.ch
- me.Subscribe("t2", r2.rcv)
- <-r2.ch
- me.managers["t1"].Add(&MessageRow{ID: sqltypes.MakeString([]byte("1"))})
- me.managers["t2"].Add(&MessageRow{ID: sqltypes.MakeString([]byte("2"))})
- <-r1.ch
- <-r2.ch
-
- // Error case.
- want := "error: message table t3 not found"
- err = me.Subscribe("t3", r1.rcv)
- if err == nil || err.Error() != want {
- t.Errorf("Subscribe: %v, want %s", err, want)
- }
-}
-
-func TestLockDB(t *testing.T) {
- db := setUpTabletServerTest(t)
- defer db.Close()
- testUtils := newTestUtils()
- config := testUtils.newQueryServiceConfig()
- config.TransactionCap = 1
- tsv := NewTabletServer(config)
- dbconfigs := testUtils.newDBConfigs(db)
- target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
- err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
- if err != nil {
- t.Fatalf("StartService failed: %v", err)
- }
- defer tsv.StopService()
-
- me := tsv.messager
- tables := map[string]*schema.Table{
- "t1": meTable,
- "t2": meTable,
- }
- me.schemaChanged(tables, []string{"t1", "t2"}, nil, nil)
- r1 := newTestReceiver(0)
- me.Subscribe("t1", r1.rcv)
- <-r1.ch
-
- row1 := &MessageRow{
- ID: sqltypes.MakeString([]byte("1")),
- }
- row2 := &MessageRow{
- TimeNext: time.Now().UnixNano() + int64(10*time.Minute),
- ID: sqltypes.MakeString([]byte("2")),
- }
- newMessages := map[string][]*MessageRow{"t1": {row1, row2}, "t3": {row1}}
- unlock := me.LockDB(newMessages, nil)
- me.UpdateCaches(newMessages, nil)
- unlock()
- <-r1.ch
- runtime.Gosched()
- // row2 should not be sent.
- select {
- case mr := <-r1.ch:
- t.Errorf("Unexpected message: %v", mr)
- default:
- }
-
- r2 := newTestReceiver(0)
- me.Subscribe("t2", r2.rcv)
- <-r2.ch
- mm := me.managers["t2"]
- mm.Add(&MessageRow{ID: sqltypes.MakeString([]byte("1"))})
- // Make sure the first message is enqueued.
- r2.WaitForCount(2)
- // "2" will be in the cache.
- mm.Add(&MessageRow{ID: sqltypes.MakeString([]byte("2"))})
- changedMessages := map[string][]string{"t2": {"2"}, "t3": {"2"}}
- unlock = me.LockDB(nil, changedMessages)
- // This should delete "2".
- me.UpdateCaches(nil, changedMessages)
- unlock()
- <-r2.ch
- runtime.Gosched()
- // There should be no more messages.
- select {
- case mr := <-r2.ch:
- t.Errorf("Unexpected message: %v", mr)
- default:
- }
-}
-
-func TestMESendDiscard(t *testing.T) {
- // This is a manual test because the discard happens
- // asynchronously, which makes the test flaky.
- t.Skip()
- db := setUpTabletServerTest(t)
- defer db.Close()
- testUtils := newTestUtils()
- config := testUtils.newQueryServiceConfig()
- config.TransactionCap = 1
- tsv := NewTabletServer(config)
- dbconfigs := testUtils.newDBConfigs(db)
- target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
- err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
- if err != nil {
- t.Fatalf("StartService failed: %v", err)
- }
- defer tsv.StopService()
-
- me := tsv.messager
- r1 := newTestReceiver(0)
- me.Subscribe("msg", r1.rcv)
- <-r1.ch
-
- db.AddQuery(
- "select time_scheduled, id from msg where id in ('1') and time_acked is null limit 10001 for update",
- &sqltypes.Result{
- Fields: []*querypb.Field{
- {Type: sqltypes.Int64},
- {Type: sqltypes.Int64},
- },
- RowsAffected: 1,
- Rows: [][]sqltypes.Value{{
- sqltypes.MakeTrusted(sqltypes.Int64, []byte("1")),
- sqltypes.MakeTrusted(sqltypes.Int64, []byte("1")),
- }},
- },
- )
- db.AddQueryPattern("update msg set time_next = .*", &sqltypes.Result{RowsAffected: 1})
- me.managers["msg"].Add(&MessageRow{ID: sqltypes.MakeString([]byte("1"))})
- <-r1.ch
- for i := 0; i < 10; i++ {
- runtime.Gosched()
- time.Sleep(10 * time.Millisecond)
- if _, ok := me.managers["msg"].cache.messages["1"]; !ok {
- return
- }
- }
- t.Error("Message 1 is still present in cache")
-}
-
-func TestMEGenerate(t *testing.T) {
- db := setUpTabletServerTest(t)
- defer db.Close()
- testUtils := newTestUtils()
- config := testUtils.newQueryServiceConfig()
- config.TransactionCap = 1
- tsv := NewTabletServer(config)
- dbconfigs := testUtils.newDBConfigs(db)
- target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
- err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
- if err != nil {
- t.Fatalf("StartService failed: %v", err)
- }
- defer tsv.StopService()
-
- me := tsv.messager
- me.schemaChanged(map[string]*schema.Table{
- "t1": meTable,
- }, []string{"t1"}, nil, nil)
- if _, _, err := me.GenerateAckQuery("t1", []string{"1"}); err != nil {
- t.Error(err)
- }
- want := "message table t2 not found in schema"
- if _, _, err := me.GenerateAckQuery("t2", []string{"1"}); err == nil || err.Error() != want {
- t.Errorf("me.GenerateAckQuery(invalid): %v, want %s", err, want)
- }
-
- if _, _, err := me.GeneratePostponeQuery("t1", []string{"1"}); err != nil {
- t.Error(err)
- }
- if _, _, err := me.GeneratePostponeQuery("t2", []string{"1"}); err == nil || err.Error() != want {
- t.Errorf("me.GeneratePostponeQuery(invalid): %v, want %s", err, want)
- }
-
- if _, _, err := me.GeneratePurgeQuery("t1", 0); err != nil {
- t.Error(err)
- }
- if _, _, err := me.GeneratePurgeQuery("t2", 0); err == nil || err.Error() != want {
- t.Errorf("me.GeneratePurgeQuery(invalid): %v, want %s", err, want)
- }
-}
diff --git a/go/vt/tabletserver/query_rule_info.go b/go/vt/tabletserver/query_rule_info.go
deleted file mode 100644
index 6bb8154242e..00000000000
--- a/go/vt/tabletserver/query_rule_info.go
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2014, Google Inc. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package tabletserver
-
-import (
- "encoding/json"
- "errors"
- "sync"
-
- log "github.com/golang/glog"
- "github.com/youtube/vitess/go/vt/tabletserver/planbuilder"
-)
-
-// QueryRuleInfo is the maintainer of QueryRules from multiple sources
-type QueryRuleInfo struct {
- // mutex to protect following queryRulesMap
- mu sync.Mutex
- // queryRulesMap maps the names of different query rule sources to the actual QueryRules structure
- queryRulesMap map[string]*QueryRules
-}
-
-// NewQueryRuleInfo returns an empty QueryRuleInfo object for use
-func NewQueryRuleInfo() *QueryRuleInfo {
- qri := &QueryRuleInfo{
- queryRulesMap: map[string]*QueryRules{},
- }
- return qri
-}
-
-// RegisterQueryRuleSource registers a query rule source name with QueryRuleInfo
-func (qri *QueryRuleInfo) RegisterQueryRuleSource(ruleSource string) {
- qri.mu.Lock()
- defer qri.mu.Unlock()
- if _, existed := qri.queryRulesMap[ruleSource]; existed {
- log.Errorf("Query rule source " + ruleSource + " has been registered")
- panic("Query rule source " + ruleSource + " has been registered")
- }
- qri.queryRulesMap[ruleSource] = NewQueryRules()
-}
-
-// UnRegisterQueryRuleSource removes a registered query rule source name
-func (qri *QueryRuleInfo) UnRegisterQueryRuleSource(ruleSource string) {
- qri.mu.Lock()
- defer qri.mu.Unlock()
- delete(qri.queryRulesMap, ruleSource)
-}
-
-// SetRules takes an external QueryRules structure and overwrite one of the
-// internal QueryRules as designated by ruleSource parameter
-func (qri *QueryRuleInfo) SetRules(ruleSource string, newRules *QueryRules) error {
- if newRules == nil {
- newRules = NewQueryRules()
- }
- qri.mu.Lock()
- defer qri.mu.Unlock()
- if _, ok := qri.queryRulesMap[ruleSource]; ok {
- qri.queryRulesMap[ruleSource] = newRules.Copy()
- return nil
- }
- return errors.New("Rule source identifier " + ruleSource + " is not valid")
-}
-
-// GetRules returns the corresponding QueryRules as designated by ruleSource parameter
-func (qri *QueryRuleInfo) GetRules(ruleSource string) (*QueryRules, error) {
- qri.mu.Lock()
- defer qri.mu.Unlock()
- if ruleset, ok := qri.queryRulesMap[ruleSource]; ok {
- return ruleset.Copy(), nil
- }
- return NewQueryRules(), errors.New("Rule source identifier " + ruleSource + " is not valid")
-}
-
-// filterByPlan creates a new QueryRules by prefiltering on all query rules that are contained in internal
-// QueryRules structures, in other words, query rules from all predefined sources will be applied
-func (qri *QueryRuleInfo) filterByPlan(query string, planid planbuilder.PlanType, tableName string) (newqrs *QueryRules) {
- qri.mu.Lock()
- defer qri.mu.Unlock()
- newqrs = NewQueryRules()
- for _, rules := range qri.queryRulesMap {
- newqrs.Append(rules.filterByPlan(query, planid, tableName))
- }
- return newqrs
-}
-
-// MarshalJSON marshals to JSON.
-func (qri *QueryRuleInfo) MarshalJSON() ([]byte, error) {
- qri.mu.Lock()
- defer qri.mu.Unlock()
- return json.Marshal(qri.queryRulesMap)
-}
diff --git a/go/vt/tabletserver/query_splitter.go b/go/vt/tabletserver/query_splitter.go
deleted file mode 100644
index 9105396f3ce..00000000000
--- a/go/vt/tabletserver/query_splitter.go
+++ /dev/null
@@ -1,319 +0,0 @@
-package tabletserver
-
-import (
- "encoding/binary"
- "fmt"
- "strconv"
-
- "github.com/youtube/vitess/go/sqltypes"
- querypb "github.com/youtube/vitess/go/vt/proto/query"
- "github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
-)
-
-// QuerySplitter splits a BoundQuery into equally sized smaller queries.
-// QuerySplits are generated by adding primary key range clauses to the
-// original query. Only a limited set of queries are supported, see
-// QuerySplitter.validateQuery() for details. Also, the table must have at least
-// one primary key and the leading primary key must be numeric, see
-// QuerySplitter.splitBoundaries()
-type QuerySplitter struct {
- sql string
- bindVariables map[string]interface{}
- splitCount int64
- se *schema.Engine
- sel *sqlparser.Select
- tableName sqlparser.TableIdent
- splitColumn sqlparser.ColIdent
- rowCount int64
-}
-
-const (
- startBindVarName = "_splitquery_start"
- endBindVarName = "_splitquery_end"
-)
-
-// NewQuerySplitter creates a new QuerySplitter. query is the original query
-// to split and splitCount is the desired number of splits. splitCount must
-// be a positive int, if not it will be set to 1.
-func NewQuerySplitter(
- sql string,
- bindVariables map[string]interface{},
- splitColumn string,
- splitCount int64,
- se *schema.Engine) *QuerySplitter {
- if splitCount < 1 {
- splitCount = 1
- }
- return &QuerySplitter{
- sql: sql,
- bindVariables: bindVariables,
- splitCount: splitCount,
- se: se,
- splitColumn: sqlparser.NewColIdent(splitColumn),
- }
-}
-
-// Ensure that the input query is a Select statement that contains no Join,
-// GroupBy, OrderBy, Limit or Distinct operations. Also ensure that the
-// source table is present in the schema and has at least one primary key.
-func (qs *QuerySplitter) validateQuery() error {
- statement, err := sqlparser.Parse(qs.sql)
- if err != nil {
- return err
- }
- var ok bool
- qs.sel, ok = statement.(*sqlparser.Select)
- if !ok {
- return fmt.Errorf("not a select statement")
- }
- if qs.sel.Distinct != "" || qs.sel.GroupBy != nil ||
- qs.sel.Having != nil || len(qs.sel.From) != 1 ||
- qs.sel.OrderBy != nil || qs.sel.Limit != nil ||
- qs.sel.Lock != "" {
- return fmt.Errorf("unsupported query")
- }
- node, ok := qs.sel.From[0].(*sqlparser.AliasedTableExpr)
- if !ok {
- return fmt.Errorf("unsupported query")
- }
- qs.tableName = sqlparser.GetTableName(node.Expr)
- if qs.tableName.IsEmpty() {
- return fmt.Errorf("not a simple table expression")
- }
- table := qs.se.GetTable(qs.tableName)
- if table == nil {
- return fmt.Errorf("can't find table in schema")
- }
- if len(table.PKColumns) == 0 {
- return fmt.Errorf("no primary keys")
- }
- if !qs.splitColumn.IsEmpty() {
- for _, index := range table.Indexes {
- for _, column := range index.Columns {
- if qs.splitColumn.Equal(column) {
- return nil
- }
- }
- }
- return fmt.Errorf("split column is not indexed or does not exist in table schema, SplitColumn: %v, Table: %v", qs.splitColumn, table)
- }
- qs.splitColumn = table.GetPKColumn(0).Name
- return nil
-}
-
-// split splits the query into multiple queries. validateQuery() must return
-// nil error before split() is called.
-func (qs *QuerySplitter) split(columnType querypb.Type, pkMinMax *sqltypes.Result) ([]querytypes.QuerySplit, error) {
- boundaries, err := qs.splitBoundaries(columnType, pkMinMax)
- if err != nil {
- return nil, err
- }
- splits := []querytypes.QuerySplit{}
- // No splits, return the original query as a single split
- if len(boundaries) == 0 {
- splits = append(splits, querytypes.QuerySplit{
- Sql: qs.sql,
- BindVariables: qs.bindVariables,
- })
- } else {
- boundaries = append(boundaries, sqltypes.Value{})
- whereClause := qs.sel.Where
- // Loop through the boundaries and generated modified where clauses
- start := sqltypes.Value{}
- for _, end := range boundaries {
- bindVars := make(map[string]interface{}, len(qs.bindVariables))
- for k, v := range qs.bindVariables {
- bindVars[k] = v
- }
- qs.sel.Where = qs.getWhereClause(whereClause, bindVars, start, end)
- split := &querytypes.QuerySplit{
- Sql: sqlparser.String(qs.sel),
- BindVariables: bindVars,
- RowCount: qs.rowCount,
- }
- splits = append(splits, *split)
- start = end
- }
- qs.sel.Where = whereClause // reset where clause
- }
- return splits, err
-}
-
-// getWhereClause returns a whereClause based on desired upper and lower
-// bounds for primary key.
-func (qs *QuerySplitter) getWhereClause(whereClause *sqlparser.Where, bindVars map[string]interface{}, start, end sqltypes.Value) *sqlparser.Where {
- var startClause *sqlparser.ComparisonExpr
- var endClause *sqlparser.ComparisonExpr
- var clauses sqlparser.Expr
- // No upper or lower bound, just return the where clause of original query
- if start.IsNull() && end.IsNull() {
- return whereClause
- }
- pk := &sqlparser.ColName{
- Name: qs.splitColumn,
- }
- if !start.IsNull() {
- startClause = &sqlparser.ComparisonExpr{
- Operator: sqlparser.GreaterEqualStr,
- Left: pk,
- Right: sqlparser.NewValArg([]byte(":" + startBindVarName)),
- }
- bindVars[startBindVarName] = start.ToNative()
- }
- // splitColumn < end
- if !end.IsNull() {
- endClause = &sqlparser.ComparisonExpr{
- Operator: sqlparser.LessThanStr,
- Left: pk,
- Right: sqlparser.NewValArg([]byte(":" + endBindVarName)),
- }
- bindVars[endBindVarName] = end.ToNative()
- }
- if startClause == nil {
- clauses = endClause
- } else {
- if endClause == nil {
- clauses = startClause
- } else {
- // splitColumn >= start AND splitColumn < end
- clauses = &sqlparser.AndExpr{
- Left: startClause,
- Right: endClause,
- }
- }
- }
- if whereClause != nil {
- clauses = &sqlparser.AndExpr{
- Left: &sqlparser.ParenExpr{Expr: whereClause.Expr},
- Right: &sqlparser.ParenExpr{Expr: clauses},
- }
- }
- return &sqlparser.Where{
- Type: sqlparser.WhereStr,
- Expr: clauses,
- }
-}
-
-func (qs *QuerySplitter) splitBoundaries(columnType querypb.Type, pkMinMax *sqltypes.Result) ([]sqltypes.Value, error) {
- switch {
- case sqltypes.IsSigned(columnType):
- return qs.splitBoundariesIntColumn(pkMinMax)
- case sqltypes.IsUnsigned(columnType):
- return qs.splitBoundariesUintColumn(pkMinMax)
- case sqltypes.IsFloat(columnType):
- return qs.splitBoundariesFloatColumn(pkMinMax)
- case sqltypes.IsBinary(columnType):
- return qs.splitBoundariesStringColumn()
- }
- return []sqltypes.Value{}, nil
-}
-
-func (qs *QuerySplitter) splitBoundariesIntColumn(pkMinMax *sqltypes.Result) ([]sqltypes.Value, error) {
- boundaries := []sqltypes.Value{}
- if pkMinMax == nil || len(pkMinMax.Rows) != 1 || pkMinMax.Rows[0][0].IsNull() || pkMinMax.Rows[0][1].IsNull() {
- return boundaries, nil
- }
- minNumeric := pkMinMax.Rows[0][0]
- maxNumeric := pkMinMax.Rows[0][1]
- min, err := minNumeric.ParseInt64()
- if err != nil {
- return nil, err
- }
- max, err := maxNumeric.ParseInt64()
- if err != nil {
- return nil, err
- }
- interval := (max - min) / qs.splitCount
- if interval == 0 {
- return nil, err
- }
- qs.rowCount = interval
- for i := int64(1); i < qs.splitCount; i++ {
- v, err := sqltypes.BuildValue(min + interval*i)
- if err != nil {
- return nil, err
- }
- boundaries = append(boundaries, v)
- }
- return boundaries, nil
-}
-
-func (qs *QuerySplitter) splitBoundariesUintColumn(pkMinMax *sqltypes.Result) ([]sqltypes.Value, error) {
- boundaries := []sqltypes.Value{}
- if pkMinMax == nil || len(pkMinMax.Rows) != 1 || pkMinMax.Rows[0][0].IsNull() || pkMinMax.Rows[0][1].IsNull() {
- return boundaries, nil
- }
- minNumeric := pkMinMax.Rows[0][0]
- maxNumeric := pkMinMax.Rows[0][1]
- min, err := minNumeric.ParseUint64()
- if err != nil {
- return nil, err
- }
- max, err := maxNumeric.ParseUint64()
- if err != nil {
- return nil, err
- }
- interval := (max - min) / uint64(qs.splitCount)
- if interval == 0 {
- return nil, err
- }
- qs.rowCount = int64(interval)
- for i := uint64(1); i < uint64(qs.splitCount); i++ {
- v, err := sqltypes.BuildValue(min + interval*i)
- if err != nil {
- return nil, err
- }
- boundaries = append(boundaries, v)
- }
- return boundaries, nil
-}
-
-func (qs *QuerySplitter) splitBoundariesFloatColumn(pkMinMax *sqltypes.Result) ([]sqltypes.Value, error) {
- boundaries := []sqltypes.Value{}
- if pkMinMax == nil || len(pkMinMax.Rows) != 1 || pkMinMax.Rows[0][0].IsNull() || pkMinMax.Rows[0][1].IsNull() {
- return boundaries, nil
- }
- min, err := strconv.ParseFloat(pkMinMax.Rows[0][0].String(), 64)
- if err != nil {
- return nil, err
- }
- max, err := strconv.ParseFloat(pkMinMax.Rows[0][1].String(), 64)
- if err != nil {
- return nil, err
- }
- interval := (max - min) / float64(qs.splitCount)
- if interval == 0 {
- return nil, err
- }
- qs.rowCount = int64(interval)
- for i := 1; i < int(qs.splitCount); i++ {
- boundary := min + interval*float64(i)
- v, err := sqltypes.BuildValue(boundary)
- if err != nil {
- return nil, err
- }
- boundaries = append(boundaries, v)
- }
- return boundaries, nil
-}
-
-// TODO(shengzhe): support split based on min, max from the string column.
-func (qs *QuerySplitter) splitBoundariesStringColumn() ([]sqltypes.Value, error) {
- splitRange := int64(0xFFFFFFFF) + 1
- splitSize := splitRange / int64(qs.splitCount)
- //TODO(shengzhe): have a better estimated row count based on table size.
- qs.rowCount = int64(splitSize)
- var boundaries []sqltypes.Value
- for i := 1; i < int(qs.splitCount); i++ {
- buf := make([]byte, 4)
- binary.BigEndian.PutUint32(buf, uint32(splitSize)*uint32(i))
- val, err := sqltypes.BuildValue(buf)
- if err != nil {
- return nil, err
- }
- boundaries = append(boundaries, val)
- }
- return boundaries, nil
-}
diff --git a/go/vt/tabletserver/query_splitter_test.go b/go/vt/tabletserver/query_splitter_test.go
deleted file mode 100644
index 629f4514a09..00000000000
--- a/go/vt/tabletserver/query_splitter_test.go
+++ /dev/null
@@ -1,568 +0,0 @@
-package tabletserver
-
-import (
- "encoding/binary"
- "fmt"
- "reflect"
- "strings"
- "testing"
-
- "github.com/youtube/vitess/go/mysqlconn"
- "github.com/youtube/vitess/go/mysqlconn/fakesqldb"
- "github.com/youtube/vitess/go/sqltypes"
- "github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
-
- querypb "github.com/youtube/vitess/go/vt/proto/query"
-)
-
-func getSchemaEngine(t *testing.T) *schema.Engine {
- db := fakesqldb.New(t)
- defer db.Close()
- for query, result := range getQueriesForSplitter() {
- db.AddQuery(query, result)
- }
- se := schema.NewEngine(DummyChecker, tabletenv.DefaultQsConfig)
- se.Open(db.ConnParams())
- return se
-}
-
-func getQueriesForSplitter() map[string]*sqltypes.Result {
- return map[string]*sqltypes.Result{
- "select unix_timestamp()": {
- Fields: []*querypb.Field{{
- Type: sqltypes.Uint64,
- }},
- RowsAffected: 1,
- Rows: [][]sqltypes.Value{
- {sqltypes.MakeTrusted(sqltypes.Int32, []byte("1427325875"))},
- },
- },
- "select @@global.sql_mode": {
- Fields: []*querypb.Field{{
- Type: sqltypes.VarChar,
- }},
- RowsAffected: 1,
- Rows: [][]sqltypes.Value{
- {sqltypes.MakeString([]byte("STRICT_TRANS_TABLES"))},
- },
- },
- "select @@autocommit": {
- Fields: []*querypb.Field{{
- Type: sqltypes.Uint64,
- }},
- RowsAffected: 1,
- Rows: [][]sqltypes.Value{
- {sqltypes.MakeString([]byte("1"))},
- },
- },
- mysqlconn.BaseShowTables: {
- Fields: mysqlconn.BaseShowTablesFields,
- RowsAffected: 3,
- Rows: [][]sqltypes.Value{
- mysqlconn.BaseShowTablesRow("test_table", false, ""),
- mysqlconn.BaseShowTablesRow("test_table_no_pk", false, ""),
- },
- },
- "select * from test_table where 1 != 1": {
- Fields: []*querypb.Field{{
- Name: "id",
- Type: sqltypes.Int64,
- }, {
- Name: "id2",
- Type: sqltypes.Int64,
- }, {
- Name: "count",
- Type: sqltypes.Int64,
- }},
- },
- "describe test_table": {
- Fields: mysqlconn.DescribeTableFields,
- RowsAffected: 1,
- Rows: [][]sqltypes.Value{
- mysqlconn.DescribeTableRow("id", "int(20)", false, "PRI", "0"),
- mysqlconn.DescribeTableRow("id2", "int(20)", false, "", "0"),
- mysqlconn.DescribeTableRow("count", "int(20)", false, "", "0"),
- },
- },
- "show index from test_table": {
- Fields: mysqlconn.ShowIndexFromTableFields,
- RowsAffected: 2,
- Rows: [][]sqltypes.Value{
- mysqlconn.ShowIndexFromTableRow("test_table", true, "PRIMARY", 1, "id", false),
- mysqlconn.ShowIndexFromTableRow("test_table", true, "idx_id2", 1, "id2", false),
- },
- },
- "select * from test_table_no_pk where 1 != 1": {
- Fields: []*querypb.Field{{
- Name: "id",
- Type: sqltypes.Int64,
- }},
- },
- "describe test_table_no_pk": {
- Fields: mysqlconn.DescribeTableFields,
- RowsAffected: 0,
- Rows: [][]sqltypes.Value{},
- },
- "show index from test_table_no_pk": {
- Fields: mysqlconn.ShowIndexFromTableFields,
- RowsAffected: 0,
- Rows: [][]sqltypes.Value{},
- },
- }
-}
-
-func TestValidateQuery(t *testing.T) {
- se := getSchemaEngine(t)
-
- splitter := NewQuerySplitter("delete from test_table", nil, "", 3, se)
- got := splitter.validateQuery()
- want := fmt.Errorf("not a select statement")
- if !reflect.DeepEqual(got, want) {
- t.Errorf("non-select validation failed, got:%v, want:%v", got, want)
- }
-
- splitter = NewQuerySplitter("select * from test_table order by id", nil, "", 3, se)
- got = splitter.validateQuery()
- want = fmt.Errorf("unsupported query")
- if !reflect.DeepEqual(got, want) {
- t.Errorf("order by query validation failed, got:%v, want:%v", got, want)
- }
-
- splitter = NewQuerySplitter("select * from test_table group by id", nil, "", 3, se)
- got = splitter.validateQuery()
- want = fmt.Errorf("unsupported query")
- if !reflect.DeepEqual(got, want) {
- t.Errorf("group by query validation failed, got:%v, want:%v", got, want)
- }
-
- splitter = NewQuerySplitter("select A.* from test_table A JOIN test_table B", nil, "", 3, se)
- got = splitter.validateQuery()
- want = fmt.Errorf("unsupported query")
- if !reflect.DeepEqual(got, want) {
- t.Errorf("join query validation failed, got:%v, want:%v", got, want)
- }
-
- splitter = NewQuerySplitter("select * from test_table_no_pk", nil, "", 3, se)
- got = splitter.validateQuery()
- want = fmt.Errorf("no primary keys")
- if !reflect.DeepEqual(got, want) {
- t.Errorf("no PK table validation failed, got:%v, want:%v", got, want)
- }
-
- splitter = NewQuerySplitter("select * from unknown_table", nil, "", 3, se)
- got = splitter.validateQuery()
- want = fmt.Errorf("can't find table in schema")
- if !reflect.DeepEqual(got, want) {
- t.Errorf("unknown table validation failed, got:%v, want:%v", got, want)
- }
-
- splitter = NewQuerySplitter("select * from test_table", nil, "", 3, se)
- got = splitter.validateQuery()
- want = nil
- if !reflect.DeepEqual(got, want) {
- t.Errorf("valid query validation failed, got:%v, want:%v", got, want)
- }
-
- splitter = NewQuerySplitter("select * from test_table where count > :count", nil, "", 3, se)
- got = splitter.validateQuery()
- want = nil
- if !reflect.DeepEqual(got, want) {
- t.Errorf("valid query validation failed, got:%v, want:%v", got, want)
- }
-
- splitter = NewQuerySplitter("select * from test_table where count > :count", nil, "id2", 0, se)
- got = splitter.validateQuery()
- want = nil
- if !reflect.DeepEqual(got, want) {
- t.Errorf("valid query validation failed, got:%v, want:%v", got, want)
- }
-
- splitter = NewQuerySplitter("invalid select * from test_table where count > :count", nil, "id2", 0, se)
- if err := splitter.validateQuery(); err == nil {
- t.Fatalf("validateQuery() = %v, want: nil", err)
- }
-
- // column id2 is indexed
- splitter = NewQuerySplitter("select * from test_table where count > :count", nil, "id2", 3, se)
- got = splitter.validateQuery()
- want = nil
- if !reflect.DeepEqual(got, want) {
- t.Errorf("valid query validation failed, got:%v, want:%v", got, want)
- }
-
- // column does not exist
- splitter = NewQuerySplitter("select * from test_table where count > :count", nil, "unknown_column", 3, se)
- got = splitter.validateQuery()
- wantStr := "split column is not indexed or does not exist in table schema"
- if !strings.Contains(got.Error(), wantStr) {
- t.Errorf("unknown table validation failed, got:%v, want:%v", got, wantStr)
- }
-
- // column is not indexed
- splitter = NewQuerySplitter("select * from test_table where count > :count", nil, "count", 3, se)
- got = splitter.validateQuery()
- wantStr = "split column is not indexed or does not exist in table schema"
- if !strings.Contains(got.Error(), wantStr) {
- t.Errorf("unknown table validation failed, got:%v, want:%v", got, wantStr)
- }
-}
-
-func TestGetWhereClause(t *testing.T) {
- splitter := &QuerySplitter{}
- sql := "select * from test_table where count > :count"
- statement, _ := sqlparser.Parse(sql)
- splitter.sel, _ = statement.(*sqlparser.Select)
- splitter.splitColumn = sqlparser.NewColIdent("id")
- bindVars := make(map[string]interface{})
- // no boundary case, start = end = nil, should not change the where clause
- nilValue := sqltypes.Value{}
- clause := splitter.getWhereClause(splitter.sel.Where, bindVars, nilValue, nilValue)
- want := " where count > :count"
- got := sqlparser.String(clause)
- if !reflect.DeepEqual(got, want) {
- t.Errorf("incorrect where clause for nil ranges, got:%v, want:%v", got, want)
- }
-
- // Set lower bound, should add the lower bound condition to where clause
- startVal := int64(20)
- start, _ := sqltypes.BuildValue(startVal)
- bindVars = make(map[string]interface{})
- bindVars[":count"] = 300
- clause = splitter.getWhereClause(splitter.sel.Where, bindVars, start, nilValue)
- want = " where (count > :count) and (id >= :" + startBindVarName + ")"
- got = sqlparser.String(clause)
- if !reflect.DeepEqual(got, want) {
- t.Errorf("incorrect where clause, got:%v, want:%v", got, want)
- }
- v, ok := bindVars[startBindVarName]
- if !ok {
- t.Fatalf("bind var: %s not found got: nil, want: %v", startBindVarName, startVal)
- }
- if v != startVal {
- t.Fatalf("bind var: %s not found got: %v, want: %v", startBindVarName, v, startVal)
- }
- // Set upper bound, should add the upper bound condition to where clause
- endVal := int64(40)
- end, _ := sqltypes.BuildValue(endVal)
- bindVars = make(map[string]interface{})
- clause = splitter.getWhereClause(splitter.sel.Where, bindVars, nilValue, end)
- want = " where (count > :count) and (id < :" + endBindVarName + ")"
- got = sqlparser.String(clause)
- if !reflect.DeepEqual(got, want) {
- t.Errorf("incorrect where clause, got:%v, want:%v", got, want)
- }
- v, ok = bindVars[endBindVarName]
- if !ok {
- t.Fatalf("bind var: %s not found got: nil, want: %v", endBindVarName, endVal)
- }
- if v != endVal {
- t.Fatalf("bind var: %s not found got: %v, want: %v", endBindVarName, v, endVal)
- }
-
- // Set both bounds, should add two conditions to where clause
- bindVars = make(map[string]interface{})
- clause = splitter.getWhereClause(splitter.sel.Where, bindVars, start, end)
- want = fmt.Sprintf(" where (count > :count) and (id >= :%s and id < :%s)", startBindVarName, endBindVarName)
- got = sqlparser.String(clause)
- if !reflect.DeepEqual(got, want) {
- t.Errorf("incorrect where clause, got:%v, want:%v", got, want)
- }
-
- // Original query with no where clause
- sql = "select * from test_table"
- statement, _ = sqlparser.Parse(sql)
- splitter.sel, _ = statement.(*sqlparser.Select)
- bindVars = make(map[string]interface{})
- // no boundary case, start = end = nil should return no where clause
- clause = splitter.getWhereClause(splitter.sel.Where, bindVars, nilValue, nilValue)
- want = ""
- got = sqlparser.String(clause)
- if !reflect.DeepEqual(got, want) {
- t.Errorf("incorrect where clause for nil ranges, got:%v, want:%v", got, want)
- }
- bindVars = make(map[string]interface{})
- // Set both bounds, should add two conditions to where clause
- clause = splitter.getWhereClause(splitter.sel.Where, bindVars, start, end)
- want = fmt.Sprintf(" where id >= :%s and id < :%s", startBindVarName, endBindVarName)
- got = sqlparser.String(clause)
- if !reflect.DeepEqual(got, want) {
- t.Errorf("incorrect where clause, got:%v, want:%v", got, want)
- }
- v, ok = bindVars[startBindVarName]
- if !ok {
- t.Fatalf("bind var: %s not found got: nil, want: %v", startBindVarName, startVal)
- }
- if v != startVal {
- t.Fatalf("bind var: %s not found got: %v, want: %v", startBindVarName, v, startVal)
- }
- v, ok = bindVars[endBindVarName]
- if !ok {
- t.Fatalf("bind var: %s not found got: nil, want: %v", endBindVarName, endVal)
- }
- if v != endVal {
- t.Fatalf("bind var: %s not found got: %v, want: %v", endBindVarName, v, endVal)
- }
-}
-
-func TestSplitBoundaries(t *testing.T) {
- min, _ := sqltypes.BuildValue(10)
- max, _ := sqltypes.BuildValue(60)
- row := []sqltypes.Value{min, max}
- rows := [][]sqltypes.Value{row}
-
- minField := &querypb.Field{Name: "min", Type: sqltypes.Int64}
- maxField := &querypb.Field{Name: "max", Type: sqltypes.Int64}
- fields := []*querypb.Field{minField, maxField}
-
- pkMinMax := &sqltypes.Result{
- Fields: fields,
- Rows: rows,
- }
-
- splitter := &QuerySplitter{}
- splitter.splitCount = 5
- boundaries, err := splitter.splitBoundaries(sqltypes.Int64, pkMinMax)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if len(boundaries) != int(splitter.splitCount-1) {
- t.Errorf("wrong number of boundaries got: %v, want: %v", len(boundaries), splitter.splitCount-1)
- }
- got, err := splitter.splitBoundaries(sqltypes.Int64, pkMinMax)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- want := []sqltypes.Value{buildVal(20), buildVal(30), buildVal(40), buildVal(50)}
- if !reflect.DeepEqual(got, want) {
- t.Errorf("incorrect boundaries, got: %v, want: %v", got, want)
- }
-
- // Test negative min value
- min, _ = sqltypes.BuildValue(-100)
- max, _ = sqltypes.BuildValue(100)
- row = []sqltypes.Value{min, max}
- rows = [][]sqltypes.Value{row}
- pkMinMax.Rows = rows
- got, err = splitter.splitBoundaries(sqltypes.Int64, pkMinMax)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- want = []sqltypes.Value{buildVal(-60), buildVal(-20), buildVal(20), buildVal(60)}
- if !reflect.DeepEqual(got, want) {
- t.Errorf("incorrect boundaries, got: %v, want: %v", got, want)
- }
-
- // Test float min max
- min, _ = sqltypes.BuildValue(10.5)
- max, _ = sqltypes.BuildValue(60.5)
- row = []sqltypes.Value{min, max}
- rows = [][]sqltypes.Value{row}
- minField = &querypb.Field{Name: "min", Type: sqltypes.Float64}
- maxField = &querypb.Field{Name: "max", Type: sqltypes.Float64}
- fields = []*querypb.Field{minField, maxField}
- pkMinMax.Rows = rows
- pkMinMax.Fields = fields
- got, err = splitter.splitBoundaries(sqltypes.Float64, pkMinMax)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- want = []sqltypes.Value{buildVal(20.5), buildVal(30.5), buildVal(40.5), buildVal(50.5)}
- if !reflect.DeepEqual(got, want) {
- t.Errorf("incorrect boundaries, got: %v, want: %v", got, want)
- }
-}
-
-func buildVal(val interface{}) sqltypes.Value {
- v, _ := sqltypes.BuildValue(val)
- return v
-}
-
-func TestSplitQuery(t *testing.T) {
- se := getSchemaEngine(t)
- splitter := NewQuerySplitter("select * from test_table where count > :count", nil, "", 3, se)
- splitter.validateQuery()
- min, _ := sqltypes.BuildValue(0)
- max, _ := sqltypes.BuildValue(300)
- minField := &querypb.Field{
- Name: "min",
- Type: sqltypes.Int64,
- }
- maxField := &querypb.Field{
- Name: "max",
- Type: sqltypes.Int64,
- }
- fields := []*querypb.Field{minField, maxField}
- pkMinMax := &sqltypes.Result{
- Fields: fields,
- }
-
- // Ensure that empty min max does not cause panic or return any error
- splits, err := splitter.split(sqltypes.Int64, pkMinMax)
- if err != nil {
- t.Errorf("unexpected error while splitting on empty pkMinMax, %s", err)
- }
-
- pkMinMax.Rows = [][]sqltypes.Value{{min, max}}
- splits, err = splitter.split(sqltypes.Int64, pkMinMax)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- got := []querytypes.BoundQuery{}
- for _, split := range splits {
- if split.RowCount != 100 {
- t.Errorf("wrong RowCount, got: %v, want: %v", split.RowCount, 100)
- }
- got = append(got, querytypes.BoundQuery{
- Sql: split.Sql,
- BindVariables: split.BindVariables,
- })
- }
- want := []querytypes.BoundQuery{
- {
- Sql: "select * from test_table where (count > :count) and (id < :" + endBindVarName + ")",
- BindVariables: map[string]interface{}{endBindVarName: int64(100)},
- },
- {
- Sql: fmt.Sprintf("select * from test_table where (count > :count) and (id >= :%s and id < :%s)", startBindVarName, endBindVarName),
- BindVariables: map[string]interface{}{
- startBindVarName: int64(100),
- endBindVarName: int64(200),
- },
- },
- {
- Sql: "select * from test_table where (count > :count) and (id >= :" + startBindVarName + ")",
- BindVariables: map[string]interface{}{startBindVarName: int64(200)},
- },
- }
- if !reflect.DeepEqual(got, want) {
- t.Errorf("wrong splits, got: %v, want: %v", got, want)
- }
-}
-
-func TestSplitQueryFractionalColumn(t *testing.T) {
- se := getSchemaEngine(t)
- splitter := NewQuerySplitter("select * from test_table where count > :count", nil, "", 3, se)
- splitter.validateQuery()
- min, _ := sqltypes.BuildValue(10.5)
- max, _ := sqltypes.BuildValue(490.5)
- minField := &querypb.Field{
- Name: "min",
- Type: sqltypes.Float32,
- }
- maxField := &querypb.Field{
- Name: "max",
- Type: sqltypes.Float32,
- }
- fields := []*querypb.Field{minField, maxField}
- pkMinMax := &sqltypes.Result{
- Fields: fields,
- Rows: [][]sqltypes.Value{{min, max}},
- }
-
- splits, err := splitter.split(sqltypes.Float32, pkMinMax)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- got := []querytypes.BoundQuery{}
- for _, split := range splits {
- if split.RowCount != 160 {
- t.Errorf("wrong RowCount, got: %v, want: %v", split.RowCount, 160)
- }
- got = append(got, querytypes.BoundQuery{
- Sql: split.Sql,
- BindVariables: split.BindVariables,
- })
- }
- want := []querytypes.BoundQuery{
- {
- Sql: "select * from test_table where (count > :count) and (id < :" + endBindVarName + ")",
- BindVariables: map[string]interface{}{endBindVarName: 170.5},
- },
- {
- Sql: fmt.Sprintf("select * from test_table where (count > :count) and (id >= :%s and id < :%s)", startBindVarName, endBindVarName),
- BindVariables: map[string]interface{}{
- startBindVarName: 170.5,
- endBindVarName: 330.5,
- },
- },
- {
- Sql: "select * from test_table where (count > :count) and (id >= :" + startBindVarName + ")",
- BindVariables: map[string]interface{}{startBindVarName: 330.5},
- },
- }
- if !reflect.DeepEqual(got, want) {
- t.Errorf("wrong splits, got: %v, want: %v", got, want)
- }
-}
-
-func TestSplitQueryVarBinaryColumn(t *testing.T) {
- se := getSchemaEngine(t)
- splitter := NewQuerySplitter("select * from test_table where count > :count", nil, "", 3, se)
- splitter.validateQuery()
- splits, err := splitter.split(sqltypes.VarBinary, nil)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- got := []querytypes.BoundQuery{}
- for _, split := range splits {
- got = append(got, querytypes.BoundQuery{
- Sql: split.Sql,
- BindVariables: split.BindVariables,
- })
- }
- want := []querytypes.BoundQuery{
- {
- Sql: "select * from test_table where (count > :count) and (id < :" + endBindVarName + ")",
- BindVariables: map[string]interface{}{endBindVarName: hexToByteUInt32(0x55555555)},
- },
- {
- Sql: fmt.Sprintf("select * from test_table where (count > :count) and (id >= :%s and id < :%s)", startBindVarName, endBindVarName),
- BindVariables: map[string]interface{}{
- startBindVarName: hexToByteUInt32(0x55555555),
- endBindVarName: hexToByteUInt32(0xAAAAAAAA),
- },
- },
- {
- Sql: "select * from test_table where (count > :count) and (id >= :" + startBindVarName + ")",
- BindVariables: map[string]interface{}{startBindVarName: hexToByteUInt32(0xAAAAAAAA)},
- },
- }
- if !reflect.DeepEqual(got, want) {
- t.Errorf("wrong splits, got: %v, want: %v", got, want)
- }
-}
-
-func TestSplitQueryVarCharColumn(t *testing.T) {
- se := getSchemaEngine(t)
- splitter := NewQuerySplitter("select * from test_table where count > :count", map[string]interface{}{"count": 123}, "", 3, se)
- splitter.validateQuery()
- splits, err := splitter.split(sqltypes.VarChar, nil)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- got := []querytypes.BoundQuery{}
- for _, split := range splits {
- got = append(got, querytypes.BoundQuery{
- Sql: split.Sql,
- BindVariables: split.BindVariables,
- })
- }
- want := []querytypes.BoundQuery{
- {
- Sql: "select * from test_table where count > :count",
- BindVariables: map[string]interface{}{"count": 123},
- },
- }
- if !reflect.DeepEqual(got, want) {
- t.Errorf("wrong splits, got: %v, want: %v", got, want)
- }
-}
-
-func hexToByteUInt32(val uint32) []byte {
- buf := make([]byte, 4)
- binary.BigEndian.PutUint32(buf, val)
- return buf
-}
diff --git a/go/vt/tabletserver/tabletconn/grpc_error.go b/go/vt/tabletserver/tabletconn/grpc_error.go
deleted file mode 100644
index 50e772b10d0..00000000000
--- a/go/vt/tabletserver/tabletconn/grpc_error.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package tabletconn
-
-import (
- "fmt"
- "io"
- "strings"
-
- "github.com/youtube/vitess/go/vt/vterrors"
- "google.golang.org/grpc"
-
- vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
-)
-
-// TabletErrorFromGRPC returns a ServerError or a
-// OperationalError from the gRPC error.
-func TabletErrorFromGRPC(err error) error {
- // io.EOF is end of stream. Don't treat it as an error.
- if err == nil || err == io.EOF {
- return nil
- }
-
- // TODO(aaijazi): Unfortunately, there's no better way to check for
- // a gRPC server error (vs a client error).
- // See: https://github.com/grpc/grpc-go/issues/319
- if !strings.Contains(err.Error(), vterrors.GRPCServerErrPrefix) {
- return OperationalError(fmt.Sprintf("vttablet: %v", err))
- }
-
- // server side error, convert it
- return &ServerError{
- Err: fmt.Sprintf("vttablet: %v", err),
- ServerCode: vterrors.GRPCCodeToErrorCode(grpc.Code(err)),
- }
-}
-
-// TabletErrorFromRPCError returns a ServerError from a vtrpcpb.ServerError
-func TabletErrorFromRPCError(err *vtrpcpb.RPCError) error {
- if err == nil {
- return nil
- }
-
- // server side error, convert it
- return &ServerError{
- Err: fmt.Sprintf("vttablet: %v", err),
- ServerCode: err.Code,
- }
-}
diff --git a/go/vt/tabletserver/tabletenv/tablet_error.go b/go/vt/tabletserver/tabletenv/tablet_error.go
deleted file mode 100644
index db4a77a0e58..00000000000
--- a/go/vt/tabletserver/tabletenv/tablet_error.go
+++ /dev/null
@@ -1,188 +0,0 @@
-// Copyright 2012, Google Inc. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package tabletenv
-
-import (
- "fmt"
- "regexp"
- "strconv"
- "strings"
-
- log "github.com/golang/glog"
-
- "github.com/youtube/vitess/go/mysqlconn"
- "github.com/youtube/vitess/go/sqldb"
- "github.com/youtube/vitess/go/tb"
-
- vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
-)
-
-const (
- maxErrLen = 5000
-)
-
-// ErrConnPoolClosed is returned / panicked when the connection pool is closed.
-var ErrConnPoolClosed = NewTabletError(
- // connection pool being closed is not the query's fault, it can be retried on a
- // different VtTablet.
- vtrpcpb.ErrorCode_INTERNAL_ERROR,
- "connection pool is closed")
-
-// TabletError is the error type we use in this library.
-// It implements vterrors.VtError interface.
-type TabletError struct {
- Message string
- SQLError int
- SQLState string
- // ErrorCode will be used to transmit the error across RPC boundaries
- ErrorCode vtrpcpb.ErrorCode
-}
-
-// NewTabletError returns a TabletError of the given type
-func NewTabletError(errCode vtrpcpb.ErrorCode, format string, args ...interface{}) *TabletError {
- return &TabletError{
- Message: printable(fmt.Sprintf(format, args...)),
- ErrorCode: errCode,
- }
-}
-
-// NewTabletErrorSQL returns a TabletError based on the error
-func NewTabletErrorSQL(errCode vtrpcpb.ErrorCode, err error) *TabletError {
- var errnum int
- errstr := err.Error()
- sqlState := sqldb.SQLStateGeneral
- if sqlErr, ok := err.(*sqldb.SQLError); ok {
- errnum = sqlErr.Number()
- sqlState = sqlErr.SQLState()
- switch errnum {
- case mysqlconn.EROptionPreventsStatement:
- // Override error type if MySQL is in read-only mode. It's probably because
- // there was a remaster and there are old clients still connected.
- if strings.Contains(errstr, "read-only") {
- errCode = vtrpcpb.ErrorCode_QUERY_NOT_SERVED
- }
- case mysqlconn.ERDupEntry:
- errCode = vtrpcpb.ErrorCode_INTEGRITY_ERROR
- case mysqlconn.ERDataTooLong, mysqlconn.ERDataOutOfRange:
- errCode = vtrpcpb.ErrorCode_BAD_INPUT
- default:
- }
- }
- return &TabletError{
- Message: printable(errstr),
- SQLError: errnum,
- SQLState: sqlState,
- ErrorCode: errCode,
- }
-}
-
-// PrefixTabletError attempts to add a string prefix to a TabletError,
-// while preserving its ErrorCode. If the given error is not a
-// TabletError, a new TabletError is returned with the desired ErrorCode.
-func PrefixTabletError(errCode vtrpcpb.ErrorCode, err error, prefix string) error {
- if terr, ok := err.(*TabletError); ok {
- return NewTabletError(terr.ErrorCode, "%s%s", prefix, terr.Message)
- }
- return NewTabletError(errCode, "%s%s", prefix, err)
-}
-
-func printable(in string) string {
- if len(in) > maxErrLen {
- in = in[:maxErrLen]
- }
- in = fmt.Sprintf("%q", in)
- return in[1 : len(in)-1]
-}
-
-var errExtract = regexp.MustCompile(`.*\(errno ([0-9]*)\).*`)
-
-// IsConnErr returns true if the error is a connection error. If
-// the error is of type TabletError or hasNumber, it checks the error
-// code. Otherwise, it parses the string looking for (errno xxxx)
-// and uses the extracted value to determine if it's a conn error.
-func IsConnErr(err error) bool {
- var sqlError int
- switch err := err.(type) {
- case *TabletError:
- sqlError = err.SQLError
- case *sqldb.SQLError:
- sqlError = err.Number()
- default:
- match := errExtract.FindStringSubmatch(err.Error())
- if len(match) < 2 {
- return false
- }
- var convErr error
- sqlError, convErr = strconv.Atoi(match[1])
- if convErr != nil {
- return false
- }
- }
- // CRServerLost means that someone sniped the query.
- if sqlError == mysqlconn.CRServerLost {
- return false
- }
- return sqlError >= 2000 && sqlError <= 2018
-}
-
-func (te *TabletError) Error() string {
- return te.Prefix() + te.Message
-}
-
-// VtErrorCode returns the underlying Vitess error code
-func (te *TabletError) VtErrorCode() vtrpcpb.ErrorCode {
- return te.ErrorCode
-}
-
-// Prefix returns the prefix for the error, like error, fatal, etc.
-func (te *TabletError) Prefix() string {
- prefix := "error: "
- switch te.ErrorCode {
- case vtrpcpb.ErrorCode_QUERY_NOT_SERVED:
- prefix = "retry: "
- case vtrpcpb.ErrorCode_INTERNAL_ERROR:
- prefix = "fatal: "
- case vtrpcpb.ErrorCode_RESOURCE_EXHAUSTED:
- prefix = "tx_pool_full: "
- case vtrpcpb.ErrorCode_NOT_IN_TX:
- prefix = "not_in_tx: "
- }
- // Special case for killed queries.
- if te.SQLError == mysqlconn.CRServerLost {
- prefix = prefix + "the query was killed either because it timed out or was canceled: "
- }
- return prefix
-}
-
-// RecordStats will record the error in the proper stat bucket
-func (te *TabletError) RecordStats() {
- switch te.ErrorCode {
- case vtrpcpb.ErrorCode_QUERY_NOT_SERVED:
- InfoErrors.Add("Retry", 1)
- case vtrpcpb.ErrorCode_INTERNAL_ERROR:
- ErrorStats.Add("Fatal", 1)
- case vtrpcpb.ErrorCode_RESOURCE_EXHAUSTED:
- ErrorStats.Add("TxPoolFull", 1)
- case vtrpcpb.ErrorCode_NOT_IN_TX:
- ErrorStats.Add("NotInTx", 1)
- default:
- switch te.SQLError {
- case mysqlconn.ERDupEntry:
- InfoErrors.Add("DupKey", 1)
- case mysqlconn.ERLockWaitTimeout, mysqlconn.ERLockDeadlock:
- ErrorStats.Add("Deadlock", 1)
- default:
- ErrorStats.Add("Fail", 1)
- }
- }
-}
-
-// LogErrors logs panics and increments InternalErrors.
-func LogError() {
- if x := recover(); x != nil {
- log.Errorf("Uncaught panic:\n%v\n%s", x, tb.Stack(4))
- InternalErrors.Add("Panic", 1)
- }
-}
diff --git a/go/vt/tabletserver/tabletenv/tablet_error_test.go b/go/vt/tabletserver/tabletenv/tablet_error_test.go
deleted file mode 100644
index 4a36e4c08e0..00000000000
--- a/go/vt/tabletserver/tabletenv/tablet_error_test.go
+++ /dev/null
@@ -1,227 +0,0 @@
-// Copyright 2015, Google Inc. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package tabletenv
-
-import (
- "fmt"
- "testing"
-
- "github.com/youtube/vitess/go/mysqlconn"
- "github.com/youtube/vitess/go/sqldb"
-
- vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
-)
-
-func TestTabletErrorCode(t *testing.T) {
- tErr := NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "error")
- wantCode := vtrpcpb.ErrorCode_INTERNAL_ERROR
- code := tErr.VtErrorCode()
- if wantCode != code {
- t.Errorf("VtErrorCode() => %v, want %v", code, wantCode)
- }
-}
-
-func TestTabletErrorRetriableErrorTypeOverwrite(t *testing.T) {
- sqlErr := sqldb.NewSQLError(mysqlconn.EROptionPreventsStatement, mysqlconn.SSUnknownSQLState, "read-only")
- tabletErr := NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, sqlErr)
- if tabletErr.ErrorCode != vtrpcpb.ErrorCode_QUERY_NOT_SERVED {
- t.Fatalf("got: %v wanted: QUERY_NOT_SERVED", tabletErr.ErrorCode)
- }
-
- sqlErr = sqldb.NewSQLError(mysqlconn.ERDupEntry, mysqlconn.SSDupKey, "error")
- tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, sqlErr)
- if tabletErr.ErrorCode != vtrpcpb.ErrorCode_INTEGRITY_ERROR {
- t.Fatalf("got: %v wanted: INTEGRITY_ERROR", tabletErr.ErrorCode)
- }
-
- sqlErr = sqldb.NewSQLError(mysqlconn.ERDataTooLong, mysqlconn.SSDataTooLong, "error")
- tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, sqlErr)
- if tabletErr.ErrorCode != vtrpcpb.ErrorCode_BAD_INPUT {
- t.Fatalf("got: %v wanted: BAD_INPUT", tabletErr.ErrorCode)
- }
-
- sqlErr = sqldb.NewSQLError(mysqlconn.ERDataOutOfRange, mysqlconn.SSDataOutOfRange, "error")
- tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, sqlErr)
- if tabletErr.ErrorCode != vtrpcpb.ErrorCode_BAD_INPUT {
- t.Fatalf("got: %v wanted: BAD_INPUT", tabletErr.ErrorCode)
- }
-}
-
-func TestTabletErrorRetriableErrorTypeOverwrite2(t *testing.T) {
-}
-
-func TestTabletErrorMsgTooLong(t *testing.T) {
- buf := make([]byte, 2*maxErrLen)
- for i := 0; i < 2*maxErrLen; i++ {
- buf[i] = 'a'
- }
- msg := string(buf)
- sqlErr := sqldb.NewSQLError(mysqlconn.ERDupEntry, mysqlconn.SSDupKey, msg)
- tabletErr := NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, sqlErr)
- if tabletErr.ErrorCode != vtrpcpb.ErrorCode_INTEGRITY_ERROR {
- t.Fatalf("got %v wanted INTEGRITY_ERROR", tabletErr.ErrorCode)
- }
- if tabletErr.Message != string(buf[:maxErrLen]) {
- t.Fatalf("message should be capped, only %d character will be shown", maxErrLen)
- }
-}
-
-func TestTabletErrorConnError(t *testing.T) {
- tabletErr := NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, sqldb.NewSQLError(1999, "HY000", "test"))
- if IsConnErr(tabletErr) {
- t.Fatalf("tablet error: %v is not a connection error", tabletErr)
- }
- tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, sqldb.NewSQLError(2000, mysqlconn.SSUnknownSQLState, "test"))
- if !IsConnErr(tabletErr) {
- t.Fatalf("tablet error: %v is a connection error", tabletErr)
- }
- tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, sqldb.NewSQLError(mysqlconn.CRServerLost, mysqlconn.SSUnknownSQLState, "test"))
- if IsConnErr(tabletErr) {
- t.Fatalf("tablet error: %v is not a connection error", tabletErr)
- }
- want := "fatal: the query was killed either because it timed out or was canceled: test (errno 2013) (sqlstate HY000)"
- if tabletErr.Error() != want {
- t.Fatalf("tablet error: %v, want %s", tabletErr, want)
- }
- sqlErr := sqldb.NewSQLError(1998, "HY000", "test")
- if IsConnErr(sqlErr) {
- t.Fatalf("sql error: %v is not a connection error", sqlErr)
- }
- sqlErr = sqldb.NewSQLError(2001, "HY000", "test")
- if !IsConnErr(sqlErr) {
- t.Fatalf("sql error: %v is a connection error", sqlErr)
- }
-
- err := fmt.Errorf("(errno 2005)")
- if !IsConnErr(err) {
- t.Fatalf("error: %v is a connection error", err)
- }
-
- err = fmt.Errorf("(errno 123456789012345678901234567890)")
- if IsConnErr(err) {
- t.Fatalf("error: %v is not a connection error", err)
- }
-}
-
-func TestTabletErrorPrefix(t *testing.T) {
- tabletErr := NewTabletErrorSQL(vtrpcpb.ErrorCode_QUERY_NOT_SERVED, sqldb.NewSQLError(2000, "HY000", "test"))
- if tabletErr.Prefix() != "retry: " {
- t.Fatalf("tablet error with error code: QUERY_NOT_SERVED should has prefix: 'retry: '")
- }
- tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, sqldb.NewSQLError(2000, "HY000", "test"))
- if tabletErr.Prefix() != "fatal: " {
- t.Fatalf("tablet error with error code: INTERNAL_ERROR should has prefix: 'fatal: '")
- }
- tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_RESOURCE_EXHAUSTED, sqldb.NewSQLError(2000, "HY000", "test"))
- if tabletErr.Prefix() != "tx_pool_full: " {
- t.Fatalf("tablet error with error code: RESOURCE_EXHAUSTED should has prefix: 'tx_pool_full: '")
- }
- tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_NOT_IN_TX, sqldb.NewSQLError(2000, "HY000", "test"))
- if tabletErr.Prefix() != "not_in_tx: " {
- t.Fatalf("tablet error with error code: NOT_IN_TX should has prefix: 'not_in_tx: '")
- }
-}
-
-func TestTabletErrorRecordStats(t *testing.T) {
- tabletErr := NewTabletErrorSQL(vtrpcpb.ErrorCode_QUERY_NOT_SERVED, sqldb.NewSQLError(2000, "HY000", "test"))
- retryCounterBefore := InfoErrors.Counts()["Retry"]
- tabletErr.RecordStats()
- retryCounterAfter := InfoErrors.Counts()["Retry"]
- if retryCounterAfter-retryCounterBefore != 1 {
- t.Fatalf("tablet error with error code QUERY_NOT_SERVED should increase Retry error count by 1")
- }
-
- tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, sqldb.NewSQLError(2000, "HY000", "test"))
- fatalCounterBefore := ErrorStats.Counts()["Fatal"]
- tabletErr.RecordStats()
- fatalCounterAfter := ErrorStats.Counts()["Fatal"]
- if fatalCounterAfter-fatalCounterBefore != 1 {
- t.Fatalf("tablet error with error code INTERNAL_ERROR should increase Fatal error count by 1")
- }
-
- tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_RESOURCE_EXHAUSTED, sqldb.NewSQLError(2000, "HY000", "test"))
- txPoolFullCounterBefore := ErrorStats.Counts()["TxPoolFull"]
- tabletErr.RecordStats()
- txPoolFullCounterAfter := ErrorStats.Counts()["TxPoolFull"]
- if txPoolFullCounterAfter-txPoolFullCounterBefore != 1 {
- t.Fatalf("tablet error with error code RESOURCE_EXHAUSTED should increase TxPoolFull error count by 1")
- }
-
- tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_NOT_IN_TX, sqldb.NewSQLError(2000, "HY000", "test"))
- notInTxCounterBefore := ErrorStats.Counts()["NotInTx"]
- tabletErr.RecordStats()
- notInTxCounterAfter := ErrorStats.Counts()["NotInTx"]
- if notInTxCounterAfter-notInTxCounterBefore != 1 {
- t.Fatalf("tablet error with error code NOT_IN_TX should increase NotInTx error count by 1")
- }
-
- tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_UNKNOWN_ERROR, sqldb.NewSQLError(mysqlconn.ERDupEntry, mysqlconn.SSDupKey, "test"))
- dupKeyCounterBefore := InfoErrors.Counts()["DupKey"]
- tabletErr.RecordStats()
- dupKeyCounterAfter := InfoErrors.Counts()["DupKey"]
- if dupKeyCounterAfter-dupKeyCounterBefore != 1 {
- t.Fatalf("sql error with SQL error mysqlconn.ERDupEntry should increase DupKey error count by 1")
- }
-
- tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_UNKNOWN_ERROR, sqldb.NewSQLError(mysqlconn.ERLockWaitTimeout, mysqlconn.SSUnknownSQLState, "test"))
- lockWaitTimeoutCounterBefore := ErrorStats.Counts()["Deadlock"]
- tabletErr.RecordStats()
- lockWaitTimeoutCounterAfter := ErrorStats.Counts()["Deadlock"]
- if lockWaitTimeoutCounterAfter-lockWaitTimeoutCounterBefore != 1 {
- t.Fatalf("sql error with SQL error mysqlconn.ERLockWaitTimeout should increase Deadlock error count by 1")
- }
-
- tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_UNKNOWN_ERROR, sqldb.NewSQLError(mysqlconn.ERLockDeadlock, mysqlconn.SSLockDeadlock, "test"))
- deadlockCounterBefore := ErrorStats.Counts()["Deadlock"]
- tabletErr.RecordStats()
- deadlockCounterAfter := ErrorStats.Counts()["Deadlock"]
- if deadlockCounterAfter-deadlockCounterBefore != 1 {
- t.Fatalf("sql error with SQL error mysqlconn.ERLockDeadlock should increase Deadlock error count by 1")
- }
-
- tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_UNKNOWN_ERROR, sqldb.NewSQLError(mysqlconn.EROptionPreventsStatement, mysqlconn.SSUnknownSQLState, "test"))
- failCounterBefore := ErrorStats.Counts()["Fail"]
- tabletErr.RecordStats()
- failCounterAfter := ErrorStats.Counts()["Fail"]
- if failCounterAfter-failCounterBefore != 1 {
- t.Fatalf("sql error with SQL error mysqlconn.EROptionPreventsStatement should increase Fail error count by 1")
- }
-}
-
-func TestTabletErrorLogUncaughtErr(t *testing.T) {
- panicCountBefore := InternalErrors.Counts()["Panic"]
- defer func() {
- panicCountAfter := InternalErrors.Counts()["Panic"]
- if panicCountAfter-panicCountBefore != 1 {
- t.Fatalf("Panic count should increase by 1 for uncaught panic")
- }
- }()
- defer LogError()
- panic("unknown error")
-}
-
-func TestTabletErrorTxPoolFull(t *testing.T) {
- tabletErr := NewTabletErrorSQL(vtrpcpb.ErrorCode_RESOURCE_EXHAUSTED, sqldb.NewSQLError(1000, "HY000", "test"))
- defer func() {
- err := recover()
- if err != nil {
- t.Fatalf("error should have been handled already")
- }
- }()
- defer LogError()
- panic(tabletErr)
-}
-
-func TestTabletErrorFatal(t *testing.T) {
- tabletErr := NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, sqldb.NewSQLError(1000, "HY000", "test"))
- defer func() {
- err := recover()
- if err != nil {
- t.Fatalf("error should have been handled already")
- }
- }()
- defer LogError()
- panic(tabletErr)
-}
diff --git a/go/vt/throttler/demo/throttler_demo.go b/go/vt/throttler/demo/throttler_demo.go
index 2f9adbb862c..efdb75f127c 100644
--- a/go/vt/throttler/demo/throttler_demo.go
+++ b/go/vt/throttler/demo/throttler_demo.go
@@ -15,11 +15,11 @@ import (
"github.com/youtube/vitess/go/vt/discovery"
"github.com/youtube/vitess/go/vt/logutil"
"github.com/youtube/vitess/go/vt/servenv"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
- "github.com/youtube/vitess/go/vt/tabletserver/grpcqueryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice/fakes"
"github.com/youtube/vitess/go/vt/throttler"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
+ "github.com/youtube/vitess/go/vt/vttablet/grpcqueryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice/fakes"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
"github.com/youtube/vitess/go/vt/wrangler/testlib"
diff --git a/go/vt/throttler/grpcthrottlerclient/grpcthrottlerclient.go b/go/vt/throttler/grpcthrottlerclient/grpcthrottlerclient.go
index 17c1fde7702..1fa12332235 100644
--- a/go/vt/throttler/grpcthrottlerclient/grpcthrottlerclient.go
+++ b/go/vt/throttler/grpcthrottlerclient/grpcthrottlerclient.go
@@ -49,7 +49,7 @@ func factory(addr string) (throttlerclient.Client, error) {
func (c *client) MaxRates(ctx context.Context) (map[string]int64, error) {
response, err := c.gRPCClient.MaxRates(ctx, &throttlerdata.MaxRatesRequest{})
if err != nil {
- return nil, vterrors.FromGRPCError(err)
+ return nil, vterrors.FromGRPC(err)
}
return response.Rates, nil
}
@@ -63,7 +63,7 @@ func (c *client) SetMaxRate(ctx context.Context, rate int64) ([]string, error) {
response, err := c.gRPCClient.SetMaxRate(ctx, request)
if err != nil {
- return nil, vterrors.FromGRPCError(err)
+ return nil, vterrors.FromGRPC(err)
}
return response.Names, nil
}
@@ -74,7 +74,7 @@ func (c *client) GetConfiguration(ctx context.Context, throttlerName string) (ma
ThrottlerName: throttlerName,
})
if err != nil {
- return nil, vterrors.FromGRPCError(err)
+ return nil, vterrors.FromGRPC(err)
}
return response.Configurations, nil
}
@@ -87,7 +87,7 @@ func (c *client) UpdateConfiguration(ctx context.Context, throttlerName string,
CopyZeroValues: copyZeroValues,
})
if err != nil {
- return nil, vterrors.FromGRPCError(err)
+ return nil, vterrors.FromGRPC(err)
}
return response.Names, nil
}
@@ -98,7 +98,7 @@ func (c *client) ResetConfiguration(ctx context.Context, throttlerName string) (
ThrottlerName: throttlerName,
})
if err != nil {
- return nil, vterrors.FromGRPCError(err)
+ return nil, vterrors.FromGRPC(err)
}
return response.Names, nil
}
diff --git a/go/vt/tlstest/tlstest.go b/go/vt/tlstest/tlstest.go
new file mode 100644
index 00000000000..7d05c36b8e3
--- /dev/null
+++ b/go/vt/tlstest/tlstest.go
@@ -0,0 +1,118 @@
+// Package tlstest contains utility methods to create test certificates.
+// It is not meant to be used in production.
+package tlstest
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path"
+
+ log "github.com/golang/glog"
+)
+
+const (
+ // CA is the name of the CA toplevel cert.
+ CA = "ca"
+
+ caConfig = `
+[ req ]
+ default_bits = 1024
+ default_keyfile = keyfile.pem
+ distinguished_name = req_distinguished_name
+ attributes = req_attributes
+ prompt = no
+ output_password = mypass
+[ req_distinguished_name ]
+ C = US
+ ST = California
+ L = Mountain View
+ O = Google
+ OU = Vitess
+ CN = CA
+ emailAddress = test@email.address
+[ req_attributes ]
+ challengePassword = A challenge password
+`
+
+ certConfig = `
+[ req ]
+ default_bits = 1024
+ default_keyfile = keyfile.pem
+ distinguished_name = req_distinguished_name
+ attributes = req_attributes
+ prompt = no
+ output_password = mypass
+[ req_distinguished_name ]
+ C = US
+ ST = California
+ L = Mountain View
+ O = Google
+ OU = Vitess
+ CN = %s
+ emailAddress = test@email.address
+[ req_attributes ]
+ challengePassword = A challenge password
+`
+)
+
+// openssl runs the openssl binary with the provided command.
+func openssl(argv ...string) {
+ cmd := exec.Command("openssl", argv...)
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ log.Fatalf("openssl %v failed: %v", argv, err)
+ }
+ if len(output) > 0 {
+ log.Infof("openssl %v returned:\n%v", argv, string(output))
+ }
+}
+
+// CreateCA creates the toplevel 'ca' certificate and key, and places it
+// in the provided directory. Temporary files are also created in that
+// directory.
+func CreateCA(root string) {
+ log.Infof("Creating test root CA in %v", root)
+ key := path.Join(root, "ca-key.pem")
+ cert := path.Join(root, "ca-cert.pem")
+ openssl("genrsa", "-out", key)
+
+ config := path.Join(root, "ca.config")
+ if err := ioutil.WriteFile(config, []byte(caConfig), os.ModePerm); err != nil {
+ log.Fatalf("cannot write file %v: %v", config, err)
+ }
+ openssl("req", "-new", "-x509", "-nodes", "-days", "3600", "-batch",
+ "-config", config,
+ "-key", key,
+ "-out", cert)
+}
+
+// CreateSignedCert creates a new certificate signed by the provided parent,
+// with the provided serial number, name and common name.
+// name is the file name to use. Common Name is the certificate common name.
+func CreateSignedCert(root, parent, serial, name, commonName string) {
+ log.Infof("Creating signed cert and key %v", commonName)
+ caKey := path.Join(root, parent+"-key.pem")
+ caCert := path.Join(root, parent+"-cert.pem")
+ key := path.Join(root, name+"-key.pem")
+ cert := path.Join(root, name+"-cert.pem")
+ req := path.Join(root, name+"-req.pem")
+
+ config := path.Join(root, name+".config")
+ if err := ioutil.WriteFile(config, []byte(fmt.Sprintf(certConfig, commonName)), os.ModePerm); err != nil {
+ log.Fatalf("cannot write file %v: %v", config, err)
+ }
+ openssl("req", "-newkey", "rsa:2048", "-days", "3600", "-nodes",
+ "-batch",
+ "-config", config,
+ "-keyout", key, "-out", req)
+ openssl("rsa", "-in", key, "-out", key)
+ openssl("x509", "-req",
+ "-in", req,
+ "-days", "3600",
+ "-CA", caCert,
+ "-CAkey", caKey,
+ "-set_serial", serial,
+ "-out", cert)
+}
diff --git a/go/vt/tlstest/tlstest_test.go b/go/vt/tlstest/tlstest_test.go
new file mode 100644
index 00000000000..b7e636ab08d
--- /dev/null
+++ b/go/vt/tlstest/tlstest_test.go
@@ -0,0 +1,134 @@
+package tlstest
+
+import (
+ "crypto/tls"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path"
+ "strings"
+ "sync"
+ "testing"
+
+ "github.com/youtube/vitess/go/vt/servenv/grpcutils"
+)
+
+// TestClientServer generates:
+// - a root CA
+// - a server intermediate CA, with a server.
+// - a client intermediate CA, with a client.
+// And then performs a few tests on them.
+func TestClientServer(t *testing.T) {
+ // Our test root.
+ root, err := ioutil.TempDir("", "tlstest")
+ if err != nil {
+ t.Fatalf("TempDir failed: %v", err)
+ }
+ defer os.RemoveAll(root)
+
+ // Create the certs and configs.
+ CreateCA(root)
+
+ CreateSignedCert(root, CA, "01", "servers", "Servers CA")
+ CreateSignedCert(root, "servers", "01", "server-instance", "Server Instance")
+
+ CreateSignedCert(root, CA, "02", "clients", "Clients CA")
+ CreateSignedCert(root, "clients", "01", "client-instance", "Client Instance")
+ serverConfig, err := grpcutils.TLSServerConfig(
+ path.Join(root, "server-instance-cert.pem"),
+ path.Join(root, "server-instance-key.pem"),
+ path.Join(root, "clients-cert.pem"))
+ if err != nil {
+ t.Fatalf("TLSServerConfig failed: %v", err)
+ }
+ clientConfig, err := grpcutils.TLSClientConfig(
+ path.Join(root, "client-instance-cert.pem"),
+ path.Join(root, "client-instance-key.pem"),
+ path.Join(root, "servers-cert.pem"),
+ "Server Instance")
+ if err != nil {
+ t.Fatalf("TLSClientConfig failed: %v", err)
+ }
+
+ // Create a TLS server listener.
+ listener, err := tls.Listen("tcp", ":0", serverConfig)
+ if err != nil {
+ t.Fatalf("Listen failed: %v", err)
+ }
+ addr := listener.Addr().String()
+ defer listener.Close()
+
+ wg := sync.WaitGroup{}
+
+ //
+ // Positive case: accept on server side, connect a client, send data.
+ //
+
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ clientConn, err := tls.Dial("tcp", addr, clientConfig)
+ if err != nil {
+ t.Fatalf("Dial failed: %v", err)
+ }
+
+ clientConn.Write([]byte{42})
+ clientConn.Close()
+ }()
+
+ serverConn, err := listener.Accept()
+ if err != nil {
+ t.Fatalf("Accept failed: %v", err)
+ }
+
+ result := make([]byte, 1)
+ if n, err := serverConn.Read(result); (err != nil && err != io.EOF) || n != 1 {
+ t.Fatalf("Read failed: %v %v", n, err)
+ }
+ if result[0] != 42 {
+ t.Fatalf("Read returned wrong result: %v", result)
+ }
+ serverConn.Close()
+
+ wg.Wait()
+
+ //
+ // Negative case: connect a client with wrong cert (using the
+ // server cert on the client side).
+ //
+
+ badClientConfig, err := grpcutils.TLSClientConfig(
+ path.Join(root, "server-instance-cert.pem"),
+ path.Join(root, "server-instance-key.pem"),
+ path.Join(root, "servers-cert.pem"),
+ "Server Instance")
+ if err != nil {
+ t.Fatalf("TLSClientConfig failed: %v", err)
+ }
+
+ wg.Add(1)
+ go func() {
+ // We expect the Accept to work, but the first read to fail.
+ defer wg.Done()
+ serverConn, err := listener.Accept()
+ if err != nil {
+ t.Fatalf("Connection failed: %v", err)
+ }
+
+ // This will fail.
+ result := make([]byte, 1)
+ if n, err := serverConn.Read(result); err == nil {
+ fmt.Printf("Was able to read from server: %v\n", n)
+ }
+ serverConn.Close()
+ }()
+
+ if _, err = tls.Dial("tcp", addr, badClientConfig); err == nil {
+ t.Fatalf("Dial was expected to fail")
+ }
+ if !strings.Contains(err.Error(), "bad certificate") {
+ t.Errorf("Wrong error returned: %v", err)
+ }
+ t.Logf("Dial returned: %v", err)
+}
diff --git a/go/vt/topo/etcd2topo/watch.go b/go/vt/topo/etcd2topo/watch.go
index 569e6b67e05..e3964c13183 100644
--- a/go/vt/topo/etcd2topo/watch.go
+++ b/go/vt/topo/etcd2topo/watch.go
@@ -37,8 +37,10 @@ func (s *Server) Watch(ctx context.Context, cell, filePath string) (*topo.WatchD
// Create a context, will be used to cancel the watch.
watchCtx, watchCancel := context.WithCancel(context.Background())
- // Create the Watcher.
- watcher := s.global.cli.Watch(watchCtx, nodePath, clientv3.WithRev(initial.Kvs[0].ModRevision))
+ // Create the Watcher. We start watching from the response we
+ // got, not from the file original version, as the server may
+ // not have that much history.
+ watcher := s.global.cli.Watch(watchCtx, nodePath, clientv3.WithRev(initial.Header.Revision))
if watcher == nil {
return &topo.WatchData{Err: fmt.Errorf("Watch failed")}, nil, nil
}
diff --git a/go/vt/vtctl/grpcvtctlserver/server.go b/go/vt/vtctl/grpcvtctlserver/server.go
index 5f5f89ce524..640499d157a 100644
--- a/go/vt/vtctl/grpcvtctlserver/server.go
+++ b/go/vt/vtctl/grpcvtctlserver/server.go
@@ -13,9 +13,9 @@ import (
"github.com/youtube/vitess/go/vt/logutil"
"github.com/youtube/vitess/go/vt/servenv"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/vtctl"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
logutilpb "github.com/youtube/vitess/go/vt/proto/logutil"
diff --git a/go/vt/vtctl/query.go b/go/vt/vtctl/query.go
index 528afcb5744..3858028c6f6 100644
--- a/go/vt/vtctl/query.go
+++ b/go/vt/vtctl/query.go
@@ -21,9 +21,9 @@ import (
"github.com/youtube/vitess/go/vt/callerid"
"github.com/youtube/vitess/go/vt/logutil"
"github.com/youtube/vitess/go/vt/servenv"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
"github.com/youtube/vitess/go/vt/topo/topoproto"
"github.com/youtube/vitess/go/vt/vtgate/vtgateconn"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletconn"
"github.com/youtube/vitess/go/vt/wrangler"
"golang.org/x/net/context"
diff --git a/go/vt/vtctl/vtctlclienttest/client.go b/go/vt/vtctl/vtctlclienttest/client.go
index abf60960bc0..c08a913801e 100644
--- a/go/vt/vtctl/vtctlclienttest/client.go
+++ b/go/vt/vtctl/vtctlclienttest/client.go
@@ -21,13 +21,13 @@ import (
"golang.org/x/net/context"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
"github.com/youtube/vitess/go/vt/vtctl/vtctlclient"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
// import the gRPC client implementation for tablet manager
- _ "github.com/youtube/vitess/go/vt/tabletmanager/grpctmclient"
+ _ "github.com/youtube/vitess/go/vt/vttablet/grpctmclient"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
)
diff --git a/go/vt/vtctld/action_repository.go b/go/vt/vtctld/action_repository.go
index 4908f9fa765..bc059fcd128 100644
--- a/go/vt/vtctld/action_repository.go
+++ b/go/vt/vtctld/action_repository.go
@@ -9,9 +9,9 @@ import (
"github.com/youtube/vitess/go/acl"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
diff --git a/go/vt/vtctld/api.go b/go/vt/vtctld/api.go
index dd10e16499e..5edc9e471da 100644
--- a/go/vt/vtctld/api.go
+++ b/go/vt/vtctld/api.go
@@ -20,10 +20,10 @@ import (
"github.com/youtube/vitess/go/acl"
"github.com/youtube/vitess/go/vt/logutil"
"github.com/youtube/vitess/go/vt/schemamanager"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
"github.com/youtube/vitess/go/vt/vtctl"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/workflow"
"github.com/youtube/vitess/go/vt/wrangler"
diff --git a/go/vt/vtctld/realtime_status_test.go b/go/vt/vtctld/realtime_status_test.go
index 03b2338dca3..152f747fb6a 100644
--- a/go/vt/vtctld/realtime_status_test.go
+++ b/go/vt/vtctld/realtime_status_test.go
@@ -11,10 +11,10 @@ import (
"github.com/golang/protobuf/proto"
"github.com/youtube/vitess/go/vt/discovery"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
- "github.com/youtube/vitess/go/vt/tabletserver/grpcqueryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice/fakes"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
+ "github.com/youtube/vitess/go/vt/vttablet/grpcqueryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice/fakes"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
"github.com/youtube/vitess/go/vt/wrangler/testlib"
diff --git a/go/vt/vtctld/tablet_data.go b/go/vt/vtctld/tablet_data.go
index c79f5934c6a..c0f756821ac 100644
--- a/go/vt/vtctld/tablet_data.go
+++ b/go/vt/vtctld/tablet_data.go
@@ -8,8 +8,8 @@ import (
log "github.com/golang/glog"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
"github.com/youtube/vitess/go/vt/topo"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletconn"
"golang.org/x/net/context"
querypb "github.com/youtube/vitess/go/vt/proto/query"
diff --git a/go/vt/vtctld/tablet_data_test.go b/go/vt/vtctld/tablet_data_test.go
index c8edc9abcea..701f6c074f8 100644
--- a/go/vt/vtctld/tablet_data_test.go
+++ b/go/vt/vtctld/tablet_data_test.go
@@ -10,11 +10,11 @@ import (
"github.com/golang/protobuf/proto"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
- "github.com/youtube/vitess/go/vt/tabletserver/grpcqueryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice/fakes"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
+ "github.com/youtube/vitess/go/vt/vttablet/grpcqueryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice/fakes"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
"github.com/youtube/vitess/go/vt/wrangler/testlib"
diff --git a/go/vt/vtctld/workflow.go b/go/vt/vtctld/workflow.go
index 3e8af44b4f2..d74921b1626 100644
--- a/go/vt/vtctld/workflow.go
+++ b/go/vt/vtctld/workflow.go
@@ -42,6 +42,7 @@ func initWorkflowManager(ts topo.Server) {
// Register the Horizontal Resharding workflow.
resharding.Register()
+
// Unregister the blacklisted workflows.
for _, name := range workflowManagerDisable {
workflow.Unregister(name)
diff --git a/go/vt/vterrors/aggregate.go b/go/vt/vterrors/aggregate.go
index eb00b8a462b..d168ede93b9 100644
--- a/go/vt/vterrors/aggregate.go
+++ b/go/vt/vterrors/aggregate.go
@@ -5,54 +5,76 @@
package vterrors
import (
+ "sort"
+ "strings"
+
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)
-// A list of all vtrpcpb.ErrorCodes, ordered by priority. These priorities are
+// A list of all vtrpcpb.Code, ordered by priority. These priorities are
// used when aggregating multiple errors in VtGate.
// Higher priority error codes are more urgent for users to see. They are
// prioritized based on the following question: assuming a scatter query produced multiple
// errors, which of the errors is the most likely to give the user useful information
// about why the query failed and how they should proceed?
const (
- PrioritySuccess = iota
- PriorityTransientError
- PriorityQueryNotServed
+ // Informational errors.
+ PriorityOK = iota
+ PriorityCanceled
+ PriorityAlreadyExists
+ PriorityOutOfRange
+ // Potentially retryable errors.
+ PriorityUnavailable
PriorityDeadlineExceeded
- PriorityCancelled
- PriorityIntegrityError
- PriorityNotInTx
- PriorityUnknownError
- PriorityInternalError
+ PriorityAborted
+ PriorityFailedPrecondition
+ // Permanent errors.
PriorityResourceExhausted
+ PriorityUnknown
PriorityUnauthenticated
PriorityPermissionDenied
- PriorityBadInput
+ PriorityInvalidArgument
+ PriorityNotFound
+ PriorityUnimplemented
+ // Serious errors.
+ PriorityInternal
+ PriorityDataLoss
)
-var errorPriorities = map[vtrpcpb.ErrorCode]int{
- vtrpcpb.ErrorCode_SUCCESS: PrioritySuccess,
- vtrpcpb.ErrorCode_CANCELLED: PriorityCancelled,
- vtrpcpb.ErrorCode_UNKNOWN_ERROR: PriorityUnknownError,
- vtrpcpb.ErrorCode_BAD_INPUT: PriorityBadInput,
- vtrpcpb.ErrorCode_DEADLINE_EXCEEDED: PriorityDeadlineExceeded,
- vtrpcpb.ErrorCode_INTEGRITY_ERROR: PriorityIntegrityError,
- vtrpcpb.ErrorCode_PERMISSION_DENIED: PriorityPermissionDenied,
- vtrpcpb.ErrorCode_RESOURCE_EXHAUSTED: PriorityResourceExhausted,
- vtrpcpb.ErrorCode_QUERY_NOT_SERVED: PriorityQueryNotServed,
- vtrpcpb.ErrorCode_NOT_IN_TX: PriorityNotInTx,
- vtrpcpb.ErrorCode_INTERNAL_ERROR: PriorityInternalError,
- vtrpcpb.ErrorCode_TRANSIENT_ERROR: PriorityTransientError,
- vtrpcpb.ErrorCode_UNAUTHENTICATED: PriorityUnauthenticated,
+var errorPriorities = map[vtrpcpb.Code]int{
+ vtrpcpb.Code_OK: PriorityOK,
+ vtrpcpb.Code_CANCELED: PriorityCanceled,
+ vtrpcpb.Code_UNKNOWN: PriorityUnknown,
+ vtrpcpb.Code_INVALID_ARGUMENT: PriorityInvalidArgument,
+ vtrpcpb.Code_DEADLINE_EXCEEDED: PriorityDeadlineExceeded,
+ vtrpcpb.Code_NOT_FOUND: PriorityNotFound,
+ vtrpcpb.Code_ALREADY_EXISTS: PriorityAlreadyExists,
+ vtrpcpb.Code_PERMISSION_DENIED: PriorityPermissionDenied,
+ vtrpcpb.Code_UNAUTHENTICATED: PriorityUnauthenticated,
+ vtrpcpb.Code_RESOURCE_EXHAUSTED: PriorityResourceExhausted,
+ vtrpcpb.Code_FAILED_PRECONDITION: PriorityFailedPrecondition,
+ vtrpcpb.Code_ABORTED: PriorityAborted,
+ vtrpcpb.Code_OUT_OF_RANGE: PriorityOutOfRange,
+ vtrpcpb.Code_UNIMPLEMENTED: PriorityUnimplemented,
+ vtrpcpb.Code_INTERNAL: PriorityInternal,
+ vtrpcpb.Code_UNAVAILABLE: PriorityUnavailable,
+ vtrpcpb.Code_DATA_LOSS: PriorityDataLoss,
+}
+
+// Aggregate aggregates several errors into a single one.
+// The resulting error code will be the one with the highest
+// priority as defined by the priority constants in this package.
+func Aggregate(errors []error) error {
+ if len(errors) == 0 {
+ return nil
+ }
+ return New(aggregateCodes(errors), aggregateErrors(errors))
}
-// AggregateVtGateErrorCodes aggregates a list of errors into a single
-// error code. It does so by finding the highest priority error code
-// in the list.
-func AggregateVtGateErrorCodes(errors []error) vtrpcpb.ErrorCode {
- highCode := vtrpcpb.ErrorCode_SUCCESS
+func aggregateCodes(errors []error) vtrpcpb.Code {
+ highCode := vtrpcpb.Code_OK
for _, e := range errors {
- code := RecoverVtErrorCode(e)
+ code := Code(e)
if errorPriorities[code] > errorPriorities[highCode] {
highCode = code
}
@@ -60,13 +82,13 @@ func AggregateVtGateErrorCodes(errors []error) vtrpcpb.ErrorCode {
return highCode
}
-// AggregateVtGateErrors aggregates several errors into a single one.
-func AggregateVtGateErrors(errors []error) error {
- if len(errors) == 0 {
- return nil
+// ConcatenateErrors aggregates an array of errors into a single error by string concatenation.
+func aggregateErrors(errs []error) string {
+ errStrs := make([]string, 0, len(errs))
+ for _, e := range errs {
+ errStrs = append(errStrs, e.Error())
}
- return FromError(
- AggregateVtGateErrorCodes(errors),
- ConcatenateErrors(errors),
- )
+ // sort the error strings so we always have deterministic ordering
+ sort.Strings(errStrs)
+ return strings.Join(errStrs, "\n")
}
diff --git a/go/vt/vterrors/aggregate_test.go b/go/vt/vterrors/aggregate_test.go
index c8c931c8c8a..f36521c0f3e 100644
--- a/go/vt/vterrors/aggregate_test.go
+++ b/go/vt/vterrors/aggregate_test.go
@@ -13,54 +13,53 @@ import (
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)
-var errGeneric = errors.New("generic error")
+var errGeneric = "generic error"
-func errFromCode(c vtrpcpb.ErrorCode) error {
- return FromError(c, errGeneric)
+func errFromCode(c vtrpcpb.Code) error {
+ return New(c, errGeneric)
}
func TestAggregateVtGateErrorCodes(t *testing.T) {
var testcases = []struct {
input []error
- expected vtrpcpb.ErrorCode
+ expected vtrpcpb.Code
}{
{
// aggregation of no errors is a success code
input: nil,
- expected: vtrpcpb.ErrorCode_SUCCESS,
+ expected: vtrpcpb.Code_OK,
},
{
// single error code gets returned directly
- input: []error{errFromCode(vtrpcpb.ErrorCode_BAD_INPUT)},
- expected: vtrpcpb.ErrorCode_BAD_INPUT,
+ input: []error{errFromCode(vtrpcpb.Code_INVALID_ARGUMENT)},
+ expected: vtrpcpb.Code_INVALID_ARGUMENT,
},
{
- // aggregate two codes to the highest priority
+ // OK should be converted to INTERNAL
input: []error{
- errFromCode(vtrpcpb.ErrorCode_SUCCESS),
- errFromCode(vtrpcpb.ErrorCode_TRANSIENT_ERROR),
+ errFromCode(vtrpcpb.Code_OK),
+ errFromCode(vtrpcpb.Code_UNAVAILABLE),
},
- expected: vtrpcpb.ErrorCode_TRANSIENT_ERROR,
+ expected: vtrpcpb.Code_INTERNAL,
},
{
+ // aggregate two codes to the highest priority
input: []error{
- errFromCode(vtrpcpb.ErrorCode_SUCCESS),
- errFromCode(vtrpcpb.ErrorCode_TRANSIENT_ERROR),
- errFromCode(vtrpcpb.ErrorCode_BAD_INPUT),
+ errFromCode(vtrpcpb.Code_UNAVAILABLE),
+ errFromCode(vtrpcpb.Code_INVALID_ARGUMENT),
},
- expected: vtrpcpb.ErrorCode_BAD_INPUT,
+ expected: vtrpcpb.Code_INVALID_ARGUMENT,
},
{
// unknown errors map to the unknown code
input: []error{
- errFromCode(vtrpcpb.ErrorCode_SUCCESS),
fmt.Errorf("unknown error"),
},
- expected: vtrpcpb.ErrorCode_UNKNOWN_ERROR,
+ expected: vtrpcpb.Code_UNKNOWN,
},
}
for _, tc := range testcases {
- out := AggregateVtGateErrorCodes(tc.input)
+ out := aggregateCodes(tc.input)
if out != tc.expected {
t.Errorf("AggregateVtGateErrorCodes(%v) = %v \nwant: %v",
tc.input, out, tc.expected)
@@ -79,18 +78,20 @@ func TestAggregateVtGateErrors(t *testing.T) {
},
{
input: []error{
- errFromCode(vtrpcpb.ErrorCode_SUCCESS),
- errFromCode(vtrpcpb.ErrorCode_TRANSIENT_ERROR),
- errFromCode(vtrpcpb.ErrorCode_BAD_INPUT),
+ errFromCode(vtrpcpb.Code_UNAVAILABLE),
+ errFromCode(vtrpcpb.Code_INVALID_ARGUMENT),
},
- expected: FromError(
- vtrpcpb.ErrorCode_BAD_INPUT,
- ConcatenateErrors([]error{errGeneric, errGeneric, errGeneric}),
+ expected: New(
+ vtrpcpb.Code_INVALID_ARGUMENT,
+ aggregateErrors([]error{
+ errors.New(errGeneric),
+ errors.New(errGeneric),
+ }),
),
},
}
for _, tc := range testcases {
- out := AggregateVtGateErrors(tc.input)
+ out := Aggregate(tc.input)
if !reflect.DeepEqual(out, tc.expected) {
t.Errorf("AggregateVtGateErrors(%+v) = %+v \nwant: %+v",
tc.input, out, tc.expected)
diff --git a/go/vt/vterrors/doc.go b/go/vt/vterrors/doc.go
index c3a8a2a7355..6b6feada228 100644
--- a/go/vt/vterrors/doc.go
+++ b/go/vt/vterrors/doc.go
@@ -13,37 +13,16 @@ errors are passed around (even through RPCs), the code is
propagated. To handle errors, only the code should be looked at (and
not string-matching on the error message).
-For instance, see this document for the Google Cloud Error Codes.
-https://cloud.google.com/datastore/docs/concepts/errors
-
Vitess defines the error codes in /proto/vtrpc.proto. Along with an
RPCError message that can be used to transmit errors through RPCs, in
-the message payloads.
-
-Vitess then defines the VtError interface, for all errors that have a code.
-See vterrors.go in this library.
-
-Vitess also defines a VitessError error implementation, that can wrap
-any error and add a code to it.
-
-To easily transmit these codes through gRPC, we map these codes to
-gRPC error codes in grpc.go, in this library. So if a gRPC call only
-returns an error, we return a gRPC error with the right gRPC error
-code. If a gRPC call needs to return both an error and some data (like
-vtgateservice.Execute that can return an updated Session along with
-the error), we can just return an RPCError in the result.
+the message payloads. These codes match the names and numbers defined
+by gRPC.
-Some libraries define their own error structures that implement the
-VtError interface. Usually, it is to add extra data to it. For an
-example, see ../tabletserver/tablet_error.go that adds the SQL error
-codes to the error structure. These SQL errors however are all mapped
-to their appropriate canonical error code, see the function NewTabletErrorSQL
-in that file for the mapping.
+Vitess also defines a standardized error implementation that allows
+you to build an error with an associated canonical code.
-When transmitting any error through RPC boundaries, we are careful to
-always preserve the error code. When augmenting / aggregating errors,
-we also preserve the error codes:
-- See WithPrefix and WithSuffix in this package for augmentation.
-- See aggregate.go in this package for aggregation.
+While sending an error through gRPC, these codes are transmitted
+using gRPC's error propagation mechanism and decoded back to
+the original code on the other end.
*/
diff --git a/go/vt/vterrors/grpc.go b/go/vt/vterrors/grpc.go
index 920b4b9d9dd..827e04fabee 100644
--- a/go/vt/vterrors/grpc.go
+++ b/go/vt/vterrors/grpc.go
@@ -18,120 +18,104 @@ import (
// Use these methods to return an error through gRPC and still
// retain its code.
-// GRPCServerErrPrefix is the string we prefix gRPC server errors with. This is
-// necessary because there is currently no good way, in gRPC, to differentiate
-// between an error from a server vs the client.
-// See: https://github.com/grpc/grpc-go/issues/319
-const GRPCServerErrPrefix = "gRPCServerError:"
-
-// GRPCCodeToErrorCode maps a gRPC codes.Code to a vtrpcpb.ErrorCode.
-func GRPCCodeToErrorCode(code codes.Code) vtrpcpb.ErrorCode {
+// CodeToLegacyErrorCode maps a vtrpcpb.Code to a vtrpcpb.LegacyErrorCode.
+func CodeToLegacyErrorCode(code vtrpcpb.Code) vtrpcpb.LegacyErrorCode {
switch code {
- case codes.OK:
- return vtrpcpb.ErrorCode_SUCCESS
- case codes.Canceled:
- return vtrpcpb.ErrorCode_CANCELLED
- case codes.Unknown:
- return vtrpcpb.ErrorCode_UNKNOWN_ERROR
- case codes.InvalidArgument:
- return vtrpcpb.ErrorCode_BAD_INPUT
- case codes.DeadlineExceeded:
- return vtrpcpb.ErrorCode_DEADLINE_EXCEEDED
- case codes.AlreadyExists:
- return vtrpcpb.ErrorCode_INTEGRITY_ERROR
- case codes.PermissionDenied:
- return vtrpcpb.ErrorCode_PERMISSION_DENIED
- case codes.ResourceExhausted:
- return vtrpcpb.ErrorCode_RESOURCE_EXHAUSTED
- case codes.FailedPrecondition:
- return vtrpcpb.ErrorCode_QUERY_NOT_SERVED
- case codes.Aborted:
- return vtrpcpb.ErrorCode_NOT_IN_TX
- case codes.Internal:
- return vtrpcpb.ErrorCode_INTERNAL_ERROR
- case codes.Unavailable:
- return vtrpcpb.ErrorCode_TRANSIENT_ERROR
- case codes.Unauthenticated:
- return vtrpcpb.ErrorCode_UNAUTHENTICATED
+ case vtrpcpb.Code_OK:
+ return vtrpcpb.LegacyErrorCode_SUCCESS_LEGACY
+ case vtrpcpb.Code_CANCELED:
+ return vtrpcpb.LegacyErrorCode_CANCELLED_LEGACY
+ case vtrpcpb.Code_UNKNOWN:
+ return vtrpcpb.LegacyErrorCode_UNKNOWN_ERROR_LEGACY
+ case vtrpcpb.Code_INVALID_ARGUMENT:
+ return vtrpcpb.LegacyErrorCode_BAD_INPUT_LEGACY
+ case vtrpcpb.Code_DEADLINE_EXCEEDED:
+ return vtrpcpb.LegacyErrorCode_DEADLINE_EXCEEDED_LEGACY
+ case vtrpcpb.Code_ALREADY_EXISTS:
+ return vtrpcpb.LegacyErrorCode_INTEGRITY_ERROR_LEGACY
+ case vtrpcpb.Code_PERMISSION_DENIED:
+ return vtrpcpb.LegacyErrorCode_PERMISSION_DENIED_LEGACY
+ case vtrpcpb.Code_RESOURCE_EXHAUSTED:
+ return vtrpcpb.LegacyErrorCode_RESOURCE_EXHAUSTED_LEGACY
+ case vtrpcpb.Code_FAILED_PRECONDITION:
+ return vtrpcpb.LegacyErrorCode_QUERY_NOT_SERVED_LEGACY
+ case vtrpcpb.Code_ABORTED:
+ return vtrpcpb.LegacyErrorCode_NOT_IN_TX_LEGACY
+ case vtrpcpb.Code_INTERNAL:
+ return vtrpcpb.LegacyErrorCode_INTERNAL_ERROR_LEGACY
+ case vtrpcpb.Code_UNAVAILABLE:
+ // Legacy code assumes Unavailable errors are sent as Internal.
+ return vtrpcpb.LegacyErrorCode_INTERNAL_ERROR_LEGACY
+ case vtrpcpb.Code_UNAUTHENTICATED:
+ return vtrpcpb.LegacyErrorCode_UNAUTHENTICATED_LEGACY
default:
- return vtrpcpb.ErrorCode_UNKNOWN_ERROR
+ return vtrpcpb.LegacyErrorCode_UNKNOWN_ERROR_LEGACY
}
}
-// ErrorCodeToGRPCCode maps a vtrpcpb.ErrorCode to a gRPC codes.Code.
-func ErrorCodeToGRPCCode(code vtrpcpb.ErrorCode) codes.Code {
+// LegacyErrorCodeToCode maps a vtrpcpb.LegacyErrorCode to a gRPC vtrpcpb.Code.
+func LegacyErrorCodeToCode(code vtrpcpb.LegacyErrorCode) vtrpcpb.Code {
switch code {
- case vtrpcpb.ErrorCode_SUCCESS:
- return codes.OK
- case vtrpcpb.ErrorCode_CANCELLED:
- return codes.Canceled
- case vtrpcpb.ErrorCode_UNKNOWN_ERROR:
- return codes.Unknown
- case vtrpcpb.ErrorCode_BAD_INPUT:
- return codes.InvalidArgument
- case vtrpcpb.ErrorCode_DEADLINE_EXCEEDED:
- return codes.DeadlineExceeded
- case vtrpcpb.ErrorCode_INTEGRITY_ERROR:
- return codes.AlreadyExists
- case vtrpcpb.ErrorCode_PERMISSION_DENIED:
- return codes.PermissionDenied
- case vtrpcpb.ErrorCode_RESOURCE_EXHAUSTED:
- return codes.ResourceExhausted
- case vtrpcpb.ErrorCode_QUERY_NOT_SERVED:
- return codes.FailedPrecondition
- case vtrpcpb.ErrorCode_NOT_IN_TX:
- return codes.Aborted
- case vtrpcpb.ErrorCode_INTERNAL_ERROR:
- return codes.Internal
- case vtrpcpb.ErrorCode_TRANSIENT_ERROR:
- return codes.Unavailable
- case vtrpcpb.ErrorCode_UNAUTHENTICATED:
- return codes.Unauthenticated
+ case vtrpcpb.LegacyErrorCode_SUCCESS_LEGACY:
+ return vtrpcpb.Code_OK
+ case vtrpcpb.LegacyErrorCode_CANCELLED_LEGACY:
+ return vtrpcpb.Code_CANCELED
+ case vtrpcpb.LegacyErrorCode_UNKNOWN_ERROR_LEGACY:
+ return vtrpcpb.Code_UNKNOWN
+ case vtrpcpb.LegacyErrorCode_BAD_INPUT_LEGACY:
+ return vtrpcpb.Code_INVALID_ARGUMENT
+ case vtrpcpb.LegacyErrorCode_DEADLINE_EXCEEDED_LEGACY:
+ return vtrpcpb.Code_DEADLINE_EXCEEDED
+ case vtrpcpb.LegacyErrorCode_INTEGRITY_ERROR_LEGACY:
+ return vtrpcpb.Code_ALREADY_EXISTS
+ case vtrpcpb.LegacyErrorCode_PERMISSION_DENIED_LEGACY:
+ return vtrpcpb.Code_PERMISSION_DENIED
+ case vtrpcpb.LegacyErrorCode_RESOURCE_EXHAUSTED_LEGACY:
+ return vtrpcpb.Code_RESOURCE_EXHAUSTED
+ case vtrpcpb.LegacyErrorCode_QUERY_NOT_SERVED_LEGACY:
+ return vtrpcpb.Code_FAILED_PRECONDITION
+ case vtrpcpb.LegacyErrorCode_NOT_IN_TX_LEGACY:
+ return vtrpcpb.Code_ABORTED
+ case vtrpcpb.LegacyErrorCode_INTERNAL_ERROR_LEGACY:
+ // Legacy code sends internal error instead of Unavailable.
+ return vtrpcpb.Code_UNAVAILABLE
+ case vtrpcpb.LegacyErrorCode_TRANSIENT_ERROR_LEGACY:
+ return vtrpcpb.Code_UNAVAILABLE
+ case vtrpcpb.LegacyErrorCode_UNAUTHENTICATED_LEGACY:
+ return vtrpcpb.Code_UNAUTHENTICATED
default:
- return codes.Unknown
- }
-}
-
-// toGRPCCode will attempt to determine the best gRPC code for a particular error.
-func toGRPCCode(err error) codes.Code {
- if err == nil {
- return codes.OK
- }
- if vtErr, ok := err.(VtError); ok {
- return ErrorCodeToGRPCCode(vtErr.VtErrorCode())
+ return vtrpcpb.Code_UNKNOWN
}
- // Returns the underlying gRPC Code, or codes.Unknown if one doesn't exist.
- return grpc.Code(err)
}
// truncateError shortens errors because gRPC has a size restriction on them.
-func truncateError(err error) error {
+func truncateError(err error) string {
// For more details see: https://github.com/grpc/grpc-go/issues/443
// The gRPC spec says "Clients may limit the size of Response-Headers,
// Trailers, and Trailers-Only, with a default of 8 KiB each suggested."
// Therefore, we assume 8 KiB minus some headroom.
GRPCErrorLimit := 8*1024 - 512
if len(err.Error()) <= GRPCErrorLimit {
- return err
+ return err.Error()
}
truncateInfo := "[...] [remainder of the error is truncated because gRPC has a size limit on errors.]"
truncatedErr := err.Error()[:GRPCErrorLimit]
- return fmt.Errorf("%v %v", truncatedErr, truncateInfo)
+ return fmt.Sprintf("%v %v", truncatedErr, truncateInfo)
}
-// ToGRPCError returns an error as a gRPC error, with the appropriate error code.
-func ToGRPCError(err error) error {
+// ToGRPC returns an error as a gRPC error, with the appropriate error code.
+func ToGRPC(err error) error {
if err == nil {
return nil
}
- return grpc.Errorf(toGRPCCode(err), "%v %v", GRPCServerErrPrefix, truncateError(err))
+ return grpc.Errorf(codes.Code(Code(err)), "%v", truncateError(err))
}
-// FromGRPCError returns a gRPC error as a VitessError, translating between error codes.
+// FromGRPC returns a gRPC error as a vtError, translating between error codes.
// However, there are a few errors which are not translated and passed as they
// are. For example, io.EOF since our code base checks for this error to find
// out that a stream has finished.
-func FromGRPCError(err error) error {
+func FromGRPC(err error) error {
if err == nil {
return nil
}
@@ -139,8 +123,5 @@ func FromGRPCError(err error) error {
// Do not wrap io.EOF because we compare against it for finished streams.
return err
}
- return &VitessError{
- Code: GRPCCodeToErrorCode(grpc.Code(err)),
- err: err,
- }
+ return New(vtrpcpb.Code(grpc.Code(err)), err.Error())
}
diff --git a/go/vt/vterrors/proto3.go b/go/vt/vterrors/proto3.go
index 030262fd568..417aaf4b26b 100644
--- a/go/vt/vterrors/proto3.go
+++ b/go/vt/vterrors/proto3.go
@@ -5,35 +5,36 @@
package vterrors
import (
- "errors"
-
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)
// This file contains the necessary methods to send and receive errors
-// as payloads of proto3 structures. It converts VitessError to and from
-// vtrpcpb.Error. Use these methods when a RPC call can return both
+// as payloads of proto3 structures. It converts vtError to and from
+// *vtrpcpb.RPCError. Use these methods when a RPC call can return both
// data and an error.
-// FromVtRPCError recovers a VitessError from a *vtrpcpb.RPCError (which is how VitessErrors
-// are transmitted across proto3 RPC boundaries).
-func FromVtRPCError(rpcErr *vtrpcpb.RPCError) error {
+// FromVTRPC recovers a vtError from a *vtrpcpb.RPCError (which is how vtError
+// is transmitted across proto3 RPC boundaries).
+func FromVTRPC(rpcErr *vtrpcpb.RPCError) error {
if rpcErr == nil {
return nil
}
- return &VitessError{
- Code: rpcErr.Code,
- err: errors.New(rpcErr.Message),
+ code := rpcErr.Code
+ if code == vtrpcpb.Code_OK {
+ code = LegacyErrorCodeToCode(rpcErr.LegacyCode)
}
+ return New(code, rpcErr.Message)
}
-// VtRPCErrorFromVtError converts from a VtError to a vtrpcpb.RPCError.
-func VtRPCErrorFromVtError(err error) *vtrpcpb.RPCError {
+// ToVTRPC converts from vtError to a vtrpcpb.RPCError.
+func ToVTRPC(err error) *vtrpcpb.RPCError {
if err == nil {
return nil
}
+ code := Code(err)
return &vtrpcpb.RPCError{
- Code: RecoverVtErrorCode(err),
- Message: err.Error(),
+ LegacyCode: CodeToLegacyErrorCode(code),
+ Code: code,
+ Message: err.Error(),
}
}
diff --git a/go/vt/vterrors/proto3_test.go b/go/vt/vterrors/proto3_test.go
new file mode 100644
index 00000000000..7716b3f7610
--- /dev/null
+++ b/go/vt/vterrors/proto3_test.go
@@ -0,0 +1,70 @@
+// Copyright 2017, Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package vterrors
+
+import (
+ "reflect"
+ "testing"
+
+ vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
+)
+
+func TestFromVtRPCError(t *testing.T) {
+ testcases := []struct {
+ in *vtrpcpb.RPCError
+ want error
+ }{{
+ in: nil,
+ want: nil,
+ }, {
+ in: &vtrpcpb.RPCError{
+ LegacyCode: vtrpcpb.LegacyErrorCode_BAD_INPUT_LEGACY,
+ Message: "bad input",
+ },
+ want: New(vtrpcpb.Code_INVALID_ARGUMENT, "bad input"),
+ }, {
+ in: &vtrpcpb.RPCError{
+ LegacyCode: vtrpcpb.LegacyErrorCode_BAD_INPUT_LEGACY,
+ Message: "bad input",
+ Code: vtrpcpb.Code_INVALID_ARGUMENT,
+ },
+ want: New(vtrpcpb.Code_INVALID_ARGUMENT, "bad input"),
+ }, {
+ in: &vtrpcpb.RPCError{
+ Message: "bad input",
+ Code: vtrpcpb.Code_INVALID_ARGUMENT,
+ },
+ want: New(vtrpcpb.Code_INVALID_ARGUMENT, "bad input"),
+ }}
+ for _, tcase := range testcases {
+ got := FromVTRPC(tcase.in)
+ if !reflect.DeepEqual(got, tcase.want) {
+ t.Errorf("FromVtRPCError(%v): %v, want %v", tcase.in, got, tcase.want)
+ }
+ }
+}
+
+func TestVtRPCErrorFromVtError(t *testing.T) {
+ testcases := []struct {
+ in error
+ want *vtrpcpb.RPCError
+ }{{
+ in: nil,
+ want: nil,
+ }, {
+ in: New(vtrpcpb.Code_INVALID_ARGUMENT, "bad input"),
+ want: &vtrpcpb.RPCError{
+ LegacyCode: vtrpcpb.LegacyErrorCode_BAD_INPUT_LEGACY,
+ Message: "bad input",
+ Code: vtrpcpb.Code_INVALID_ARGUMENT,
+ },
+ }}
+ for _, tcase := range testcases {
+ got := ToVTRPC(tcase.in)
+ if !reflect.DeepEqual(got, tcase.want) {
+ t.Errorf("VtRPCErrorFromVtError(%v): %v, want %v", tcase.in, got, tcase.want)
+ }
+ }
+}
diff --git a/go/vt/vterrors/vterrors.go b/go/vt/vterrors/vterrors.go
index 6cae91cf20b..3f627749b99 100644
--- a/go/vt/vterrors/vterrors.go
+++ b/go/vt/vterrors/vterrors.go
@@ -2,138 +2,58 @@ package vterrors
import (
"fmt"
- "sort"
- "strings"
+ "time"
+ "golang.org/x/net/context"
+
+ "github.com/youtube/vitess/go/tb"
+ "github.com/youtube/vitess/go/vt/logutil"
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)
-// ConcatenateErrors aggregates an array of errors into a single error by string concatenation.
-func ConcatenateErrors(errors []error) error {
- errStrs := make([]string, 0, len(errors))
- for _, e := range errors {
- errStrs = append(errStrs, fmt.Sprintf("%v", e))
- }
- // sort the error strings so we always have deterministic ordering
- sort.Strings(errStrs)
- return fmt.Errorf("%v", strings.Join(errStrs, "\n"))
-}
+var logger = logutil.NewThrottledLogger("vterror", 5*time.Second)
-// VtError is implemented by any type that exposes a vtrpcpb.ErrorCode.
-type VtError interface {
- VtErrorCode() vtrpcpb.ErrorCode
+type vtError struct {
+ code vtrpcpb.Code
+ err string
}
-// RecoverVtErrorCode attempts to recover a vtrpcpb.ErrorCode from an error.
-func RecoverVtErrorCode(err error) vtrpcpb.ErrorCode {
- if vtErr, ok := err.(VtError); ok {
- return vtErr.VtErrorCode()
+// New creates a new error using the code and input string.
+func New(code vtrpcpb.Code, in string) error {
+ if code == vtrpcpb.Code_OK {
+ logger.Errorf("OK is an invalid code, using INTERNAL instead: %s\n%s", in, tb.Stack(2))
+ code = vtrpcpb.Code_INTERNAL
}
- return vtrpcpb.ErrorCode_UNKNOWN_ERROR
-}
-
-// VitessError is the error type that we use internally for passing structured errors.
-type VitessError struct {
- // Error code of the Vitess error.
- Code vtrpcpb.ErrorCode
- // Error message that should be returned. This allows us to change an error message
- // without losing the underlying error. For example, if you have an error like
- // context.DeadlikeExceeded, you don't want to modify it - otherwise you would lose
- // the ability to programatically check for that error. However, you might want to
- // add some context to the error, giving you a message like "command failed: deadline exceeded".
- // To do that, you can create a NewVitessError to wrap the original error, but redefine
- // the error message.
- Message string
- err error
-}
-
-// Error implements the error interface. It will return the redefined error message, if there
-// is one. If there isn't, it will return the original error message.
-func (e *VitessError) Error() string {
- if e.Message == "" {
- return fmt.Sprintf("%v", e.err)
+ return &vtError{
+ code: code,
+ err: in,
}
- return e.Message
}
-// VtErrorCode returns the underlying Vitess error code.
-func (e *VitessError) VtErrorCode() vtrpcpb.ErrorCode {
- return e.Code
+// Errorf returns a new error built using Printf style arguments.
+func Errorf(code vtrpcpb.Code, format string, args ...interface{}) error {
+ return New(code, fmt.Sprintf(format, args...))
}
-// AsString returns a VitessError as a string, with more detailed information than Error().
-func (e *VitessError) AsString() string {
- if e.Message != "" {
- return fmt.Sprintf("Code: %v, Message: %v, err: %v", e.Code, e.Message, e.err)
- }
- return fmt.Sprintf("Code: %v, err: %v", e.Code, e.err)
+func (e *vtError) Error() string {
+ return e.err
}
-// FromError returns a VitessError with the supplied error code by wrapping an
-// existing error.
-// Use this method also when you want to create a VitessError without a custom
-// message. For example:
-// err := vterrors.FromError(vtrpcpb.ErrorCode_INTERNAL_ERROR,
-// errors.New("no valid endpoint"))
-func FromError(code vtrpcpb.ErrorCode, err error) error {
- return &VitessError{
- Code: code,
- err: err,
+// Code returns the error code if it's a vtError.
+// If err is nil, it returns ok. Otherwise, it returns unknown.
+func Code(err error) vtrpcpb.Code {
+ if err == nil {
+ return vtrpcpb.Code_OK
}
-}
-
-// NewVitessError returns a VitessError backed error with the given arguments.
-// Useful for preserving an underlying error while creating a new error message.
-func NewVitessError(code vtrpcpb.ErrorCode, err error, format string, args ...interface{}) error {
- return &VitessError{
- Code: code,
- Message: fmt.Sprintf(format, args...),
- err: err,
- }
-}
-
-// WithPrefix allows a string to be prefixed to an error.
-// If the original error implements the VtError interface it returns a VitessError wrapping the
-// original error (with one exception: if the original error is an instance of VitessError it
-// doesn't wrap it in a new VitessError instance, but only changes the 'Message' field).
-// Otherwise, it returns a string prefixed with the given prefix.
-func WithPrefix(prefix string, in error) error {
- if vitessError, ok := in.(*VitessError); ok {
- return &VitessError{
- Code: vitessError.Code,
- err: vitessError.err,
- Message: fmt.Sprintf("%s%s", prefix, in.Error()),
- }
- }
- if vtError, ok := in.(VtError); ok {
- return &VitessError{
- Code: vtError.VtErrorCode(),
- err: in,
- Message: fmt.Sprintf("%s%s", prefix, in.Error()),
- }
- }
- return fmt.Errorf("%s%s", prefix, in)
-}
-
-// WithSuffix allows a string to be suffixed to an error.
-// If the original error implements the VtError interface it returns a VitessError wrapping the
-// original error (with one exception: if the original error is an instance of VitessError
-// it doesn't wrap it in a new VitessError instance, but only changes the 'Message' field).
-// Otherwise, it returns a string suffixed with the given suffix.
-func WithSuffix(in error, suffix string) error {
- if vitessError, ok := in.(*VitessError); ok {
- return &VitessError{
- Code: vitessError.Code,
- err: vitessError.err,
- Message: fmt.Sprintf("%s%s", in.Error(), suffix),
- }
+ if err, ok := err.(*vtError); ok {
+ return err.code
}
- if vtError, ok := in.(VtError); ok {
- return &VitessError{
- Code: vtError.VtErrorCode(),
- err: in,
- Message: fmt.Sprintf("%s%s", in.Error(), suffix),
- }
+ // Handle some special cases.
+ switch err {
+ case context.Canceled:
+ return vtrpcpb.Code_CANCELED
+ case context.DeadlineExceeded:
+ return vtrpcpb.Code_DEADLINE_EXCEEDED
}
- return fmt.Errorf("%s%s", in, suffix)
+ return vtrpcpb.Code_UNKNOWN
}
diff --git a/go/vt/vterrors/vterrors_test.go b/go/vt/vterrors/vterrors_test.go
new file mode 100644
index 00000000000..d3e23b879f6
--- /dev/null
+++ b/go/vt/vterrors/vterrors_test.go
@@ -0,0 +1,61 @@
+package vterrors
+
+import (
+ "errors"
+ "testing"
+
+ "golang.org/x/net/context"
+
+ vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
+)
+
+func TestCreation(t *testing.T) {
+ testcases := []struct {
+ in, want vtrpcpb.Code
+ }{{
+ in: vtrpcpb.Code_CANCELED,
+ want: vtrpcpb.Code_CANCELED,
+ }, {
+ in: vtrpcpb.Code_UNKNOWN,
+ want: vtrpcpb.Code_UNKNOWN,
+ }, {
+ // Invalid code OK should be converted to INTERNAL.
+ in: vtrpcpb.Code_OK,
+ want: vtrpcpb.Code_INTERNAL,
+ }}
+ for _, tcase := range testcases {
+ if got := Code(New(tcase.in, "")); got != tcase.want {
+ t.Errorf("Code(New(%v)): %v, want %v", tcase.in, got, tcase.want)
+ }
+ if got := Code(Errorf(tcase.in, "")); got != tcase.want {
+ t.Errorf("Code(Errorf(%v)): %v, want %v", tcase.in, got, tcase.want)
+ }
+ }
+}
+
+func TestCode(t *testing.T) {
+ testcases := []struct {
+ in error
+ want vtrpcpb.Code
+ }{{
+ in: nil,
+ want: vtrpcpb.Code_OK,
+ }, {
+ in: errors.New("generic"),
+ want: vtrpcpb.Code_UNKNOWN,
+ }, {
+ in: New(vtrpcpb.Code_CANCELED, "generic"),
+ want: vtrpcpb.Code_CANCELED,
+ }, {
+ in: context.Canceled,
+ want: vtrpcpb.Code_CANCELED,
+ }, {
+ in: context.DeadlineExceeded,
+ want: vtrpcpb.Code_DEADLINE_EXCEEDED,
+ }}
+ for _, tcase := range testcases {
+ if got := Code(tcase.in); got != tcase.want {
+ t.Errorf("Code(%v): %v, want %v", tcase.in, got, tcase.want)
+ }
+ }
+}
diff --git a/go/vt/vtgate/buffer/buffer.go b/go/vt/vtgate/buffer/buffer.go
index cf5ba44c61e..89075ed2c31 100644
--- a/go/vt/vtgate/buffer/buffer.go
+++ b/go/vt/vtgate/buffer/buffer.go
@@ -12,7 +12,6 @@ package buffer
import (
"context"
- "errors"
"fmt"
"strings"
"sync"
@@ -29,9 +28,9 @@ import (
)
var (
- bufferFullError = vterrors.FromError(vtrpcpb.ErrorCode_TRANSIENT_ERROR, errors.New("master buffer is full"))
- entryEvictedError = vterrors.FromError(vtrpcpb.ErrorCode_TRANSIENT_ERROR, errors.New("buffer full: request evicted for newer request"))
- contextCanceledError = vterrors.FromError(vtrpcpb.ErrorCode_TRANSIENT_ERROR, errors.New("context was canceled before failover finished"))
+ bufferFullError = vterrors.New(vtrpcpb.Code_UNAVAILABLE, "master buffer is full")
+ entryEvictedError = vterrors.New(vtrpcpb.Code_UNAVAILABLE, "buffer full: request evicted for newer request")
+ contextCanceledError = vterrors.New(vtrpcpb.Code_UNAVAILABLE, "context was canceled before failover finished")
)
// bufferMode specifies how the buffer is configured for a given shard.
@@ -99,13 +98,13 @@ func New() *Buffer {
header := "Buffering limited to configured "
limited := ""
if len(keyspaces) > 0 {
- limited = "keyspaces: " + setToString(keyspaces)
+ limited += "keyspaces: " + setToString(keyspaces)
}
if len(shards) > 0 {
if limited == "" {
limited += " and "
}
- limited = "shards: " + setToString(shards)
+ limited += "shards: " + setToString(shards)
}
if limited != "" {
limited = header + limited
@@ -218,31 +217,27 @@ func (b *Buffer) StatsUpdate(ts *discovery.TabletStats) {
func causedByFailover(err error) bool {
log.V(2).Infof("Checking error (type: %T) if it is caused by a failover. err: %v", err, err)
- if vtErr, ok := err.(vterrors.VtError); ok {
- switch vtErr.VtErrorCode() {
- case vtrpcpb.ErrorCode_QUERY_NOT_SERVED:
- // All flavors.
- if strings.Contains(err.Error(), "retry: operation not allowed in state NOT_SERVING") ||
- strings.Contains(err.Error(), "retry: operation not allowed in state SHUTTING_DOWN") ||
- // Match 1290 if -queryserver-config-terse-errors explicitly hid the error message
- // (which it does to avoid logging the original query including any PII).
- strings.Contains(err.Error(), "retry: (errno 1290) (sqlstate HY000) during query:") {
- return true
- }
- // MariaDB flavor.
- if strings.Contains(err.Error(), "retry: The MariaDB server is running with the --read-only option so it cannot execute this statement (errno 1290) (sqlstate HY000)") {
- return true
- }
- // MySQL flavor.
- if strings.Contains(err.Error(), "retry: The MySQL server is running with the --read-only option so it cannot execute this statement (errno 1290) (sqlstate HY000)") {
- return true
- }
- case vtrpcpb.ErrorCode_INTERNAL_ERROR:
- // Google internal flavor.
- if strings.Contains(err.Error(), "fatal: failover in progress (errno 1227) (sqlstate 42000)") {
- return true
- }
- }
+ // TODO(sougou): Remove the INTERNAL check after rollout.
+ if code := vterrors.Code(err); code != vtrpcpb.Code_FAILED_PRECONDITION && code != vtrpcpb.Code_INTERNAL {
+ return false
+ }
+ switch {
+ // All flavors.
+ case strings.Contains(err.Error(), "operation not allowed in state NOT_SERVING") ||
+ strings.Contains(err.Error(), "operation not allowed in state SHUTTING_DOWN") ||
+ // Match 1290 if -queryserver-config-terse-errors explicitly hid the error message
+ // (which it does to avoid logging the original query including any PII).
+ strings.Contains(err.Error(), "(errno 1290) (sqlstate HY000) during query:"):
+ return true
+ // MariaDB flavor.
+ case strings.Contains(err.Error(), "The MariaDB server is running with the --read-only option so it cannot execute this statement (errno 1290) (sqlstate HY000)"):
+ return true
+ // MySQL flavor.
+ case strings.Contains(err.Error(), "The MySQL server is running with the --read-only option so it cannot execute this statement (errno 1290) (sqlstate HY000)"):
+ return true
+ // Google internal flavor.
+ case strings.Contains(err.Error(), "failover in progress (errno 1227) (sqlstate 42000)"):
+ return true
}
return false
}
diff --git a/go/vt/vtgate/buffer/buffer_test.go b/go/vt/vtgate/buffer/buffer_test.go
index e64e809d4ea..de5d0a55d67 100644
--- a/go/vt/vtgate/buffer/buffer_test.go
+++ b/go/vt/vtgate/buffer/buffer_test.go
@@ -26,14 +26,14 @@ const (
)
var (
- failoverErr = vterrors.FromError(vtrpcpb.ErrorCode_QUERY_NOT_SERVED,
- errors.New("vttablet: rpc error: code = 9 desc = gRPCServerError: retry: operation not allowed in state SHUTTING_DOWN"))
- nonFailoverErr = vterrors.FromError(vtrpcpb.ErrorCode_QUERY_NOT_SERVED,
- errors.New("vttablet: rpc error: code = 9 desc = gRPCServerError: retry: TODO(mberlin): Insert here any realistic error not caused by a failover"))
+ failoverErr = vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION,
+ "vttablet: rpc error: code = 9 desc = gRPCServerError: retry: operation not allowed in state SHUTTING_DOWN")
+ nonFailoverErr = vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION,
+ "vttablet: rpc error: code = 9 desc = gRPCServerError: retry: TODO(mberlin): Insert here any realistic error not caused by a failover")
statsKeyJoined = fmt.Sprintf("%s.%s", keyspace, shard)
- statsKeyJoinedFailoverEndDetected = statsKeyJoined + "." + string(stopReasonFailoverEndDetected)
+ statsKeyJoinedFailoverEndDetected = statsKeyJoined + "." + string(stopFailoverEndDetected)
statsKeyJoinedWindowExceeded = statsKeyJoined + "." + string(evictedWindowExceeded)
@@ -148,6 +148,9 @@ func TestBuffer(t *testing.T) {
if err := waitForState(b, stateIdle); err != nil {
t.Fatal(err)
}
+ if err := waitForPoolSlots(b, *size); err != nil {
+ t.Fatal(err)
+ }
// Stop counter must have been increased for the second failover.
if got, want := stops.Counts()[statsKeyJoinedFailoverEndDetected], int64(2); got != want {
@@ -180,7 +183,9 @@ func issueRequestAndBlockRetry(ctx context.Context, t *testing.T, b *Buffer, err
// Wait for the test's signal before we tell the buffer that we retried.
<-markRetryDone
}
- defer retryDone()
+ if retryDone != nil {
+ defer retryDone()
+ }
defer close(bufferingStopped)
}()
@@ -188,7 +193,7 @@ func issueRequestAndBlockRetry(ctx context.Context, t *testing.T, b *Buffer, err
}
// waitForRequestsInFlight blocks until the buffer queue has reached "count".
-// This check is potentially racy and therefore retried up to a timeout of 2s.
+// This check is potentially racy and therefore retried up to a timeout of 10s.
func waitForRequestsInFlight(b *Buffer, count int) error {
start := time.Now()
sb := b.getOrCreateBuffer(keyspace, shard)
@@ -204,7 +209,7 @@ func waitForRequestsInFlight(b *Buffer, count int) error {
}
}
-// waitForState polls the buffer data for up to 2 seconds and returns an error
+// waitForState polls the buffer data for up to 10 seconds and returns an error
// if shardBuffer doesn't have the wanted state by then.
func waitForState(b *Buffer, want bufferState) error {
sb := b.getOrCreateBuffer(keyspace, shard)
@@ -221,6 +226,24 @@ func waitForState(b *Buffer, want bufferState) error {
}
}
+// waitForPoolSlots waits up to 10s that all buffer pool slots have been
+// returned. The wait is necessary because in some cases the buffer code
+// does not block itself on the wait. But in any case, the slot should be
+// returned when the request has finished. See also shardBuffer.unblockAndWait().
+func waitForPoolSlots(b *Buffer, want int) error {
+ start := time.Now()
+ for {
+ got := b.bufferSizeSema.Size()
+ if got == want {
+ return nil
+ }
+
+ if time.Since(start) > 10*time.Second {
+ return fmt.Errorf("not all pool slots were returned: got = %v, want = %v", got, want)
+ }
+ }
+}
+
// TestDryRun tests the case when only the dry-run mode is enabled globally.
func TestDryRun(t *testing.T) {
resetVariables()
@@ -237,6 +260,9 @@ func TestDryRun(t *testing.T) {
if err := waitForState(b, stateBuffering); err != nil {
t.Fatal(err)
}
+ if err := waitForPoolSlots(b, *size); err != nil {
+ t.Fatal(err)
+ }
if got, want := starts.Counts()[statsKeyJoined], int64(1); got != want {
t.Fatalf("buffering start was not tracked: got = %v, want = %v", got, want)
}
@@ -274,6 +300,10 @@ func TestPassthrough(t *testing.T) {
if retryDone, err := b.WaitForFailoverEnd(context.Background(), keyspace, shard, nonFailoverErr); err != nil || retryDone != nil {
t.Fatalf("requests with non-failover errors must never be buffered. err: %v retryDone: %v", err, retryDone)
}
+
+ if err := waitForPoolSlots(b, *size); err != nil {
+ t.Fatal(err)
+ }
}
// TestPassThroughLastReparentTooRecent tests that buffering is skipped if
@@ -305,6 +335,9 @@ func TestPassThroughLastReparentTooRecent(t *testing.T) {
if retryDone, err := b.WaitForFailoverEnd(context.Background(), keyspace, shard, failoverErr); err != nil || retryDone != nil {
t.Fatalf("requests where the failover end was recently detected before the start must not be buffered. err: %v retryDone: %v", err, retryDone)
}
+ if err := waitForPoolSlots(b, *size); err != nil {
+ t.Fatal(err)
+ }
if got, want := requestsSkipped.Counts()[statsKeyJoinedLastReparentTooRecent], int64(1); got != want {
t.Fatalf("skipped request was not tracked: got = %v, want = %v", got, want)
}
@@ -353,6 +386,9 @@ func TestPassthroughDuringDrain(t *testing.T) {
if err := waitForState(b, stateIdle); err != nil {
t.Fatal(err)
}
+ if err := waitForPoolSlots(b, *size); err != nil {
+ t.Fatal(err)
+ }
}
// TestPassthroughIgnoredKeyspaceOrShard tests that the explicit whitelisting
@@ -376,6 +412,9 @@ func TestPassthroughIgnoredKeyspaceOrShard(t *testing.T) {
if retryDone, err := b.WaitForFailoverEnd(context.Background(), keyspace, ignoredShard, failoverErr); err != nil || retryDone != nil {
t.Fatalf("requests for ignored shards must not be buffered. err: %v retryDone: %v", err, retryDone)
}
+ if err := waitForPoolSlots(b, *size); err != nil {
+ t.Fatal(err)
+ }
statsKeyJoined = strings.Join([]string{keyspace, ignoredShard, skippedDisabled}, ".")
if got, want := requestsSkipped.Counts()[statsKeyJoined], int64(1); got != want {
t.Fatalf("request was not skipped as disabled: got = %v, want = %v", got, want)
@@ -463,6 +502,9 @@ func testRequestCanceled(t *testing.T, explicitEnd bool) {
if err := waitForState(b, stateIdle); err != nil {
t.Fatal(err)
}
+ if err := waitForPoolSlots(b, *size); err != nil {
+ t.Fatal(err)
+ }
}
func TestEviction(t *testing.T) {
@@ -511,13 +553,16 @@ func TestEviction(t *testing.T) {
if err := waitForState(b, stateIdle); err != nil {
t.Fatal(err)
}
+ if err := waitForPoolSlots(b, 2); err != nil {
+ t.Fatal(err)
+ }
}
func isCanceledError(err error) error {
if err == nil {
return fmt.Errorf("buffering should have stopped early and returned an error because the request was canceled from the outside")
}
- if got, want := vterrors.RecoverVtErrorCode(err), vtrpcpb.ErrorCode_TRANSIENT_ERROR; got != want {
+ if got, want := vterrors.Code(err), vtrpcpb.Code_UNAVAILABLE; got != want {
return fmt.Errorf("wrong error code for canceled buffered request. got = %v, want = %v", got, want)
}
if got, want := err.Error(), "context was canceled before failover finished: context canceled"; got != want {
@@ -531,7 +576,7 @@ func isEvictedError(err error) error {
if err == nil {
return errors.New("request should have been evicted because the buffer was full")
}
- if got, want := vterrors.RecoverVtErrorCode(err), vtrpcpb.ErrorCode_TRANSIENT_ERROR; got != want {
+ if got, want := vterrors.Code(err), vtrpcpb.Code_UNAVAILABLE; got != want {
return fmt.Errorf("wrong error code for evicted buffered request. got = %v, want = %v full error: %v", got, want, err)
}
if got, want := err.Error(), entryEvictedError.Error(); !strings.Contains(got, want) {
@@ -568,7 +613,7 @@ func TestEvictionNotPossible(t *testing.T) {
if bufferErr == nil || retryDone != nil {
t.Fatalf("buffer should have returned an error because it's full: err: %v retryDone: %v", bufferErr, retryDone)
}
- if got, want := vterrors.RecoverVtErrorCode(bufferErr), vtrpcpb.ErrorCode_TRANSIENT_ERROR; got != want {
+ if got, want := vterrors.Code(bufferErr), vtrpcpb.Code_UNAVAILABLE; got != want {
t.Fatalf("wrong error code for evicted buffered request. got = %v, want = %v", got, want)
}
if got, want := bufferErr.Error(), bufferFullError.Error(); !strings.Contains(got, want) {
@@ -587,6 +632,9 @@ func TestEvictionNotPossible(t *testing.T) {
if err := waitForState(b, stateIdle); err != nil {
t.Fatal(err)
}
+ if err := waitForPoolSlots(b, 1); err != nil {
+ t.Fatal(err)
+ }
statsKeyJoined := strings.Join([]string{keyspace, shard2, string(skippedBufferFull)}, ".")
if got, want := requestsSkipped.Counts()[statsKeyJoined], int64(1); got != want {
t.Fatalf("skipped request was not tracked: got = %v, want = %v", got, want)
@@ -676,6 +724,9 @@ func TestWindow(t *testing.T) {
if err := waitForState(b, stateIdle); err != nil {
t.Fatal(err)
}
+ if err := waitForPoolSlots(b, 1); err != nil {
+ t.Fatal(err)
+ }
}
func waitForRequestsExceededWindow(count int) error {
@@ -715,6 +766,10 @@ func TestShutdown(t *testing.T) {
if err := <-stopped1; err != nil {
t.Fatalf("request should have been buffered and not returned an error: %v", err)
}
+
+ if err := waitForPoolSlots(b, *size); err != nil {
+ t.Fatal(err)
+ }
}
// resetVariables resets the task level variables. The code does not reset these
diff --git a/go/vt/vtgate/buffer/flags.go b/go/vt/vtgate/buffer/flags.go
index 570484afd12..508c9f9d5b0 100644
--- a/go/vt/vtgate/buffer/flags.go
+++ b/go/vt/vtgate/buffer/flags.go
@@ -28,6 +28,7 @@ func resetFlagsForTesting() {
// Set all flags to their default value.
flag.Set("enable_buffer", "false")
flag.Set("enable_buffer_dry_run", "false")
+ flag.Set("buffer_size", "10")
flag.Set("buffer_window", "10s")
flag.Set("buffer_keyspace_shards", "")
flag.Set("buffer_max_failover_duration", "20s")
diff --git a/go/vt/vtgate/buffer/shard_buffer.go b/go/vt/vtgate/buffer/shard_buffer.go
index e0fd1160131..51348cc3ba9 100644
--- a/go/vt/vtgate/buffer/shard_buffer.go
+++ b/go/vt/vtgate/buffer/shard_buffer.go
@@ -97,12 +97,15 @@ type entry struct {
}
func newShardBuffer(mode bufferMode, keyspace, shard string, bufferSizeSema *sync2.Semaphore) *shardBuffer {
+ statsKey := []string{keyspace, shard}
+ initVariablesForShard(statsKey)
+
return &shardBuffer{
mode: mode,
keyspace: keyspace,
shard: shard,
bufferSizeSema: bufferSizeSema,
- statsKey: []string{keyspace, shard},
+ statsKey: statsKey,
statsKeyJoined: fmt.Sprintf("%s.%s", keyspace, shard),
logTooRecent: logutil.NewThrottledLogger(fmt.Sprintf("FailoverTooRecent-%v", topoproto.KeyspaceShardString(keyspace, shard)), 5*time.Second),
state: stateIdle,
@@ -197,7 +200,7 @@ func (sb *shardBuffer) waitForFailoverEnd(ctx context.Context, keyspace, shard s
if err != nil {
return nil, err
}
- return entry.bufferCancel, sb.wait(ctx, entry)
+ return sb.wait(ctx, entry)
}
// shouldBufferLocked returns true if the current request should be buffered
@@ -311,8 +314,9 @@ func (sb *shardBuffer) bufferRequestLocked(ctx context.Context) (*entry, error)
// the request retried and finished.
// If blockingWait is true, this call will block until the request retried and
// finished. This mode is used during the drain (to avoid flooding the master)
-// while the non-blocking mode is used when evicting a request e.g. because the
-// buffer is full or it exceeded the buffering window
+// while the non-blocking mode is used when a) evicting a request (e.g. because
+// the buffer is full or it exceeded the buffering window) or b) when the
+// request was canceled from outside and we removed it.
func (sb *shardBuffer) unblockAndWait(e *entry, err error, releaseSlot, blockingWait bool) {
// Set error such that the request will see it.
e.err = err
@@ -345,13 +349,14 @@ func (sb *shardBuffer) waitForRequestFinish(e *entry, releaseSlot, async bool) {
}
// wait blocks while the request is buffered during the failover.
-func (sb *shardBuffer) wait(ctx context.Context, e *entry) error {
+// See Buffer.WaitForFailoverEnd() for the API contract of the return values.
+func (sb *shardBuffer) wait(ctx context.Context, e *entry) (RetryDoneFunc, error) {
select {
case <-ctx.Done():
sb.remove(e)
- return vterrors.WithSuffix(contextCanceledError, fmt.Sprintf(": %v", ctx.Err()))
+ return nil, vterrors.Errorf(vterrors.Code(contextCanceledError), "%v: %v", contextCanceledError, ctx.Err())
case <-e.done:
- return e.err
+ return e.bufferCancel, e.err
}
}
@@ -406,9 +411,21 @@ func (sb *shardBuffer) remove(toRemove *entry) {
if e == toRemove {
// Delete entry at index "i" from slice.
sb.queue = append(sb.queue[:i], sb.queue[i+1:]...)
- // Entry was not canceled internally yet. Finish it explicitly. This way,
- // timeoutThread will find out about it as well.
- close(toRemove.done)
+
+ // Cancel the entry's "bufferCtx".
+ // The usual drain or eviction code would unblock the request and then
+ // wait for the "bufferCtx" to be done.
+ // But this code path is different because it's going to return an error
+ // to the request and not the "e.bufferCancel" function i.e. the request
+ // cannot cancel the "bufferCtx" itself.
+ // Therefore, we call "e.bufferCancel". This also avoids that the
+ // context's Go routine could leak.
+ e.bufferCancel()
+ // Release the buffer slot and close the "e.done" channel.
+ // By closing "e.done", we finish it explicitly and timeoutThread will
+ // find out about it as well.
+ sb.unblockAndWait(e, nil /* err */, true /* releaseSlot */, false /* blockingWait */)
+
// Track it as "ContextDone" eviction.
statsKeyWithReason := append(sb.statsKey, string(evictedContextDone))
requestsEvicted.Add(statsKeyWithReason, 1)
@@ -445,14 +462,14 @@ func (sb *shardBuffer) recordExternallyReparentedTimestamp(timestamp int64) {
// First non-zero value after startup. Remember it.
sb.externallyReparentedAfterStart = timestamp
}
- sb.stopBufferingLocked(stopReasonFailoverEndDetected, "failover end detected")
+ sb.stopBufferingLocked(stopFailoverEndDetected, "failover end detected")
}
func (sb *shardBuffer) stopBufferingDueToMaxDuration() {
sb.mu.Lock()
defer sb.mu.Unlock()
- sb.stopBufferingLocked(stopReasonMaxFailoverDurationExceeded,
+ sb.stopBufferingLocked(stopMaxFailoverDurationExceeded,
fmt.Sprintf("stopping buffering because failover did not finish in time (%v)", *maxFailoverDuration))
}
diff --git a/go/vt/vtgate/buffer/variables.go b/go/vt/vtgate/buffer/variables.go
index b998f96975c..5a4c9ecfc9c 100644
--- a/go/vt/vtgate/buffer/variables.go
+++ b/go/vt/vtgate/buffer/variables.go
@@ -57,15 +57,19 @@ var (
// stopReason is used in "stopsByReason" as "Reason" label.
type stopReason string
+var stopReasons = []stopReason{stopFailoverEndDetected, stopMaxFailoverDurationExceeded, stopShutdown}
+
const (
- stopReasonFailoverEndDetected stopReason = "NewMasterSeen"
- stopReasonMaxFailoverDurationExceeded = "MaxDurationExceeded"
- stopShutdown = "Shutdown"
+ stopFailoverEndDetected stopReason = "NewMasterSeen"
+ stopMaxFailoverDurationExceeded = "MaxDurationExceeded"
+ stopShutdown = "Shutdown"
)
// evictedReason is used in "requestsEvicted" as "Reason" label.
type evictedReason string
+var evictReasons = []evictedReason{evictedContextDone, evictedBufferFull, evictedWindowExceeded}
+
const (
evictedContextDone evictedReason = "ContextDone"
evictedBufferFull = "BufferFull"
@@ -75,6 +79,8 @@ const (
// skippedReason is used in "requestsSkipped" as "Reason" label.
type skippedReason string
+var skippedReasons = []skippedReason{skippedBufferFull, skippedDisabled, skippedShutdown, skippedLastReparentTooRecent, skippedLastFailoverTooRecent}
+
const (
// skippedBufferFull occurs when all slots in the buffer are occupied by one
// or more concurrent failovers. Unlike "evictedBufferFull", no request could
@@ -88,6 +94,36 @@ const (
skippedLastFailoverTooRecent = "LastFailoverTooRecent"
)
+// initVariablesForShard is used to initialize all shard variables to 0.
+// If we don't do this, monitoring frameworks may not correctly calculate rates
+// for the first failover of the shard because they see a transition from
+// "no value for this label set (NaN)" to "a value".
+// "statsKey" should have two members for keyspace and shard.
+func initVariablesForShard(statsKey []string) {
+ starts.Set(statsKey, 0)
+ for _, reason := range stopReasons {
+ key := append(statsKey, string(reason))
+ stops.Set(key, 0)
+ }
+
+ failoverDurationSumMs.Set(statsKey, 0)
+
+ utilizationSum.Set(statsKey, 0)
+ utilizationDryRunSum.Set(statsKey, 0)
+
+ requestsBuffered.Set(statsKey, 0)
+ requestsBufferedDryRun.Set(statsKey, 0)
+ requestsDrained.Set(statsKey, 0)
+ for _, reason := range evictReasons {
+ key := append(statsKey, string(reason))
+ requestsEvicted.Set(key, 0)
+ }
+ for _, reason := range skippedReasons {
+ key := append(statsKey, string(reason))
+ requestsSkipped.Set(key, 0)
+ }
+}
+
// TODO(mberlin): Remove the gauge values below once we store them
// internally and have a /bufferz page where we can show this.
var (
diff --git a/go/vt/vtgate/buffer/variables_test.go b/go/vt/vtgate/buffer/variables_test.go
index 56152257a0d..d9c04f61f17 100644
--- a/go/vt/vtgate/buffer/variables_test.go
+++ b/go/vt/vtgate/buffer/variables_test.go
@@ -1,8 +1,13 @@
package buffer
import (
+ "context"
"flag"
+ "fmt"
+ "strings"
"testing"
+
+ "github.com/youtube/vitess/go/stats"
)
func TestVariables(t *testing.T) {
@@ -16,3 +21,62 @@ func TestVariables(t *testing.T) {
t.Fatalf("BufferSize variable not set during initilization: got = %v, want = %v", got, want)
}
}
+
+func TestVariablesAreInitialized(t *testing.T) {
+ // Create a new buffer and make a call which will create the shardBuffer object.
+ // After that, the variables should be initialized for that shard.
+ b := New()
+ _, err := b.WaitForFailoverEnd(context.Background(), "init_test", "0", nil /* err */)
+ if err != nil {
+ t.Fatalf("buffer should just passthrough and not return an error: %v", err)
+ }
+
+ statsKey := []string{"init_test", "0"}
+ type testCase struct {
+ desc string
+ counter *stats.MultiCounters
+ statsKey []string
+ }
+ testCases := []testCase{
+ {"starts", starts, statsKey},
+ {"failoverDurationSumMs", failoverDurationSumMs, statsKey},
+ {"utilizationSum", utilizationSum, statsKey},
+ {"utilizationDryRunSum", utilizationDryRunSum, statsKey},
+ {"requestsBuffered", requestsBuffered, statsKey},
+ {"requestsBufferedDryRun", requestsBufferedDryRun, statsKey},
+ {"requestsDrained", requestsDrained, statsKey},
+ }
+ for _, r := range stopReasons {
+ testCases = append(testCases, testCase{"stops", stops, append(statsKey, string(r))})
+ }
+ for _, r := range evictReasons {
+ testCases = append(testCases, testCase{"evicted", requestsEvicted, append(statsKey, string(r))})
+ }
+ for _, r := range skippedReasons {
+ testCases = append(testCases, testCase{"skipped", requestsSkipped, append(statsKey, string(r))})
+ }
+
+ for _, tc := range testCases {
+ wantValue := 0
+ if len(tc.statsKey) == 3 && tc.statsKey[2] == string(skippedDisabled) {
+ // The request passed through above was registered as skipped.
+ wantValue = 1
+ }
+ if err := checkEntry(tc.counter, tc.statsKey, wantValue); err != nil {
+ t.Fatalf("variable: %v not correctly initialized: %v", tc.desc, err)
+ }
+ }
+}
+
+func checkEntry(counters *stats.MultiCounters, statsKey []string, want int) error {
+ name := strings.Join(statsKey, ".")
+ got, ok := counters.Counts()[name]
+ if !ok {
+ return fmt.Errorf("no entry for: %v", name)
+ }
+ if got != int64(want) {
+ return fmt.Errorf("wrong value for entry: %v got = %v, want = %v", name, got, want)
+ }
+
+ return nil
+}
diff --git a/go/vt/vtgate/engine/primitive.go b/go/vt/vtgate/engine/primitive.go
index e0532205582..45ff8b4171e 100644
--- a/go/vt/vtgate/engine/primitive.go
+++ b/go/vt/vtgate/engine/primitive.go
@@ -7,8 +7,8 @@ package engine
import (
"github.com/youtube/vitess/go/sqltypes"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
"github.com/youtube/vitess/go/vt/vtgate/queryinfo"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
)
// SeqVarName is a reserved bind var name for sequence values.
diff --git a/go/vt/vtgate/engine/route.go b/go/vt/vtgate/engine/route.go
index ed107a15a76..d67cc464289 100644
--- a/go/vt/vtgate/engine/route.go
+++ b/go/vt/vtgate/engine/route.go
@@ -14,9 +14,9 @@ import (
"github.com/youtube/vitess/go/sqltypes"
querypb "github.com/youtube/vitess/go/vt/proto/query"
"github.com/youtube/vitess/go/vt/sqlannotation"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
"github.com/youtube/vitess/go/vt/vtgate/queryinfo"
"github.com/youtube/vitess/go/vt/vtgate/vindexes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
)
// Route represents the instructions to route a query to
@@ -446,12 +446,7 @@ func (route *Route) execInsertUnsharded(vcursor VCursor, queryConstruct *queryin
if err != nil {
return nil, fmt.Errorf("execInsertUnsharded: %v", err)
}
- if insertid != 0 {
- if result.InsertID != 0 {
- return nil, fmt.Errorf("sequence and db generated a value each for insert")
- }
- result.InsertID = uint64(insertid)
- }
+ result.InsertID = uint64(insertid)
return result, nil
}
@@ -472,9 +467,6 @@ func (route *Route) execInsertSharded(vcursor VCursor, queryConstruct *queryinfo
}
if insertid != 0 {
- if result.InsertID != 0 {
- return nil, fmt.Errorf("sequence and db generated a value each for insert")
- }
result.InsertID = uint64(insertid)
}
diff --git a/go/vt/vtgate/gateway/discoverygateway.go b/go/vt/vtgate/gateway/discoverygateway.go
index 6f80fea5d8c..a427be7c173 100644
--- a/go/vt/vtgate/gateway/discoverygateway.go
+++ b/go/vt/vtgate/gateway/discoverygateway.go
@@ -18,12 +18,11 @@ import (
"github.com/youtube/vitess/go/flagutil"
"github.com/youtube/vitess/go/vt/discovery"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/vterrors"
"github.com/youtube/vitess/go/vt/vtgate/buffer"
"github.com/youtube/vitess/go/vt/vtgate/masterbuffer"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
@@ -162,7 +161,7 @@ func (dg *discoveryGateway) CacheStatus() TabletCacheStatusList {
// the middle of a transaction. While returning the error check if it maybe a result of
// a resharding event, and set the re-resolve bit and let the upper layers
// re-resolve and retry.
-func (dg *discoveryGateway) withRetry(ctx context.Context, target *querypb.Target, conn queryservice.QueryService, name string, inTransaction, isStreaming bool, inner func(ctx context.Context, target *querypb.Target, conn queryservice.QueryService) error) error {
+func (dg *discoveryGateway) withRetry(ctx context.Context, target *querypb.Target, conn queryservice.QueryService, name string, inTransaction bool, inner func(ctx context.Context, target *querypb.Target, conn queryservice.QueryService) (error, bool)) error {
var tabletLastUsed *topodatapb.Tablet
var err error
invalidTablets := make(map[string]bool)
@@ -179,11 +178,10 @@ func (dg *discoveryGateway) withRetry(ctx context.Context, target *querypb.Targe
retryDone, bufferErr := dg.buffer.WaitForFailoverEnd(ctx, target.Keyspace, target.Shard, err)
if bufferErr != nil {
// Buffering failed e.g. buffer is already full. Do not retry.
- err = vterrors.WithSuffix(
- vterrors.WithPrefix(
- "failed to automatically buffer and retry failed request during failover: ",
- bufferErr),
- fmt.Sprintf(" original err (type=%T): %v", err, err))
+ err = vterrors.Errorf(
+ vterrors.Code(bufferErr),
+ "failed to automatically buffer and retry failed request during failover: %v original err (type=%T): %v",
+ bufferErr, err, err)
break
}
@@ -199,7 +197,7 @@ func (dg *discoveryGateway) withRetry(ctx context.Context, target *querypb.Targe
tablets := dg.tsc.GetHealthyTabletStats(target.Keyspace, target.Shard, target.TabletType)
if len(tablets) == 0 {
// fail fast if there is no tablet
- err = vterrors.FromError(vtrpcpb.ErrorCode_INTERNAL_ERROR, fmt.Errorf("no valid tablet"))
+ err = vterrors.New(vtrpcpb.Code_UNAVAILABLE, "no valid tablet")
break
}
shuffleTablets(tablets)
@@ -215,7 +213,7 @@ func (dg *discoveryGateway) withRetry(ctx context.Context, target *querypb.Targe
if ts == nil {
if err == nil {
// do not override error from last attempt.
- err = vterrors.FromError(vtrpcpb.ErrorCode_INTERNAL_ERROR, fmt.Errorf("no available connection"))
+ err = vterrors.New(vtrpcpb.Code_UNAVAILABLE, "no available connection")
}
break
}
@@ -224,7 +222,7 @@ func (dg *discoveryGateway) withRetry(ctx context.Context, target *querypb.Targe
tabletLastUsed = ts.Tablet
conn := dg.hc.GetConnection(ts.Key)
if conn == nil {
- err = vterrors.FromError(vtrpcpb.ErrorCode_INTERNAL_ERROR, fmt.Errorf("no connection for key %v tablet %+v", ts.Key, ts.Tablet))
+ err = vterrors.Errorf(vtrpcpb.Code_UNAVAILABLE, "no connection for key %v tablet %+v", ts.Key, ts.Tablet)
invalidTablets[ts.Key] = true
continue
}
@@ -235,9 +233,10 @@ func (dg *discoveryGateway) withRetry(ctx context.Context, target *querypb.Targe
}
startTime := time.Now()
- err = inner(ctx, ts.Target, conn)
+ var canRetry bool
+ err, canRetry = inner(ctx, ts.Target, conn)
dg.updateStats(target, startTime, err)
- if dg.canRetry(ctx, err, inTransaction, isStreaming) {
+ if canRetry {
invalidTablets[ts.Key] = true
continue
}
@@ -246,45 +245,6 @@ func (dg *discoveryGateway) withRetry(ctx context.Context, target *querypb.Targe
return NewShardError(err, target, tabletLastUsed, inTransaction)
}
-// canRetry determines whether a query can be retried or not.
-// OperationalErrors like retry/fatal are retryable if query is not in a txn.
-// All other errors are non-retryable.
-func (dg *discoveryGateway) canRetry(ctx context.Context, err error, inTransaction, isStreaming bool) bool {
- if err == nil {
- return false
- }
- // Do not retry if ctx.Done() is closed.
- select {
- case <-ctx.Done():
- return false
- default:
- }
- if serverError, ok := err.(*tabletconn.ServerError); ok {
- switch serverError.ServerCode {
- case vtrpcpb.ErrorCode_INTERNAL_ERROR:
- // Do not retry on fatal error for streaming query.
- // For streaming query, vttablet sends:
- // - QUERY_NOT_SERVED, if streaming is not started yet;
- // - INTERNAL_ERROR, if streaming is broken halfway.
- // For non-streaming query, handle as QUERY_NOT_SERVED.
- if isStreaming {
- return false
- }
- fallthrough
- case vtrpcpb.ErrorCode_QUERY_NOT_SERVED:
- // Retry on QUERY_NOT_SERVED and
- // INTERNAL_ERROR if not in a transaction.
- return !inTransaction
- default:
- // Not retry for RESOURCE_EXHAUSTED and normal
- // server errors.
- return false
- }
- }
- // Do not retry on operational error.
- return false
-}
-
func shuffleTablets(tablets []discovery.TabletStats) {
index := 0
length := len(tablets)
diff --git a/go/vt/vtgate/gateway/discoverygateway_test.go b/go/vt/vtgate/gateway/discoverygateway_test.go
index a8196c9f1a0..5e516ad72f0 100644
--- a/go/vt/vtgate/gateway/discoverygateway_test.go
+++ b/go/vt/vtgate/gateway/discoverygateway_test.go
@@ -2,16 +2,15 @@ package gateway
import (
"fmt"
- "reflect"
"testing"
"golang.org/x/net/context"
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/discovery"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/vterrors"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
@@ -129,7 +128,7 @@ func testDiscoveryGatewayGeneric(t *testing.T, streaming bool, f func(dg Gateway
dg.tsc.ResetForTesting()
want := "target: ks.0.replica, no valid tablet"
err := f(dg, target)
- verifyShardError(t, err, want, vtrpcpb.ErrorCode_INTERNAL_ERROR)
+ verifyShardError(t, err, want, vtrpcpb.Code_UNAVAILABLE)
// tablet with error
hc.Reset()
@@ -137,7 +136,7 @@ func testDiscoveryGatewayGeneric(t *testing.T, streaming bool, f func(dg Gateway
hc.AddTestTablet("cell", "1.1.1.1", 1001, keyspace, shard, tabletType, false, 10, fmt.Errorf("no connection"))
want = "target: ks.0.replica, no valid tablet"
err = f(dg, target)
- verifyShardError(t, err, want, vtrpcpb.ErrorCode_INTERNAL_ERROR)
+ verifyShardError(t, err, want, vtrpcpb.Code_UNAVAILABLE)
// tablet without connection
hc.Reset()
@@ -145,20 +144,20 @@ func testDiscoveryGatewayGeneric(t *testing.T, streaming bool, f func(dg Gateway
ep1 := hc.AddTestTablet("cell", "1.1.1.1", 1001, keyspace, shard, tabletType, false, 10, nil).Tablet()
want = fmt.Sprintf(`target: ks.0.replica, no valid tablet`)
err = f(dg, target)
- verifyShardError(t, err, want, vtrpcpb.ErrorCode_INTERNAL_ERROR)
+ verifyShardError(t, err, want, vtrpcpb.Code_UNAVAILABLE)
// retry error
hc.Reset()
dg.tsc.ResetForTesting()
sc1 := hc.AddTestTablet("cell", "1.1.1.1", 1001, keyspace, shard, tabletType, true, 10, nil)
sc2 := hc.AddTestTablet("cell", "1.1.1.1", 1002, keyspace, shard, tabletType, true, 10, nil)
- sc1.MustFailRetry = 1
- sc2.MustFailRetry = 1
+ sc1.MustFailCodes[vtrpcpb.Code_FAILED_PRECONDITION] = 1
+ sc2.MustFailCodes[vtrpcpb.Code_FAILED_PRECONDITION] = 1
ep1 = sc1.Tablet()
ep2 := sc2.Tablet()
wants := map[string]int{
- fmt.Sprintf(`target: ks.0.replica, used tablet: (%+v), retry: err`, ep1): 0,
- fmt.Sprintf(`target: ks.0.replica, used tablet: (%+v), retry: err`, ep2): 0,
+ fmt.Sprintf(`target: ks.0.replica, used tablet: (%+v), FAILED_PRECONDITION error`, ep1): 0,
+ fmt.Sprintf(`target: ks.0.replica, used tablet: (%+v), FAILED_PRECONDITION error`, ep2): 0,
}
err = f(dg, target)
if _, ok := wants[fmt.Sprintf("%v", err)]; !ok {
@@ -170,13 +169,13 @@ func testDiscoveryGatewayGeneric(t *testing.T, streaming bool, f func(dg Gateway
dg.tsc.ResetForTesting()
sc1 = hc.AddTestTablet("cell", "1.1.1.1", 1001, keyspace, shard, tabletType, true, 10, nil)
sc2 = hc.AddTestTablet("cell", "1.1.1.1", 1002, keyspace, shard, tabletType, true, 10, nil)
- sc1.MustFailFatal = 1
- sc2.MustFailFatal = 1
+ sc1.MustFailCodes[vtrpcpb.Code_FAILED_PRECONDITION] = 1
+ sc2.MustFailCodes[vtrpcpb.Code_FAILED_PRECONDITION] = 1
ep1 = sc1.Tablet()
ep2 = sc2.Tablet()
wants = map[string]int{
- fmt.Sprintf(`target: ks.0.replica, used tablet: (%+v), fatal: err`, ep1): 0,
- fmt.Sprintf(`target: ks.0.replica, used tablet: (%+v), fatal: err`, ep2): 0,
+ fmt.Sprintf(`target: ks.0.replica, used tablet: (%+v), FAILED_PRECONDITION error`, ep1): 0,
+ fmt.Sprintf(`target: ks.0.replica, used tablet: (%+v), FAILED_PRECONDITION error`, ep2): 0,
}
err = f(dg, target)
if _, ok := wants[fmt.Sprintf("%v", err)]; !ok {
@@ -187,21 +186,11 @@ func testDiscoveryGatewayGeneric(t *testing.T, streaming bool, f func(dg Gateway
hc.Reset()
dg.tsc.ResetForTesting()
sc1 = hc.AddTestTablet("cell", "1.1.1.1", 1001, keyspace, shard, tabletType, true, 10, nil)
- sc1.MustFailServer = 1
+ sc1.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
ep1 = sc1.Tablet()
- want = fmt.Sprintf(`target: ks.0.replica, used tablet: (%+v), error: err`, ep1)
+ want = fmt.Sprintf(`target: ks.0.replica, used tablet: (%+v), INVALID_ARGUMENT error`, ep1)
err = f(dg, target)
- verifyShardError(t, err, want, vtrpcpb.ErrorCode_BAD_INPUT)
-
- // conn error - no retry
- hc.Reset()
- dg.tsc.ResetForTesting()
- sc1 = hc.AddTestTablet("cell", "1.1.1.1", 1001, keyspace, shard, tabletType, true, 10, nil)
- sc1.MustFailConn = 1
- ep1 = sc1.Tablet()
- want = fmt.Sprintf(`target: ks.0.replica, used tablet: (%+v), error: conn`, ep1)
- err = f(dg, target)
- verifyShardError(t, err, want, vtrpcpb.ErrorCode_UNKNOWN_ERROR)
+ verifyShardError(t, err, want, vtrpcpb.Code_INVALID_ARGUMENT)
// no failure
hc.Reset()
@@ -230,39 +219,35 @@ func testDiscoveryGatewayTransact(t *testing.T, streaming bool, f func(dg Gatewa
dg.tsc.ResetForTesting()
sc1 := hc.AddTestTablet("cell", "1.1.1.1", 1001, keyspace, shard, tabletType, true, 10, nil)
sc2 := hc.AddTestTablet("cell", "1.1.1.1", 1002, keyspace, shard, tabletType, true, 10, nil)
- sc1.MustFailRetry = 1
- sc2.MustFailRetry = 1
+ sc1.MustFailCodes[vtrpcpb.Code_FAILED_PRECONDITION] = 1
+ sc2.MustFailCodes[vtrpcpb.Code_FAILED_PRECONDITION] = 1
ep1 := sc1.Tablet()
ep2 := sc2.Tablet()
wants := map[string]int{
- fmt.Sprintf(`target: ks.0.replica, used tablet: (%+v), retry: err`, ep1): 0,
- fmt.Sprintf(`target: ks.0.replica, used tablet: (%+v), retry: err`, ep2): 0,
+ fmt.Sprintf(`target: ks.0.replica, used tablet: (%+v), FAILED_PRECONDITION error`, ep1): 0,
+ fmt.Sprintf(`target: ks.0.replica, used tablet: (%+v), FAILED_PRECONDITION error`, ep2): 0,
}
err := f(dg, target)
if _, ok := wants[fmt.Sprintf("%v", err)]; !ok {
t.Errorf("wanted error: %+v, got error: %v", wants, err)
}
- // conn error - no retry
+ // server error - no retry
hc.Reset()
dg.tsc.ResetForTesting()
sc1 = hc.AddTestTablet("cell", "1.1.1.1", 1001, keyspace, shard, tabletType, true, 10, nil)
- sc1.MustFailConn = 1
+ sc1.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
ep1 = sc1.Tablet()
- want := fmt.Sprintf(`target: ks.0.replica, used tablet: (%+v), error: conn`, ep1)
+ want := fmt.Sprintf(`target: ks.0.replica, used tablet: (%+v), INVALID_ARGUMENT error`, ep1)
err = f(dg, target)
- verifyShardError(t, err, want, vtrpcpb.ErrorCode_UNKNOWN_ERROR)
+ verifyShardError(t, err, want, vtrpcpb.Code_INVALID_ARGUMENT)
}
-func verifyShardError(t *testing.T, err error, wantErr string, wantCode vtrpcpb.ErrorCode) {
+func verifyShardError(t *testing.T, err error, wantErr string, wantCode vtrpcpb.Code) {
if err == nil || err.Error() != wantErr {
t.Errorf("wanted error: %s, got error: %v", wantErr, err)
}
- if _, ok := err.(*ShardError); !ok {
- t.Errorf("wanted error type *ShardConnError, got error type: %v", reflect.TypeOf(err))
- }
- code := vterrors.RecoverVtErrorCode(err)
- if code != wantCode {
+ if code := vterrors.Code(err); code != wantCode {
t.Errorf("wanted error code: %s, got: %v", wantCode, code)
}
}
diff --git a/go/vt/vtgate/gateway/gateway.go b/go/vt/vtgate/gateway/gateway.go
index 41bbdd6fc2c..56170ecd766 100644
--- a/go/vt/vtgate/gateway/gateway.go
+++ b/go/vt/vtgate/gateway/gateway.go
@@ -14,8 +14,8 @@ import (
"golang.org/x/net/context"
"github.com/youtube/vitess/go/vt/discovery"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
"github.com/youtube/vitess/go/vt/topo"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
)
diff --git a/go/vt/vtgate/gateway/l2vtgategateway.go b/go/vt/vtgate/gateway/l2vtgategateway.go
index 91e6e6a0a02..2e381b22a94 100644
--- a/go/vt/vtgate/gateway/l2vtgategateway.go
+++ b/go/vt/vtgate/gateway/l2vtgategateway.go
@@ -18,14 +18,13 @@ import (
"github.com/youtube/vitess/go/flagutil"
"github.com/youtube/vitess/go/vt/discovery"
"github.com/youtube/vitess/go/vt/key"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletconn"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
- vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)
const (
@@ -205,7 +204,7 @@ func (lg *l2VTGateGateway) getConn(keyspace, shard string) (*l2VTGateConn, error
// the middle of a transaction. While returning the error check if it maybe a result of
// a resharding event, and set the re-resolve bit and let the upper layers
// re-resolve and retry.
-func (lg *l2VTGateGateway) withRetry(ctx context.Context, target *querypb.Target, conn queryservice.QueryService, name string, inTransaction, isStreaming bool, inner func(context.Context, *querypb.Target, queryservice.QueryService) error) error {
+func (lg *l2VTGateGateway) withRetry(ctx context.Context, target *querypb.Target, conn queryservice.QueryService, name string, inTransaction bool, inner func(context.Context, *querypb.Target, queryservice.QueryService) (error, bool)) error {
l2conn, err := lg.getConn(target.Keyspace, target.Shard)
if err != nil {
return fmt.Errorf("no configured destination for %v/%v: %v", target.Keyspace, target.Shard, err)
@@ -213,9 +212,10 @@ func (lg *l2VTGateGateway) withRetry(ctx context.Context, target *querypb.Target
for i := 0; i < lg.retryCount+1; i++ {
startTime := time.Now()
- err = inner(ctx, target, l2conn.conn)
+ var canRetry bool
+ err, canRetry = inner(ctx, target, l2conn.conn)
lg.updateStats(l2conn, target.TabletType, startTime, err)
- if lg.canRetry(ctx, err, inTransaction, isStreaming) {
+ if canRetry {
continue
}
break
@@ -223,45 +223,6 @@ func (lg *l2VTGateGateway) withRetry(ctx context.Context, target *querypb.Target
return NewShardError(err, target, nil, inTransaction)
}
-// canRetry determines whether a query can be retried or not.
-// OperationalErrors like retry/fatal are retryable if query is not in a txn.
-// All other errors are non-retryable.
-func (lg *l2VTGateGateway) canRetry(ctx context.Context, err error, inTransaction, isStreaming bool) bool {
- if err == nil {
- return false
- }
- // Do not retry if ctx.Done() is closed.
- select {
- case <-ctx.Done():
- return false
- default:
- }
- if serverError, ok := err.(*tabletconn.ServerError); ok {
- switch serverError.ServerCode {
- case vtrpcpb.ErrorCode_INTERNAL_ERROR:
- // Do not retry on fatal error for streaming query.
- // For streaming query, vttablet sends:
- // - QUERY_NOT_SERVED, if streaming is not started yet;
- // - INTERNAL_ERROR, if streaming is broken halfway.
- // For non-streaming query, handle as QUERY_NOT_SERVED.
- if isStreaming {
- return false
- }
- fallthrough
- case vtrpcpb.ErrorCode_QUERY_NOT_SERVED:
- // Retry on QUERY_NOT_SERVED and
- // INTERNAL_ERROR if not in a transaction.
- return !inTransaction
- default:
- // Not retry for RESOURCE_EXHAUSTED and normal
- // server errors.
- return false
- }
- }
- // Do not retry on operational error.
- return false
-}
-
func (lg *l2VTGateGateway) updateStats(conn *l2VTGateConn, tabletType topodatapb.TabletType, startTime time.Time, err error) {
elapsed := time.Now().Sub(startTime)
aggr := lg.getStatsAggregator(conn, tabletType)
diff --git a/go/vt/vtgate/gateway/shard_error.go b/go/vt/vtgate/gateway/shard_error.go
index 6ddff542d05..99515b3a99b 100644
--- a/go/vt/vtgate/gateway/shard_error.go
+++ b/go/vt/vtgate/gateway/shard_error.go
@@ -5,62 +5,20 @@
package gateway
import (
- "fmt"
-
"github.com/youtube/vitess/go/vt/topo/topoproto"
"github.com/youtube/vitess/go/vt/vterrors"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
- vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)
-// ShardError is the error about a specific shard.
-// It implements vterrors.VtError.
-type ShardError struct {
- // ShardIdentifier is the keyspace+shard.
- ShardIdentifier string
- // InTransaction indicates if it is inside a transaction.
- InTransaction bool
- // Err preserves the original error, so that we don't need to parse the error string.
- Err error
- // ErrorCode is the error code to use for all the tablet errors in aggregate
- ErrorCode vtrpcpb.ErrorCode
-}
-
-// Error returns the error string.
-func (e *ShardError) Error() string {
- if e.ShardIdentifier == "" {
- return fmt.Sprintf("%v", e.Err)
- }
- return fmt.Sprintf("%s, %v", e.ShardIdentifier, e.Err)
-}
-
-// VtErrorCode returns the underlying Vitess error code.
-// This is part of vterrors.VtError interface.
-func (e *ShardError) VtErrorCode() vtrpcpb.ErrorCode {
- return e.ErrorCode
-}
-
-// NewShardError returns a ShardError which preserves the original
-// error code if possible, adds the connection context and adds a bit
-// to determine whether the keyspace/shard needs to be re-resolved for
-// a potential sharding event (namely, if we were in a transaction).
+// NewShardError returns a new error with the shard info amended.
func NewShardError(in error, target *querypb.Target, tablet *topodatapb.Tablet, inTransaction bool) error {
if in == nil {
return nil
}
- var shardIdentifier string
if tablet != nil {
- shardIdentifier = fmt.Sprintf("target: %s.%s.%s, used tablet: (%+v)", target.Keyspace, target.Shard, topoproto.TabletTypeLString(target.TabletType), tablet)
- } else {
- shardIdentifier = fmt.Sprintf("target: %s.%s.%s", target.Keyspace, target.Shard, topoproto.TabletTypeLString(target.TabletType))
- }
-
- return &ShardError{
- ShardIdentifier: shardIdentifier,
- InTransaction: inTransaction,
- Err: in,
- ErrorCode: vterrors.RecoverVtErrorCode(in),
+ return vterrors.Errorf(vterrors.Code(in), "target: %s.%s.%s, used tablet: (%+v), %v", target.Keyspace, target.Shard, topoproto.TabletTypeLString(target.TabletType), tablet, in)
}
+ return vterrors.Errorf(vterrors.Code(in), "target: %s.%s.%s, %v", target.Keyspace, target.Shard, topoproto.TabletTypeLString(target.TabletType), in)
}
diff --git a/go/vt/vtgate/gatewaytest/grpc_discovery_test.go b/go/vt/vtgate/gatewaytest/grpc_discovery_test.go
index 2aa4084b43c..be7e64994e8 100644
--- a/go/vt/vtgate/gatewaytest/grpc_discovery_test.go
+++ b/go/vt/vtgate/gatewaytest/grpc_discovery_test.go
@@ -12,13 +12,13 @@ import (
"google.golang.org/grpc"
"github.com/youtube/vitess/go/vt/discovery"
- "github.com/youtube/vitess/go/vt/tabletserver/grpcqueryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconntest"
"github.com/youtube/vitess/go/vt/vtgate/gateway"
"github.com/youtube/vitess/go/vt/vtgate/l2vtgate"
+ "github.com/youtube/vitess/go/vt/vttablet/grpcqueryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletconntest"
// We will use gRPC to connect, register the dialer
- _ "github.com/youtube/vitess/go/vt/tabletserver/grpctabletconn"
+ _ "github.com/youtube/vitess/go/vt/vttablet/grpctabletconn"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
)
diff --git a/go/vt/vtgate/gatewaytest/suite.go b/go/vt/vtgate/gatewaytest/suite.go
index 4f496d3725a..2bbf8707a7f 100644
--- a/go/vt/vtgate/gatewaytest/suite.go
+++ b/go/vt/vtgate/gatewaytest/suite.go
@@ -14,12 +14,12 @@ import (
"golang.org/x/net/context"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconntest"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
"github.com/youtube/vitess/go/vt/vtgate/gateway"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletconn"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletconntest"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
diff --git a/go/vt/vtgate/grpcvtgateconn/conn.go b/go/vt/vtgate/grpcvtgateconn/conn.go
index 2f701f039fa..92f4f83572f 100644
--- a/go/vt/vtgate/grpcvtgateconn/conn.go
+++ b/go/vt/vtgate/grpcvtgateconn/conn.go
@@ -15,9 +15,9 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/callerid"
"github.com/youtube/vitess/go/vt/servenv/grpcutils"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
"github.com/youtube/vitess/go/vt/vterrors"
"github.com/youtube/vitess/go/vt/vtgate/vtgateconn"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
"golang.org/x/net/context"
querypb "github.com/youtube/vitess/go/vt/proto/query"
@@ -77,10 +77,10 @@ func (conn *vtgateConn) Execute(ctx context.Context, query string, bindVars map[
}
response, err := conn.c.Execute(ctx, request)
if err != nil {
- return nil, session, vterrors.FromGRPCError(err)
+ return nil, session, vterrors.FromGRPC(err)
}
if response.Error != nil {
- return nil, response.Session, vterrors.FromVtRPCError(response.Error)
+ return nil, response.Session, vterrors.FromVTRPC(response.Error)
}
return sqltypes.Proto3ToResult(response.Result), response.Session, nil
}
@@ -105,10 +105,10 @@ func (conn *vtgateConn) ExecuteShards(ctx context.Context, query string, keyspac
}
response, err := conn.c.ExecuteShards(ctx, request)
if err != nil {
- return nil, session, vterrors.FromGRPCError(err)
+ return nil, session, vterrors.FromGRPC(err)
}
if response.Error != nil {
- return nil, response.Session, vterrors.FromVtRPCError(response.Error)
+ return nil, response.Session, vterrors.FromVTRPC(response.Error)
}
return sqltypes.Proto3ToResult(response.Result), response.Session, nil
}
@@ -133,10 +133,10 @@ func (conn *vtgateConn) ExecuteKeyspaceIds(ctx context.Context, query string, ke
}
response, err := conn.c.ExecuteKeyspaceIds(ctx, request)
if err != nil {
- return nil, session, vterrors.FromGRPCError(err)
+ return nil, session, vterrors.FromGRPC(err)
}
if response.Error != nil {
- return nil, response.Session, vterrors.FromVtRPCError(response.Error)
+ return nil, response.Session, vterrors.FromVTRPC(response.Error)
}
return sqltypes.Proto3ToResult(response.Result), response.Session, nil
}
@@ -161,10 +161,10 @@ func (conn *vtgateConn) ExecuteKeyRanges(ctx context.Context, query string, keys
}
response, err := conn.c.ExecuteKeyRanges(ctx, request)
if err != nil {
- return nil, session, vterrors.FromGRPCError(err)
+ return nil, session, vterrors.FromGRPC(err)
}
if response.Error != nil {
- return nil, response.Session, vterrors.FromVtRPCError(response.Error)
+ return nil, response.Session, vterrors.FromVTRPC(response.Error)
}
return sqltypes.Proto3ToResult(response.Result), response.Session, nil
}
@@ -190,10 +190,10 @@ func (conn *vtgateConn) ExecuteEntityIds(ctx context.Context, query string, keys
}
response, err := conn.c.ExecuteEntityIds(ctx, request)
if err != nil {
- return nil, session, vterrors.FromGRPCError(err)
+ return nil, session, vterrors.FromGRPC(err)
}
if response.Error != nil {
- return nil, response.Session, vterrors.FromVtRPCError(response.Error)
+ return nil, response.Session, vterrors.FromVTRPC(response.Error)
}
return sqltypes.Proto3ToResult(response.Result), response.Session, nil
}
@@ -218,10 +218,10 @@ func (conn *vtgateConn) ExecuteBatch(ctx context.Context, queryList []string, bi
}
response, err := conn.c.ExecuteBatch(ctx, request)
if err != nil {
- return nil, session, vterrors.FromGRPCError(err)
+ return nil, session, vterrors.FromGRPC(err)
}
if response.Error != nil {
- return nil, response.Session, vterrors.FromVtRPCError(response.Error)
+ return nil, response.Session, vterrors.FromVTRPC(response.Error)
}
return sqltypes.Proto3ToQueryReponses(response.Results), response.Session, nil
}
@@ -241,10 +241,10 @@ func (conn *vtgateConn) ExecuteBatchShards(ctx context.Context, queries []*vtgat
}
response, err := conn.c.ExecuteBatchShards(ctx, request)
if err != nil {
- return nil, session, vterrors.FromGRPCError(err)
+ return nil, session, vterrors.FromGRPC(err)
}
if response.Error != nil {
- return nil, response.Session, vterrors.FromVtRPCError(response.Error)
+ return nil, response.Session, vterrors.FromVTRPC(response.Error)
}
return sqltypes.Proto3ToResults(response.Results), response.Session, nil
}
@@ -264,10 +264,10 @@ func (conn *vtgateConn) ExecuteBatchKeyspaceIds(ctx context.Context, queries []*
}
response, err := conn.c.ExecuteBatchKeyspaceIds(ctx, request)
if err != nil {
- return nil, session, vterrors.FromGRPCError(err)
+ return nil, session, vterrors.FromGRPC(err)
}
if response.Error != nil {
- return nil, response.Session, vterrors.FromVtRPCError(response.Error)
+ return nil, response.Session, vterrors.FromVTRPC(response.Error)
}
return sqltypes.Proto3ToResults(response.Results), response.Session, nil
}
@@ -280,10 +280,7 @@ type streamExecuteAdapter struct {
func (a *streamExecuteAdapter) Recv() (*sqltypes.Result, error) {
qr, err := a.recv()
if err != nil {
- if err != io.EOF {
- err = vterrors.FromGRPCError(err)
- }
- return nil, err
+ return nil, vterrors.FromGRPC(err)
}
if a.fields == nil {
a.fields = qr.Fields
@@ -305,7 +302,7 @@ func (conn *vtgateConn) StreamExecute(ctx context.Context, query string, bindVar
}
stream, err := conn.c.StreamExecute(ctx, req)
if err != nil {
- return nil, vterrors.FromGRPCError(err)
+ return nil, vterrors.FromGRPC(err)
}
return &streamExecuteAdapter{
recv: func() (*querypb.QueryResult, error) {
@@ -333,7 +330,7 @@ func (conn *vtgateConn) StreamExecuteShards(ctx context.Context, query string, k
}
stream, err := conn.c.StreamExecuteShards(ctx, req)
if err != nil {
- return nil, vterrors.FromGRPCError(err)
+ return nil, vterrors.FromGRPC(err)
}
return &streamExecuteAdapter{
recv: func() (*querypb.QueryResult, error) {
@@ -361,7 +358,7 @@ func (conn *vtgateConn) StreamExecuteKeyRanges(ctx context.Context, query string
}
stream, err := conn.c.StreamExecuteKeyRanges(ctx, req)
if err != nil {
- return nil, vterrors.FromGRPCError(err)
+ return nil, vterrors.FromGRPC(err)
}
return &streamExecuteAdapter{
recv: func() (*querypb.QueryResult, error) {
@@ -389,7 +386,7 @@ func (conn *vtgateConn) StreamExecuteKeyspaceIds(ctx context.Context, query stri
}
stream, err := conn.c.StreamExecuteKeyspaceIds(ctx, req)
if err != nil {
- return nil, vterrors.FromGRPCError(err)
+ return nil, vterrors.FromGRPC(err)
}
return &streamExecuteAdapter{
recv: func() (*querypb.QueryResult, error) {
@@ -409,7 +406,7 @@ func (conn *vtgateConn) Begin(ctx context.Context, singledb bool) (interface{},
}
response, err := conn.c.Begin(ctx, request)
if err != nil {
- return nil, vterrors.FromGRPCError(err)
+ return nil, vterrors.FromGRPC(err)
}
return response.Session, nil
}
@@ -421,7 +418,7 @@ func (conn *vtgateConn) Commit(ctx context.Context, session interface{}, twopc b
Atomic: twopc,
}
_, err := conn.c.Commit(ctx, request)
- return vterrors.FromGRPCError(err)
+ return vterrors.FromGRPC(err)
}
func (conn *vtgateConn) Rollback(ctx context.Context, session interface{}) error {
@@ -430,7 +427,7 @@ func (conn *vtgateConn) Rollback(ctx context.Context, session interface{}) error
Session: session.(*vtgatepb.Session),
}
_, err := conn.c.Rollback(ctx, request)
- return vterrors.FromGRPCError(err)
+ return vterrors.FromGRPC(err)
}
func (conn *vtgateConn) ResolveTransaction(ctx context.Context, dtid string) error {
@@ -439,7 +436,7 @@ func (conn *vtgateConn) ResolveTransaction(ctx context.Context, dtid string) err
Dtid: dtid,
}
_, err := conn.c.ResolveTransaction(ctx, request)
- return vterrors.FromGRPCError(err)
+ return vterrors.FromGRPC(err)
}
func (conn *vtgateConn) MessageStream(ctx context.Context, keyspace string, shard string, keyRange *topodatapb.KeyRange, name string, callback func(*sqltypes.Result) error) error {
@@ -452,14 +449,14 @@ func (conn *vtgateConn) MessageStream(ctx context.Context, keyspace string, shar
}
stream, err := conn.c.MessageStream(ctx, request)
if err != nil {
- return vterrors.FromGRPCError(err)
+ return vterrors.FromGRPC(err)
}
var fields []*querypb.Field
for {
r, err := stream.Recv()
if err != nil {
if err != io.EOF {
- return vterrors.FromGRPCError(err)
+ return vterrors.FromGRPC(err)
}
return nil
}
@@ -482,7 +479,7 @@ func (conn *vtgateConn) MessageAck(ctx context.Context, keyspace string, name st
}
r, err := conn.c.MessageAck(ctx, request)
if err != nil {
- return 0, vterrors.FromGRPCError(err)
+ return 0, vterrors.FromGRPC(err)
}
return int64(r.Result.RowsAffected), nil
}
@@ -512,7 +509,7 @@ func (conn *vtgateConn) SplitQuery(
}
response, err := conn.c.SplitQuery(ctx, request)
if err != nil {
- return nil, vterrors.FromGRPCError(err)
+ return nil, vterrors.FromGRPC(err)
}
return response.Splits, nil
}
@@ -523,7 +520,7 @@ func (conn *vtgateConn) GetSrvKeyspace(ctx context.Context, keyspace string) (*t
}
response, err := conn.c.GetSrvKeyspace(ctx, request)
if err != nil {
- return nil, vterrors.FromGRPCError(err)
+ return nil, vterrors.FromGRPC(err)
}
return response.SrvKeyspace, nil
}
@@ -535,10 +532,7 @@ type updateStreamAdapter struct {
func (a *updateStreamAdapter) Recv() (*querypb.StreamEvent, int64, error) {
r, err := a.stream.Recv()
if err != nil {
- if err != io.EOF {
- err = vterrors.FromGRPCError(err)
- }
- return nil, 0, err
+ return nil, 0, vterrors.FromGRPC(err)
}
return r.Event, r.ResumeTimestamp, nil
}
@@ -555,7 +549,7 @@ func (conn *vtgateConn) UpdateStream(ctx context.Context, keyspace string, shard
}
stream, err := conn.c.UpdateStream(ctx, req)
if err != nil {
- return nil, vterrors.FromGRPCError(err)
+ return nil, vterrors.FromGRPC(err)
}
return &updateStreamAdapter{
stream: stream,
diff --git a/go/vt/vtgate/grpcvtgateservice/server.go b/go/vt/vtgate/grpcvtgateservice/server.go
index e67c5335ef3..aefd83127de 100644
--- a/go/vt/vtgate/grpcvtgateservice/server.go
+++ b/go/vt/vtgate/grpcvtgateservice/server.go
@@ -16,10 +16,10 @@ import (
"github.com/youtube/vitess/go/vt/callerid"
"github.com/youtube/vitess/go/vt/callinfo"
"github.com/youtube/vitess/go/vt/servenv"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
"github.com/youtube/vitess/go/vt/vterrors"
"github.com/youtube/vitess/go/vt/vtgate"
"github.com/youtube/vitess/go/vt/vtgate/vtgateservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
"golang.org/x/net/context"
querypb "github.com/youtube/vitess/go/vt/proto/query"
@@ -89,13 +89,13 @@ func (vtg *VTGate) Execute(ctx context.Context, request *vtgatepb.ExecuteRequest
ctx = withCallerIDContext(ctx, request.CallerId)
bv, err := querytypes.Proto3ToBindVariables(request.Query.BindVariables)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
result, err := vtg.server.Execute(ctx, string(request.Query.Sql), bv, request.Keyspace, request.TabletType, request.Session, request.NotInTransaction, request.Options)
return &vtgatepb.ExecuteResponse{
Result: sqltypes.ResultToProto3(result),
Session: request.Session,
- Error: vterrors.VtRPCErrorFromVtError(err),
+ Error: vterrors.ToVTRPC(err),
}, nil
}
@@ -105,7 +105,7 @@ func (vtg *VTGate) ExecuteShards(ctx context.Context, request *vtgatepb.ExecuteS
ctx = withCallerIDContext(ctx, request.CallerId)
bv, err := querytypes.Proto3ToBindVariables(request.Query.BindVariables)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
result, err := vtg.server.ExecuteShards(ctx,
string(request.Query.Sql),
@@ -119,7 +119,7 @@ func (vtg *VTGate) ExecuteShards(ctx context.Context, request *vtgatepb.ExecuteS
return &vtgatepb.ExecuteShardsResponse{
Result: sqltypes.ResultToProto3(result),
Session: request.Session,
- Error: vterrors.VtRPCErrorFromVtError(err),
+ Error: vterrors.ToVTRPC(err),
}, nil
}
@@ -129,7 +129,7 @@ func (vtg *VTGate) ExecuteKeyspaceIds(ctx context.Context, request *vtgatepb.Exe
ctx = withCallerIDContext(ctx, request.CallerId)
bv, err := querytypes.Proto3ToBindVariables(request.Query.BindVariables)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
result, err := vtg.server.ExecuteKeyspaceIds(ctx,
string(request.Query.Sql),
@@ -143,7 +143,7 @@ func (vtg *VTGate) ExecuteKeyspaceIds(ctx context.Context, request *vtgatepb.Exe
return &vtgatepb.ExecuteKeyspaceIdsResponse{
Result: sqltypes.ResultToProto3(result),
Session: request.Session,
- Error: vterrors.VtRPCErrorFromVtError(err),
+ Error: vterrors.ToVTRPC(err),
}, nil
}
@@ -153,7 +153,7 @@ func (vtg *VTGate) ExecuteKeyRanges(ctx context.Context, request *vtgatepb.Execu
ctx = withCallerIDContext(ctx, request.CallerId)
bv, err := querytypes.Proto3ToBindVariables(request.Query.BindVariables)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
result, err := vtg.server.ExecuteKeyRanges(ctx,
string(request.Query.Sql),
@@ -167,7 +167,7 @@ func (vtg *VTGate) ExecuteKeyRanges(ctx context.Context, request *vtgatepb.Execu
return &vtgatepb.ExecuteKeyRangesResponse{
Result: sqltypes.ResultToProto3(result),
Session: request.Session,
- Error: vterrors.VtRPCErrorFromVtError(err),
+ Error: vterrors.ToVTRPC(err),
}, nil
}
@@ -177,7 +177,7 @@ func (vtg *VTGate) ExecuteEntityIds(ctx context.Context, request *vtgatepb.Execu
ctx = withCallerIDContext(ctx, request.CallerId)
bv, err := querytypes.Proto3ToBindVariables(request.Query.BindVariables)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
result, err := vtg.server.ExecuteEntityIds(ctx,
string(request.Query.Sql),
@@ -192,7 +192,7 @@ func (vtg *VTGate) ExecuteEntityIds(ctx context.Context, request *vtgatepb.Execu
return &vtgatepb.ExecuteEntityIdsResponse{
Result: sqltypes.ResultToProto3(result),
Session: request.Session,
- Error: vterrors.VtRPCErrorFromVtError(err),
+ Error: vterrors.ToVTRPC(err),
}, nil
}
@@ -206,7 +206,7 @@ func (vtg *VTGate) ExecuteBatch(ctx context.Context, request *vtgatepb.ExecuteBa
for queryNum, query := range request.Queries {
bv, err := querytypes.Proto3ToBindVariables(query.BindVariables)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
sqlQueries[queryNum] = query.Sql
bindVars[queryNum] = bv
@@ -215,7 +215,7 @@ func (vtg *VTGate) ExecuteBatch(ctx context.Context, request *vtgatepb.ExecuteBa
return &vtgatepb.ExecuteBatchResponse{
Results: sqltypes.QueryResponsesToProto3(results),
Session: request.Session,
- Error: vterrors.VtRPCErrorFromVtError(err),
+ Error: vterrors.ToVTRPC(err),
}, nil
}
@@ -232,7 +232,7 @@ func (vtg *VTGate) ExecuteBatchShards(ctx context.Context, request *vtgatepb.Exe
return &vtgatepb.ExecuteBatchShardsResponse{
Results: sqltypes.ResultsToProto3(result),
Session: request.Session,
- Error: vterrors.VtRPCErrorFromVtError(err),
+ Error: vterrors.ToVTRPC(err),
}, nil
}
@@ -250,7 +250,7 @@ func (vtg *VTGate) ExecuteBatchKeyspaceIds(ctx context.Context, request *vtgatep
return &vtgatepb.ExecuteBatchKeyspaceIdsResponse{
Results: sqltypes.ResultsToProto3(result),
Session: request.Session,
- Error: vterrors.VtRPCErrorFromVtError(err),
+ Error: vterrors.ToVTRPC(err),
}, nil
}
@@ -260,7 +260,7 @@ func (vtg *VTGate) StreamExecute(request *vtgatepb.StreamExecuteRequest, stream
ctx := withCallerIDContext(stream.Context(), request.CallerId)
bv, err := querytypes.Proto3ToBindVariables(request.Query.BindVariables)
if err != nil {
- return vterrors.ToGRPCError(err)
+ return vterrors.ToGRPC(err)
}
vtgErr := vtg.server.StreamExecute(ctx,
string(request.Query.Sql),
@@ -273,7 +273,7 @@ func (vtg *VTGate) StreamExecute(request *vtgatepb.StreamExecuteRequest, stream
Result: sqltypes.ResultToProto3(value),
})
})
- return vterrors.ToGRPCError(vtgErr)
+ return vterrors.ToGRPC(vtgErr)
}
// StreamExecuteShards is the RPC version of vtgateservice.VTGateService method
@@ -282,7 +282,7 @@ func (vtg *VTGate) StreamExecuteShards(request *vtgatepb.StreamExecuteShardsRequ
ctx := withCallerIDContext(stream.Context(), request.CallerId)
bv, err := querytypes.Proto3ToBindVariables(request.Query.BindVariables)
if err != nil {
- return vterrors.ToGRPCError(err)
+ return vterrors.ToGRPC(err)
}
vtgErr := vtg.server.StreamExecuteShards(ctx,
string(request.Query.Sql),
@@ -296,7 +296,7 @@ func (vtg *VTGate) StreamExecuteShards(request *vtgatepb.StreamExecuteShardsRequ
Result: sqltypes.ResultToProto3(value),
})
})
- return vterrors.ToGRPCError(vtgErr)
+ return vterrors.ToGRPC(vtgErr)
}
// StreamExecuteKeyspaceIds is the RPC version of
@@ -306,7 +306,7 @@ func (vtg *VTGate) StreamExecuteKeyspaceIds(request *vtgatepb.StreamExecuteKeysp
ctx := withCallerIDContext(stream.Context(), request.CallerId)
bv, err := querytypes.Proto3ToBindVariables(request.Query.BindVariables)
if err != nil {
- return vterrors.ToGRPCError(err)
+ return vterrors.ToGRPC(err)
}
vtgErr := vtg.server.StreamExecuteKeyspaceIds(ctx,
string(request.Query.Sql),
@@ -320,7 +320,7 @@ func (vtg *VTGate) StreamExecuteKeyspaceIds(request *vtgatepb.StreamExecuteKeysp
Result: sqltypes.ResultToProto3(value),
})
})
- return vterrors.ToGRPCError(vtgErr)
+ return vterrors.ToGRPC(vtgErr)
}
// StreamExecuteKeyRanges is the RPC version of
@@ -330,7 +330,7 @@ func (vtg *VTGate) StreamExecuteKeyRanges(request *vtgatepb.StreamExecuteKeyRang
ctx := withCallerIDContext(stream.Context(), request.CallerId)
bv, err := querytypes.Proto3ToBindVariables(request.Query.BindVariables)
if err != nil {
- return vterrors.ToGRPCError(err)
+ return vterrors.ToGRPC(err)
}
vtgErr := vtg.server.StreamExecuteKeyRanges(ctx,
string(request.Query.Sql),
@@ -344,7 +344,7 @@ func (vtg *VTGate) StreamExecuteKeyRanges(request *vtgatepb.StreamExecuteKeyRang
Result: sqltypes.ResultToProto3(value),
})
})
- return vterrors.ToGRPCError(vtgErr)
+ return vterrors.ToGRPC(vtgErr)
}
// Begin is the RPC version of vtgateservice.VTGateService method
@@ -357,7 +357,7 @@ func (vtg *VTGate) Begin(ctx context.Context, request *vtgatepb.BeginRequest) (r
Session: session,
}, nil
}
- return nil, vterrors.ToGRPCError(vtgErr)
+ return nil, vterrors.ToGRPC(vtgErr)
}
// Commit is the RPC version of vtgateservice.VTGateService method
@@ -369,7 +369,7 @@ func (vtg *VTGate) Commit(ctx context.Context, request *vtgatepb.CommitRequest)
if vtgErr == nil {
return response, nil
}
- return nil, vterrors.ToGRPCError(vtgErr)
+ return nil, vterrors.ToGRPC(vtgErr)
}
// Rollback is the RPC version of vtgateservice.VTGateService method
@@ -381,7 +381,7 @@ func (vtg *VTGate) Rollback(ctx context.Context, request *vtgatepb.RollbackReque
if vtgErr == nil {
return response, nil
}
- return nil, vterrors.ToGRPCError(vtgErr)
+ return nil, vterrors.ToGRPC(vtgErr)
}
// ResolveTransaction is the RPC version of vtgateservice.VTGateService method
@@ -393,7 +393,7 @@ func (vtg *VTGate) ResolveTransaction(ctx context.Context, request *vtgatepb.Res
if vtgErr == nil {
return response, nil
}
- return nil, vterrors.ToGRPCError(vtgErr)
+ return nil, vterrors.ToGRPC(vtgErr)
}
// MessageStream is the RPC version of vtgateservice.VTGateService method
@@ -405,7 +405,7 @@ func (vtg *VTGate) MessageStream(request *vtgatepb.MessageStreamRequest, stream
Result: sqltypes.ResultToProto3(qr),
})
})
- return vterrors.ToGRPCError(vtgErr)
+ return vterrors.ToGRPC(vtgErr)
}
// MessageAck is the RPC version of vtgateservice.VTGateService method
@@ -414,7 +414,7 @@ func (vtg *VTGate) MessageAck(ctx context.Context, request *vtgatepb.MessageAckR
ctx = withCallerIDContext(ctx, request.CallerId)
count, vtgErr := vtg.server.MessageAck(ctx, request.Keyspace, request.Name, request.Ids)
if vtgErr != nil {
- return nil, vterrors.ToGRPCError(vtgErr)
+ return nil, vterrors.ToGRPC(vtgErr)
}
return &querypb.MessageAckResponse{
Result: &querypb.QueryResult{
@@ -430,7 +430,7 @@ func (vtg *VTGate) SplitQuery(ctx context.Context, request *vtgatepb.SplitQueryR
ctx = withCallerIDContext(ctx, request.CallerId)
bv, err := querytypes.Proto3ToBindVariables(request.Query.BindVariables)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
splits, vtgErr := vtg.server.SplitQuery(
ctx,
@@ -442,7 +442,7 @@ func (vtg *VTGate) SplitQuery(ctx context.Context, request *vtgatepb.SplitQueryR
request.NumRowsPerQueryPart,
request.Algorithm)
if vtgErr != nil {
- return nil, vterrors.ToGRPCError(vtgErr)
+ return nil, vterrors.ToGRPC(vtgErr)
}
return &vtgatepb.SplitQueryResponse{
Splits: splits,
@@ -454,7 +454,7 @@ func (vtg *VTGate) GetSrvKeyspace(ctx context.Context, request *vtgatepb.GetSrvK
defer vtg.server.HandlePanic(&err)
sk, vtgErr := vtg.server.GetSrvKeyspace(ctx, request.Keyspace)
if vtgErr != nil {
- return nil, vterrors.ToGRPCError(vtgErr)
+ return nil, vterrors.ToGRPC(vtgErr)
}
return &vtgatepb.GetSrvKeyspaceResponse{
SrvKeyspace: sk,
@@ -478,7 +478,7 @@ func (vtg *VTGate) UpdateStream(request *vtgatepb.UpdateStreamRequest, stream vt
ResumeTimestamp: resumeTimestamp,
})
})
- return vterrors.ToGRPCError(vtgErr)
+ return vterrors.ToGRPC(vtgErr)
}
func init() {
diff --git a/go/vt/vtgate/l2vtgate/l2vtgate.go b/go/vt/vtgate/l2vtgate/l2vtgate.go
index 4da4fcd5410..cbfc6b6fdf3 100644
--- a/go/vt/vtgate/l2vtgate/l2vtgate.go
+++ b/go/vt/vtgate/l2vtgate/l2vtgate.go
@@ -15,11 +15,11 @@ import (
"github.com/youtube/vitess/go/stats"
"github.com/youtube/vitess/go/vt/discovery"
"github.com/youtube/vitess/go/vt/servenv"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
"github.com/youtube/vitess/go/vt/vterrors"
"github.com/youtube/vitess/go/vt/vtgate/gateway"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
@@ -65,12 +65,13 @@ func Init(hc discovery.HealthCheck, topoServer topo.Server, serv topo.SrvTopoSer
}
l2VTGate.QueryService = queryservice.Wrap(
gw,
- func(ctx context.Context, target *querypb.Target, conn queryservice.QueryService, name string, inTransaction, isStreaming bool, inner func(context.Context, *querypb.Target, queryservice.QueryService) error) (err error) {
+ func(ctx context.Context, target *querypb.Target, conn queryservice.QueryService, name string, inTransaction bool, inner func(context.Context, *querypb.Target, queryservice.QueryService) (error, bool)) (err error) {
if target != nil {
startTime, statsKey := l2VTGate.startAction(name, target)
defer l2VTGate.endAction(startTime, statsKey, &err)
}
- return inner(ctx, target, conn)
+ err, _ = inner(ctx, target, conn)
+ return err
},
)
servenv.OnRun(func() {
@@ -97,8 +98,8 @@ func (l *L2VTGate) endAction(startTime time.Time, statsKey []string, err *error)
// Don't increment the error counter for duplicate
// keys or bad queries, as those errors are caused by
// client queries and are not VTGate's fault.
- ec := vterrors.RecoverVtErrorCode(*err)
- if ec != vtrpcpb.ErrorCode_INTEGRITY_ERROR && ec != vtrpcpb.ErrorCode_BAD_INPUT {
+ ec := vterrors.Code(*err)
+ if ec != vtrpcpb.Code_ALREADY_EXISTS && ec != vtrpcpb.Code_INVALID_ARGUMENT {
l.tabletCallErrorCount.Add(statsKey, 1)
}
}
diff --git a/go/vt/vtgate/masterbuffer/masterbuffer.go b/go/vt/vtgate/masterbuffer/masterbuffer.go
index 5b2801b2323..c96569b2918 100644
--- a/go/vt/vtgate/masterbuffer/masterbuffer.go
+++ b/go/vt/vtgate/masterbuffer/masterbuffer.go
@@ -16,7 +16,6 @@ but will not return transient errors during the buffering time.
package masterbuffer
import (
- "errors"
"flag"
"sync"
"time"
@@ -47,10 +46,7 @@ var (
var timeSleep = time.Sleep
// errBufferFull is the error returned a buffer request is rejected because the buffer is full.
-var errBufferFull = vterrors.FromError(
- vtrpcpb.ErrorCode_TRANSIENT_ERROR,
- errors.New("master request buffer full, rejecting request"),
-)
+var errBufferFull = vterrors.New(vtrpcpb.Code_UNAVAILABLE, "master request buffer full, rejecting request")
// FakeBuffer will pretend to buffer master requests in VTGate.
// Requests *will NOT actually be buffered*, they will just be delayed.
diff --git a/go/vt/vtgate/plugin_mysql_server.go b/go/vt/vtgate/plugin_mysql_server.go
index 6a0d2e5c2f7..fd9973c0d30 100644
--- a/go/vt/vtgate/plugin_mysql_server.go
+++ b/go/vt/vtgate/plugin_mysql_server.go
@@ -12,6 +12,7 @@ import (
"github.com/youtube/vitess/go/mysqlconn"
"github.com/youtube/vitess/go/sqldb"
"github.com/youtube/vitess/go/sqltypes"
+ "github.com/youtube/vitess/go/vt/callerid"
"github.com/youtube/vitess/go/vt/servenv"
querypb "github.com/youtube/vitess/go/vt/proto/query"
@@ -20,9 +21,30 @@ import (
)
var (
- mysqlServerPort = flag.Int("mysql_server_port", 0, "If set, also listen for MySQL binary protocol connections on this port.")
+ mysqlServerPort = flag.Int("mysql_server_port", 0, "If set, also listen for MySQL binary protocol connections on this port.")
+ mysqlAuthServerImpl = flag.String("mysql_auth_server_impl", "config", "Which auth server implementation to use.")
+ mysqlAuthServerConfigFile = flag.String("mysql_auth_server_config_file", "", "JSON File to read the users/passwords from.")
+ mysqlAuthServerConfigString = flag.String("mysql_auth_server_config_string", "", "JSON representation of the users/passwords config.")
+ mysqlAllowClearTextWithoutTLS = flag.Bool("mysql_allow_clear_text_without_tls", false, "If set, the server will allow the use of a clear text password over non-SSL connections.")
)
+// Handles initializing the AuthServerConfig if necessary.
+func initAuthServerConfig() {
+ // Check parameters.
+ if *mysqlAuthServerConfigFile == "" && *mysqlAuthServerConfigString == "" {
+ // Not configured, nothing to do.
+ log.Infof("Not configuring AuthServerConfig, as mysql_auth_server_config_file and mysql_auth_server_config_string are empty")
+ return
+ }
+ if *mysqlAuthServerConfigFile != "" && *mysqlAuthServerConfigString != "" {
+ // Both parameters specified, can only use on.
+ log.Fatalf("Both mysql_auth_server_config_file and mysql_auth_server_config_string specified, can only use one.")
+ }
+
+ // Create and register auth server.
+ mysqlconn.RegisterAuthServerConfigFromParams(*mysqlAuthServerConfigFile, *mysqlAuthServerConfigString)
+}
+
// vtgateHandler implements the Listener interface.
// It stores the Session in the ClientData of a Connection, if a transaction
// is in progress.
@@ -103,12 +125,24 @@ func (vh *vtgateHandler) rollback(ctx context.Context, c *mysqlconn.Conn) (*sqlt
}
func (vh *vtgateHandler) ComQuery(c *mysqlconn.Conn, query string) (*sqltypes.Result, error) {
- // FIXME(alainjobart): do something better for context.
- // Include some kind of callerid reference, using the
- // authenticated user.
- // Add some kind of timeout too.
+ // FIXME(alainjobart): Add some kind of timeout to the context.
ctx := context.Background()
+ // Fill in the ImmediateCallerID with the UserData returned by
+ // the AuthServer plugin for that user. If nothing was
+ // returned, use the User. This lets the plugin map a MySQL
+ // user used for authentication to a Vitess User used for
+ // Table ACLs and Vitess authentication in general.
+ im := callerid.NewImmediateCallerID(c.UserData)
+ if c.UserData == "" {
+ im.Username = c.User
+ }
+ ef := callerid.NewEffectiveCallerID(
+ c.User, /* principal: who */
+ c.RemoteAddr().String(), /* component: running client process */
+ "VTGate MySQL Connector" /* subcomponent: part of the client */)
+ ctx = callerid.NewContext(ctx, ef, im)
+
// FIXME(alainjobart) would be good to have the parser understand this.
switch {
case strings.EqualFold(query, "begin"):
@@ -117,6 +151,9 @@ func (vh *vtgateHandler) ComQuery(c *mysqlconn.Conn, query string) (*sqltypes.Re
return vh.commit(ctx, c)
case strings.EqualFold(query, "rollback"):
return vh.rollback(ctx, c)
+ case strings.EqualFold(query, "set autocommit=0"):
+ // This is done by the python MySQL connector, we ignore it.
+ return &sqltypes.Result{}, nil
default:
// Grab the current session, if any.
var session *vtgatepb.Session
@@ -146,18 +183,18 @@ func init() {
return
}
+ // Initialize the config AuthServer if necessary.
+ initAuthServerConfig()
+ authServer := mysqlconn.GetAuthServer(*mysqlAuthServerImpl)
+
// Create a Listener.
var err error
vh := newVtgateHandler(rpcVTGate)
- listener, err = mysqlconn.NewListener("tcp", net.JoinHostPort("", fmt.Sprintf("%v", *mysqlServerPort)), vh)
+ listener, err = mysqlconn.NewListener("tcp", net.JoinHostPort("", fmt.Sprintf("%v", *mysqlServerPort)), authServer, vh)
if err != nil {
log.Fatalf("mysqlconn.NewListener failed: %v", err)
}
-
- // Add fake users for now.
- // FIXME(alainjobart): add a config file with users
- // and passwords.
- listener.PasswordMap["mysql_user"] = "mysql_password"
+ listener.AllowClearTextWithoutTLS = *mysqlAllowClearTextWithoutTLS
// And starts listening.
go func() {
diff --git a/go/vt/vtgate/query_executor.go b/go/vt/vtgate/query_executor.go
index 2c68454d1f6..2751334b047 100644
--- a/go/vt/vtgate/query_executor.go
+++ b/go/vt/vtgate/query_executor.go
@@ -12,7 +12,7 @@ import (
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
vtgatepb "github.com/youtube/vitess/go/vt/proto/vtgate"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
)
type queryExecutor struct {
diff --git a/go/vt/vtgate/resolver.go b/go/vt/vtgate/resolver.go
index 60da1398357..8afc2f99838 100644
--- a/go/vt/vtgate/resolver.go
+++ b/go/vt/vtgate/resolver.go
@@ -54,14 +54,7 @@ func NewResolver(serv topo.SrvTopoServer, cell string, sc *ScatterConn) *Resolve
// isRetryableError will be true if the error should be retried.
func isRetryableError(err error) bool {
- switch e := err.(type) {
- case *ScatterConnError:
- return e.Retryable
- case *gateway.ShardError:
- return e.ErrorCode == vtrpcpb.ErrorCode_QUERY_NOT_SERVED
- default:
- return false
- }
+ return vterrors.Code(err) == vtrpcpb.Code_FAILED_PRECONDITION
}
// ExecuteKeyspaceIds executes a non-streaming query based on KeyspaceIds.
@@ -70,10 +63,7 @@ func isRetryableError(err error) bool {
// on being able to uniquely route a write.
func (res *Resolver) ExecuteKeyspaceIds(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, keyspaceIds [][]byte, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, options *querypb.ExecuteOptions) (*sqltypes.Result, error) {
if sqlannotation.IsDML(sql) && len(keyspaceIds) > 1 {
- return nil, vterrors.FromError(
- vtrpcpb.ErrorCode_BAD_INPUT,
- fmt.Errorf("DML should not span multiple keyspace_ids"),
- )
+ return nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "DML should not span multiple keyspace_ids")
}
mapToShards := func(k string) (string, []string, error) {
return mapKeyspaceIdsToShards(
diff --git a/go/vt/vtgate/resolver_test.go b/go/vt/vtgate/resolver_test.go
index 85a836639b6..b485f22e35c 100644
--- a/go/vt/vtgate/resolver_test.go
+++ b/go/vt/vtgate/resolver_test.go
@@ -15,9 +15,7 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/discovery"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
"github.com/youtube/vitess/go/vt/topo"
- "github.com/youtube/vitess/go/vt/vtgate/gateway"
"golang.org/x/net/context"
querypb "github.com/youtube/vitess/go/vt/proto/query"
@@ -201,11 +199,11 @@ func testResolverGeneric(t *testing.T, name string, action func(res *Resolver) (
hc.Reset()
sbc0 = hc.AddTestTablet("aa", "-20", 1, name, "-20", topodatapb.TabletType_MASTER, true, 1, nil)
sbc1 = hc.AddTestTablet("aa", "20-40", 1, name, "20-40", topodatapb.TabletType_MASTER, true, 1, nil)
- sbc0.MustFailServer = 1
- sbc1.MustFailRetry = 1
+ sbc0.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
+ sbc1.MustFailCodes[vtrpcpb.Code_INTERNAL] = 1
_, err = action(res)
- want1 := fmt.Sprintf("target: %s.-20.master, used tablet: (alias: hostname:\"-20\" port_map: keyspace:\"%s\" shard:\"-20\" type:MASTER ), error: err", name, name)
- want2 := fmt.Sprintf("target: %s.20-40.master, used tablet: (alias: hostname:\"20-40\" port_map: keyspace:\"%s\" shard:\"20-40\" type:MASTER ), retry: err", name, name)
+ want1 := fmt.Sprintf("target: %s.-20.master, used tablet: (alias: hostname:\"-20\" port_map: keyspace:\"%s\" shard:\"-20\" type:MASTER ), INVALID_ARGUMENT error", name, name)
+ want2 := fmt.Sprintf("target: %s.20-40.master, used tablet: (alias: hostname:\"20-40\" port_map: keyspace:\"%s\" shard:\"20-40\" type:MASTER ), INTERNAL error", name, name)
want := []string{want1, want2}
sort.Strings(want)
if err == nil {
@@ -234,11 +232,11 @@ func testResolverGeneric(t *testing.T, name string, action func(res *Resolver) (
hc.Reset()
sbc0 = hc.AddTestTablet("aa", "-20", 1, name, "-20", topodatapb.TabletType_MASTER, true, 1, nil)
sbc1 = hc.AddTestTablet("aa", "20-40", 1, name, "20-40", topodatapb.TabletType_MASTER, true, 1, nil)
- sbc0.MustFailRetry = 1
- sbc1.MustFailFatal = 1
+ sbc0.MustFailCodes[vtrpcpb.Code_FAILED_PRECONDITION] = 1
+ sbc1.MustFailCodes[vtrpcpb.Code_FAILED_PRECONDITION] = 1
_, err = action(res)
- want1 = fmt.Sprintf("target: %s.-20.master, used tablet: (alias: hostname:\"-20\" port_map: keyspace:\"%s\" shard:\"-20\" type:MASTER ), retry: err", name, name)
- want2 = fmt.Sprintf("target: %s.20-40.master, used tablet: (alias: hostname:\"20-40\" port_map: keyspace:\"%s\" shard:\"20-40\" type:MASTER ), fatal: err", name, name)
+ want1 = fmt.Sprintf("target: %s.-20.master, used tablet: (alias: hostname:\"-20\" port_map: keyspace:\"%s\" shard:\"-20\" type:MASTER ), FAILED_PRECONDITION error", name, name)
+ want2 = fmt.Sprintf("target: %s.20-40.master, used tablet: (alias: hostname:\"20-40\" port_map: keyspace:\"%s\" shard:\"20-40\" type:MASTER ), FAILED_PRECONDITION error", name, name)
want = []string{want1, want2}
sort.Strings(want)
if err == nil {
@@ -300,7 +298,7 @@ func testResolverGeneric(t *testing.T, name string, action func(res *Resolver) (
hc.Reset()
sbc0 = hc.AddTestTablet("aa", "1.1.1.1", 1001, name, "-20", topodatapb.TabletType_MASTER, true, 1, nil)
sbc1 = hc.AddTestTablet("aa", "1.1.1.1", 1002, name, "20-40", topodatapb.TabletType_MASTER, true, 1, nil)
- sbc1.MustFailFatal = 1
+ sbc1.MustFailCodes[vtrpcpb.Code_FAILED_PRECONDITION] = 1
i := 0
s.SrvKeyspaceCallback = func() {
if i == 1 {
@@ -332,7 +330,7 @@ func testResolverGeneric(t *testing.T, name string, action func(res *Resolver) (
hc.Reset()
sbc0 = hc.AddTestTablet("aa", "1.1.1.1", 1001, name, "-20", topodatapb.TabletType_MASTER, true, 1, nil)
sbc1 = hc.AddTestTablet("aa", "1.1.1.1", 1002, name, "20-40", topodatapb.TabletType_MASTER, true, 1, nil)
- sbc1.MustFailRetry = 1
+ sbc1.MustFailCodes[vtrpcpb.Code_FAILED_PRECONDITION] = 1
i = 0
s.SrvKeyspaceCallback = func() {
if i == 1 {
@@ -380,9 +378,9 @@ func testResolverStreamGeneric(t *testing.T, name string, action func(res *Resol
hc.Reset()
sbc0 = hc.AddTestTablet("aa", "-20", 1, name, "-20", topodatapb.TabletType_MASTER, true, 1, nil)
hc.AddTestTablet("aa", "20-40", 1, name, "20-40", topodatapb.TabletType_MASTER, true, 1, nil)
- sbc0.MustFailRetry = 1
+ sbc0.MustFailCodes[vtrpcpb.Code_INTERNAL] = 1
_, err = action(res)
- want := fmt.Sprintf("target: %s.-20.master, used tablet: (alias: hostname:\"-20\" port_map: keyspace:\"%s\" shard:\"-20\" type:MASTER ), retry: err", name, name)
+ want := fmt.Sprintf("target: %s.-20.master, used tablet: (alias: hostname:\"-20\" port_map: keyspace:\"%s\" shard:\"-20\" type:MASTER ), INTERNAL error", name, name)
if err == nil || err.Error() != want {
t.Errorf("want\n%s\ngot\n%v", want, err)
}
@@ -484,7 +482,7 @@ func TestResolverExecBatchReresolve(t *testing.T) {
res := newTestResolver(hc, new(sandboxTopo), "aa")
sbc := hc.AddTestTablet("aa", "0", 1, keyspace, "0", topodatapb.TabletType_MASTER, true, 1, nil)
- sbc.MustFailRetry = 20
+ sbc.MustFailCodes[vtrpcpb.Code_FAILED_PRECONDITION] = 20
callcount := 0
buildBatchRequest := func() (*scatterBatchRequest, error) {
@@ -501,7 +499,7 @@ func TestResolverExecBatchReresolve(t *testing.T) {
}
_, err := res.ExecuteBatch(context.Background(), topodatapb.TabletType_MASTER, false, nil, nil, buildBatchRequest)
- want := "target: TestResolverExecBatchReresolve.0.master, used tablet: (alias: hostname:\"0\" port_map: keyspace:\"TestResolverExecBatchReresolve\" shard:\"0\" type:MASTER ), retry: err"
+ want := "target: TestResolverExecBatchReresolve.0.master, used tablet: (alias: hostname:\"0\" port_map: keyspace:\"TestResolverExecBatchReresolve\" shard:\"0\" type:MASTER ), FAILED_PRECONDITION error"
if err == nil || err.Error() != want {
t.Errorf("want %s, got %v", want, err)
}
@@ -521,7 +519,7 @@ func TestResolverExecBatchAsTransaction(t *testing.T) {
res := newTestResolver(hc, new(sandboxTopo), "aa")
sbc := hc.AddTestTablet("aa", "0", 1, keyspace, "0", topodatapb.TabletType_MASTER, true, 1, nil)
- sbc.MustFailRetry = 20
+ sbc.MustFailCodes[vtrpcpb.Code_INTERNAL] = 20
callcount := 0
buildBatchRequest := func() (*scatterBatchRequest, error) {
@@ -538,7 +536,7 @@ func TestResolverExecBatchAsTransaction(t *testing.T) {
}
_, err := res.ExecuteBatch(context.Background(), topodatapb.TabletType_MASTER, true, nil, nil, buildBatchRequest)
- want := "target: TestResolverExecBatchAsTransaction.0.master, used tablet: (alias: hostname:\"0\" port_map: keyspace:\"TestResolverExecBatchAsTransaction\" shard:\"0\" type:MASTER ), retry: err"
+ want := "target: TestResolverExecBatchAsTransaction.0.master, used tablet: (alias: hostname:\"0\" port_map: keyspace:\"TestResolverExecBatchAsTransaction\" shard:\"0\" type:MASTER ), INTERNAL error"
if err == nil || err.Error() != want {
t.Errorf("want %v, got %v", want, err)
}
@@ -552,32 +550,6 @@ func TestResolverExecBatchAsTransaction(t *testing.T) {
}
}
-func TestIsRetryableError(t *testing.T) {
- var connErrorTests = []struct {
- in error
- outBool bool
- }{
- {fmt.Errorf("generic error"), false},
- {&ScatterConnError{Retryable: true}, true},
- {&ScatterConnError{Retryable: false}, false},
- {&gateway.ShardError{ErrorCode: vtrpcpb.ErrorCode_QUERY_NOT_SERVED}, true},
- {&gateway.ShardError{ErrorCode: vtrpcpb.ErrorCode_INTERNAL_ERROR}, false},
- // tabletconn.ServerError will not come directly here,
- // they'll be wrapped in ScatterConnError or ShardConnError.
- // So they can't be retried as is.
- {&tabletconn.ServerError{ServerCode: vtrpcpb.ErrorCode_QUERY_NOT_SERVED}, false},
- {&tabletconn.ServerError{ServerCode: vtrpcpb.ErrorCode_PERMISSION_DENIED}, false},
- }
-
- for _, tt := range connErrorTests {
- gotBool := isRetryableError(tt.in)
- if gotBool != tt.outBool {
- t.Errorf("isConnError(%v) => %v, want %v",
- tt.in, gotBool, tt.outBool)
- }
- }
-}
-
func newTestResolver(hc discovery.HealthCheck, serv topo.SrvTopoServer, cell string) *Resolver {
sc := newTestScatterConn(hc, serv, cell)
return NewResolver(serv, cell, sc)
diff --git a/go/vt/vtgate/router_dml_test.go b/go/vt/vtgate/router_dml_test.go
index ef7cf667d6e..52e1bbc101b 100644
--- a/go/vt/vtgate/router_dml_test.go
+++ b/go/vt/vtgate/router_dml_test.go
@@ -10,11 +10,12 @@ import (
"testing"
"github.com/youtube/vitess/go/sqltypes"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
- "github.com/youtube/vitess/go/vt/tabletserver/sandboxconn"
_ "github.com/youtube/vitess/go/vt/vtgate/vindexes"
+ "github.com/youtube/vitess/go/vt/vttablet/sandboxconn"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
querypb "github.com/youtube/vitess/go/vt/proto/query"
+ vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)
func TestUpdateEqual(t *testing.T) {
@@ -623,14 +624,14 @@ func TestInsertFail(t *testing.T) {
t.Errorf("routerExec: %v, want %v", err, want)
}
- sbclookup.MustFailServer = 1
+ sbclookup.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
_, err = routerExec(router, "insert into user(id, v, name) values (null, 2, 'myname')", nil)
want = "execInsertSharded: "
if err == nil || !strings.HasPrefix(err.Error(), want) {
t.Errorf("routerExec: %v, want prefix %v", err, want)
}
- sbclookup.MustFailServer = 1
+ sbclookup.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
_, err = routerExec(router, "insert into user(id, v, name) values (1, 2, 'myname')", nil)
want = "execInsertSharded: getInsertShardedRoute: lookup.Create: "
if err == nil || !strings.HasPrefix(err.Error(), want) {
@@ -643,7 +644,7 @@ func TestInsertFail(t *testing.T) {
t.Errorf("routerExec: %v, want %v", err, want)
}
- sbclookup.MustFailServer = 1
+ sbclookup.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
_, err = routerExec(router, "insert into music_extra_reversed(music_id, user_id) values (1, 1)", nil)
want = "execInsertSharded: getInsertShardedRoute: lookup.Map"
if err == nil || !strings.HasPrefix(err.Error(), want) {
@@ -672,14 +673,14 @@ func TestInsertFail(t *testing.T) {
}
getSandbox("TestRouter").ShardSpec = DefaultShardSpec
- sbclookup.MustFailServer = 1
+ sbclookup.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
_, err = routerExec(router, "insert into music(user_id, id) values (1, null)", nil)
want = "execInsertSharded:"
if err == nil || !strings.HasPrefix(err.Error(), want) {
t.Errorf("routerExec: %v, want prefix %v", err, want)
}
- sbclookup.MustFailServer = 1
+ sbclookup.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
_, err = routerExec(router, "insert into music(user_id, id) values (1, 2)", nil)
want = "execInsertSharded: getInsertShardedRoute: lookup.Create: execInsertUnsharded: target: TestUnsharded.0.master"
if err == nil || !strings.HasPrefix(err.Error(), want) {
@@ -704,7 +705,7 @@ func TestInsertFail(t *testing.T) {
t.Errorf("routerExec: %v, want %v", err, want)
}
- sbc.MustFailServer = 1
+ sbc.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
_, err = routerExec(router, "insert into user(id, v, name) values (1, 2, 'myname')", nil)
want = "execInsertSharded: target: TestRouter.-20.master"
if err == nil || !strings.HasPrefix(err.Error(), want) {
@@ -722,35 +723,6 @@ func TestInsertFail(t *testing.T) {
if err == nil || !strings.HasPrefix(err.Error(), want) {
t.Errorf("routerExec: %v, want prefix %v", err, want)
}
-
- sbc.SetResults([]*sqltypes.Result{{RowsAffected: 1, InsertID: 1}})
- sbclookup.SetResults([]*sqltypes.Result{{
- Rows: [][]sqltypes.Value{{
- sqltypes.MakeTrusted(sqltypes.Int64, []byte("1")),
- }},
- RowsAffected: 1,
- InsertID: 1,
- }})
- _, err = routerExec(router, "insert into user(id, v, name) values (null, 2, 'myname')", nil)
- want = "sequence and db generated a value each for insert"
- if err == nil || !strings.HasPrefix(err.Error(), want) {
- t.Errorf("routerExec: %v, want prefix %v", err, want)
- }
- sbclookup.SetResults([]*sqltypes.Result{{
- Rows: [][]sqltypes.Value{{
- sqltypes.MakeTrusted(sqltypes.Int64, []byte("1")),
- }},
- RowsAffected: 1,
- InsertID: 1,
- }, {
- RowsAffected: 1,
- InsertID: 1,
- }})
- _, err = routerExec(router, "insert into main1(id, v, name) values (null, 2, 'myname')", nil)
- want = "sequence and db generated a value each for insert"
- if err == nil || !strings.HasPrefix(err.Error(), want) {
- t.Errorf("routerExec: %v, want prefix %v", err, want)
- }
}
func TestMultiInsertSharded(t *testing.T) {
diff --git a/go/vt/vtgate/router_framework_test.go b/go/vt/vtgate/router_framework_test.go
index dc6850cfa28..85eeb035de1 100644
--- a/go/vt/vtgate/router_framework_test.go
+++ b/go/vt/vtgate/router_framework_test.go
@@ -7,7 +7,7 @@ package vtgate
import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/discovery"
- "github.com/youtube/vitess/go/vt/tabletserver/sandboxconn"
+ "github.com/youtube/vitess/go/vt/vttablet/sandboxconn"
"golang.org/x/net/context"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
diff --git a/go/vt/vtgate/router_select_test.go b/go/vt/vtgate/router_select_test.go
index b561147fd63..29e94141382 100644
--- a/go/vt/vtgate/router_select_test.go
+++ b/go/vt/vtgate/router_select_test.go
@@ -14,12 +14,13 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/discovery"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
- "github.com/youtube/vitess/go/vt/tabletserver/sandboxconn"
_ "github.com/youtube/vitess/go/vt/vtgate/vindexes"
+ "github.com/youtube/vitess/go/vt/vttablet/sandboxconn"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
+ vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)
func TestUnsharded(t *testing.T) {
@@ -427,7 +428,7 @@ func TestSelectEqualFail(t *testing.T) {
t.Errorf("routerExec: %v, want %v", err, want)
}
- sbclookup.MustFailServer = 1
+ sbclookup.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
_, err = routerExec(router, "select id from music where id = 1", nil)
want = "paramsSelectEqual: lookup.Map"
if err == nil || !strings.HasPrefix(err.Error(), want) {
@@ -442,7 +443,7 @@ func TestSelectEqualFail(t *testing.T) {
}
s.ShardSpec = DefaultShardSpec
- sbclookup.MustFailServer = 1
+ sbclookup.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
_, err = routerExec(router, "select id from user where name = 'foo'", nil)
want = "paramsSelectEqual: lookup.Map"
if err == nil || !strings.HasPrefix(err.Error(), want) {
@@ -1254,11 +1255,11 @@ func TestJoinErrors(t *testing.T) {
router, sbc1, sbc2, _ := createRouterEnv()
// First query fails
- sbc1.MustFailServer = 1
+ sbc1.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
_, err := routerExec(router, "select u1.id, u2.id from user u1 join user u2 on u2.id = u1.col where u1.id = 1", nil)
- want := "error: err"
+ want := "INVALID_ARGUMENT error"
if err == nil || !strings.Contains(err.Error(), want) {
- t.Errorf("err: %v, must start with %s", err, want)
+ t.Errorf("err: %v, must contain %s", err, want)
}
// Field query fails
@@ -1267,11 +1268,11 @@ func TestJoinErrors(t *testing.T) {
{Name: "id", Type: sqltypes.Int32},
},
}})
- sbc1.MustFailServer = 1
+ sbc1.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
_, err = routerExec(router, "select u1.id, u2.id from user u1 join user u2 on u2.id = u1.col where u1.id = 3", nil)
- want = "error: err"
+ want = "INVALID_ARGUMENT error"
if err == nil || !strings.Contains(err.Error(), want) {
- t.Errorf("err: %v, must start with %s", err, want)
+ t.Errorf("err: %v, must contain %s", err, want)
}
// Second query fails
@@ -1285,11 +1286,11 @@ func TestJoinErrors(t *testing.T) {
sqltypes.MakeTrusted(sqltypes.Int32, []byte("3")),
}},
}})
- sbc2.MustFailServer = 1
+ sbc2.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
_, err = routerExec(router, "select u1.id, u2.id from user u1 join user u2 on u2.id = u1.col where u1.id = 1", nil)
- want = "error: err"
+ want = "INVALID_ARGUMENT error"
if err == nil || !strings.Contains(err.Error(), want) {
- t.Errorf("err: %v, must start with %s", err, want)
+ t.Errorf("err: %v, must contain %s", err, want)
}
// Nested join query fails on get fields
@@ -1299,11 +1300,11 @@ func TestJoinErrors(t *testing.T) {
{Name: "col", Type: sqltypes.Int32},
},
}})
- sbc1.MustFailServer = 1
+ sbc1.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
_, err = routerExec(router, "select u1.id, u2.id from user u1 join (user u2 join user u3 on u3.id = u2.col) where u1.id = 3", nil)
- want = "error: err"
+ want = "INVALID_ARGUMENT error"
if err == nil || !strings.Contains(err.Error(), want) {
- t.Errorf("err: %v, must start with %s", err, want)
+ t.Errorf("err: %v, must contain %s", err, want)
}
// Field query fails on stream join
@@ -1312,11 +1313,11 @@ func TestJoinErrors(t *testing.T) {
{Name: "id", Type: sqltypes.Int32},
},
}})
- sbc1.MustFailServer = 1
+ sbc1.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
_, err = routerStream(router, "select u1.id, u2.id from user u1 join user u2 on u2.id = u1.col where u1.id = 3")
- want = "error: err"
+ want = "INVALID_ARGUMENT error"
if err == nil || !strings.Contains(err.Error(), want) {
- t.Errorf("err: %v, must start with %s", err, want)
+ t.Errorf("err: %v, must contain %s", err, want)
}
// Second query fails on stream join
@@ -1330,10 +1331,10 @@ func TestJoinErrors(t *testing.T) {
sqltypes.MakeTrusted(sqltypes.Int32, []byte("3")),
}},
}})
- sbc2.MustFailServer = 1
+ sbc2.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
_, err = routerStream(router, "select u1.id, u2.id from user u1 join user u2 on u2.id = u1.col where u1.id = 1")
- want = "error: err"
+ want = "INVALID_ARGUMENT error"
if err == nil || !strings.Contains(err.Error(), want) {
- t.Errorf("err: %v, must start with %s", err, want)
+ t.Errorf("err: %v, must contain %s", err, want)
}
}
diff --git a/go/vt/vtgate/safe_session.go b/go/vt/vtgate/safe_session.go
index b8df70a7893..e773474e1ce 100644
--- a/go/vt/vtgate/safe_session.go
+++ b/go/vt/vtgate/safe_session.go
@@ -5,7 +5,6 @@
package vtgate
import (
- "fmt"
"sync"
"github.com/youtube/vitess/go/vt/vterrors"
@@ -63,7 +62,7 @@ func (session *SafeSession) Append(shardSession *vtgatepb.Session_ShardSession)
session.ShardSessions = append(session.ShardSessions, shardSession)
if session.SingleDb && len(session.ShardSessions) > 1 {
session.mustRollback = true
- return vterrors.FromError(vtrpcpb.ErrorCode_BAD_INPUT, fmt.Errorf("multi-db transaction attempted: %v", session.ShardSessions))
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "multi-db transaction attempted: %v", session.ShardSessions)
}
return nil
}
diff --git a/go/vt/vtgate/sandbox_test.go b/go/vt/vtgate/sandbox_test.go
index a842e8bacac..42cbae89c61 100644
--- a/go/vt/vtgate/sandbox_test.go
+++ b/go/vt/vtgate/sandbox_test.go
@@ -12,14 +12,16 @@ import (
"time"
"github.com/youtube/vitess/go/vt/key"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/sandboxconn"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
"github.com/youtube/vitess/go/vt/topo"
+ "github.com/youtube/vitess/go/vt/vterrors"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/sandboxconn"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletconn"
"golang.org/x/net/context"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
vschemapb "github.com/youtube/vitess/go/vt/proto/vschema"
+ vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)
// sandbox_test.go provides a sandbox for unit testing VTGate.
@@ -268,12 +270,12 @@ func sandboxDialer(tablet *topodatapb.Tablet, timeout time.Duration) (queryservi
sand.DialCounter++
if sand.DialMustFail > 0 {
sand.DialMustFail--
- return nil, tabletconn.OperationalError(fmt.Sprintf("conn error"))
+ return nil, vterrors.New(vtrpcpb.Code_UNAVAILABLE, "conn error")
}
if sand.DialMustTimeout > 0 {
time.Sleep(timeout)
sand.DialMustTimeout--
- return nil, tabletconn.OperationalError(fmt.Sprintf("conn unreachable"))
+ return nil, vterrors.New(vtrpcpb.Code_UNAVAILABLE, "conn unreachable")
}
sbc := sandboxconn.NewSandboxConn(tablet)
return sbc, nil
diff --git a/go/vt/vtgate/scatter_conn.go b/go/vt/vtgate/scatter_conn.go
index 6032f70638c..44e19318c63 100644
--- a/go/vt/vtgate/scatter_conn.go
+++ b/go/vt/vtgate/scatter_conn.go
@@ -5,7 +5,6 @@
package vtgate
import (
- "fmt"
"math/rand"
"sync"
"time"
@@ -15,10 +14,10 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/stats"
"github.com/youtube/vitess/go/vt/concurrency"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
"github.com/youtube/vitess/go/vt/topo/topoproto"
"github.com/youtube/vitess/go/vt/vterrors"
"github.com/youtube/vitess/go/vt/vtgate/gateway"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
@@ -78,11 +77,11 @@ func (stc *ScatterConn) endAction(startTime time.Time, allErrors *concurrency.Al
// Don't increment the error counter for duplicate
// keys or bad queries, as those errors are caused by
// client queries and are not VTGate's fault.
- ec := vterrors.RecoverVtErrorCode(*err)
- if ec != vtrpcpb.ErrorCode_INTEGRITY_ERROR && ec != vtrpcpb.ErrorCode_BAD_INPUT {
+ ec := vterrors.Code(*err)
+ if ec != vtrpcpb.Code_ALREADY_EXISTS && ec != vtrpcpb.Code_INVALID_ARGUMENT {
stc.tabletCallErrorCount.Add(statsKey, 1)
}
- if ec == vtrpcpb.ErrorCode_RESOURCE_EXHAUSTED || ec == vtrpcpb.ErrorCode_NOT_IN_TX {
+ if ec == vtrpcpb.Code_RESOURCE_EXHAUSTED || ec == vtrpcpb.Code_ABORTED {
session.SetRollback()
}
}
@@ -322,7 +321,7 @@ func (stc *ScatterConn) ExecuteBatch(
stc.txConn.Rollback(ctx, session)
}
if allErrors.HasErrors() {
- return nil, allErrors.AggrError(stc.aggregateErrors)
+ return nil, allErrors.AggrError(vterrors.Aggregate)
}
return results, nil
}
@@ -359,7 +358,7 @@ func (stc *ScatterConn) StreamExecute(
return stc.processOneStreamingResult(&mu, &fieldSent, qr, callback)
})
})
- return allErrors.AggrError(stc.aggregateErrors)
+ return allErrors.AggrError(vterrors.Aggregate)
}
// StreamExecuteMulti is like StreamExecute,
@@ -383,7 +382,7 @@ func (stc *ScatterConn) StreamExecuteMulti(
return stc.processOneStreamingResult(&mu, &fieldSent, qr, callback)
})
})
- return allErrors.AggrError(stc.aggregateErrors)
+ return allErrors.AggrError(vterrors.Aggregate)
}
// MessageStream streams messages from the specified shards.
@@ -396,7 +395,7 @@ func (stc *ScatterConn) MessageStream(ctx context.Context, keyspace string, shar
return stc.processOneStreamingResult(&mu, &fieldSent, qr, callback)
})
})
- return allErrors.AggrError(stc.aggregateErrors)
+ return allErrors.AggrError(vterrors.Aggregate)
}
// MessageAck acks messages across multiple shards.
@@ -417,7 +416,7 @@ func (stc *ScatterConn) MessageAck(ctx context.Context, keyspace string, shardID
mu.Unlock()
return nil
})
- return totalCount, allErrors.AggrError(stc.aggregateErrors)
+ return totalCount, allErrors.AggrError(vterrors.Aggregate)
}
// UpdateStream just sends the query to the gateway,
@@ -489,7 +488,7 @@ func (stc *ScatterConn) SplitQuery(
)
if allErrors.HasErrors() {
- err := allErrors.AggrError(stc.aggregateErrors)
+ err := allErrors.AggrError(vterrors.Aggregate)
return nil, err
}
// We shuffle the query-parts here. External frameworks like MapReduce may
@@ -547,42 +546,6 @@ func (stc *ScatterConn) GetGatewayCacheStatus() gateway.TabletCacheStatusList {
return stc.gateway.CacheStatus()
}
-// ScatterConnError is the ScatterConn specific error.
-// It implements vterrors.VtError.
-type ScatterConnError struct {
- Retryable bool
- // Preserve the original errors, so that we don't need to parse the error string.
- Errs []error
- // serverCode is the error code to use for all the server errors in aggregate
- serverCode vtrpcpb.ErrorCode
-}
-
-func (e *ScatterConnError) Error() string {
- return fmt.Sprintf("%v", vterrors.ConcatenateErrors(e.Errs))
-}
-
-// VtErrorCode returns the underlying Vitess error code
-// This is part of vterrors.VtError interface.
-func (e *ScatterConnError) VtErrorCode() vtrpcpb.ErrorCode {
- return e.serverCode
-}
-
-func (stc *ScatterConn) aggregateErrors(errors []error) error {
- allRetryableError := true
- for _, e := range errors {
- connError, ok := e.(*gateway.ShardError)
- if !ok || (connError.ErrorCode != vtrpcpb.ErrorCode_QUERY_NOT_SERVED && connError.ErrorCode != vtrpcpb.ErrorCode_INTERNAL_ERROR) || connError.InTransaction {
- allRetryableError = false
- break
- }
- }
- return &ScatterConnError{
- Retryable: allRetryableError,
- Errs: errors,
- serverCode: vterrors.AggregateVtGateErrorCodes(errors),
- }
-}
-
// multiGo performs the requested 'action' on the specified
// shards in parallel. This does not handle any transaction state.
// The action function must match the shardActionFunc signature.
@@ -699,7 +662,7 @@ end:
stc.txConn.Rollback(ctx, session)
}
if allErrors.HasErrors() {
- return allErrors.AggrError(stc.aggregateErrors)
+ return allErrors.AggrError(vterrors.Aggregate)
}
return nil
}
diff --git a/go/vt/vtgate/scatter_conn_test.go b/go/vt/vtgate/scatter_conn_test.go
index f8f98964fe3..4b68251cb69 100644
--- a/go/vt/vtgate/scatter_conn_test.go
+++ b/go/vt/vtgate/scatter_conn_test.go
@@ -12,7 +12,6 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/discovery"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/vterrors"
"github.com/youtube/vitess/go/vt/vtgate/gateway"
@@ -22,7 +21,7 @@ import (
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
vtgatepb "github.com/youtube/vitess/go/vt/proto/vtgate"
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
)
// This file uses the sandbox_test framework.
@@ -98,15 +97,11 @@ func TestScatterConnStreamExecuteMulti(t *testing.T) {
// verifyScatterConnError checks that a returned error has the expected message,
// type, and error code.
-func verifyScatterConnError(t *testing.T, err error, wantErr string, wantCode vtrpcpb.ErrorCode) {
+func verifyScatterConnError(t *testing.T, err error, wantErr string, wantCode vtrpcpb.Code) {
if err == nil || err.Error() != wantErr {
t.Errorf("wanted error: %s, got error: %v", wantErr, err)
}
- if _, ok := err.(*ScatterConnError); !ok {
- t.Errorf("wanted error type *ScatterConnError, got error type: %v", reflect.TypeOf(err))
- }
- code := vterrors.RecoverVtErrorCode(err)
- if code != wantCode {
+ if code := vterrors.Code(err); code != wantCode {
t.Errorf("wanted error code: %s, got: %v", wantCode, code)
}
}
@@ -129,9 +124,9 @@ func testScatterConnGeneric(t *testing.T, name string, f func(sc *ScatterConn, s
s.Reset()
sc = newTestScatterConn(hc, new(sandboxTopo), "aa")
sbc := hc.AddTestTablet("aa", "0", 1, name, "0", topodatapb.TabletType_REPLICA, true, 1, nil)
- sbc.MustFailServer = 1
+ sbc.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
qr, err = f(sc, []string{"0"})
- want := fmt.Sprintf("target: %v.0.replica, used tablet: (alias: hostname:\"0\" port_map: keyspace:\"%s\" shard:\"0\" type:REPLICA ), error: err", name, name)
+ want := fmt.Sprintf("target: %v.0.replica, used tablet: (alias: hostname:\"0\" port_map: keyspace:\"%s\" shard:\"0\" type:REPLICA ), INVALID_ARGUMENT error", name, name)
// Verify server error string.
if err == nil || err.Error() != want {
t.Errorf("want %s, got %v", want, err)
@@ -147,12 +142,12 @@ func testScatterConnGeneric(t *testing.T, name string, f func(sc *ScatterConn, s
sc = newTestScatterConn(hc, new(sandboxTopo), "aa")
sbc0 := hc.AddTestTablet("aa", "0", 1, name, "0", topodatapb.TabletType_REPLICA, true, 1, nil)
sbc1 := hc.AddTestTablet("aa", "1", 1, name, "1", topodatapb.TabletType_REPLICA, true, 1, nil)
- sbc0.MustFailServer = 1
- sbc1.MustFailServer = 1
+ sbc0.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
+ sbc1.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
_, err = f(sc, []string{"0", "1"})
// Verify server errors are consolidated.
- want = fmt.Sprintf("target: %v.0.replica, used tablet: (alias: hostname:\"0\" port_map: keyspace:\"%v\" shard:\"0\" type:REPLICA ), error: err\ntarget: %v.1.replica, used tablet: (alias: hostname:\"1\" port_map: keyspace:\"%v\" shard:\"1\" type:REPLICA ), error: err", name, name, name, name)
- verifyScatterConnError(t, err, want, vtrpcpb.ErrorCode_BAD_INPUT)
+ want = fmt.Sprintf("target: %v.0.replica, used tablet: (alias: hostname:\"0\" port_map: keyspace:\"%v\" shard:\"0\" type:REPLICA ), INVALID_ARGUMENT error\ntarget: %v.1.replica, used tablet: (alias: hostname:\"1\" port_map: keyspace:\"%v\" shard:\"1\" type:REPLICA ), INVALID_ARGUMENT error", name, name, name, name)
+ verifyScatterConnError(t, err, want, vtrpcpb.Code_INVALID_ARGUMENT)
// Ensure that we tried only once.
if execCount := sbc0.ExecCount.Get(); execCount != 1 {
t.Errorf("want 1, got %v", execCount)
@@ -167,13 +162,13 @@ func testScatterConnGeneric(t *testing.T, name string, f func(sc *ScatterConn, s
sc = newTestScatterConn(hc, new(sandboxTopo), "aa")
sbc0 = hc.AddTestTablet("aa", "0", 1, name, "0", topodatapb.TabletType_REPLICA, true, 1, nil)
sbc1 = hc.AddTestTablet("aa", "1", 1, name, "1", topodatapb.TabletType_REPLICA, true, 1, nil)
- sbc0.MustFailServer = 1
- sbc1.MustFailTxPool = 1
+ sbc0.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
+ sbc1.MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 1
_, err = f(sc, []string{"0", "1"})
// Verify server errors are consolidated.
- want = fmt.Sprintf("target: %v.0.replica, used tablet: (alias: hostname:\"0\" port_map: keyspace:\"%v\" shard:\"0\" type:REPLICA ), error: err\ntarget: %v.1.replica, used tablet: (alias: hostname:\"1\" port_map: keyspace:\"%v\" shard:\"1\" type:REPLICA ), tx_pool_full: err", name, name, name, name)
+ want = fmt.Sprintf("target: %v.0.replica, used tablet: (alias: hostname:\"0\" port_map: keyspace:\"%v\" shard:\"0\" type:REPLICA ), INVALID_ARGUMENT error\ntarget: %v.1.replica, used tablet: (alias: hostname:\"1\" port_map: keyspace:\"%v\" shard:\"1\" type:REPLICA ), RESOURCE_EXHAUSTED error", name, name, name, name)
// We should only surface the higher priority error code
- verifyScatterConnError(t, err, want, vtrpcpb.ErrorCode_BAD_INPUT)
+ verifyScatterConnError(t, err, want, vtrpcpb.Code_INVALID_ARGUMENT)
// Ensure that we tried only once.
if execCount := sbc0.ExecCount.Get(); execCount != 1 {
t.Errorf("want 1, got %v", execCount)
@@ -278,24 +273,6 @@ func TestScatterConnStreamExecuteSendError(t *testing.T) {
}
}
-func TestScatterConnError(t *testing.T) {
- err := &ScatterConnError{
- Retryable: false,
- Errs: []error{
- &gateway.ShardError{ErrorCode: vtrpcpb.ErrorCode_PERMISSION_DENIED, Err: &tabletconn.ServerError{Err: "tabletconn error"}},
- fmt.Errorf("generic error"),
- tabletconn.ConnClosed,
- },
- }
-
- errString := err.Error()
- wantErrString := "generic error\ntabletconn error\nvttablet: Connection Closed"
-
- if errString != wantErrString {
- t.Errorf("got: %v, want: %v", errString, wantErrString)
- }
-}
-
func TestScatterConnQueryNotInTransaction(t *testing.T) {
s := createSandbox("TestScatterConnQueryNotInTransaction")
hc := discovery.NewFakeHealthCheck()
diff --git a/go/vt/vtgate/topo_utils.go b/go/vt/vtgate/topo_utils.go
index d6c440224c2..31f86a37e57 100644
--- a/go/vt/vtgate/topo_utils.go
+++ b/go/vt/vtgate/topo_utils.go
@@ -6,14 +6,13 @@ package vtgate
import (
"encoding/hex"
- "fmt"
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/key"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
"github.com/youtube/vitess/go/vt/vterrors"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
"golang.org/x/net/context"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
@@ -47,9 +46,7 @@ func getAnyShard(ctx context.Context, topoServ topo.SrvTopoServer, cell, keyspac
return "", "", err
}
if len(allShards) == 0 {
- return "", "", vterrors.FromError(vtrpcpb.ErrorCode_BAD_INPUT,
- fmt.Errorf("No shards found for this tabletType"),
- )
+ return "", "", vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "No shards found for this tabletType")
}
return keyspace, allShards[0].Name, nil
}
@@ -57,10 +54,7 @@ func getAnyShard(ctx context.Context, topoServ topo.SrvTopoServer, cell, keyspac
func getKeyspaceShards(ctx context.Context, topoServ topo.SrvTopoServer, cell, keyspace string, tabletType topodatapb.TabletType) (string, *topodatapb.SrvKeyspace, []*topodatapb.ShardReference, error) {
srvKeyspace, err := topoServ.GetSrvKeyspace(ctx, cell, keyspace)
if err != nil {
- return "", nil, nil, vterrors.NewVitessError(
- vtrpcpb.ErrorCode_INTERNAL_ERROR, err,
- "keyspace %v fetch error: %v", keyspace, err,
- )
+ return "", nil, nil, vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "keyspace %v fetch error: %v", keyspace, err)
}
// check if the keyspace has been redirected for this tabletType.
@@ -69,29 +63,21 @@ func getKeyspaceShards(ctx context.Context, topoServ topo.SrvTopoServer, cell, k
keyspace = sf.Keyspace
srvKeyspace, err = topoServ.GetSrvKeyspace(ctx, cell, keyspace)
if err != nil {
- return "", nil, nil, vterrors.NewVitessError(
- vtrpcpb.ErrorCode_INTERNAL_ERROR, err,
- "keyspace %v fetch error: %v", keyspace, err,
- )
+ return "", nil, nil, vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "keyspace %v fetch error: %v", keyspace, err)
}
}
}
partition := topoproto.SrvKeyspaceGetPartition(srvKeyspace, tabletType)
if partition == nil {
- return "", nil, nil, vterrors.NewVitessError(
- vtrpcpb.ErrorCode_INTERNAL_ERROR, err,
- "No partition found for tabletType %v in keyspace %v", topoproto.TabletTypeLString(tabletType), keyspace,
- )
+ return "", nil, nil, vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "No partition found for tabletType %v in keyspace %v", topoproto.TabletTypeLString(tabletType), keyspace)
}
return keyspace, srvKeyspace, partition.ShardReferences, nil
}
func getShardForKeyspaceID(allShards []*topodatapb.ShardReference, keyspaceID []byte) (string, error) {
if len(allShards) == 0 {
- return "", vterrors.FromError(vtrpcpb.ErrorCode_BAD_INPUT,
- fmt.Errorf("No shards found for this tabletType"),
- )
+ return "", vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "No shards found for this tabletType")
}
for _, shardReference := range allShards {
@@ -99,9 +85,7 @@ func getShardForKeyspaceID(allShards []*topodatapb.ShardReference, keyspaceID []
return shardReference.Name, nil
}
}
- return "", vterrors.FromError(vtrpcpb.ErrorCode_BAD_INPUT,
- fmt.Errorf("KeyspaceId %v didn't match any shards %+v", hex.EncodeToString(keyspaceID), allShards),
- )
+ return "", vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "KeyspaceId %v didn't match any shards %+v", hex.EncodeToString(keyspaceID), allShards)
}
func mapEntityIdsToShards(ctx context.Context, topoServ topo.SrvTopoServer, cell, keyspace string, entityIds []*vtgatepb.ExecuteEntityIdsRequest_EntityId, tabletType topodatapb.TabletType) (string, map[string][]interface{}, error) {
@@ -179,9 +163,7 @@ func mapExactShards(ctx context.Context, topoServ topo.SrvTopoServer, cell, keys
}
shardnum++
}
- return keyspace, nil, vterrors.FromError(vtrpcpb.ErrorCode_BAD_INPUT,
- fmt.Errorf("keyrange %v does not exactly match shards", key.KeyRangeString(kr)),
- )
+ return keyspace, nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "keyrange %v does not exactly match shards", key.KeyRangeString(kr))
}
func boundShardQueriesToScatterBatchRequest(boundQueries []*vtgatepb.BoundShardQuery) (*scatterBatchRequest, error) {
diff --git a/go/vt/vtgate/topo_utils_test.go b/go/vt/vtgate/topo_utils_test.go
index 773a0e393a9..32f8dc9cc79 100644
--- a/go/vt/vtgate/topo_utils_test.go
+++ b/go/vt/vtgate/topo_utils_test.go
@@ -12,7 +12,7 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/key"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
"golang.org/x/net/context"
querypb "github.com/youtube/vitess/go/vt/proto/query"
diff --git a/go/vt/vtgate/tx_conn.go b/go/vt/vtgate/tx_conn.go
index 53df3f4daf5..9335f23bcb2 100644
--- a/go/vt/vtgate/tx_conn.go
+++ b/go/vt/vtgate/tx_conn.go
@@ -5,8 +5,6 @@
package vtgate
import (
- "errors"
- "fmt"
"sync"
"golang.org/x/net/context"
@@ -37,10 +35,10 @@ func NewTxConn(gw gateway.Gateway) *TxConn {
// is used to ensure atomicity.
func (txc *TxConn) Commit(ctx context.Context, twopc bool, session *SafeSession) error {
if session == nil {
- return vterrors.FromError(vtrpcpb.ErrorCode_BAD_INPUT, errors.New("cannot commit: empty session"))
+ return vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "cannot commit: empty session")
}
if !session.InTransaction() {
- return vterrors.FromError(vtrpcpb.ErrorCode_NOT_IN_TX, errors.New("cannot commit: not in transaction"))
+ return vterrors.New(vtrpcpb.Code_ABORTED, "cannot commit: not in transaction")
}
if twopc {
return txc.commit2PC(ctx, session)
@@ -155,7 +153,7 @@ func (txc *TxConn) Resolve(ctx context.Context, dtid string) error {
}
default:
// Should never happen.
- return vterrors.FromError(vtrpcpb.ErrorCode_INTERNAL_ERROR, fmt.Errorf("invalid state: %v", transaction.State))
+ return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid state: %v", transaction.State)
}
return nil
}
@@ -199,7 +197,7 @@ func (txc *TxConn) runSessions(shardSessions []*vtgatepb.Session_ShardSession, a
}(s)
}
wg.Wait()
- return allErrors.AggrError(aggregateTxConnErrors)
+ return allErrors.AggrError(vterrors.Aggregate)
}
// runTargets executes the action for all targets in parallel and returns a consolildated error.
@@ -220,13 +218,5 @@ func (txc *TxConn) runTargets(targets []*querypb.Target, action func(*querypb.Ta
}(t)
}
wg.Wait()
- return allErrors.AggrError(aggregateTxConnErrors)
-}
-
-func aggregateTxConnErrors(errors []error) error {
- return &ScatterConnError{
- Retryable: false,
- Errs: errors,
- serverCode: vterrors.AggregateVtGateErrorCodes(errors),
- }
+ return allErrors.AggrError(vterrors.Aggregate)
}
diff --git a/go/vt/vtgate/tx_conn_test.go b/go/vt/vtgate/tx_conn_test.go
index da6a10931d1..d5abb230862 100644
--- a/go/vt/vtgate/tx_conn_test.go
+++ b/go/vt/vtgate/tx_conn_test.go
@@ -6,14 +6,13 @@ package vtgate
import (
"context"
- "fmt"
"reflect"
"strings"
"testing"
"github.com/youtube/vitess/go/vt/discovery"
- "github.com/youtube/vitess/go/vt/tabletserver/sandboxconn"
"github.com/youtube/vitess/go/vt/vterrors"
+ "github.com/youtube/vitess/go/vt/vttablet/sandboxconn"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
@@ -25,8 +24,8 @@ func TestTxConnCommitRollbackIncorrectSession(t *testing.T) {
sc, _, _ := newTestTxConnEnv("TestTxConn")
// nil session
err := sc.txConn.Commit(context.Background(), false, nil)
- if got := vterrors.RecoverVtErrorCode(err); got != vtrpcpb.ErrorCode_BAD_INPUT {
- t.Errorf("Commit: %v, want %v", got, vtrpcpb.ErrorCode_BAD_INPUT)
+ if got := vterrors.Code(err); got != vtrpcpb.Code_INVALID_ARGUMENT {
+ t.Errorf("Commit: %v, want %v", got, vtrpcpb.Code_INVALID_ARGUMENT)
}
err = sc.txConn.Rollback(context.Background(), nil)
@@ -37,8 +36,8 @@ func TestTxConnCommitRollbackIncorrectSession(t *testing.T) {
// not in transaction
session := NewSafeSession(&vtgatepb.Session{})
err = sc.txConn.Commit(context.Background(), false, session)
- if got := vterrors.RecoverVtErrorCode(err); got != vtrpcpb.ErrorCode_NOT_IN_TX {
- t.Errorf("Commit: %v, want %v", got, vtrpcpb.ErrorCode_NOT_IN_TX)
+ if got := vterrors.Code(err); got != vtrpcpb.Code_ABORTED {
+ t.Errorf("Commit: %v, want %v", got, vtrpcpb.Code_ABORTED)
}
}
@@ -85,9 +84,9 @@ func TestTxConnCommitSuccess(t *testing.T) {
t.Errorf("Session:\n%+v, want\n%+v", *session.Session, wantSession)
}
- sbc0.MustFailServer = 1
+ sbc0.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
err := sc.txConn.Commit(context.Background(), false, session)
- want := "error: err"
+ want := "INVALID_ARGUMENT error"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("Commit: %v, want %s", err, want)
}
@@ -423,9 +422,9 @@ func TestTxConnResolveReadTransactionFail(t *testing.T) {
sc, sbc0, _ := newTestTxConnEnv("TestTxConn")
dtid := "TestTxConn:0:1234"
- sbc0.MustFailServer = 1
+ sbc0.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
err := sc.txConn.Resolve(context.Background(), dtid)
- want := "error: err"
+ want := "INVALID_ARGUMENT error"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("Resolve: %v, want %s", err, want)
}
@@ -592,7 +591,7 @@ func TestTxConnMultiGoSessions(t *testing.T) {
},
}}
err := txc.runSessions(input, func(s *vtgatepb.Session_ShardSession) error {
- return vterrors.FromError(vtrpcpb.ErrorCode_INTERNAL_ERROR, fmt.Errorf("err %s", s.Target.Keyspace))
+ return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "err %s", s.Target.Keyspace)
})
want := "err 0"
if err == nil || err.Error() != want {
@@ -609,16 +608,15 @@ func TestTxConnMultiGoSessions(t *testing.T) {
},
}}
err = txc.runSessions(input, func(s *vtgatepb.Session_ShardSession) error {
- return vterrors.FromError(vtrpcpb.ErrorCode_INTERNAL_ERROR, fmt.Errorf("err %s", s.Target.Keyspace))
+ return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "err %s", s.Target.Keyspace)
})
want = "err 0\nerr 1"
if err == nil || err.Error() != want {
t.Errorf("runSessions(2): %v, want %s", err, want)
}
- errCode := err.(*ScatterConnError).VtErrorCode()
- wantCode := vtrpcpb.ErrorCode_INTERNAL_ERROR
- if errCode != wantCode {
- t.Errorf("Error code: %v, want %v", errCode, wantCode)
+ wantCode := vtrpcpb.Code_INTERNAL
+ if code := vterrors.Code(err); code != wantCode {
+ t.Errorf("Error code: %v, want %v", code, wantCode)
}
err = txc.runSessions(input, func(s *vtgatepb.Session_ShardSession) error {
@@ -635,7 +633,7 @@ func TestTxConnMultiGoTargets(t *testing.T) {
Keyspace: "0",
}}
err := txc.runTargets(input, func(t *querypb.Target) error {
- return vterrors.FromError(vtrpcpb.ErrorCode_INTERNAL_ERROR, fmt.Errorf("err %s", t.Keyspace))
+ return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "err %s", t.Keyspace)
})
want := "err 0"
if err == nil || err.Error() != want {
@@ -648,16 +646,15 @@ func TestTxConnMultiGoTargets(t *testing.T) {
Keyspace: "1",
}}
err = txc.runTargets(input, func(t *querypb.Target) error {
- return vterrors.FromError(vtrpcpb.ErrorCode_INTERNAL_ERROR, fmt.Errorf("err %s", t.Keyspace))
+ return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "err %s", t.Keyspace)
})
want = "err 0\nerr 1"
if err == nil || err.Error() != want {
t.Errorf("runTargets(2): %v, want %s", err, want)
}
- errCode := err.(*ScatterConnError).VtErrorCode()
- wantCode := vtrpcpb.ErrorCode_INTERNAL_ERROR
- if errCode != wantCode {
- t.Errorf("Error code: %v, want %v", errCode, wantCode)
+ wantCode := vtrpcpb.Code_INTERNAL
+ if code := vterrors.Code(err); code != wantCode {
+ t.Errorf("Error code: %v, want %v", code, wantCode)
}
err = txc.runTargets(input, func(t *querypb.Target) error {
diff --git a/go/vt/vtgate/vindexes/lookup_hash_test.go b/go/vt/vtgate/vindexes/lookup_hash_test.go
index 8832607268c..dd4d02c479b 100644
--- a/go/vt/vtgate/vindexes/lookup_hash_test.go
+++ b/go/vt/vtgate/vindexes/lookup_hash_test.go
@@ -13,7 +13,7 @@ import (
"github.com/youtube/vitess/go/sqltypes"
querypb "github.com/youtube/vitess/go/vt/proto/query"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
)
type vcursor struct {
diff --git a/go/vt/vtgate/vindexes/lookup_hash_unique_test.go b/go/vt/vtgate/vindexes/lookup_hash_unique_test.go
index 2386c1e81d2..1c2c8522d21 100644
--- a/go/vt/vtgate/vindexes/lookup_hash_unique_test.go
+++ b/go/vt/vtgate/vindexes/lookup_hash_unique_test.go
@@ -8,7 +8,7 @@ import (
"reflect"
"testing"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
)
var lhu Vindex
diff --git a/go/vt/vtgate/vindexes/lookup_test.go b/go/vt/vtgate/vindexes/lookup_test.go
index 5054a48a53b..6deb025a833 100644
--- a/go/vt/vtgate/vindexes/lookup_test.go
+++ b/go/vt/vtgate/vindexes/lookup_test.go
@@ -6,7 +6,7 @@ import (
"strings"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
)
var lookupUnique Vindex
diff --git a/go/vt/vtgate/vtgate.go b/go/vt/vtgate/vtgate.go
index 354b86d9588..c682ba645c9 100644
--- a/go/vt/vtgate/vtgate.go
+++ b/go/vt/vtgate/vtgate.go
@@ -7,7 +7,6 @@
package vtgate
import (
- "errors"
"flag"
"fmt"
"math"
@@ -26,11 +25,10 @@ import (
"github.com/youtube/vitess/go/vt/logutil"
"github.com/youtube/vitess/go/vt/servenv"
"github.com/youtube/vitess/go/vt/sqlannotation"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
"github.com/youtube/vitess/go/vt/vterrors"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
"github.com/youtube/vitess/go/vt/vtgate/gateway"
"github.com/youtube/vitess/go/vt/vtgate/vtgateservice"
@@ -82,11 +80,10 @@ var (
errorsByOperation *stats.Rates
errorsByKeyspace *stats.Rates
errorsByDbType *stats.Rates
+ errorsByCode *stats.Rates
// Error counters should be global so they can be set from anywhere
- normalErrors *stats.MultiCounters
- infoErrors *stats.Counters
- internalErrors *stats.Counters
+ errorCounts *stats.MultiCounters
)
// VTGate is the rpc interface to vtgate. Only one instance
@@ -181,17 +178,16 @@ func Init(ctx context.Context, hc discovery.HealthCheck, topoServer topo.Server,
logMessageStream: logutil.NewThrottledLogger("MessageStream", 5*time.Second),
}
- normalErrors = stats.NewMultiCounters("VtgateApiErrorCounts", []string{"Operation", "Keyspace", "DbType"})
- infoErrors = stats.NewCounters("VtgateInfoErrorCounts")
- internalErrors = stats.NewCounters("VtgateInternalErrorCounts")
+ errorCounts = stats.NewMultiCounters("VtgateApiErrorCounts", []string{"Operation", "Keyspace", "DbType", "Code"})
qpsByOperation = stats.NewRates("QPSByOperation", stats.CounterForDimension(rpcVTGate.timings, "Operation"), 15, 1*time.Minute)
qpsByKeyspace = stats.NewRates("QPSByKeyspace", stats.CounterForDimension(rpcVTGate.timings, "Keyspace"), 15, 1*time.Minute)
qpsByDbType = stats.NewRates("QPSByDbType", stats.CounterForDimension(rpcVTGate.timings, "DbType"), 15, 1*time.Minute)
- errorsByOperation = stats.NewRates("ErrorsByOperation", stats.CounterForDimension(normalErrors, "Operation"), 15, 1*time.Minute)
- errorsByKeyspace = stats.NewRates("ErrorsByKeyspace", stats.CounterForDimension(normalErrors, "Keyspace"), 15, 1*time.Minute)
- errorsByDbType = stats.NewRates("ErrorsByDbType", stats.CounterForDimension(normalErrors, "DbType"), 15, 1*time.Minute)
+ errorsByOperation = stats.NewRates("ErrorsByOperation", stats.CounterForDimension(errorCounts, "Operation"), 15, 1*time.Minute)
+ errorsByKeyspace = stats.NewRates("ErrorsByKeyspace", stats.CounterForDimension(errorCounts, "Keyspace"), 15, 1*time.Minute)
+ errorsByDbType = stats.NewRates("ErrorsByDbType", stats.CounterForDimension(errorCounts, "DbType"), 15, 1*time.Minute)
+ errorsByCode = stats.NewRates("ErrorsByCode", stats.CounterForDimension(errorCounts, "Code"), 15, 1*time.Minute)
servenv.OnRun(func() {
for _, f := range RegisterVTGates {
@@ -245,7 +241,7 @@ func (vtg *VTGate) Execute(ctx context.Context, sql string, bindVariables map[st
"NotInTransaction": notInTransaction,
"Options": options,
}
- err = handleExecuteError(err, statsKey, query, vtg.logExecute)
+ err = recordAndAnnotateError(err, statsKey, query, vtg.logExecute)
return nil, err
}
@@ -286,7 +282,7 @@ func (vtg *VTGate) ExecuteShards(ctx context.Context, sql string, bindVariables
"NotInTransaction": notInTransaction,
"Options": options,
}
- err = handleExecuteError(err, statsKey, query, vtg.logExecuteShards)
+ err = recordAndAnnotateError(err, statsKey, query, vtg.logExecuteShards)
return nil, err
}
@@ -315,7 +311,7 @@ func (vtg *VTGate) ExecuteKeyspaceIds(ctx context.Context, sql string, bindVaria
"NotInTransaction": notInTransaction,
"Options": options,
}
- err = handleExecuteError(err, statsKey, query, vtg.logExecuteKeyspaceIds)
+ err = recordAndAnnotateError(err, statsKey, query, vtg.logExecuteKeyspaceIds)
return nil, err
}
@@ -344,7 +340,7 @@ func (vtg *VTGate) ExecuteKeyRanges(ctx context.Context, sql string, bindVariabl
"NotInTransaction": notInTransaction,
"Options": options,
}
- err = handleExecuteError(err, statsKey, query, vtg.logExecuteKeyRanges)
+ err = recordAndAnnotateError(err, statsKey, query, vtg.logExecuteKeyRanges)
return nil, err
}
@@ -374,7 +370,7 @@ func (vtg *VTGate) ExecuteEntityIds(ctx context.Context, sql string, bindVariabl
"NotInTransaction": notInTransaction,
"Options": options,
}
- err = handleExecuteError(err, statsKey, query, vtg.logExecuteEntityIds)
+ err = recordAndAnnotateError(err, statsKey, query, vtg.logExecuteEntityIds)
return nil, err
}
@@ -404,7 +400,7 @@ func (vtg *VTGate) ExecuteBatch(ctx context.Context, sqlList []string, bindVaria
"AsTransaction": asTransaction,
"Options": options,
}
- err = handleExecuteError(err, statsKey, query, vtg.logExecute)
+ err = recordAndAnnotateError(err, statsKey, query, vtg.logExecute)
return nil, err
}
@@ -442,7 +438,7 @@ func (vtg *VTGate) ExecuteBatchShards(ctx context.Context, queries []*vtgatepb.B
"Session": session,
"Options": options,
}
- err = handleExecuteError(err, statsKey, query, vtg.logExecuteBatchShards)
+ err = recordAndAnnotateError(err, statsKey, query, vtg.logExecuteBatchShards)
return nil, err
}
@@ -478,7 +474,7 @@ func (vtg *VTGate) ExecuteBatchKeyspaceIds(ctx context.Context, queries []*vtgat
"Session": session,
"Options": options,
}
- err = handleExecuteError(err, statsKey, query, vtg.logExecuteBatchKeyspaceIds)
+ err = recordAndAnnotateError(err, statsKey, query, vtg.logExecuteBatchKeyspaceIds)
return nil, err
}
@@ -509,7 +505,7 @@ func (vtg *VTGate) StreamExecute(ctx context.Context, sql string, bindVariables
"TabletType": ltt,
"Options": options,
}
- return handleExecuteError(err, statsKey, query, vtg.logStreamExecute)
+ return recordAndAnnotateError(err, statsKey, query, vtg.logStreamExecute)
}
return nil
}
@@ -548,7 +544,7 @@ func (vtg *VTGate) StreamExecuteKeyspaceIds(ctx context.Context, sql string, bin
"TabletType": ltt,
"Options": options,
}
- return handleExecuteError(err, statsKey, query, vtg.logStreamExecuteKeyspaceIds)
+ return recordAndAnnotateError(err, statsKey, query, vtg.logStreamExecuteKeyspaceIds)
}
return nil
}
@@ -587,7 +583,7 @@ func (vtg *VTGate) StreamExecuteKeyRanges(ctx context.Context, sql string, bindV
"TabletType": ltt,
"Options": options,
}
- return handleExecuteError(err, statsKey, query, vtg.logStreamExecuteKeyRanges)
+ return recordAndAnnotateError(err, statsKey, query, vtg.logStreamExecuteKeyRanges)
}
return nil
}
@@ -623,7 +619,7 @@ func (vtg *VTGate) StreamExecuteShards(ctx context.Context, sql string, bindVari
"TabletType": ltt,
"Options": options,
}
- return handleExecuteError(err, statsKey, query, vtg.logStreamExecuteShards)
+ return recordAndAnnotateError(err, statsKey, query, vtg.logStreamExecuteShards)
}
return nil
}
@@ -631,7 +627,7 @@ func (vtg *VTGate) StreamExecuteShards(ctx context.Context, sql string, bindVari
// Begin begins a transaction. It has to be concluded by a Commit or Rollback.
func (vtg *VTGate) Begin(ctx context.Context, singledb bool) (*vtgatepb.Session, error) {
if !singledb && vtg.transactionMode == TxSingle {
- return nil, vterrors.FromError(vtrpcpb.ErrorCode_BAD_INPUT, errors.New("multi-db transaction disallowed"))
+ return nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "multi-db transaction disallowed")
}
return &vtgatepb.Session{
InTransaction: true,
@@ -644,7 +640,7 @@ func (vtg *VTGate) Commit(ctx context.Context, twopc bool, session *vtgatepb.Ses
if twopc && vtg.transactionMode != TxTwoPC {
// Rollback the transaction to prevent future deadlocks.
vtg.txConn.Rollback(ctx, NewSafeSession(session))
- return vterrors.FromError(vtrpcpb.ErrorCode_BAD_INPUT, errors.New("2pc transaction disallowed"))
+ return vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "2pc transaction disallowed")
}
return formatError(vtg.txConn.Commit(ctx, twopc, NewSafeSession(session)))
}
@@ -823,15 +819,14 @@ func (vtg *VTGate) MessageStream(ctx context.Context, keyspace string, shard str
callback,
)
if err != nil {
- normalErrors.Add(statsKey, 1)
- query := map[string]interface{}{
+ request := map[string]interface{}{
"Keyspace": keyspace,
"Shard": shard,
"KeyRange": keyRange,
"TabletType": ltt,
"MessageName": name,
}
- logError(err, query, vtg.logMessageStream)
+ recordAndAnnotateError(err, statsKey, request, vtg.logMessageStream)
}
return formatError(err)
}
@@ -866,15 +861,14 @@ func (vtg *VTGate) UpdateStream(ctx context.Context, keyspace string, shard stri
callback,
)
if err != nil {
- normalErrors.Add(statsKey, 1)
- query := map[string]interface{}{
+ request := map[string]interface{}{
"Keyspace": keyspace,
"Shard": shard,
"KeyRange": keyRange,
"TabletType": ltt,
"Timestamp": timestamp,
}
- logError(err, query, vtg.logUpdateStream)
+ recordAndAnnotateError(err, statsKey, request, vtg.logUpdateStream)
}
return formatError(err)
}
@@ -889,103 +883,30 @@ func (vtg *VTGate) VSchemaStats() *VSchemaStats {
return vtg.router.planner.VSchemaStats()
}
-// Any errors that are caused by VTGate dependencies (e.g, VtTablet) should be logged
-// as errors in those components, but logged to Info in VTGate itself.
-func logError(err error, query map[string]interface{}, logger *logutil.ThrottledLogger) {
- if err == context.DeadlineExceeded {
- // Count these but don't log them because they are very common and not
- // likely to indicate a bug in VTGate
- infoErrors.Add("TimeoutErrors", 1)
- return
- }
- logMethod := logger.Errorf
- if !isErrorCausedByVTGate(err) {
- infoErrors.Add("NonVtgateErrors", 1)
- // Log non-vtgate errors (e.g. a query failed on vttablet because a failover
- // is in progress) on INFO only because vttablet already logs them as ERROR.
- logMethod = logger.Infof
- }
- logMethod("%v, query: %+v", err, query)
-}
-
-// Returns true if a given error is caused entirely due to VTGate, and not any
-// of the components that it depends on.
-// If the error is an aggregation of multiple errors e.g. in case of a scatter
-// query, the function returns true if *any* error is caused by vtgate.
-// Consequently, the function returns false if *all* errors are caused by
-// vttablet (actual errors) or the client (e.g. context canceled).
-func isErrorCausedByVTGate(err error) bool {
- var errQueue []error
- errQueue = append(errQueue, err)
- for len(errQueue) > 0 {
- // pop the first item from the queue
- e := errQueue[0]
- errQueue = errQueue[1:]
-
- switch e := e.(type) {
- case *ScatterConnError:
- errQueue = append(errQueue, e.Errs...)
- case *gateway.ShardError:
- errQueue = append(errQueue, e.Err)
- case tabletconn.OperationalError:
- // Communication with vttablet failed i.e. the error is caused by vtgate.
- // (For actual vttablet errors, see the next case "ServerError".)
- return true
- case *tabletconn.ServerError:
- // The query failed on vttablet and it returned this error.
- // Ignore it and check the next error in the queue.
- default:
- if e == context.Canceled {
- // Caused by the client and not vtgate.
- // Ignore it and check the next error in the queue.
- continue
- }
-
- // Return true if even a single error within
- // the error queue was caused by VTGate. If
- // we're not certain what caused the error, we
- // default to assuming that VTGate was at fault.
- return true
- }
+func recordAndAnnotateError(err error, statsKey []string, request map[string]interface{}, logger *logutil.ThrottledLogger) error {
+ ec := vterrors.Code(err)
+ fullKey := []string{
+ statsKey[0],
+ statsKey[1],
+ statsKey[2],
+ ec.String(),
}
- return false
-}
-
-func handleExecuteError(err error, statsKey []string, query map[string]interface{}, logger *logutil.ThrottledLogger) error {
- // First we log in the right category.
- ec := vterrors.RecoverVtErrorCode(err)
+ errorCounts.Add(fullKey, 1)
+ // Most errors are not logged by vtgate beecause they're either too spammy or logged elsewhere.
switch ec {
- case vtrpcpb.ErrorCode_INTEGRITY_ERROR:
- // Duplicate key error, no need to log.
- infoErrors.Add("DupKey", 1)
- case vtrpcpb.ErrorCode_RESOURCE_EXHAUSTED, vtrpcpb.ErrorCode_BAD_INPUT:
- // Tx pool full error, or bad input, no need to log.
- normalErrors.Add(statsKey, 1)
- case vtrpcpb.ErrorCode_PERMISSION_DENIED:
- // User violated permissions (TableACL), no need to log.
- infoErrors.Add("PermissionDenied", 1)
- case vtrpcpb.ErrorCode_TRANSIENT_ERROR:
- // Temporary error which should be retried by user. Do not log.
- // As of 01/2017, only the vttablet transaction throttler and the vtgate
- // master buffer (if buffer full) return this error.
- infoErrors.Add("TransientError", 1)
- default:
- // Regular error, we will log if caused by vtgate.
- normalErrors.Add(statsKey, 1)
- logError(err, query, logger)
+ case vtrpcpb.Code_UNKNOWN, vtrpcpb.Code_INTERNAL, vtrpcpb.Code_DATA_LOSS:
+ logger.Errorf("%v, request: %+v", err, request)
+ case vtrpcpb.Code_UNAVAILABLE:
+ logger.Infof("%v, request: %+v", err, request)
}
-
- // Then we suffix the error with our address.
- s := fmt.Sprintf(", vtgate: %v", servenv.ListeningURL.String())
- return vterrors.WithSuffix(err, s)
+ return vterrors.Errorf(vterrors.Code(err), "vtgate: %s: %v", servenv.ListeningURL.String(), err)
}
func formatError(err error) error {
if err == nil {
return nil
}
- s := fmt.Sprintf(", vtgate: %v", servenv.ListeningURL.String())
- return vterrors.WithSuffix(err, s)
+ return vterrors.Errorf(vterrors.Code(err), "vtgate: %s: %v", servenv.ListeningURL.String(), err)
}
// HandlePanic recovers from panics, and logs / increment counters
@@ -993,7 +914,7 @@ func (vtg *VTGate) HandlePanic(err *error) {
if x := recover(); x != nil {
log.Errorf("Uncaught panic:\n%v\n%s", x, tb.Stack(4))
*err = fmt.Errorf("uncaught panic: %v, vtgate: %v", x, servenv.ListeningURL.String())
- internalErrors.Add("Panic", 1)
+ errorCounts.Add([]string{"Panic", "Unknown", "Unknown", vtrpcpb.Code_INTERNAL.String()}, 1)
}
}
diff --git a/go/vt/vtgate/vtgate_test.go b/go/vt/vtgate/vtgate_test.go
index af7a7faeca5..0d92e0fbb10 100644
--- a/go/vt/vtgate/vtgate_test.go
+++ b/go/vt/vtgate/vtgate_test.go
@@ -16,12 +16,10 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/discovery"
"github.com/youtube/vitess/go/vt/key"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
- "github.com/youtube/vitess/go/vt/tabletserver/sandboxconn"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/vterrors"
- "github.com/youtube/vitess/go/vt/vtgate/gateway"
+ "github.com/youtube/vitess/go/vt/vttablet/sandboxconn"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
"golang.org/x/net/context"
querypb "github.com/youtube/vitess/go/vt/proto/query"
@@ -288,7 +286,7 @@ func TestVTGateExecuteWithKeyspace(t *testing.T) {
nil,
false,
nil)
- want := "keyspace aa not found in vschema, vtgate: "
+ want := "vtgate: : keyspace aa not found in vschema"
if err == nil || err.Error() != want {
t.Errorf("Execute: %v, want %s", err, want)
}
@@ -1190,53 +1188,6 @@ func TestVTGateSplitQueryUnsharded(t *testing.T) {
}
}
-func TestIsErrorCausedByVTGate(t *testing.T) {
- unknownError := fmt.Errorf("unknown error")
- serverError := &tabletconn.ServerError{
- ServerCode: vtrpcpb.ErrorCode_QUERY_NOT_SERVED,
- Err: "vttablet: retry: error message",
- }
- shardConnUnknownErr := &gateway.ShardError{Err: unknownError}
- shardConnServerErr := &gateway.ShardError{Err: serverError}
- shardConnCancelledErr := &gateway.ShardError{Err: context.Canceled}
- scatterConnErrAllUnknownErrs := &ScatterConnError{
- Errs: []error{unknownError, unknownError, unknownError},
- }
- scatterConnErrMixed := &ScatterConnError{
- Errs: []error{unknownError, shardConnServerErr, shardConnCancelledErr},
- }
- scatterConnErrAllNonVTGateErrs := &ScatterConnError{
- Errs: []error{shardConnServerErr, shardConnServerErr, shardConnCancelledErr},
- }
-
- inputToWant := map[error]bool{
- unknownError: true,
- serverError: false,
- context.Canceled: false,
- // OperationalErrors that are not tabletconn.Cancelled might be from VTGate
- tabletconn.ConnClosed: true,
- // Errors wrapped in ShardConnError should get unwrapped
- shardConnUnknownErr: true,
- shardConnServerErr: false,
- shardConnCancelledErr: false,
- // We consider a ScatterConnErr with all unknown errors to be from VTGate
- scatterConnErrAllUnknownErrs: true,
- // We consider a ScatterConnErr with a mix of errors to be from VTGate
- scatterConnErrMixed: true,
- // If every error in ScatterConnErr list is caused by external components, we shouldn't
- // consider the error to be from VTGate
- scatterConnErrAllNonVTGateErrs: false,
- }
-
- for input, want := range inputToWant {
- got := isErrorCausedByVTGate(input)
- if got != want {
- t.Errorf("isErrorCausedByVTGate(%v) => %v, want %v",
- input, got, want)
- }
- }
-}
-
// Functions for testing
// keyspace_id and 'filtered_replication_unfriendly'
// annotations.
@@ -1602,7 +1553,7 @@ func verifyBoundQueriesAnnotatedAsUnfriendly(t *testing.T, expectedNumQueries in
}
}
-func testErrorPropagation(t *testing.T, sbcs []*sandboxconn.SandboxConn, before func(sbc *sandboxconn.SandboxConn), after func(sbc *sandboxconn.SandboxConn), expected vtrpcpb.ErrorCode) {
+func testErrorPropagation(t *testing.T, sbcs []*sandboxconn.SandboxConn, before func(sbc *sandboxconn.SandboxConn), after func(sbc *sandboxconn.SandboxConn), expected vtrpcpb.Code) {
// Execute
for _, sbc := range sbcs {
@@ -1619,7 +1570,7 @@ func testErrorPropagation(t *testing.T, sbcs []*sandboxconn.SandboxConn, before
if err == nil {
t.Errorf("error %v not propagated for Execute", expected)
} else {
- ec := vterrors.RecoverVtErrorCode(err)
+ ec := vterrors.Code(err)
if ec != expected {
t.Errorf("unexpected error, got %v want %v: %v", ec, expected, err)
}
@@ -1644,7 +1595,7 @@ func testErrorPropagation(t *testing.T, sbcs []*sandboxconn.SandboxConn, before
if err == nil {
t.Errorf("error %v not propagated for ExecuteShards", expected)
} else {
- ec := vterrors.RecoverVtErrorCode(err)
+ ec := vterrors.Code(err)
if ec != expected {
t.Errorf("unexpected error, got %v want %v: %v", ec, expected, err)
}
@@ -1669,7 +1620,7 @@ func testErrorPropagation(t *testing.T, sbcs []*sandboxconn.SandboxConn, before
if err == nil {
t.Errorf("error %v not propagated for ExecuteKeyspaceIds", expected)
} else {
- ec := vterrors.RecoverVtErrorCode(err)
+ ec := vterrors.Code(err)
if ec != expected {
t.Errorf("unexpected error, got %v want %v: %v", ec, expected, err)
}
@@ -1694,7 +1645,7 @@ func testErrorPropagation(t *testing.T, sbcs []*sandboxconn.SandboxConn, before
if err == nil {
t.Errorf("error %v not propagated for ExecuteKeyRanges", expected)
} else {
- ec := vterrors.RecoverVtErrorCode(err)
+ ec := vterrors.Code(err)
if ec != expected {
t.Errorf("unexpected error, got %v want %v: %v", ec, expected, err)
}
@@ -1726,7 +1677,7 @@ func testErrorPropagation(t *testing.T, sbcs []*sandboxconn.SandboxConn, before
if err == nil {
t.Errorf("error %v not propagated for ExecuteEntityIds", expected)
} else {
- ec := vterrors.RecoverVtErrorCode(err)
+ ec := vterrors.Code(err)
if ec != expected {
t.Errorf("unexpected error, got %v want %v: %v", ec, expected, err)
}
@@ -1762,7 +1713,7 @@ func testErrorPropagation(t *testing.T, sbcs []*sandboxconn.SandboxConn, before
if err == nil {
t.Errorf("error %v not propagated for ExecuteBatchShards", expected)
} else {
- ec := vterrors.RecoverVtErrorCode(err)
+ ec := vterrors.Code(err)
if ec != expected {
t.Errorf("unexpected error, got %v want %v: %v", ec, expected, err)
}
@@ -1800,7 +1751,7 @@ func testErrorPropagation(t *testing.T, sbcs []*sandboxconn.SandboxConn, before
if err == nil {
t.Errorf("error %v not propagated for ExecuteBatchShards", expected)
} else {
- ec := vterrors.RecoverVtErrorCode(err)
+ ec := vterrors.Code(err)
if ec != expected {
t.Errorf("unexpected error, got %v want %v: %v", ec, expected, err)
}
@@ -1825,7 +1776,7 @@ func testErrorPropagation(t *testing.T, sbcs []*sandboxconn.SandboxConn, before
if err == nil {
t.Errorf("error %v not propagated for StreamExecute", expected)
} else {
- ec := vterrors.RecoverVtErrorCode(err)
+ ec := vterrors.Code(err)
if ec != expected {
t.Errorf("unexpected error, got %v want %v: %v", ec, expected, err)
}
@@ -1851,7 +1802,7 @@ func testErrorPropagation(t *testing.T, sbcs []*sandboxconn.SandboxConn, before
if err == nil {
t.Errorf("error %v not propagated for StreamExecuteShards", expected)
} else {
- ec := vterrors.RecoverVtErrorCode(err)
+ ec := vterrors.Code(err)
if ec != expected {
t.Errorf("unexpected error, got %v want %v: %v", ec, expected, err)
}
@@ -1877,7 +1828,7 @@ func testErrorPropagation(t *testing.T, sbcs []*sandboxconn.SandboxConn, before
if err == nil {
t.Errorf("error %v not propagated for StreamExecuteKeyspaceIds", expected)
} else {
- ec := vterrors.RecoverVtErrorCode(err)
+ ec := vterrors.Code(err)
if ec != expected {
t.Errorf("unexpected error, got %v want %v: %v", ec, expected, err)
}
@@ -1903,7 +1854,7 @@ func testErrorPropagation(t *testing.T, sbcs []*sandboxconn.SandboxConn, before
if err == nil {
t.Errorf("error %v not propagated for StreamExecuteKeyRanges", expected)
} else {
- ec := vterrors.RecoverVtErrorCode(err)
+ ec := vterrors.Code(err)
if ec != expected {
t.Errorf("unexpected error, got %v want %v: %v", ec, expected, err)
}
@@ -1933,7 +1884,7 @@ func testErrorPropagation(t *testing.T, sbcs []*sandboxconn.SandboxConn, before
if err == nil {
t.Errorf("error %v not propagated for Commit", expected)
} else {
- ec := vterrors.RecoverVtErrorCode(err)
+ ec := vterrors.Code(err)
if ec != expected {
t.Errorf("unexpected error, got %v want %v: %v", ec, expected, err)
}
@@ -1959,7 +1910,7 @@ func testErrorPropagation(t *testing.T, sbcs []*sandboxconn.SandboxConn, before
if err == nil {
t.Errorf("error %v not propagated for SplitQuery", expected)
} else {
- ec := vterrors.RecoverVtErrorCode(err)
+ ec := vterrors.Code(err)
if ec != expected {
t.Errorf("unexpected error, got %v want %v: %v", ec, expected, err)
}
@@ -1983,89 +1934,77 @@ func TestErrorPropagation(t *testing.T) {
sbcrdonly,
}
- // ErrorCode_CANCELLED
testErrorPropagation(t, sbcs, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailCanceled = 20
+ sbc.MustFailCodes[vtrpcpb.Code_CANCELED] = 20
}, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailCanceled = 0
- }, vtrpcpb.ErrorCode_CANCELLED)
+ sbc.MustFailCodes[vtrpcpb.Code_CANCELED] = 0
+ }, vtrpcpb.Code_CANCELED)
- // ErrorCode_UNKNOWN_ERROR
testErrorPropagation(t, sbcs, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailUnknownError = 20
+ sbc.MustFailCodes[vtrpcpb.Code_UNKNOWN] = 20
}, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailUnknownError = 0
- }, vtrpcpb.ErrorCode_UNKNOWN_ERROR)
+ sbc.MustFailCodes[vtrpcpb.Code_UNKNOWN] = 0
+ }, vtrpcpb.Code_UNKNOWN)
- // ErrorCode_BAD_INPUT
testErrorPropagation(t, sbcs, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailServer = 20
+ sbc.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 20
}, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailServer = 0
- }, vtrpcpb.ErrorCode_BAD_INPUT)
+ sbc.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 0
+ }, vtrpcpb.Code_INVALID_ARGUMENT)
- // ErrorCode_DEADLINE_EXCEEDED
testErrorPropagation(t, sbcs, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailDeadlineExceeded = 20
+ sbc.MustFailCodes[vtrpcpb.Code_DEADLINE_EXCEEDED] = 20
}, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailDeadlineExceeded = 0
- }, vtrpcpb.ErrorCode_DEADLINE_EXCEEDED)
+ sbc.MustFailCodes[vtrpcpb.Code_DEADLINE_EXCEEDED] = 0
+ }, vtrpcpb.Code_DEADLINE_EXCEEDED)
- // ErrorCode_INTEGRITY_ERROR
testErrorPropagation(t, sbcs, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailIntegrityError = 20
+ sbc.MustFailCodes[vtrpcpb.Code_ALREADY_EXISTS] = 20
}, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailIntegrityError = 0
- }, vtrpcpb.ErrorCode_INTEGRITY_ERROR)
+ sbc.MustFailCodes[vtrpcpb.Code_ALREADY_EXISTS] = 0
+ }, vtrpcpb.Code_ALREADY_EXISTS)
- // ErrorCode_PERMISSION_DENIED
testErrorPropagation(t, sbcs, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailPermissionDenied = 20
+ sbc.MustFailCodes[vtrpcpb.Code_PERMISSION_DENIED] = 20
}, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailPermissionDenied = 0
- }, vtrpcpb.ErrorCode_PERMISSION_DENIED)
+ sbc.MustFailCodes[vtrpcpb.Code_PERMISSION_DENIED] = 0
+ }, vtrpcpb.Code_PERMISSION_DENIED)
- // ErrorCode_RESOURCE_EXHAUSTED
testErrorPropagation(t, sbcs, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailTxPool = 20
+ sbc.MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 20
}, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailTxPool = 0
- }, vtrpcpb.ErrorCode_RESOURCE_EXHAUSTED)
+ sbc.MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 0
+ }, vtrpcpb.Code_RESOURCE_EXHAUSTED)
- // ErrorCode_QUERY_NOT_SERVED
testErrorPropagation(t, sbcs, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailRetry = 20
+ sbc.MustFailCodes[vtrpcpb.Code_FAILED_PRECONDITION] = 20
}, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailRetry = 0
- }, vtrpcpb.ErrorCode_QUERY_NOT_SERVED)
+ sbc.MustFailCodes[vtrpcpb.Code_FAILED_PRECONDITION] = 0
+ }, vtrpcpb.Code_FAILED_PRECONDITION)
- // ErrorCode_NOT_IN_TX
testErrorPropagation(t, sbcs, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailNotTx = 20
+ sbc.MustFailCodes[vtrpcpb.Code_ABORTED] = 20
}, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailNotTx = 0
- }, vtrpcpb.ErrorCode_NOT_IN_TX)
+ sbc.MustFailCodes[vtrpcpb.Code_ABORTED] = 0
+ }, vtrpcpb.Code_ABORTED)
- // ErrorCode_INTERNAL_ERROR
testErrorPropagation(t, sbcs, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailFatal = 20
+ sbc.MustFailCodes[vtrpcpb.Code_INTERNAL] = 20
}, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailFatal = 0
- }, vtrpcpb.ErrorCode_INTERNAL_ERROR)
+ sbc.MustFailCodes[vtrpcpb.Code_INTERNAL] = 0
+ }, vtrpcpb.Code_INTERNAL)
- // ErrorCode_TRANSIENT_ERROR
testErrorPropagation(t, sbcs, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailTransientError = 20
+ sbc.MustFailCodes[vtrpcpb.Code_UNAVAILABLE] = 20
}, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailTransientError = 0
- }, vtrpcpb.ErrorCode_TRANSIENT_ERROR)
+ sbc.MustFailCodes[vtrpcpb.Code_UNAVAILABLE] = 0
+ }, vtrpcpb.Code_UNAVAILABLE)
- // ErrorCode_UNAUTHENTICATED
testErrorPropagation(t, sbcs, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailUnauthenticated = 20
+ sbc.MustFailCodes[vtrpcpb.Code_UNAUTHENTICATED] = 20
}, func(sbc *sandboxconn.SandboxConn) {
- sbc.MustFailUnauthenticated = 0
- }, vtrpcpb.ErrorCode_UNAUTHENTICATED)
+ sbc.MustFailCodes[vtrpcpb.Code_UNAUTHENTICATED] = 0
+ }, vtrpcpb.Code_UNAUTHENTICATED)
}
// This test makes sure that if we start a transaction and hit a critical
@@ -2077,7 +2016,7 @@ func TestErrorIssuesRollback(t *testing.T) {
// Start a transaction, send one statement.
// Simulate an error that should trigger a rollback:
- // vtrpcpb.ErrorCode_NOT_IN_TX case.
+ // vtrpcpb.Code_ABORTED case.
session, err := rpcVTGate.Begin(context.Background(), false)
if err != nil {
t.Fatalf("cannot start a transaction: %v", err)
@@ -2096,7 +2035,7 @@ func TestErrorIssuesRollback(t *testing.T) {
if sbc.RollbackCount.Get() != 0 {
t.Errorf("want 0, got %d", sbc.RollbackCount.Get())
}
- sbc.MustFailNotTx = 20
+ sbc.MustFailCodes[vtrpcpb.Code_ABORTED] = 20
_, err = rpcVTGate.Execute(context.Background(),
"select id from t1",
nil,
@@ -2112,7 +2051,7 @@ func TestErrorIssuesRollback(t *testing.T) {
t.Errorf("want 1, got %d", sbc.RollbackCount.Get())
}
sbc.RollbackCount.Set(0)
- sbc.MustFailNotTx = 0
+ sbc.MustFailCodes[vtrpcpb.Code_ABORTED] = 0
// Start a transaction, send one statement.
// Simulate an error that should trigger a rollback:
@@ -2135,7 +2074,7 @@ func TestErrorIssuesRollback(t *testing.T) {
if sbc.RollbackCount.Get() != 0 {
t.Errorf("want 0, got %d", sbc.RollbackCount.Get())
}
- sbc.MustFailTxPool = 20
+ sbc.MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 20
_, err = rpcVTGate.Execute(context.Background(),
"select id from t1",
nil,
@@ -2151,11 +2090,11 @@ func TestErrorIssuesRollback(t *testing.T) {
t.Errorf("want 1, got %d", sbc.RollbackCount.Get())
}
sbc.RollbackCount.Set(0)
- sbc.MustFailTxPool = 0
+ sbc.MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 0
// Start a transaction, send one statement.
// Simulate an error that should *not* trigger a rollback:
- // vtrpcpb.ErrorCode_INTEGRITY_ERROR case.
+ // vtrpcpb.Code_ALREADY_EXISTS case.
session, err = rpcVTGate.Begin(context.Background(), false)
if err != nil {
t.Fatalf("cannot start a transaction: %v", err)
@@ -2174,7 +2113,7 @@ func TestErrorIssuesRollback(t *testing.T) {
if sbc.RollbackCount.Get() != 0 {
t.Errorf("want 0, got %d", sbc.RollbackCount.Get())
}
- sbc.MustFailIntegrityError = 20
+ sbc.MustFailCodes[vtrpcpb.Code_ALREADY_EXISTS] = 20
_, err = rpcVTGate.Execute(context.Background(),
"select id from t1",
nil,
@@ -2189,5 +2128,5 @@ func TestErrorIssuesRollback(t *testing.T) {
if sbc.RollbackCount.Get() != 0 {
t.Errorf("want 0, got %d", sbc.RollbackCount.Get())
}
- sbc.MustFailIntegrityError = 0
+ sbc.MustFailCodes[vtrpcpb.Code_ALREADY_EXISTS] = 0
}
diff --git a/go/vt/vtgate/vtgateconntest/client.go b/go/vt/vtgate/vtgateconntest/client.go
index 298f6d0f472..d7103844f8c 100644
--- a/go/vt/vtgate/vtgateconntest/client.go
+++ b/go/vt/vtgate/vtgateconntest/client.go
@@ -42,9 +42,9 @@ type fakeVTGateService struct {
}
const expectedErrMatch string = "test vtgate error"
-const expectedCode vtrpcpb.ErrorCode = vtrpcpb.ErrorCode_BAD_INPUT
+const expectedCode vtrpcpb.Code = vtrpcpb.Code_INVALID_ARGUMENT
-var errTestVtGateError = vterrors.FromError(expectedCode, errors.New(expectedErrMatch))
+var errTestVtGateError = vterrors.New(expectedCode, expectedErrMatch)
func newContext() context.Context {
ctx := context.Background()
@@ -985,14 +985,10 @@ func verifyError(t *testing.T, err error, method string) {
return
}
// verify error code
- code := vterrors.RecoverVtErrorCode(err)
+ code := vterrors.Code(err)
if code != expectedCode {
t.Errorf("Unexpected error code from %s: got %v, wanted %v", method, code, expectedCode)
}
- // verify error type
- if _, ok := err.(*vterrors.VitessError); !ok {
- t.Errorf("Unexpected error type from %s: got %v, wanted *vterrors.VitessError", method, reflect.TypeOf(err))
- }
verifyErrorString(t, err, method)
}
diff --git a/go/vt/tabletmanager/agentrpctest/test_agent_rpc.go b/go/vt/vttablet/agentrpctest/test_agent_rpc.go
similarity index 99%
rename from go/vt/tabletmanager/agentrpctest/test_agent_rpc.go
rename to go/vt/vttablet/agentrpctest/test_agent_rpc.go
index 8bd9696d880..af8b8a0add2 100644
--- a/go/vt/tabletmanager/agentrpctest/test_agent_rpc.go
+++ b/go/vt/vttablet/agentrpctest/test_agent_rpc.go
@@ -19,8 +19,8 @@ import (
"github.com/youtube/vitess/go/vt/hook"
"github.com/youtube/vitess/go/vt/logutil"
"github.com/youtube/vitess/go/vt/mysqlctl/tmutils"
- "github.com/youtube/vitess/go/vt/tabletmanager"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletmanager"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
querypb "github.com/youtube/vitess/go/vt/proto/query"
replicationdatapb "github.com/youtube/vitess/go/vt/proto/replicationdata"
diff --git a/go/vt/tabletserver/customrule/filecustomrule/filecustomrule.go b/go/vt/vttablet/customrule/filecustomrule/filecustomrule.go
similarity index 81%
rename from go/vt/tabletserver/customrule/filecustomrule/filecustomrule.go
rename to go/vt/vttablet/customrule/filecustomrule/filecustomrule.go
index 1d2381713af..33c8f746a62 100644
--- a/go/vt/tabletserver/customrule/filecustomrule/filecustomrule.go
+++ b/go/vt/vttablet/customrule/filecustomrule/filecustomrule.go
@@ -11,7 +11,8 @@ import (
"time"
log "github.com/golang/glog"
- "github.com/youtube/vitess/go/vt/tabletserver"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/rules"
)
var (
@@ -24,9 +25,9 @@ var (
// FileCustomRule is an implementation of CustomRuleManager, it reads custom query
// rules from local file for once and push it to vttablet
type FileCustomRule struct {
- path string // Path to the file containing custom query rules
- currentRuleSet *tabletserver.QueryRules // Query rules built from local file
- currentRuleSetTimestamp int64 // Unix timestamp when currentRuleSet is built from local file
+ path string // Path to the file containing custom query rules
+ currentRuleSet *rules.Rules // Query rules built from local file
+ currentRuleSetTimestamp int64 // Unix timestamp when currentRuleSet is built from local file
}
// FileCustomRuleSource is the name of the file based custom rule source
@@ -36,7 +37,7 @@ const FileCustomRuleSource string = "FILE_CUSTOM_RULE"
func NewFileCustomRule() (fcr *FileCustomRule) {
fcr = new(FileCustomRule)
fcr.path = ""
- fcr.currentRuleSet = tabletserver.NewQueryRules()
+ fcr.currentRuleSet = rules.New()
return fcr
}
@@ -53,7 +54,7 @@ func (fcr *FileCustomRule) Open(qsc tabletserver.Controller, rulePath string) er
// Don't update any internal cache, just return error
return err
}
- qrs := tabletserver.NewQueryRules()
+ qrs := rules.New()
err = qrs.UnmarshalJSON(data)
if err != nil {
log.Warningf("Error unmarshaling query rules %v", err)
@@ -68,7 +69,7 @@ func (fcr *FileCustomRule) Open(qsc tabletserver.Controller, rulePath string) er
}
// GetRules returns query rules built from local file
-func (fcr *FileCustomRule) GetRules() (qrs *tabletserver.QueryRules, version int64, err error) {
+func (fcr *FileCustomRule) GetRules() (qrs *rules.Rules, version int64, err error) {
return fcr.currentRuleSet.Copy(), fcr.currentRuleSetTimestamp, nil
}
diff --git a/go/vt/tabletserver/customrule/filecustomrule/filecustomrule_test.go b/go/vt/vttablet/customrule/filecustomrule/filecustomrule_test.go
similarity index 89%
rename from go/vt/tabletserver/customrule/filecustomrule/filecustomrule_test.go
rename to go/vt/vttablet/customrule/filecustomrule/filecustomrule_test.go
index a9d795e3043..ab173c226c6 100644
--- a/go/vt/tabletserver/customrule/filecustomrule/filecustomrule_test.go
+++ b/go/vt/vttablet/customrule/filecustomrule/filecustomrule_test.go
@@ -10,8 +10,8 @@ import (
"path"
"testing"
- "github.com/youtube/vitess/go/vt/tabletserver"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletservermock"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/rules"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletservermock"
)
var customRule1 = `[
@@ -29,7 +29,7 @@ var customRule1 = `[
func TestFileCustomRule(t *testing.T) {
tqsc := tabletservermock.NewController()
- var qrs *tabletserver.QueryRules
+ var qrs *rules.Rules
rulepath := path.Join(os.TempDir(), ".customrule.json")
// Set r1 and try to get it back
err := ioutil.WriteFile(rulepath, []byte(customRule1), os.FileMode(0644))
diff --git a/go/vt/tabletserver/customrule/zkcustomrule/zkcustomrule.go b/go/vt/vttablet/customrule/zkcustomrule/zkcustomrule.go
similarity index 91%
rename from go/vt/tabletserver/customrule/zkcustomrule/zkcustomrule.go
rename to go/vt/vttablet/customrule/zkcustomrule/zkcustomrule.go
index c11f02d60f2..db3188bd641 100644
--- a/go/vt/tabletserver/customrule/zkcustomrule/zkcustomrule.go
+++ b/go/vt/vttablet/customrule/zkcustomrule/zkcustomrule.go
@@ -15,7 +15,8 @@ import (
"golang.org/x/net/context"
"github.com/youtube/vitess/go/vt/servenv"
- "github.com/youtube/vitess/go/vt/tabletserver"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/rules"
"github.com/youtube/vitess/go/vt/topo/zk2topo"
)
@@ -42,7 +43,7 @@ type ZkCustomRule struct {
// mu protects all the following fields.
mu sync.Mutex
watch <-chan zk.Event // Zookeeper watch for listenning data change notifications
- currentRuleSet *tabletserver.QueryRules
+ currentRuleSet *rules.Rules
currentRuleSetVersion int64 // implemented with Zookeeper modification version
done chan struct{}
}
@@ -52,13 +53,13 @@ func NewZkCustomRule(server, path string) *ZkCustomRule {
return &ZkCustomRule{
zconn: zk2topo.Connect(server),
path: path,
- currentRuleSet: tabletserver.NewQueryRules(),
+ currentRuleSet: rules.New(),
currentRuleSetVersion: invalidQueryRulesVersion,
done: make(chan struct{}),
}
}
-// Start registers Zookeeper watch, gets inital QueryRules and starts
+// Start registers Zookeeper watch, gets inital Rules and starts
// polling routine.
func (zkcr *ZkCustomRule) Start(qsc tabletserver.Controller) (err error) {
err = zkcr.refreshWatch()
@@ -86,8 +87,8 @@ func (zkcr *ZkCustomRule) refreshWatch() error {
return nil
}
-// refreshData gets query rules from Zookeeper and refresh internal QueryRules cache
-// this function will also call TabletServer.SetQueryRules to propagate rule changes to query service
+// refreshData gets query rules from Zookeeper and refresh internal Rules cache
+// this function will also call rules.SetQueryRules to propagate rule changes to query service
func (zkcr *ZkCustomRule) refreshData(qsc tabletserver.Controller, nodeRemoval bool) error {
ctx := context.Background()
data, stat, err := zkcr.zconn.Get(ctx, zkcr.path)
@@ -96,7 +97,7 @@ func (zkcr *ZkCustomRule) refreshData(qsc tabletserver.Controller, nodeRemoval b
return err
}
- qrs := tabletserver.NewQueryRules()
+ qrs := rules.New()
if !nodeRemoval {
if err = qrs.UnmarshalJSON([]byte(data)); err != nil {
log.Warningf("Error unmarshaling query rules %v, original data '%s'", err, data)
@@ -154,7 +155,7 @@ func (zkcr *ZkCustomRule) Stop() {
}
// GetRules retrives cached rules.
-func (zkcr *ZkCustomRule) GetRules() (qrs *tabletserver.QueryRules, version int64, err error) {
+func (zkcr *ZkCustomRule) GetRules() (qrs *rules.Rules, version int64, err error) {
zkcr.mu.Lock()
defer zkcr.mu.Unlock()
return zkcr.currentRuleSet.Copy(), zkcr.currentRuleSetVersion, nil
diff --git a/go/vt/tabletserver/customrule/zkcustomrule/zkcustomrule_test.go b/go/vt/vttablet/customrule/zkcustomrule/zkcustomrule_test.go
similarity index 94%
rename from go/vt/tabletserver/customrule/zkcustomrule/zkcustomrule_test.go
rename to go/vt/vttablet/customrule/zkcustomrule/zkcustomrule_test.go
index 5bc404d9df7..ea4aeec5846 100644
--- a/go/vt/tabletserver/customrule/zkcustomrule/zkcustomrule_test.go
+++ b/go/vt/vttablet/customrule/zkcustomrule/zkcustomrule_test.go
@@ -13,8 +13,8 @@ import (
"github.com/samuel/go-zookeeper/zk"
"github.com/youtube/vitess/go/testfiles"
- "github.com/youtube/vitess/go/vt/tabletserver"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletservermock"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/rules"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletservermock"
"github.com/youtube/vitess/go/vt/topo/zk2topo"
"github.com/youtube/vitess/go/zk/zkctl"
)
@@ -67,7 +67,7 @@ func TestZkCustomRule(t *testing.T) {
}
defer zkcr.Stop()
- var qrs *tabletserver.QueryRules
+ var qrs *rules.Rules
// Test if we can successfully fetch the original rule (test GetRules)
qrs, _, err = zkcr.GetRules()
if err != nil {
@@ -101,7 +101,7 @@ func TestZkCustomRule(t *testing.T) {
if err != nil {
t.Fatalf("GetRules of ZkCustomRule should always return nil error, but we receive %v", err)
}
- if reflect.DeepEqual(qrs, tabletserver.NewQueryRules()) {
+ if reflect.DeepEqual(qrs, rules.New()) {
t.Fatalf("Expect empty rule at this point")
}
diff --git a/go/vt/tabletserver/endtoend/acl_test.go b/go/vt/vttablet/endtoend/acl_test.go
similarity index 93%
rename from go/vt/tabletserver/endtoend/acl_test.go
rename to go/vt/vttablet/endtoend/acl_test.go
index b09891fd1d6..97f8a3eb02a 100644
--- a/go/vt/tabletserver/endtoend/acl_test.go
+++ b/go/vt/vttablet/endtoend/acl_test.go
@@ -10,14 +10,14 @@ import (
"strings"
"testing"
- "github.com/youtube/vitess/go/vt/tabletserver"
- "github.com/youtube/vitess/go/vt/tabletserver/endtoend/framework"
+ "github.com/youtube/vitess/go/vt/vttablet/endtoend/framework"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/rules"
)
func TestTableACLNoAccess(t *testing.T) {
client := framework.NewClient()
- aclErr := "error: table acl error"
+ aclErr := "table acl error"
execCases := []struct {
query string
err string
@@ -122,7 +122,7 @@ var rulesJSON = []byte(`[{
}]`)
func TestQueryRules(t *testing.T) {
- rules := tabletserver.NewQueryRules()
+ rules := rules.New()
err := rules.UnmarshalJSON(rulesJSON)
if err != nil {
t.Error(err)
@@ -163,12 +163,12 @@ func TestQueryRules(t *testing.T) {
query := "select * from vitess_test where intval=:asdfg"
bv := map[string]interface{}{"asdfg": 1}
_, err = client.Execute(query, bv)
- want = "error: Query disallowed due to rule: disallow bindvar 'asdfg'"
+ want = "disallowed due to rule: disallow bindvar 'asdfg'"
if err == nil || err.Error() != want {
t.Errorf("Error: %v, want %s", err, want)
}
_, err = client.StreamExecute(query, bv)
- want = "error: Query disallowed due to rule: disallow bindvar 'asdfg'"
+ want = "disallowed due to rule: disallow bindvar 'asdfg'"
if err == nil || err.Error() != want {
t.Errorf("Error: %v, want %s", err, want)
}
diff --git a/go/vt/tabletserver/endtoend/batch_test.go b/go/vt/vttablet/endtoend/batch_test.go
similarity index 95%
rename from go/vt/tabletserver/endtoend/batch_test.go
rename to go/vt/vttablet/endtoend/batch_test.go
index c6db1ef0ac6..2eeea8308a2 100644
--- a/go/vt/tabletserver/endtoend/batch_test.go
+++ b/go/vt/vttablet/endtoend/batch_test.go
@@ -10,8 +10,8 @@ import (
"github.com/youtube/vitess/go/sqltypes"
querypb "github.com/youtube/vitess/go/vt/proto/query"
- "github.com/youtube/vitess/go/vt/tabletserver/endtoend/framework"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/endtoend/framework"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
)
func TestBatchRead(t *testing.T) {
@@ -182,7 +182,7 @@ func TestBatchTransaction(t *testing.T) {
}
defer client.Rollback()
qrl, err = client.ExecuteBatch(queries, true)
- want := "error: cannot start a new transaction in the scope of an existing one"
+ want := "cannot start a new transaction in the scope of an existing one"
if err == nil || err.Error() != want {
t.Errorf("Error: %v, want %s", err, want)
}
diff --git a/go/vt/tabletserver/endtoend/compatibility_test.go b/go/vt/vttablet/endtoend/compatibility_test.go
similarity index 97%
rename from go/vt/tabletserver/endtoend/compatibility_test.go
rename to go/vt/vttablet/endtoend/compatibility_test.go
index d07682ae3a2..5fc6605a415 100644
--- a/go/vt/tabletserver/endtoend/compatibility_test.go
+++ b/go/vt/vttablet/endtoend/compatibility_test.go
@@ -12,7 +12,7 @@ import (
"github.com/youtube/vitess/go/sqltypes"
querypb "github.com/youtube/vitess/go/vt/proto/query"
- "github.com/youtube/vitess/go/vt/tabletserver/endtoend/framework"
+ "github.com/youtube/vitess/go/vt/vttablet/endtoend/framework"
)
var point12 = "\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@"
@@ -680,39 +680,39 @@ func TestTypeLimits(t *testing.T) {
}{{
query: "insert into vitess_ints(tiny) values('str')",
bv: nil,
- out: "error: strconv.ParseInt",
+ out: "strconv.ParseInt",
}, {
query: "insert into vitess_ints(tiny) values(:str)",
bv: map[string]interface{}{"str": "str"},
- out: "error: strconv.ParseInt",
+ out: "strconv.ParseInt",
}, {
query: "insert into vitess_ints(tiny) values(1.2)",
bv: nil,
- out: "error: DML too complex",
+ out: "DML too complex",
}, {
query: "insert into vitess_ints(tiny) values(:fl)",
bv: map[string]interface{}{"fl": 1.2},
- out: "error: type mismatch",
+ out: "type mismatch",
}, {
query: "insert into vitess_strings(vb) values(1)",
bv: nil,
- out: "error: type mismatch",
+ out: "type mismatch",
}, {
query: "insert into vitess_strings(vb) values(:id)",
bv: map[string]interface{}{"id": 1},
- out: "error: type mismatch",
+ out: "type mismatch",
}, {
query: "insert into vitess_strings(vb) select tiny from vitess_ints",
bv: nil,
- out: "error: type mismatch",
+ out: "type mismatch",
}, {
query: "insert into vitess_ints(tiny) select num from vitess_fracts",
bv: nil,
- out: "error: type mismatch",
+ out: "type mismatch",
}, {
query: "insert into vitess_ints(tiny) select vb from vitess_strings",
bv: nil,
- out: "error: type mismatch",
+ out: "type mismatch",
}}
for _, tcase := range mismatchCases {
_, err := client.Execute(tcase.query, tcase.bv)
@@ -721,7 +721,7 @@ func TestTypeLimits(t *testing.T) {
}
}
- want := "error: Out of range"
+ want := "Out of range"
for _, query := range []string{
"insert into vitess_ints(tiny) values(-129)",
"insert into vitess_ints(tiny) select medium from vitess_ints",
@@ -732,7 +732,7 @@ func TestTypeLimits(t *testing.T) {
}
}
- want = "error: Data too long"
+ want = "Data too long"
_, err := client.Execute("insert into vitess_strings(vb) values('12345678901234567')", nil)
if err == nil || !strings.HasPrefix(err.Error(), want) {
t.Errorf("Error: %v, want %s", err, want)
diff --git a/go/vt/tabletserver/endtoend/config_test.go b/go/vt/vttablet/endtoend/config_test.go
similarity index 91%
rename from go/vt/tabletserver/endtoend/config_test.go
rename to go/vt/vttablet/endtoend/config_test.go
index 296cd6c5737..3b312a75087 100644
--- a/go/vt/tabletserver/endtoend/config_test.go
+++ b/go/vt/vttablet/endtoend/config_test.go
@@ -11,8 +11,10 @@ import (
"testing"
"time"
- "github.com/youtube/vitess/go/vt/tabletserver/endtoend/framework"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
+ "github.com/youtube/vitess/go/vt/vttablet/endtoend/framework"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vterrors"
)
// compareIntDiff returns an error if end[tag] != start[tag]+diff.
@@ -103,10 +105,14 @@ func TestConfigVars(t *testing.T) {
}
func TestPoolSize(t *testing.T) {
- vstart := framework.DebugVars()
defer framework.Server.SetPoolSize(framework.Server.PoolSize())
framework.Server.SetPoolSize(1)
+ vstart := framework.DebugVars()
+ if err := verifyIntValue(vstart, "ConnPoolCapacity", 1); err != nil {
+ t.Error(err)
+ }
+
var wg sync.WaitGroup
wg.Add(2)
go func() {
@@ -120,12 +126,14 @@ func TestPoolSize(t *testing.T) {
}()
wg.Wait()
- vend := framework.DebugVars()
- if err := verifyIntValue(vend, "ConnPoolCapacity", 1); err != nil {
- t.Error(err)
- }
- if err := compareIntDiff(vend, "ConnPoolWaitCount", vstart, 1); err != nil {
- t.Error(err)
+ // Parallel plan building can cause multiple conn pool waits.
+ // Check that the wait count was at least incremented once so
+ // we know it's working.
+ tag := "ConnPoolWaitCount"
+ got := framework.FetchInt(framework.DebugVars(), tag)
+ want := framework.FetchInt(vstart, tag)
+ if got <= want {
+ t.Errorf("%s: %d, must be greater than %d", tag, got, want)
}
}
@@ -175,7 +183,7 @@ func TestMexResultSize(t *testing.T) {
client := framework.NewClient()
query := "select * from vitess_test"
_, err := client.Execute(query, nil)
- want := "error: Row count exceeded"
+ want := "Row count exceeded"
if err == nil || !strings.HasPrefix(err.Error(), want) {
t.Errorf("Error: %v, must start with %s", err, want)
}
@@ -305,14 +313,12 @@ func TestQueryTimeout(t *testing.T) {
return
}
_, err = client.Execute("select sleep(1) from vitess_test", nil)
- want := "error: the query was killed"
- if err == nil || !strings.HasPrefix(err.Error(), want) {
- t.Errorf("Error: %v, must start with %s", err, want)
+ if code := vterrors.Code(err); code != vtrpcpb.Code_DEADLINE_EXCEEDED {
+ t.Errorf("Error code: %v, want %v", code, vtrpcpb.Code_DEADLINE_EXCEEDED)
}
_, err = client.Execute("select 1 from dual", nil)
- want = "not_in_tx: Transaction"
- if err == nil || !strings.HasPrefix(err.Error(), want) {
- t.Errorf("Error: %v, must start with %s", err, want)
+ if code := vterrors.Code(err); code != vtrpcpb.Code_ABORTED {
+ t.Errorf("Error code: %v, want %v", code, vtrpcpb.Code_ABORTED)
}
vend := framework.DebugVars()
if err := verifyIntValue(vend, "QueryTimeout", int(100*time.Millisecond)); err != nil {
@@ -347,7 +353,7 @@ func TestStrictMode(t *testing.T) {
}
defer client.Rollback()
- want := "error: DML too complex"
+ want := "DML too complex"
for _, query := range queries {
_, err = client.Execute(query, nil)
if err == nil || err.Error() != want {
diff --git a/go/vt/tabletserver/endtoend/endtoend.go b/go/vt/vttablet/endtoend/endtoend.go
similarity index 100%
rename from go/vt/tabletserver/endtoend/endtoend.go
rename to go/vt/vttablet/endtoend/endtoend.go
diff --git a/go/vt/tabletserver/endtoend/framework/client.go b/go/vt/vttablet/endtoend/framework/client.go
similarity index 98%
rename from go/vt/tabletserver/endtoend/framework/client.go
rename to go/vt/vttablet/endtoend/framework/client.go
index c69e841db18..b08b8dc34a4 100644
--- a/go/vt/tabletserver/endtoend/framework/client.go
+++ b/go/vt/vttablet/endtoend/framework/client.go
@@ -9,8 +9,8 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/callerid"
- "github.com/youtube/vitess/go/vt/tabletserver"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
"golang.org/x/net/context"
querypb "github.com/youtube/vitess/go/vt/proto/query"
diff --git a/go/vt/tabletserver/endtoend/framework/debugschema.go b/go/vt/vttablet/endtoend/framework/debugschema.go
similarity index 100%
rename from go/vt/tabletserver/endtoend/framework/debugschema.go
rename to go/vt/vttablet/endtoend/framework/debugschema.go
diff --git a/go/vt/tabletserver/endtoend/framework/debugvars.go b/go/vt/vttablet/endtoend/framework/debugvars.go
similarity index 100%
rename from go/vt/tabletserver/endtoend/framework/debugvars.go
rename to go/vt/vttablet/endtoend/framework/debugvars.go
diff --git a/go/vt/tabletserver/endtoend/framework/eventcatcher.go b/go/vt/vttablet/endtoend/framework/eventcatcher.go
similarity index 95%
rename from go/vt/tabletserver/endtoend/framework/eventcatcher.go
rename to go/vt/vttablet/endtoend/framework/eventcatcher.go
index 51506351601..8377d8b7d0a 100644
--- a/go/vt/tabletserver/endtoend/framework/eventcatcher.go
+++ b/go/vt/vttablet/endtoend/framework/eventcatcher.go
@@ -9,8 +9,8 @@ import (
"time"
"github.com/youtube/vitess/go/streamlog"
- "github.com/youtube/vitess/go/vt/tabletserver"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
)
// TxCatcher allows you to capture and fetch transactions that are being
diff --git a/go/vt/tabletserver/endtoend/framework/querystats.go b/go/vt/vttablet/endtoend/framework/querystats.go
similarity index 100%
rename from go/vt/tabletserver/endtoend/framework/querystats.go
rename to go/vt/vttablet/endtoend/framework/querystats.go
diff --git a/go/vt/tabletserver/endtoend/framework/server.go b/go/vt/vttablet/endtoend/framework/server.go
similarity index 93%
rename from go/vt/tabletserver/endtoend/framework/server.go
rename to go/vt/vttablet/endtoend/framework/server.go
index 0c7c0069a93..0a172796dcf 100644
--- a/go/vt/tabletserver/endtoend/framework/server.go
+++ b/go/vt/vttablet/endtoend/framework/server.go
@@ -17,8 +17,8 @@ import (
"github.com/youtube/vitess/go/vt/mysqlctl"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
- "github.com/youtube/vitess/go/vt/tabletserver"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
"github.com/youtube/vitess/go/vt/vtgate/fakerpcvtgateconn"
"github.com/youtube/vitess/go/vt/vtgate/vtgateconn"
)
@@ -71,7 +71,7 @@ func StartServer(connParams sqldb.ConnParams) error {
TabletType: topodatapb.TabletType_MASTER,
}
- Server = tabletserver.NewTabletServer(config)
+ Server = tabletserver.NewTabletServerWithNilTopoServer(config)
Server.Register()
err := Server.StartService(Target, dbcfgs, mysqld)
if err != nil {
diff --git a/go/vt/tabletserver/endtoend/framework/streamqueryz.go b/go/vt/vttablet/endtoend/framework/streamqueryz.go
similarity index 100%
rename from go/vt/tabletserver/endtoend/framework/streamqueryz.go
rename to go/vt/vttablet/endtoend/framework/streamqueryz.go
diff --git a/go/vt/tabletserver/endtoend/framework/testcase.go b/go/vt/vttablet/endtoend/framework/testcase.go
similarity index 100%
rename from go/vt/tabletserver/endtoend/framework/testcase.go
rename to go/vt/vttablet/endtoend/framework/testcase.go
diff --git a/go/vt/tabletserver/endtoend/main_test.go b/go/vt/vttablet/endtoend/main_test.go
similarity index 98%
rename from go/vt/tabletserver/endtoend/main_test.go
rename to go/vt/vttablet/endtoend/main_test.go
index 58ac73a4421..67c1b06a026 100644
--- a/go/vt/tabletserver/endtoend/main_test.go
+++ b/go/vt/vttablet/endtoend/main_test.go
@@ -15,8 +15,8 @@ import (
"github.com/youtube/vitess/go/sqldb"
"github.com/youtube/vitess/go/vt/tableacl"
"github.com/youtube/vitess/go/vt/tableacl/simpleacl"
- "github.com/youtube/vitess/go/vt/tabletserver/endtoend/framework"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/endtoend/framework"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
"github.com/youtube/vitess/go/vt/vttest"
)
diff --git a/go/vt/tabletserver/endtoend/message_test.go b/go/vt/vttablet/endtoend/message_test.go
similarity index 98%
rename from go/vt/tabletserver/endtoend/message_test.go
rename to go/vt/vttablet/endtoend/message_test.go
index dc47f24807f..1b18caa0671 100644
--- a/go/vt/tabletserver/endtoend/message_test.go
+++ b/go/vt/vttablet/endtoend/message_test.go
@@ -13,7 +13,7 @@ import (
"time"
"github.com/youtube/vitess/go/sqltypes"
- "github.com/youtube/vitess/go/vt/tabletserver/endtoend/framework"
+ "github.com/youtube/vitess/go/vt/vttablet/endtoend/framework"
querypb "github.com/youtube/vitess/go/vt/proto/query"
)
diff --git a/go/vt/tabletserver/endtoend/metadata_test.go b/go/vt/vttablet/endtoend/metadata_test.go
similarity index 98%
rename from go/vt/tabletserver/endtoend/metadata_test.go
rename to go/vt/vttablet/endtoend/metadata_test.go
index 3fef81e5189..e74b3fcf2e1 100644
--- a/go/vt/tabletserver/endtoend/metadata_test.go
+++ b/go/vt/vttablet/endtoend/metadata_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"github.com/youtube/vitess/go/sqltypes"
- "github.com/youtube/vitess/go/vt/tabletserver/endtoend/framework"
+ "github.com/youtube/vitess/go/vt/vttablet/endtoend/framework"
querypb "github.com/youtube/vitess/go/vt/proto/query"
)
diff --git a/go/vt/tabletserver/endtoend/misc_test.go b/go/vt/vttablet/endtoend/misc_test.go
similarity index 97%
rename from go/vt/tabletserver/endtoend/misc_test.go
rename to go/vt/vttablet/endtoend/misc_test.go
index ba2019f5991..a0b9ea550c7 100644
--- a/go/vt/tabletserver/endtoend/misc_test.go
+++ b/go/vt/vttablet/endtoend/misc_test.go
@@ -19,7 +19,7 @@ import (
"github.com/youtube/vitess/go/sqldb"
"github.com/youtube/vitess/go/sqltypes"
- "github.com/youtube/vitess/go/vt/tabletserver/endtoend/framework"
+ "github.com/youtube/vitess/go/vt/vttablet/endtoend/framework"
querypb "github.com/youtube/vitess/go/vt/proto/query"
)
@@ -157,7 +157,7 @@ func TestNocacheListArgs(t *testing.T) {
"list": []interface{}{},
},
)
- want := "error: empty list supplied for list"
+ want := "empty list supplied for list"
if err == nil || err.Error() != want {
t.Errorf("Error: %v, want %s", err, want)
return
@@ -168,11 +168,11 @@ func TestIntegrityError(t *testing.T) {
vstart := framework.DebugVars()
client := framework.NewClient()
_, err := client.Execute("insert into vitess_test values(1, null, null, null)", nil)
- want := "error: Duplicate entry '1'"
+ want := "Duplicate entry '1'"
if err == nil || !strings.HasPrefix(err.Error(), want) {
t.Errorf("Error: %v, want prefix %s", err, want)
}
- if err := compareIntDiff(framework.DebugVars(), "InfoErrors/DupKey", vstart, 1); err != nil {
+ if err := compareIntDiff(framework.DebugVars(), "Errors/ALREADY_EXISTS", vstart, 1); err != nil {
t.Error(err)
}
}
@@ -220,7 +220,7 @@ func TestUpsertNonPKHit(t *testing.T) {
"(2, 1) on duplicate key update id2 = 2",
nil,
)
- want := "error: Duplicate entry '1' for key 'id2_idx'"
+ want := "Duplicate entry '1' for key 'id2_idx'"
if err == nil || !strings.HasPrefix(err.Error(), want) {
t.Errorf("Execute: %v, must start with %s", err, want)
}
@@ -251,7 +251,7 @@ func TestSchemaReload(t *testing.T) {
if err == nil {
return
}
- want := "error: table vitess_temp not found in schema"
+ want := "table vitess_temp not found in schema"
if err.Error() != want {
t.Errorf("Error: %v, want %s", err, want)
return
diff --git a/go/vt/tabletserver/endtoend/queries_test.go b/go/vt/vttablet/endtoend/queries_test.go
similarity index 99%
rename from go/vt/tabletserver/endtoend/queries_test.go
rename to go/vt/vttablet/endtoend/queries_test.go
index 9ea819cd9d4..3f5746f4d23 100644
--- a/go/vt/tabletserver/endtoend/queries_test.go
+++ b/go/vt/vttablet/endtoend/queries_test.go
@@ -7,7 +7,7 @@ package endtoend
import (
"testing"
- "github.com/youtube/vitess/go/vt/tabletserver/endtoend/framework"
+ "github.com/youtube/vitess/go/vt/vttablet/endtoend/framework"
)
var frameworkErrors = `fail failed:
diff --git a/go/vt/tabletserver/endtoend/sequence_test.go b/go/vt/vttablet/endtoend/sequence_test.go
similarity index 95%
rename from go/vt/tabletserver/endtoend/sequence_test.go
rename to go/vt/vttablet/endtoend/sequence_test.go
index 8b2a37b5c3e..4ee6e004c66 100644
--- a/go/vt/tabletserver/endtoend/sequence_test.go
+++ b/go/vt/vttablet/endtoend/sequence_test.go
@@ -11,7 +11,7 @@ import (
"github.com/youtube/vitess/go/sqltypes"
querypb "github.com/youtube/vitess/go/vt/proto/query"
- "github.com/youtube/vitess/go/vt/tabletserver/endtoend/framework"
+ "github.com/youtube/vitess/go/vt/vttablet/endtoend/framework"
)
func TestSequence(t *testing.T) {
diff --git a/go/vt/tabletserver/endtoend/stream_test.go b/go/vt/vttablet/endtoend/stream_test.go
similarity index 90%
rename from go/vt/tabletserver/endtoend/stream_test.go
rename to go/vt/vttablet/endtoend/stream_test.go
index 486d04d4de3..ee1f8469a14 100644
--- a/go/vt/tabletserver/endtoend/stream_test.go
+++ b/go/vt/vttablet/endtoend/stream_test.go
@@ -13,7 +13,9 @@ import (
"time"
"github.com/youtube/vitess/go/sqltypes"
- "github.com/youtube/vitess/go/vt/tabletserver/endtoend/framework"
+ vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
+ "github.com/youtube/vitess/go/vt/vttablet/endtoend/framework"
+ "github.com/youtube/vitess/go/vt/vterrors"
)
func TestStreamUnion(t *testing.T) {
@@ -102,9 +104,8 @@ func TestStreamTerminate(t *testing.T) {
return nil
},
)
- want := "error: the query was killed"
- if err == nil || !strings.HasPrefix(err.Error(), want) {
- t.Errorf("Error: %v, must start with %s", err, want)
+ if code := vterrors.Code(err); code != vtrpcpb.Code_DEADLINE_EXCEEDED {
+ t.Errorf("Errorcode: %v, want %v", code, vtrpcpb.Code_DEADLINE_EXCEEDED)
}
}
@@ -140,7 +141,7 @@ func populateBigData(client *framework.QueryClient) error {
func TestStreamError(t *testing.T) {
_, err := framework.NewClient().StreamExecute("select count(abcd) from vitess_big", nil)
- want := "error: Unknown column"
+ want := "Unknown column"
if err == nil || !strings.HasPrefix(err.Error(), want) {
t.Errorf("Error: %v, must start with %s", err, want)
}
diff --git a/go/vt/tabletserver/endtoend/transaction_test.go b/go/vt/vttablet/endtoend/transaction_test.go
similarity index 95%
rename from go/vt/tabletserver/endtoend/transaction_test.go
rename to go/vt/vttablet/endtoend/transaction_test.go
index b84fe36f79f..84791a7bbfb 100644
--- a/go/vt/tabletserver/endtoend/transaction_test.go
+++ b/go/vt/vttablet/endtoend/transaction_test.go
@@ -12,12 +12,14 @@ import (
"time"
"github.com/youtube/vitess/go/sqldb"
- "github.com/youtube/vitess/go/vt/tabletserver"
- "github.com/youtube/vitess/go/vt/tabletserver/endtoend/framework"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver"
+ "github.com/youtube/vitess/go/vt/vttablet/endtoend/framework"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vterrors"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
+ vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)
func TestCommit(t *testing.T) {
@@ -290,9 +292,9 @@ func TestAutoCommitOff(t *testing.T) {
defer framework.Server.SetAutoCommit(true)
_, err := framework.NewClient().Execute("insert into vitess_test values(4, null, null, null)", nil)
- want := "error: Disallowed outside transaction"
+ want := "disallowed outside transaction"
if err == nil || !strings.HasPrefix(err.Error(), want) {
- t.Errorf("Error: %v, must start with %s", err, want)
+ t.Errorf("%v, must start with %s", err, want)
}
}
@@ -328,11 +330,11 @@ func TestTxPoolSize(t *testing.T) {
client2 := framework.NewClient()
err = client2.Begin()
- want := "tx_pool_full"
+ want := "connection limit exceeded"
if err == nil || !strings.Contains(err.Error(), want) {
- t.Errorf("Error: %v, must contain %s", err, want)
+ t.Errorf("%v, must contain %s", err, want)
}
- if err := compareIntDiff(framework.DebugVars(), "Errors/TxPoolFull", vstart, 1); err != nil {
+ if err := compareIntDiff(framework.DebugVars(), "Errors/RESOURCE_EXHAUSTED", vstart, 1); err != nil {
t.Error(err)
}
}
@@ -368,9 +370,8 @@ func TestTxTimeout(t *testing.T) {
// Ensure commit fails.
err = client.Commit()
- want := "not_in_tx: Transaction"
- if err == nil || !strings.HasPrefix(err.Error(), want) {
- t.Errorf("Error: %v, must contain %s", err, want)
+ if code := vterrors.Code(err); code != vtrpcpb.Code_ABORTED {
+ t.Errorf("Commit code: %v, want %v", code, vtrpcpb.Code_ABORTED)
}
}
@@ -379,9 +380,9 @@ func TestForUpdate(t *testing.T) {
client := framework.NewClient()
query := fmt.Sprintf("select * from vitess_test where intval=2 %s", mode)
_, err := client.Execute(query, nil)
- want := "error: Disallowed"
+ want := "disallowed"
if err == nil || !strings.HasPrefix(err.Error(), want) {
- t.Errorf("Error: %v, must have prefix %s", err, want)
+ t.Errorf("%v, must have prefix %s", err, want)
}
// We should not get errors here
@@ -555,7 +556,7 @@ func TestMMCommitFlow(t *testing.T) {
err = client.CreateTransaction("aa", []*querypb.Target{})
want := "Duplicate entry"
if err == nil || !strings.Contains(err.Error(), want) {
- t.Errorf("Error: %v, must contain %s", err, want)
+ t.Errorf("%v, must contain %s", err, want)
}
err = client.StartCommit("aa")
@@ -564,9 +565,9 @@ func TestMMCommitFlow(t *testing.T) {
}
err = client.SetRollback("aa", 0)
- want = "error: could not transition to ROLLBACK: aa"
+ want = "could not transition to ROLLBACK: aa"
if err == nil || err.Error() != want {
- t.Errorf("Error: %v, must contain %s", err, want)
+ t.Errorf("%v, must contain %s", err, want)
}
info, err := client.ReadTransaction("aa")
diff --git a/go/vt/tabletmanager/faketmclient/fake_client.go b/go/vt/vttablet/faketmclient/fake_client.go
similarity index 99%
rename from go/vt/tabletmanager/faketmclient/fake_client.go
rename to go/vt/vttablet/faketmclient/fake_client.go
index 71aabc19213..3932472d3c7 100644
--- a/go/vt/tabletmanager/faketmclient/fake_client.go
+++ b/go/vt/vttablet/faketmclient/fake_client.go
@@ -18,7 +18,7 @@ import (
"github.com/youtube/vitess/go/vt/hook"
"github.com/youtube/vitess/go/vt/logutil"
"github.com/youtube/vitess/go/vt/mysqlctl/tmutils"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
logutilpb "github.com/youtube/vitess/go/vt/proto/logutil"
querypb "github.com/youtube/vitess/go/vt/proto/query"
diff --git a/go/vt/tabletserver/grpcqueryservice/server.go b/go/vt/vttablet/grpcqueryservice/server.go
similarity index 90%
rename from go/vt/tabletserver/grpcqueryservice/server.go
rename to go/vt/vttablet/grpcqueryservice/server.go
index 05217d6ce9f..131b015363d 100644
--- a/go/vt/tabletserver/grpcqueryservice/server.go
+++ b/go/vt/vttablet/grpcqueryservice/server.go
@@ -10,8 +10,8 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/callerid"
"github.com/youtube/vitess/go/vt/callinfo"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
"github.com/youtube/vitess/go/vt/vterrors"
"golang.org/x/net/context"
@@ -34,11 +34,11 @@ func (q *query) Execute(ctx context.Context, request *querypb.ExecuteRequest) (r
)
bv, err := querytypes.Proto3ToBindVariables(request.Query.BindVariables)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
result, err := q.server.Execute(ctx, request.Target, request.Query.Sql, bv, request.TransactionId, request.Options)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
return &querypb.ExecuteResponse{
Result: sqltypes.ResultToProto3(result),
@@ -54,11 +54,11 @@ func (q *query) ExecuteBatch(ctx context.Context, request *querypb.ExecuteBatchR
)
bql, err := querytypes.Proto3ToBoundQueryList(request.Queries)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
results, err := q.server.ExecuteBatch(ctx, request.Target, bql, request.AsTransaction, request.TransactionId, request.Options)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
return &querypb.ExecuteBatchResponse{
Results: sqltypes.ResultsToProto3(results),
@@ -74,14 +74,14 @@ func (q *query) StreamExecute(request *querypb.StreamExecuteRequest, stream quer
)
bv, err := querytypes.Proto3ToBindVariables(request.Query.BindVariables)
if err != nil {
- return vterrors.ToGRPCError(err)
+ return vterrors.ToGRPC(err)
}
if err := q.server.StreamExecute(ctx, request.Target, request.Query.Sql, bv, request.Options, func(reply *sqltypes.Result) error {
return stream.Send(&querypb.StreamExecuteResponse{
Result: sqltypes.ResultToProto3(reply),
})
}); err != nil {
- return vterrors.ToGRPCError(err)
+ return vterrors.ToGRPC(err)
}
return nil
}
@@ -95,7 +95,7 @@ func (q *query) Begin(ctx context.Context, request *querypb.BeginRequest) (respo
)
transactionID, err := q.server.Begin(ctx, request.Target)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
return &querypb.BeginResponse{
@@ -111,7 +111,7 @@ func (q *query) Commit(ctx context.Context, request *querypb.CommitRequest) (res
request.ImmediateCallerId,
)
if err := q.server.Commit(ctx, request.Target, request.TransactionId); err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
return &querypb.CommitResponse{}, nil
}
@@ -124,7 +124,7 @@ func (q *query) Rollback(ctx context.Context, request *querypb.RollbackRequest)
request.ImmediateCallerId,
)
if err := q.server.Rollback(ctx, request.Target, request.TransactionId); err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
return &querypb.RollbackResponse{}, nil
@@ -138,7 +138,7 @@ func (q *query) Prepare(ctx context.Context, request *querypb.PrepareRequest) (r
request.ImmediateCallerId,
)
if err := q.server.Prepare(ctx, request.Target, request.TransactionId, request.Dtid); err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
return &querypb.PrepareResponse{}, nil
@@ -152,7 +152,7 @@ func (q *query) CommitPrepared(ctx context.Context, request *querypb.CommitPrepa
request.ImmediateCallerId,
)
if err := q.server.CommitPrepared(ctx, request.Target, request.Dtid); err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
return &querypb.CommitPreparedResponse{}, nil
@@ -166,7 +166,7 @@ func (q *query) RollbackPrepared(ctx context.Context, request *querypb.RollbackP
request.ImmediateCallerId,
)
if err := q.server.RollbackPrepared(ctx, request.Target, request.Dtid, request.TransactionId); err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
return &querypb.RollbackPreparedResponse{}, nil
@@ -180,7 +180,7 @@ func (q *query) CreateTransaction(ctx context.Context, request *querypb.CreateTr
request.ImmediateCallerId,
)
if err := q.server.CreateTransaction(ctx, request.Target, request.Dtid, request.Participants); err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
return &querypb.CreateTransactionResponse{}, nil
@@ -194,7 +194,7 @@ func (q *query) StartCommit(ctx context.Context, request *querypb.StartCommitReq
request.ImmediateCallerId,
)
if err := q.server.StartCommit(ctx, request.Target, request.TransactionId, request.Dtid); err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
return &querypb.StartCommitResponse{}, nil
@@ -208,7 +208,7 @@ func (q *query) SetRollback(ctx context.Context, request *querypb.SetRollbackReq
request.ImmediateCallerId,
)
if err := q.server.SetRollback(ctx, request.Target, request.Dtid, request.TransactionId); err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
return &querypb.SetRollbackResponse{}, nil
@@ -222,7 +222,7 @@ func (q *query) ConcludeTransaction(ctx context.Context, request *querypb.Conclu
request.ImmediateCallerId,
)
if err := q.server.ConcludeTransaction(ctx, request.Target, request.Dtid); err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
return &querypb.ConcludeTransactionResponse{}, nil
@@ -237,7 +237,7 @@ func (q *query) ReadTransaction(ctx context.Context, request *querypb.ReadTransa
)
result, err := q.server.ReadTransaction(ctx, request.Target, request.Dtid)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
return &querypb.ReadTransactionResponse{Metadata: result}, nil
@@ -252,7 +252,7 @@ func (q *query) BeginExecute(ctx context.Context, request *querypb.BeginExecuteR
)
bv, err := querytypes.Proto3ToBindVariables(request.Query.BindVariables)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
result, transactionID, err := q.server.BeginExecute(ctx, request.Target, request.Query.Sql, bv, request.Options)
@@ -260,11 +260,11 @@ func (q *query) BeginExecute(ctx context.Context, request *querypb.BeginExecuteR
// if we have a valid transactionID, return the error in-band
if transactionID != 0 {
return &querypb.BeginExecuteResponse{
- Error: vterrors.VtRPCErrorFromVtError(err),
+ Error: vterrors.ToVTRPC(err),
TransactionId: transactionID,
}, nil
}
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
return &querypb.BeginExecuteResponse{
Result: sqltypes.ResultToProto3(result),
@@ -281,7 +281,7 @@ func (q *query) BeginExecuteBatch(ctx context.Context, request *querypb.BeginExe
)
bql, err := querytypes.Proto3ToBoundQueryList(request.Queries)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
results, transactionID, err := q.server.BeginExecuteBatch(ctx, request.Target, bql, request.AsTransaction, request.Options)
@@ -289,11 +289,11 @@ func (q *query) BeginExecuteBatch(ctx context.Context, request *querypb.BeginExe
// if we have a valid transactionID, return the error in-band
if transactionID != 0 {
return &querypb.BeginExecuteBatchResponse{
- Error: vterrors.VtRPCErrorFromVtError(err),
+ Error: vterrors.ToVTRPC(err),
TransactionId: transactionID,
}, nil
}
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
return &querypb.BeginExecuteBatchResponse{
Results: sqltypes.ResultsToProto3(results),
@@ -313,7 +313,7 @@ func (q *query) MessageStream(request *querypb.MessageStreamRequest, stream quer
Result: sqltypes.ResultToProto3(qr),
})
}); err != nil {
- return vterrors.ToGRPCError(err)
+ return vterrors.ToGRPC(err)
}
return nil
}
@@ -327,7 +327,7 @@ func (q *query) MessageAck(ctx context.Context, request *querypb.MessageAckReque
)
count, err := q.server.MessageAck(ctx, request.Target, request.Name, request.Ids)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
return &querypb.MessageAckResponse{
Result: &querypb.QueryResult{
@@ -346,7 +346,7 @@ func (q *query) SplitQuery(ctx context.Context, request *querypb.SplitQueryReque
bq, err := querytypes.Proto3ToBoundQuery(request.Query)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
splits := []querytypes.QuerySplit{}
splits, err = q.server.SplitQuery(
@@ -358,11 +358,11 @@ func (q *query) SplitQuery(ctx context.Context, request *querypb.SplitQueryReque
request.NumRowsPerQueryPart,
request.Algorithm)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
qs, err := querytypes.QuerySplitsToProto3(splits)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
return &querypb.SplitQueryResponse{Queries: qs}, nil
}
@@ -371,7 +371,7 @@ func (q *query) SplitQuery(ctx context.Context, request *querypb.SplitQueryReque
func (q *query) StreamHealth(request *querypb.StreamHealthRequest, stream queryservicepb.Query_StreamHealthServer) (err error) {
defer q.server.HandlePanic(&err)
if err = q.server.StreamHealth(stream.Context(), stream.Send); err != nil {
- return vterrors.ToGRPCError(err)
+ return vterrors.ToGRPC(err)
}
return nil
}
@@ -388,7 +388,7 @@ func (q *query) UpdateStream(request *querypb.UpdateStreamRequest, stream querys
Event: reply,
})
}); err != nil {
- return vterrors.ToGRPCError(err)
+ return vterrors.ToGRPC(err)
}
return nil
}
diff --git a/go/vt/tabletserver/grpctabletconn/conn.go b/go/vt/vttablet/grpctabletconn/conn.go
similarity index 92%
rename from go/vt/tabletserver/grpctabletconn/conn.go
rename to go/vt/vttablet/grpctabletconn/conn.go
index a9d240a3c5d..1e3534cd469 100644
--- a/go/vt/tabletserver/grpctabletconn/conn.go
+++ b/go/vt/vttablet/grpctabletconn/conn.go
@@ -14,9 +14,9 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/callerid"
"github.com/youtube/vitess/go/vt/servenv/grpcutils"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletconn"
"golang.org/x/net/context"
"google.golang.org/grpc"
@@ -104,7 +104,7 @@ func (conn *gRPCQueryClient) Execute(ctx context.Context, target *querypb.Target
}
er, err := conn.c.Execute(ctx, req)
if err != nil {
- return nil, tabletconn.TabletErrorFromGRPC(err)
+ return nil, tabletconn.ErrorFromGRPC(err)
}
return sqltypes.Proto3ToResult(er.Result), nil
}
@@ -135,7 +135,7 @@ func (conn *gRPCQueryClient) ExecuteBatch(ctx context.Context, target *querypb.T
}
ebr, err := conn.c.ExecuteBatch(ctx, req)
if err != nil {
- return nil, tabletconn.TabletErrorFromGRPC(err)
+ return nil, tabletconn.ErrorFromGRPC(err)
}
return sqltypes.Proto3ToResults(ebr.Results), nil
}
@@ -173,7 +173,7 @@ func (conn *gRPCQueryClient) StreamExecute(ctx context.Context, target *querypb.
}
stream, err := conn.c.StreamExecute(ctx, req)
if err != nil {
- return nil, tabletconn.TabletErrorFromGRPC(err)
+ return nil, tabletconn.ErrorFromGRPC(err)
}
return stream, nil
}()
@@ -184,7 +184,7 @@ func (conn *gRPCQueryClient) StreamExecute(ctx context.Context, target *querypb.
for {
ser, err := stream.Recv()
if err != nil {
- return tabletconn.TabletErrorFromGRPC(err)
+ return tabletconn.ErrorFromGRPC(err)
}
if fields == nil {
fields = ser.Result.Fields
@@ -213,7 +213,7 @@ func (conn *gRPCQueryClient) Begin(ctx context.Context, target *querypb.Target)
}
br, err := conn.c.Begin(ctx, req)
if err != nil {
- return 0, tabletconn.TabletErrorFromGRPC(err)
+ return 0, tabletconn.ErrorFromGRPC(err)
}
return br.TransactionId, nil
}
@@ -234,7 +234,7 @@ func (conn *gRPCQueryClient) Commit(ctx context.Context, target *querypb.Target,
}
_, err := conn.c.Commit(ctx, req)
if err != nil {
- return tabletconn.TabletErrorFromGRPC(err)
+ return tabletconn.ErrorFromGRPC(err)
}
return nil
}
@@ -255,7 +255,7 @@ func (conn *gRPCQueryClient) Rollback(ctx context.Context, target *querypb.Targe
}
_, err := conn.c.Rollback(ctx, req)
if err != nil {
- return tabletconn.TabletErrorFromGRPC(err)
+ return tabletconn.ErrorFromGRPC(err)
}
return nil
}
@@ -277,7 +277,7 @@ func (conn *gRPCQueryClient) Prepare(ctx context.Context, target *querypb.Target
}
_, err := conn.c.Prepare(ctx, req)
if err != nil {
- return tabletconn.TabletErrorFromGRPC(err)
+ return tabletconn.ErrorFromGRPC(err)
}
return nil
}
@@ -298,7 +298,7 @@ func (conn *gRPCQueryClient) CommitPrepared(ctx context.Context, target *querypb
}
_, err := conn.c.CommitPrepared(ctx, req)
if err != nil {
- return tabletconn.TabletErrorFromGRPC(err)
+ return tabletconn.ErrorFromGRPC(err)
}
return nil
}
@@ -320,7 +320,7 @@ func (conn *gRPCQueryClient) RollbackPrepared(ctx context.Context, target *query
}
_, err := conn.c.RollbackPrepared(ctx, req)
if err != nil {
- return tabletconn.TabletErrorFromGRPC(err)
+ return tabletconn.ErrorFromGRPC(err)
}
return nil
}
@@ -342,7 +342,7 @@ func (conn *gRPCQueryClient) CreateTransaction(ctx context.Context, target *quer
}
_, err := conn.c.CreateTransaction(ctx, req)
if err != nil {
- return tabletconn.TabletErrorFromGRPC(err)
+ return tabletconn.ErrorFromGRPC(err)
}
return nil
}
@@ -365,7 +365,7 @@ func (conn *gRPCQueryClient) StartCommit(ctx context.Context, target *querypb.Ta
}
_, err := conn.c.StartCommit(ctx, req)
if err != nil {
- return tabletconn.TabletErrorFromGRPC(err)
+ return tabletconn.ErrorFromGRPC(err)
}
return nil
}
@@ -388,7 +388,7 @@ func (conn *gRPCQueryClient) SetRollback(ctx context.Context, target *querypb.Ta
}
_, err := conn.c.SetRollback(ctx, req)
if err != nil {
- return tabletconn.TabletErrorFromGRPC(err)
+ return tabletconn.ErrorFromGRPC(err)
}
return nil
}
@@ -410,7 +410,7 @@ func (conn *gRPCQueryClient) ConcludeTransaction(ctx context.Context, target *qu
}
_, err := conn.c.ConcludeTransaction(ctx, req)
if err != nil {
- return tabletconn.TabletErrorFromGRPC(err)
+ return tabletconn.ErrorFromGRPC(err)
}
return nil
}
@@ -431,7 +431,7 @@ func (conn *gRPCQueryClient) ReadTransaction(ctx context.Context, target *queryp
}
response, err := conn.c.ReadTransaction(ctx, req)
if err != nil {
- return nil, tabletconn.TabletErrorFromGRPC(err)
+ return nil, tabletconn.ErrorFromGRPC(err)
}
return response.Metadata, nil
}
@@ -458,10 +458,10 @@ func (conn *gRPCQueryClient) BeginExecute(ctx context.Context, target *querypb.T
}
reply, err := conn.c.BeginExecute(ctx, req)
if err != nil {
- return nil, 0, tabletconn.TabletErrorFromGRPC(err)
+ return nil, 0, tabletconn.ErrorFromGRPC(err)
}
if reply.Error != nil {
- return nil, reply.TransactionId, tabletconn.TabletErrorFromRPCError(reply.Error)
+ return nil, reply.TransactionId, tabletconn.ErrorFromVTRPC(reply.Error)
}
return sqltypes.Proto3ToResult(reply.Result), reply.TransactionId, nil
}
@@ -492,10 +492,10 @@ func (conn *gRPCQueryClient) BeginExecuteBatch(ctx context.Context, target *quer
reply, err := conn.c.BeginExecuteBatch(ctx, req)
if err != nil {
- return nil, 0, tabletconn.TabletErrorFromGRPC(err)
+ return nil, 0, tabletconn.ErrorFromGRPC(err)
}
if reply.Error != nil {
- return nil, reply.TransactionId, tabletconn.TabletErrorFromRPCError(reply.Error)
+ return nil, reply.TransactionId, tabletconn.ErrorFromVTRPC(reply.Error)
}
return sqltypes.Proto3ToResults(reply.Results), reply.TransactionId, nil
}
@@ -521,7 +521,7 @@ func (conn *gRPCQueryClient) MessageStream(ctx context.Context, target *querypb.
}
stream, err := conn.c.MessageStream(ctx, req)
if err != nil {
- return nil, tabletconn.TabletErrorFromGRPC(err)
+ return nil, tabletconn.ErrorFromGRPC(err)
}
return stream, nil
}()
@@ -532,7 +532,7 @@ func (conn *gRPCQueryClient) MessageStream(ctx context.Context, target *querypb.
for {
msr, err := stream.Recv()
if err != nil {
- return tabletconn.TabletErrorFromGRPC(err)
+ return tabletconn.ErrorFromGRPC(err)
}
if fields == nil {
fields = msr.Result.Fields
@@ -562,7 +562,7 @@ func (conn *gRPCQueryClient) MessageAck(ctx context.Context, target *querypb.Tar
}
reply, err := conn.c.MessageAck(ctx, req)
if err != nil {
- return 0, tabletconn.TabletErrorFromGRPC(err)
+ return 0, tabletconn.ErrorFromGRPC(err)
}
return int64(reply.Result.RowsAffected), nil
}
@@ -586,7 +586,7 @@ func (conn *gRPCQueryClient) SplitQuery(
q, err := querytypes.BoundQueryToProto3(query.Sql, query.BindVariables)
if err != nil {
- return nil, tabletconn.TabletErrorFromGRPC(err)
+ return nil, tabletconn.ErrorFromGRPC(err)
}
req := &querypb.SplitQueryRequest{
Target: target,
@@ -600,11 +600,11 @@ func (conn *gRPCQueryClient) SplitQuery(
}
sqr, err := conn.c.SplitQuery(ctx, req)
if err != nil {
- return nil, tabletconn.TabletErrorFromGRPC(err)
+ return nil, tabletconn.ErrorFromGRPC(err)
}
split, err := querytypes.Proto3ToQuerySplits(sqr.Queries)
if err != nil {
- return nil, tabletconn.TabletErrorFromGRPC(err)
+ return nil, tabletconn.ErrorFromGRPC(err)
}
return split, nil
}
@@ -624,7 +624,7 @@ func (conn *gRPCQueryClient) StreamHealth(ctx context.Context, callback func(*qu
stream, err := conn.c.StreamHealth(ctx, &querypb.StreamHealthRequest{})
if err != nil {
- return nil, tabletconn.TabletErrorFromGRPC(err)
+ return nil, tabletconn.ErrorFromGRPC(err)
}
return stream, nil
}()
@@ -634,7 +634,7 @@ func (conn *gRPCQueryClient) StreamHealth(ctx context.Context, callback func(*qu
for {
shr, err := stream.Recv()
if err != nil {
- return tabletconn.TabletErrorFromGRPC(err)
+ return tabletconn.ErrorFromGRPC(err)
}
if err := callback(shr); err != nil {
if err == nil || err == io.EOF {
@@ -667,7 +667,7 @@ func (conn *gRPCQueryClient) UpdateStream(ctx context.Context, target *querypb.T
}
stream, err := conn.c.UpdateStream(ctx, req)
if err != nil {
- return nil, tabletconn.TabletErrorFromGRPC(err)
+ return nil, tabletconn.ErrorFromGRPC(err)
}
return stream, nil
}()
@@ -677,7 +677,7 @@ func (conn *gRPCQueryClient) UpdateStream(ctx context.Context, target *querypb.T
for {
r, err := stream.Recv()
if err != nil {
- return tabletconn.TabletErrorFromGRPC(err)
+ return tabletconn.ErrorFromGRPC(err)
}
if err := callback(r.Event); err != nil {
if err == nil || err == io.EOF {
diff --git a/go/vt/tabletserver/grpctabletconn/conn_test.go b/go/vt/vttablet/grpctabletconn/conn_test.go
similarity index 89%
rename from go/vt/tabletserver/grpctabletconn/conn_test.go
rename to go/vt/vttablet/grpctabletconn/conn_test.go
index cb65cda801d..79e029704c9 100644
--- a/go/vt/tabletserver/grpctabletconn/conn_test.go
+++ b/go/vt/vttablet/grpctabletconn/conn_test.go
@@ -10,8 +10,8 @@ import (
"google.golang.org/grpc"
- "github.com/youtube/vitess/go/vt/tabletserver/grpcqueryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconntest"
+ "github.com/youtube/vitess/go/vt/vttablet/grpcqueryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletconntest"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
)
diff --git a/go/vt/tabletmanager/grpctmclient/client.go b/go/vt/vttablet/grpctmclient/client.go
similarity index 99%
rename from go/vt/tabletmanager/grpctmclient/client.go
rename to go/vt/vttablet/grpctmclient/client.go
index e3cc802d53a..7c94965f4d0 100644
--- a/go/vt/tabletmanager/grpctmclient/client.go
+++ b/go/vt/vttablet/grpctmclient/client.go
@@ -17,7 +17,7 @@ import (
"github.com/youtube/vitess/go/vt/logutil"
"github.com/youtube/vitess/go/vt/mysqlctl/tmutils"
"github.com/youtube/vitess/go/vt/servenv/grpcutils"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/topo/topoproto"
"golang.org/x/net/context"
diff --git a/go/vt/tabletmanager/grpctmserver/server.go b/go/vt/vttablet/grpctmserver/server.go
similarity index 99%
rename from go/vt/tabletmanager/grpctmserver/server.go
rename to go/vt/vttablet/grpctmserver/server.go
index c4000988750..6ffcc480dc3 100644
--- a/go/vt/tabletmanager/grpctmserver/server.go
+++ b/go/vt/vttablet/grpctmserver/server.go
@@ -15,7 +15,7 @@ import (
"github.com/youtube/vitess/go/vt/logutil"
"github.com/youtube/vitess/go/vt/mysqlctl/tmutils"
"github.com/youtube/vitess/go/vt/servenv"
- "github.com/youtube/vitess/go/vt/tabletmanager"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletmanager"
"github.com/youtube/vitess/go/vt/vterrors"
logutilpb "github.com/youtube/vitess/go/vt/proto/logutil"
@@ -172,7 +172,7 @@ func (s *server) ExecuteFetchAsDba(ctx context.Context, request *tabletmanagerda
response = &tabletmanagerdatapb.ExecuteFetchAsDbaResponse{}
qr, err := s.agent.ExecuteFetchAsDba(ctx, request.Query, request.DbName, int(request.MaxRows), request.DisableBinlogs, request.ReloadSchema)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
response.Result = qr
return response, nil
@@ -184,7 +184,7 @@ func (s *server) ExecuteFetchAsAllPrivs(ctx context.Context, request *tabletmana
response = &tabletmanagerdatapb.ExecuteFetchAsAllPrivsResponse{}
qr, err := s.agent.ExecuteFetchAsAllPrivs(ctx, request.Query, request.DbName, int(request.MaxRows), request.ReloadSchema)
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
response.Result = qr
return response, nil
@@ -196,7 +196,7 @@ func (s *server) ExecuteFetchAsApp(ctx context.Context, request *tabletmanagerda
response = &tabletmanagerdatapb.ExecuteFetchAsAppResponse{}
qr, err := s.agent.ExecuteFetchAsApp(ctx, request.Query, int(request.MaxRows))
if err != nil {
- return nil, vterrors.ToGRPCError(err)
+ return nil, vterrors.ToGRPC(err)
}
response.Result = qr
return response, nil
diff --git a/go/vt/tabletmanager/grpctmserver/server_test.go b/go/vt/vttablet/grpctmserver/server_test.go
similarity index 91%
rename from go/vt/tabletmanager/grpctmserver/server_test.go
rename to go/vt/vttablet/grpctmserver/server_test.go
index 6d2379d9804..bf3a2a6c444 100644
--- a/go/vt/tabletmanager/grpctmserver/server_test.go
+++ b/go/vt/vttablet/grpctmserver/server_test.go
@@ -8,8 +8,8 @@ import (
"net"
"testing"
- "github.com/youtube/vitess/go/vt/tabletmanager/agentrpctest"
- "github.com/youtube/vitess/go/vt/tabletmanager/grpctmclient"
+ "github.com/youtube/vitess/go/vt/vttablet/agentrpctest"
+ "github.com/youtube/vitess/go/vt/vttablet/grpctmclient"
"google.golang.org/grpc"
tabletmanagerservicepb "github.com/youtube/vitess/go/vt/proto/tabletmanagerservice"
diff --git a/go/vt/tabletserver/queryservice/fakes/error_query_service.go b/go/vt/vttablet/queryservice/fakes/error_query_service.go
similarity index 64%
rename from go/vt/tabletserver/queryservice/fakes/error_query_service.go
rename to go/vt/vttablet/queryservice/fakes/error_query_service.go
index a221e3d84ea..a5c01701720 100644
--- a/go/vt/tabletserver/queryservice/fakes/error_query_service.go
+++ b/go/vt/vttablet/queryservice/fakes/error_query_service.go
@@ -3,7 +3,7 @@ package fakes
import (
"fmt"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
"golang.org/x/net/context"
querypb "github.com/youtube/vitess/go/vt/proto/query"
@@ -12,7 +12,7 @@ import (
// ErrorQueryService is an object that returns an error for all methods.
var ErrorQueryService = queryservice.Wrap(
nil,
- func(ctx context.Context, target *querypb.Target, conn queryservice.QueryService, name string, inTransaction, isStreaming bool, inner func(context.Context, *querypb.Target, queryservice.QueryService) error) error {
+ func(ctx context.Context, target *querypb.Target, conn queryservice.QueryService, name string, inTransaction bool, inner func(context.Context, *querypb.Target, queryservice.QueryService) (error, bool)) error {
return fmt.Errorf("ErrorQueryService does not implement any method")
},
)
diff --git a/go/vt/tabletserver/queryservice/fakes/stream_health_query_service.go b/go/vt/vttablet/queryservice/fakes/stream_health_query_service.go
similarity index 98%
rename from go/vt/tabletserver/queryservice/fakes/stream_health_query_service.go
rename to go/vt/vttablet/queryservice/fakes/stream_health_query_service.go
index 476a9dfc896..e1161567a41 100644
--- a/go/vt/tabletserver/queryservice/fakes/stream_health_query_service.go
+++ b/go/vt/vttablet/queryservice/fakes/stream_health_query_service.go
@@ -7,7 +7,7 @@ import (
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
)
const (
diff --git a/go/vt/tabletserver/queryservice/queryservice.go b/go/vt/vttablet/queryservice/queryservice.go
similarity index 98%
rename from go/vt/tabletserver/queryservice/queryservice.go
rename to go/vt/vttablet/queryservice/queryservice.go
index fa5b1505fb9..9f72127ea66 100644
--- a/go/vt/tabletserver/queryservice/queryservice.go
+++ b/go/vt/vttablet/queryservice/queryservice.go
@@ -12,7 +12,7 @@ import (
"golang.org/x/net/context"
"github.com/youtube/vitess/go/sqltypes"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
querypb "github.com/youtube/vitess/go/vt/proto/query"
)
diff --git a/go/vt/tabletserver/queryservice/wrapped.go b/go/vt/vttablet/queryservice/wrapped.go
similarity index 58%
rename from go/vt/tabletserver/queryservice/wrapped.go
rename to go/vt/vttablet/queryservice/wrapped.go
index b2d02115bd3..635df7a660a 100644
--- a/go/vt/tabletserver/queryservice/wrapped.go
+++ b/go/vt/vttablet/queryservice/wrapped.go
@@ -8,16 +8,21 @@ import (
"golang.org/x/net/context"
"github.com/youtube/vitess/go/sqltypes"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vterrors"
querypb "github.com/youtube/vitess/go/vt/proto/query"
+ vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)
var _ QueryService = &wrappedService{}
// WrapperFunc defines the signature for the wrapper function used by Wrap.
// Parameter ordering is as follows: original parameters, connection, method name, additional parameters and inner func.
-type WrapperFunc func(ctx context.Context, target *querypb.Target, conn QueryService, name string, inTransaction, isStreaming bool, inner func(context.Context, *querypb.Target, QueryService) error) error
+// The inner function returns err and canRetry.
+// If canRetry is true, the error is specific to the current vttablet and can be retried elsewhere.
+// The flag will be false if there was no error.
+type WrapperFunc func(ctx context.Context, target *querypb.Target, conn QueryService, name string, inTransaction bool, inner func(context.Context, *querypb.Target, QueryService) (err error, canRetry bool)) error
// Wrap returns a wrapped version of the original QueryService implementation.
// This lets you avoid repeating boiler-plate code by consolidating it in the
@@ -38,6 +43,27 @@ func Wrap(impl QueryService, wrapper WrapperFunc) QueryService {
}
}
+// canRetry returns true if the error is retryable on a different vttablet.
+// Nil error or a canceled context make it return
+// false. Otherwise, the error code determines the outcome.
+func canRetry(ctx context.Context, err error) bool {
+ if err == nil {
+ return false
+ }
+
+ select {
+ case <-ctx.Done():
+ return false
+ default:
+ }
+
+ switch vterrors.Code(err) {
+ case vtrpcpb.Code_UNAVAILABLE, vtrpcpb.Code_FAILED_PRECONDITION:
+ return true
+ }
+ return false
+}
+
// wrappedService wraps an existing QueryService with
// a decorator function.
type wrappedService struct {
@@ -46,152 +72,177 @@ type wrappedService struct {
}
func (ws *wrappedService) Begin(ctx context.Context, target *querypb.Target) (transactionID int64, err error) {
- err = ws.wrapper(ctx, target, ws.impl, "Begin", false, false, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
+ err = ws.wrapper(ctx, target, ws.impl, "Begin", false, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
var innerErr error
transactionID, innerErr = conn.Begin(ctx, target)
- return innerErr
+ return innerErr, canRetry(ctx, innerErr)
})
return transactionID, err
}
func (ws *wrappedService) Commit(ctx context.Context, target *querypb.Target, transactionID int64) error {
- return ws.wrapper(ctx, target, ws.impl, "Commit", true, false, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
- return conn.Commit(ctx, target, transactionID)
+ return ws.wrapper(ctx, target, ws.impl, "Commit", true, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
+ innerErr := conn.Commit(ctx, target, transactionID)
+ return innerErr, canRetry(ctx, innerErr)
})
}
func (ws *wrappedService) Rollback(ctx context.Context, target *querypb.Target, transactionID int64) error {
- return ws.wrapper(ctx, target, ws.impl, "Rollback", true, false, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
- return conn.Rollback(ctx, target, transactionID)
+ return ws.wrapper(ctx, target, ws.impl, "Rollback", true, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
+ innerErr := conn.Rollback(ctx, target, transactionID)
+ return innerErr, canRetry(ctx, innerErr)
})
}
func (ws *wrappedService) Prepare(ctx context.Context, target *querypb.Target, transactionID int64, dtid string) error {
- return ws.wrapper(ctx, target, ws.impl, "Prepare", true, false, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
- return conn.Prepare(ctx, target, transactionID, dtid)
+ return ws.wrapper(ctx, target, ws.impl, "Prepare", true, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
+ innerErr := conn.Prepare(ctx, target, transactionID, dtid)
+ return innerErr, canRetry(ctx, innerErr)
})
}
func (ws *wrappedService) CommitPrepared(ctx context.Context, target *querypb.Target, dtid string) (err error) {
- return ws.wrapper(ctx, target, ws.impl, "CommitPrepared", true, false, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
- return conn.CommitPrepared(ctx, target, dtid)
+ return ws.wrapper(ctx, target, ws.impl, "CommitPrepared", true, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
+ innerErr := conn.CommitPrepared(ctx, target, dtid)
+ return innerErr, canRetry(ctx, innerErr)
})
}
func (ws *wrappedService) RollbackPrepared(ctx context.Context, target *querypb.Target, dtid string, originalID int64) (err error) {
- return ws.wrapper(ctx, target, ws.impl, "RollbackPrepared", true, false, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
- return conn.RollbackPrepared(ctx, target, dtid, originalID)
+ return ws.wrapper(ctx, target, ws.impl, "RollbackPrepared", true, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
+ innerErr := conn.RollbackPrepared(ctx, target, dtid, originalID)
+ return innerErr, canRetry(ctx, innerErr)
})
}
func (ws *wrappedService) CreateTransaction(ctx context.Context, target *querypb.Target, dtid string, participants []*querypb.Target) (err error) {
- return ws.wrapper(ctx, target, ws.impl, "CreateTransaction", true, false, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
- return conn.CreateTransaction(ctx, target, dtid, participants)
+ return ws.wrapper(ctx, target, ws.impl, "CreateTransaction", true, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
+ innerErr := conn.CreateTransaction(ctx, target, dtid, participants)
+ return innerErr, canRetry(ctx, innerErr)
})
}
func (ws *wrappedService) StartCommit(ctx context.Context, target *querypb.Target, transactionID int64, dtid string) (err error) {
- return ws.wrapper(ctx, target, ws.impl, "StartCommit", true, false, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
- return conn.StartCommit(ctx, target, transactionID, dtid)
+ return ws.wrapper(ctx, target, ws.impl, "StartCommit", true, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
+ innerErr := conn.StartCommit(ctx, target, transactionID, dtid)
+ return innerErr, canRetry(ctx, innerErr)
})
}
func (ws *wrappedService) SetRollback(ctx context.Context, target *querypb.Target, dtid string, transactionID int64) (err error) {
- return ws.wrapper(ctx, target, ws.impl, "SetRollback", true, false, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
- return conn.SetRollback(ctx, target, dtid, transactionID)
+ return ws.wrapper(ctx, target, ws.impl, "SetRollback", true, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
+ innerErr := conn.SetRollback(ctx, target, dtid, transactionID)
+ return innerErr, canRetry(ctx, innerErr)
})
}
func (ws *wrappedService) ConcludeTransaction(ctx context.Context, target *querypb.Target, dtid string) (err error) {
- return ws.wrapper(ctx, target, ws.impl, "ConcludeTransaction", true, false, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
- return conn.ConcludeTransaction(ctx, target, dtid)
+ return ws.wrapper(ctx, target, ws.impl, "ConcludeTransaction", true, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
+ innerErr := conn.ConcludeTransaction(ctx, target, dtid)
+ return innerErr, canRetry(ctx, innerErr)
})
}
func (ws *wrappedService) ReadTransaction(ctx context.Context, target *querypb.Target, dtid string) (metadata *querypb.TransactionMetadata, err error) {
- err = ws.wrapper(ctx, target, ws.impl, "ReadTransaction", false, false, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
+ err = ws.wrapper(ctx, target, ws.impl, "ReadTransaction", false, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
var innerErr error
metadata, innerErr = conn.ReadTransaction(ctx, target, dtid)
- return innerErr
+ return innerErr, canRetry(ctx, innerErr)
})
return metadata, err
}
func (ws *wrappedService) Execute(ctx context.Context, target *querypb.Target, query string, bindVars map[string]interface{}, transactionID int64, options *querypb.ExecuteOptions) (qr *sqltypes.Result, err error) {
- err = ws.wrapper(ctx, target, ws.impl, "Execute", transactionID != 0, false, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
+ inTransaction := (transactionID != 0)
+ err = ws.wrapper(ctx, target, ws.impl, "Execute", inTransaction, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
var innerErr error
qr, innerErr = conn.Execute(ctx, target, query, bindVars, transactionID, options)
- return innerErr
+ // You cannot retry if you're in a transaction.
+ retryable := canRetry(ctx, innerErr) && (!inTransaction)
+ return innerErr, retryable
})
return qr, err
}
func (ws *wrappedService) StreamExecute(ctx context.Context, target *querypb.Target, query string, bindVars map[string]interface{}, options *querypb.ExecuteOptions, callback func(*sqltypes.Result) error) error {
- return ws.wrapper(ctx, target, ws.impl, "StreamExecute", false, true, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
- return conn.StreamExecute(ctx, target, query, bindVars, options, callback)
+ return ws.wrapper(ctx, target, ws.impl, "StreamExecute", false, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
+ streamingStarted := false
+ innerErr := conn.StreamExecute(ctx, target, query, bindVars, options, func(qr *sqltypes.Result) error {
+ streamingStarted = true
+ return callback(qr)
+ })
+ // You cannot restart a stream once it's sent results.
+ retryable := canRetry(ctx, innerErr) && (!streamingStarted)
+ return innerErr, retryable
})
}
func (ws *wrappedService) ExecuteBatch(ctx context.Context, target *querypb.Target, queries []querytypes.BoundQuery, asTransaction bool, transactionID int64, options *querypb.ExecuteOptions) (qrs []sqltypes.Result, err error) {
- err = ws.wrapper(ctx, target, ws.impl, "ExecuteBatch", transactionID != 0, false, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
+ inTransaction := (transactionID != 0)
+ err = ws.wrapper(ctx, target, ws.impl, "ExecuteBatch", inTransaction, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
var innerErr error
qrs, innerErr = conn.ExecuteBatch(ctx, target, queries, asTransaction, transactionID, options)
- return innerErr
+ // You cannot retry if you're in a transaction.
+ retryable := canRetry(ctx, innerErr) && (!inTransaction)
+ return innerErr, retryable
})
return qrs, err
}
func (ws *wrappedService) BeginExecute(ctx context.Context, target *querypb.Target, query string, bindVars map[string]interface{}, options *querypb.ExecuteOptions) (qr *sqltypes.Result, transactionID int64, err error) {
- err = ws.wrapper(ctx, target, ws.impl, "BeginExecute", false, false, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
+ err = ws.wrapper(ctx, target, ws.impl, "BeginExecute", false, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
var innerErr error
qr, transactionID, innerErr = conn.BeginExecute(ctx, target, query, bindVars, options)
- return innerErr
+ return innerErr, canRetry(ctx, innerErr)
})
return qr, transactionID, err
}
func (ws *wrappedService) BeginExecuteBatch(ctx context.Context, target *querypb.Target, queries []querytypes.BoundQuery, asTransaction bool, options *querypb.ExecuteOptions) (qrs []sqltypes.Result, transactionID int64, err error) {
- err = ws.wrapper(ctx, target, ws.impl, "BeginExecuteBatch", false, false, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
+ err = ws.wrapper(ctx, target, ws.impl, "BeginExecuteBatch", false, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
var innerErr error
qrs, transactionID, innerErr = conn.BeginExecuteBatch(ctx, target, queries, asTransaction, options)
- return innerErr
+ return innerErr, canRetry(ctx, innerErr)
})
return qrs, transactionID, err
}
func (ws *wrappedService) MessageStream(ctx context.Context, target *querypb.Target, name string, callback func(*sqltypes.Result) error) error {
- return ws.wrapper(ctx, target, ws.impl, "MessageStream", false, true, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
- return conn.MessageStream(ctx, target, name, callback)
+ return ws.wrapper(ctx, target, ws.impl, "MessageStream", false, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
+ innerErr := conn.MessageStream(ctx, target, name, callback)
+ return innerErr, canRetry(ctx, innerErr)
})
}
func (ws *wrappedService) MessageAck(ctx context.Context, target *querypb.Target, name string, ids []*querypb.Value) (count int64, err error) {
- err = ws.wrapper(ctx, target, ws.impl, "MessageAck", false, false, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
+ err = ws.wrapper(ctx, target, ws.impl, "MessageAck", false, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
var innerErr error
count, innerErr = conn.MessageAck(ctx, target, name, ids)
- return innerErr
+ return innerErr, canRetry(ctx, innerErr)
})
return count, err
}
func (ws *wrappedService) SplitQuery(ctx context.Context, target *querypb.Target, query querytypes.BoundQuery, splitColumns []string, splitCount int64, numRowsPerQueryPart int64, algorithm querypb.SplitQueryRequest_Algorithm) (queries []querytypes.QuerySplit, err error) {
- err = ws.wrapper(ctx, target, ws.impl, "SplitQuery", false, false, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
+ err = ws.wrapper(ctx, target, ws.impl, "SplitQuery", false, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
var innerErr error
queries, innerErr = conn.SplitQuery(ctx, target, query, splitColumns, splitCount, numRowsPerQueryPart, algorithm)
- return innerErr
+ return innerErr, canRetry(ctx, innerErr)
})
return queries, err
}
func (ws *wrappedService) UpdateStream(ctx context.Context, target *querypb.Target, position string, timestamp int64, callback func(*querypb.StreamEvent) error) error {
- return ws.wrapper(ctx, target, ws.impl, "UpdateStream", false, true, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
- return conn.UpdateStream(ctx, target, position, timestamp, callback)
+ return ws.wrapper(ctx, target, ws.impl, "UpdateStream", false, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
+ innerErr := conn.UpdateStream(ctx, target, position, timestamp, callback)
+ return innerErr, canRetry(ctx, innerErr)
})
}
func (ws *wrappedService) StreamHealth(ctx context.Context, callback func(*querypb.StreamHealthResponse) error) error {
- return ws.wrapper(ctx, nil, ws.impl, "StreamHealth", false, true, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
- return conn.StreamHealth(ctx, callback)
+ return ws.wrapper(ctx, nil, ws.impl, "StreamHealth", false, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
+ innerErr := conn.StreamHealth(ctx, callback)
+ return innerErr, canRetry(ctx, innerErr)
})
}
@@ -200,7 +251,8 @@ func (ws *wrappedService) HandlePanic(err *error) {
}
func (ws *wrappedService) Close(ctx context.Context) error {
- return ws.wrapper(ctx, nil, ws.impl, "Close", false, false, func(ctx context.Context, target *querypb.Target, conn QueryService) error {
- return conn.Close(ctx)
+ return ws.wrapper(ctx, nil, ws.impl, "Close", false, func(ctx context.Context, target *querypb.Target, conn QueryService) (error, bool) {
+ // No point retrying Close.
+ return conn.Close(ctx), false
})
}
diff --git a/go/vt/tabletserver/sandboxconn/sandboxconn.go b/go/vt/vttablet/sandboxconn/sandboxconn.go
similarity index 76%
rename from go/vt/tabletserver/sandboxconn/sandboxconn.go
rename to go/vt/vttablet/sandboxconn/sandboxconn.go
index 982e1e9b11a..21474c2ec7c 100644
--- a/go/vt/tabletserver/sandboxconn/sandboxconn.go
+++ b/go/vt/vttablet/sandboxconn/sandboxconn.go
@@ -11,9 +11,9 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/sync2"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vterrors"
"golang.org/x/net/context"
querypb "github.com/youtube/vitess/go/vt/proto/query"
@@ -26,19 +26,7 @@ type SandboxConn struct {
tablet *topodatapb.Tablet
// These errors work for all functions.
- MustFailRetry int
- MustFailFatal int
- MustFailServer int
- MustFailConn int
- MustFailTxPool int
- MustFailNotTx int
- MustFailCanceled int
- MustFailUnknownError int
- MustFailDeadlineExceeded int
- MustFailIntegrityError int
- MustFailPermissionDenied int
- MustFailTransientError int
- MustFailUnauthenticated int
+ MustFailCodes map[vtrpcpb.Code]int
// These errors are triggered only for specific functions.
// For now these are just for the 2PC functions.
@@ -95,100 +83,19 @@ var _ queryservice.QueryService = (*SandboxConn)(nil) // compile-time interface
// NewSandboxConn returns a new SandboxConn targeted to the provided tablet.
func NewSandboxConn(t *topodatapb.Tablet) *SandboxConn {
return &SandboxConn{
- tablet: t,
+ tablet: t,
+ MustFailCodes: make(map[vtrpcpb.Code]int),
}
}
func (sbc *SandboxConn) getError() error {
- if sbc.MustFailRetry > 0 {
- sbc.MustFailRetry--
- return &tabletconn.ServerError{
- Err: "retry: err",
- ServerCode: vtrpcpb.ErrorCode_QUERY_NOT_SERVED,
+ for code, count := range sbc.MustFailCodes {
+ if count == 0 {
+ continue
}
+ sbc.MustFailCodes[code] = count - 1
+ return vterrors.New(code, fmt.Sprintf("%v error", code))
}
- if sbc.MustFailFatal > 0 {
- sbc.MustFailFatal--
- return &tabletconn.ServerError{
- Err: "fatal: err",
- ServerCode: vtrpcpb.ErrorCode_INTERNAL_ERROR,
- }
- }
- if sbc.MustFailServer > 0 {
- sbc.MustFailServer--
- return &tabletconn.ServerError{
- Err: "error: err",
- ServerCode: vtrpcpb.ErrorCode_BAD_INPUT,
- }
- }
- if sbc.MustFailConn > 0 {
- sbc.MustFailConn--
- return tabletconn.OperationalError(fmt.Sprintf("error: conn"))
- }
- if sbc.MustFailTxPool > 0 {
- sbc.MustFailTxPool--
- return &tabletconn.ServerError{
- Err: "tx_pool_full: err",
- ServerCode: vtrpcpb.ErrorCode_RESOURCE_EXHAUSTED,
- }
- }
- if sbc.MustFailNotTx > 0 {
- sbc.MustFailNotTx--
- return &tabletconn.ServerError{
- Err: "not_in_tx: err",
- ServerCode: vtrpcpb.ErrorCode_NOT_IN_TX,
- }
- }
- if sbc.MustFailCanceled > 0 {
- sbc.MustFailCanceled--
- return &tabletconn.ServerError{
- Err: "canceled: err",
- ServerCode: vtrpcpb.ErrorCode_CANCELLED,
- }
- }
- if sbc.MustFailUnknownError > 0 {
- sbc.MustFailUnknownError--
- return &tabletconn.ServerError{
- Err: "unknown error: err",
- ServerCode: vtrpcpb.ErrorCode_UNKNOWN_ERROR,
- }
- }
- if sbc.MustFailDeadlineExceeded > 0 {
- sbc.MustFailDeadlineExceeded--
- return &tabletconn.ServerError{
- Err: "deadline exceeded: err",
- ServerCode: vtrpcpb.ErrorCode_DEADLINE_EXCEEDED,
- }
- }
- if sbc.MustFailIntegrityError > 0 {
- sbc.MustFailIntegrityError--
- return &tabletconn.ServerError{
- Err: "integrity error: err",
- ServerCode: vtrpcpb.ErrorCode_INTEGRITY_ERROR,
- }
- }
- if sbc.MustFailPermissionDenied > 0 {
- sbc.MustFailPermissionDenied--
- return &tabletconn.ServerError{
- Err: "permission denied: err",
- ServerCode: vtrpcpb.ErrorCode_PERMISSION_DENIED,
- }
- }
- if sbc.MustFailTransientError > 0 {
- sbc.MustFailTransientError--
- return &tabletconn.ServerError{
- Err: "transient error: err",
- ServerCode: vtrpcpb.ErrorCode_TRANSIENT_ERROR,
- }
- }
- if sbc.MustFailUnauthenticated > 0 {
- sbc.MustFailUnauthenticated--
- return &tabletconn.ServerError{
- Err: "unauthenticated: err",
- ServerCode: vtrpcpb.ErrorCode_UNAUTHENTICATED,
- }
- }
-
return nil
}
@@ -279,10 +186,7 @@ func (sbc *SandboxConn) Prepare(ctx context.Context, target *querypb.Target, tra
sbc.PrepareCount.Add(1)
if sbc.MustFailPrepare > 0 {
sbc.MustFailPrepare--
- return &tabletconn.ServerError{
- Err: "error: err",
- ServerCode: vtrpcpb.ErrorCode_QUERY_NOT_SERVED,
- }
+ return vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION, "error: err")
}
return sbc.getError()
}
@@ -292,10 +196,7 @@ func (sbc *SandboxConn) CommitPrepared(ctx context.Context, target *querypb.Targ
sbc.CommitPreparedCount.Add(1)
if sbc.MustFailCommitPrepared > 0 {
sbc.MustFailCommitPrepared--
- return &tabletconn.ServerError{
- Err: "error: err",
- ServerCode: vtrpcpb.ErrorCode_QUERY_NOT_SERVED,
- }
+ return vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION, "error: err")
}
return sbc.getError()
}
@@ -305,10 +206,7 @@ func (sbc *SandboxConn) RollbackPrepared(ctx context.Context, target *querypb.Ta
sbc.RollbackPreparedCount.Add(1)
if sbc.MustFailRollbackPrepared > 0 {
sbc.MustFailRollbackPrepared--
- return &tabletconn.ServerError{
- Err: "error: err",
- ServerCode: vtrpcpb.ErrorCode_QUERY_NOT_SERVED,
- }
+ return vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION, "error: err")
}
return sbc.getError()
}
@@ -318,10 +216,7 @@ func (sbc *SandboxConn) CreateTransaction(ctx context.Context, target *querypb.T
sbc.CreateTransactionCount.Add(1)
if sbc.MustFailCreateTransaction > 0 {
sbc.MustFailCreateTransaction--
- return &tabletconn.ServerError{
- Err: "error: err",
- ServerCode: vtrpcpb.ErrorCode_QUERY_NOT_SERVED,
- }
+ return vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION, "error: err")
}
return sbc.getError()
}
@@ -332,10 +227,7 @@ func (sbc *SandboxConn) StartCommit(ctx context.Context, target *querypb.Target,
sbc.StartCommitCount.Add(1)
if sbc.MustFailStartCommit > 0 {
sbc.MustFailStartCommit--
- return &tabletconn.ServerError{
- Err: "error: err",
- ServerCode: vtrpcpb.ErrorCode_QUERY_NOT_SERVED,
- }
+ return vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION, "error: err")
}
return sbc.getError()
}
@@ -346,10 +238,7 @@ func (sbc *SandboxConn) SetRollback(ctx context.Context, target *querypb.Target,
sbc.SetRollbackCount.Add(1)
if sbc.MustFailSetRollback > 0 {
sbc.MustFailSetRollback--
- return &tabletconn.ServerError{
- Err: "error: err",
- ServerCode: vtrpcpb.ErrorCode_QUERY_NOT_SERVED,
- }
+ return vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION, "error: err")
}
return sbc.getError()
}
@@ -360,10 +249,7 @@ func (sbc *SandboxConn) ConcludeTransaction(ctx context.Context, target *querypb
sbc.ConcludeTransactionCount.Add(1)
if sbc.MustFailConcludeTransaction > 0 {
sbc.MustFailConcludeTransaction--
- return &tabletconn.ServerError{
- Err: "error: err",
- ServerCode: vtrpcpb.ErrorCode_QUERY_NOT_SERVED,
- }
+ return vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION, "error: err")
}
return sbc.getError()
}
diff --git a/go/vt/tabletserver/sysloglogger/sysloglogger.go b/go/vt/vttablet/sysloglogger/sysloglogger.go
similarity index 96%
rename from go/vt/tabletserver/sysloglogger/sysloglogger.go
rename to go/vt/vttablet/sysloglogger/sysloglogger.go
index 52cd67f6718..2bd34b396da 100644
--- a/go/vt/tabletserver/sysloglogger/sysloglogger.go
+++ b/go/vt/vttablet/sysloglogger/sysloglogger.go
@@ -7,7 +7,7 @@ import (
log "github.com/golang/glog"
"github.com/youtube/vitess/go/vt/servenv"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
)
// syslogWriter is an interface that wraps syslog.Writer, so it can be mocked in unit tests.
diff --git a/go/vt/tabletserver/sysloglogger/sysloglogger_test.go b/go/vt/vttablet/sysloglogger/sysloglogger_test.go
similarity index 98%
rename from go/vt/tabletserver/sysloglogger/sysloglogger_test.go
rename to go/vt/vttablet/sysloglogger/sysloglogger_test.go
index d3081e20d25..77999b83ce6 100644
--- a/go/vt/tabletserver/sysloglogger/sysloglogger_test.go
+++ b/go/vt/vttablet/sysloglogger/sysloglogger_test.go
@@ -11,7 +11,7 @@ import (
"golang.org/x/net/context"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
)
// fakeWriter is a mock of the real syslog writer, to enable capturing and playing back of log messages in unit testing.
diff --git a/go/vt/vttablet/tabletconn/grpc_error.go b/go/vt/vttablet/tabletconn/grpc_error.go
new file mode 100644
index 00000000000..e10a1449178
--- /dev/null
+++ b/go/vt/vttablet/tabletconn/grpc_error.go
@@ -0,0 +1,33 @@
+package tabletconn
+
+import (
+ "io"
+
+ "github.com/youtube/vitess/go/vt/vterrors"
+ "google.golang.org/grpc"
+
+ vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
+)
+
+// ErrorFromGRPC converts a GRPC error to vtError for
+// tabletserver calls.
+func ErrorFromGRPC(err error) error {
+ // io.EOF is end of stream. Don't treat it as an error.
+ if err == nil || err == io.EOF {
+ return nil
+ }
+ return vterrors.Errorf(vtrpcpb.Code(grpc.Code(err)), "vttablet: %v", err)
+}
+
+// ErrorFromVTRPC converts a *vtrpcpb.RPCError to vtError for
+// tabletserver calls.
+func ErrorFromVTRPC(err *vtrpcpb.RPCError) error {
+ if err == nil {
+ return nil
+ }
+ code := err.Code
+ if code == vtrpcpb.Code_OK {
+ code = vterrors.LegacyErrorCodeToCode(err.LegacyCode)
+ }
+ return vterrors.Errorf(code, "vttablet: %s", err.Message)
+}
diff --git a/go/vt/vttablet/tabletconn/grpc_error_test.go b/go/vt/vttablet/tabletconn/grpc_error_test.go
new file mode 100644
index 00000000000..3369ce49200
--- /dev/null
+++ b/go/vt/vttablet/tabletconn/grpc_error_test.go
@@ -0,0 +1,44 @@
+// Copyright 2017, Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package tabletconn
+
+import (
+ "testing"
+
+ vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
+ "github.com/youtube/vitess/go/vt/vterrors"
+)
+
+func TestTabletErrorFromRPCError(t *testing.T) {
+ testcases := []struct {
+ in *vtrpcpb.RPCError
+ want vtrpcpb.Code
+ }{{
+ in: &vtrpcpb.RPCError{
+ LegacyCode: vtrpcpb.LegacyErrorCode_BAD_INPUT_LEGACY,
+ Message: "bad input",
+ },
+ want: vtrpcpb.Code_INVALID_ARGUMENT,
+ }, {
+ in: &vtrpcpb.RPCError{
+ LegacyCode: vtrpcpb.LegacyErrorCode_BAD_INPUT_LEGACY,
+ Message: "bad input",
+ Code: vtrpcpb.Code_INVALID_ARGUMENT,
+ },
+ want: vtrpcpb.Code_INVALID_ARGUMENT,
+ }, {
+ in: &vtrpcpb.RPCError{
+ Message: "bad input",
+ Code: vtrpcpb.Code_INVALID_ARGUMENT,
+ },
+ want: vtrpcpb.Code_INVALID_ARGUMENT,
+ }}
+ for _, tcase := range testcases {
+ got := vterrors.Code(ErrorFromVTRPC(tcase.in))
+ if got != tcase.want {
+ t.Errorf("FromVtRPCError(%v):\n%v, want\n%v", tcase.in, got, tcase.want)
+ }
+ }
+}
diff --git a/go/vt/tabletserver/tabletconn/tablet_conn.go b/go/vt/vttablet/tabletconn/tablet_conn.go
similarity index 62%
rename from go/vt/tabletserver/tabletconn/tablet_conn.go
rename to go/vt/vttablet/tabletconn/tablet_conn.go
index 95b38ca7c6e..7ad40e27b01 100644
--- a/go/vt/tabletserver/tabletconn/tablet_conn.go
+++ b/go/vt/vttablet/tabletconn/tablet_conn.go
@@ -10,15 +10,16 @@ import (
log "github.com/golang/glog"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vterrors"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)
-const (
+var (
// ConnClosed is returned when the underlying connection was closed.
- ConnClosed = OperationalError("vttablet: Connection Closed")
+ ConnClosed = vterrors.New(vtrpcpb.Code_UNAVAILABLE, "vttablet: Connection Closed")
)
var (
@@ -26,32 +27,6 @@ var (
TabletProtocol = flag.String("tablet_protocol", "grpc", "how to talk to the vttablets")
)
-// ServerError represents an error that was returned from
-// a vttablet server. it implements vterrors.VtError.
-type ServerError struct {
- Err string
- // ServerCode is the error code that we got from the server.
- ServerCode vtrpcpb.ErrorCode
-}
-
-func (e *ServerError) Error() string { return e.Err }
-
-// VtErrorCode returns the underlying Vitess error code.
-// This makes ServerError implement vterrors.VtError.
-func (e *ServerError) VtErrorCode() vtrpcpb.ErrorCode { return e.ServerCode }
-
-// OperationalError represents an error due to a failure to
-// communicate with vttablet.
-type OperationalError string
-
-func (e OperationalError) Error() string { return string(e) }
-
-// In all the following calls, context is an opaque structure that may
-// carry data related to the call. For instance, if an incoming RPC
-// call is responsible for these outgoing calls, and the incoming
-// protocol and outgoing protocols support forwarding information, use
-// context.
-
// TabletDialer represents a function that will return a QueryService
// object that can communicate with a tablet. Only the tablet's
// HostName and PortMap should be used (and maybe the alias for debug
diff --git a/go/vt/tabletserver/tabletconntest/fakequeryservice.go b/go/vt/vttablet/tabletconntest/fakequeryservice.go
similarity index 99%
rename from go/vt/tabletserver/tabletconntest/fakequeryservice.go
rename to go/vt/vttablet/tabletconntest/fakequeryservice.go
index 2e9c3e09a79..6a8fdffec83 100644
--- a/go/vt/tabletserver/tabletconntest/fakequeryservice.go
+++ b/go/vt/vttablet/tabletconntest/fakequeryservice.go
@@ -11,8 +11,7 @@ import (
"github.com/golang/protobuf/proto"
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/callerid"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
@@ -28,7 +27,7 @@ type FakeQueryService struct {
// these fields are used to simulate and synchronize on errors
HasError bool
HasBeginError bool
- TabletError *tabletenv.TabletError
+ TabletError error
ErrorWait chan struct{}
// these fields are used to simulate and synchronize on panics
diff --git a/go/vt/tabletserver/tabletconntest/tabletconntest.go b/go/vt/vttablet/tabletconntest/tabletconntest.go
similarity index 95%
rename from go/vt/tabletserver/tabletconntest/tabletconntest.go
rename to go/vt/vttablet/tabletconntest/tabletconntest.go
index 35e6f92393b..b634095f38b 100644
--- a/go/vt/tabletserver/tabletconntest/tabletconntest.go
+++ b/go/vt/vttablet/tabletconntest/tabletconntest.go
@@ -15,9 +15,8 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/callerid"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletconn"
"github.com/youtube/vitess/go/vt/vterrors"
"golang.org/x/net/context"
@@ -29,24 +28,24 @@ import (
// testErrorHelper will check one instance of each error type,
// to make sure we propagate the errors properly.
func testErrorHelper(t *testing.T, f *FakeQueryService, name string, ef func(context.Context) error) {
- errors := []*tabletenv.TabletError{
+ errors := []error{
// A few generic errors
- tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "generic error"),
- tabletenv.NewTabletError(vtrpcpb.ErrorCode_UNKNOWN_ERROR, "uncaught panic"),
- tabletenv.NewTabletError(vtrpcpb.ErrorCode_UNAUTHENTICATED, "missing caller id"),
- tabletenv.NewTabletError(vtrpcpb.ErrorCode_PERMISSION_DENIED, "table acl error: nil acl"),
+ vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "generic error"),
+ vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "uncaught panic"),
+ vterrors.Errorf(vtrpcpb.Code_UNAUTHENTICATED, "missing caller id"),
+ vterrors.Errorf(vtrpcpb.Code_PERMISSION_DENIED, "table acl error: nil acl"),
// Client will retry on this specific error
- tabletenv.NewTabletError(vtrpcpb.ErrorCode_QUERY_NOT_SERVED, "Query disallowed due to rule: %v", "cool rule"),
+ vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "query disallowed due to rule: %v", "cool rule"),
// Client may retry on another server on this specific error
- tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "Could not verify strict mode"),
+ vterrors.Errorf(vtrpcpb.Code_INTERNAL, "could not verify strict mode"),
// This is usually transaction pool full
- tabletenv.NewTabletError(vtrpcpb.ErrorCode_RESOURCE_EXHAUSTED, "Transaction pool connection limit exceeded"),
+ vterrors.Errorf(vtrpcpb.Code_RESOURCE_EXHAUSTED, "transaction pool connection limit exceeded"),
// Transaction expired or was unknown
- tabletenv.NewTabletError(vtrpcpb.ErrorCode_NOT_IN_TX, "Transaction 12"),
+ vterrors.Errorf(vtrpcpb.Code_ABORTED, "transaction 12"),
}
for _, e := range errors {
f.TabletError = e
@@ -58,23 +57,14 @@ func testErrorHelper(t *testing.T, f *FakeQueryService, name string, ef func(con
}
// First we check the recoverable vtrpc code is right.
- code := vterrors.RecoverVtErrorCode(err)
- if code != e.ErrorCode {
- t.Errorf("unexpected server code from %v: got %v, wanted %v", name, code, e.ErrorCode)
+ code := vterrors.Code(err)
+ wantcode := vterrors.Code(e)
+ if code != wantcode {
+ t.Errorf("unexpected server code from %v: got %v, wanted %v", name, code, wantcode)
}
- // Double-check we always get a ServerError, although
- // we don't really care that much.
- if !f.TestingGateway {
- if _, ok := err.(*tabletconn.ServerError); !ok {
- t.Errorf("error wasn't a tabletconn.ServerError for %v?", name)
- continue
- }
- }
-
- // and last we check we preserve the text, with the right prefix
- if !strings.Contains(err.Error(), e.Prefix()+e.Message) {
- t.Errorf("client error message '%v' for %v doesn't contain expected server text message '%v'", err.Error(), name, e.Prefix()+e.Message)
+ if !strings.Contains(err.Error(), e.Error()) {
+ t.Errorf("client error message '%v' for %v doesn't contain expected server text message '%v'", err.Error(), name, e)
}
}
f.TabletError = nil
diff --git a/go/vt/tabletmanager/action_agent.go b/go/vt/vttablet/tabletmanager/action_agent.go
similarity index 98%
rename from go/vt/tabletmanager/action_agent.go
rename to go/vt/vttablet/tabletmanager/action_agent.go
index 3c28cf8bc1b..7472db16a2b 100644
--- a/go/vt/tabletmanager/action_agent.go
+++ b/go/vt/vttablet/tabletmanager/action_agent.go
@@ -48,10 +48,10 @@ import (
"github.com/youtube/vitess/go/vt/logutil"
"github.com/youtube/vitess/go/vt/mysqlctl"
"github.com/youtube/vitess/go/vt/servenv"
- "github.com/youtube/vitess/go/vt/tabletserver"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletservermock"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletservermock"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
@@ -546,7 +546,7 @@ func (agent *ActionAgent) Start(ctx context.Context, mysqlPort, vtPort, gRPCPort
// (it needs the dbname, so it has to be delayed up to here,
// but it has to be before updateState below that may use it)
if initUpdateStream {
- us := binlog.NewUpdateStream(agent.MysqlDaemon, agent.DBConfigs.App.DbName)
+ us := binlog.NewUpdateStream(agent.MysqlDaemon, agent.QueryServiceControl.SchemaEngine(), agent.DBConfigs.App.DbName)
agent.UpdateStream = us
servenv.OnRun(func() {
us.RegisterService()
diff --git a/go/vt/tabletmanager/binlog_players.go b/go/vt/vttablet/tabletmanager/binlog_players.go
similarity index 100%
rename from go/vt/tabletmanager/binlog_players.go
rename to go/vt/vttablet/tabletmanager/binlog_players.go
diff --git a/go/vt/tabletmanager/binlog_players_test.go b/go/vt/vttablet/tabletmanager/binlog_players_test.go
similarity index 99%
rename from go/vt/tabletmanager/binlog_players_test.go
rename to go/vt/vttablet/tabletmanager/binlog_players_test.go
index e0219c4b266..ea9709fef5d 100644
--- a/go/vt/tabletmanager/binlog_players_test.go
+++ b/go/vt/vttablet/tabletmanager/binlog_players_test.go
@@ -19,9 +19,9 @@ import (
"github.com/youtube/vitess/go/vt/key"
"github.com/youtube/vitess/go/vt/mysqlctl"
"github.com/youtube/vitess/go/vt/mysqlctl/tmutils"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice/fakes"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice/fakes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletconn"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
diff --git a/go/vt/tabletmanager/events/state_change.go b/go/vt/vttablet/tabletmanager/events/state_change.go
similarity index 100%
rename from go/vt/tabletmanager/events/state_change.go
rename to go/vt/vttablet/tabletmanager/events/state_change.go
diff --git a/go/vt/tabletmanager/healthcheck.go b/go/vt/vttablet/tabletmanager/healthcheck.go
similarity index 100%
rename from go/vt/tabletmanager/healthcheck.go
rename to go/vt/vttablet/tabletmanager/healthcheck.go
diff --git a/go/vt/tabletmanager/healthcheck_test.go b/go/vt/vttablet/tabletmanager/healthcheck_test.go
similarity index 99%
rename from go/vt/tabletmanager/healthcheck_test.go
rename to go/vt/vttablet/tabletmanager/healthcheck_test.go
index 1d6a0e686a7..673877919c6 100644
--- a/go/vt/tabletmanager/healthcheck_test.go
+++ b/go/vt/vttablet/tabletmanager/healthcheck_test.go
@@ -20,8 +20,8 @@ import (
"github.com/youtube/vitess/go/vt/binlog/binlogplayer"
"github.com/youtube/vitess/go/vt/health"
"github.com/youtube/vitess/go/vt/mysqlctl"
- "github.com/youtube/vitess/go/vt/tabletserver"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletservermock"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletservermock"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
diff --git a/go/vt/tabletmanager/init_tablet.go b/go/vt/vttablet/tabletmanager/init_tablet.go
similarity index 100%
rename from go/vt/tabletmanager/init_tablet.go
rename to go/vt/vttablet/tabletmanager/init_tablet.go
diff --git a/go/vt/tabletmanager/init_tablet_test.go b/go/vt/vttablet/tabletmanager/init_tablet_test.go
similarity index 100%
rename from go/vt/tabletmanager/init_tablet_test.go
rename to go/vt/vttablet/tabletmanager/init_tablet_test.go
diff --git a/go/vt/tabletmanager/initial_rebuild.go b/go/vt/vttablet/tabletmanager/initial_rebuild.go
similarity index 85%
rename from go/vt/tabletmanager/initial_rebuild.go
rename to go/vt/vttablet/tabletmanager/initial_rebuild.go
index 56a41d2cfe8..540787606b4 100644
--- a/go/vt/tabletmanager/initial_rebuild.go
+++ b/go/vt/vttablet/tabletmanager/initial_rebuild.go
@@ -33,5 +33,10 @@ func (agent *ActionAgent) maybeRebuildKeyspace(cell, keyspace string) {
if err := topotools.RebuildKeyspace(agent.batchCtx, logutil.NewConsoleLogger(), agent.TopoServer, keyspace, []string{cell}); err != nil {
log.Warningf("RebuildKeyspace(%v,%v) failed: %v, may need to run 'vtctl RebuildKeyspaceGraph %v')", cell, keyspace, err, keyspace)
+ return
+ }
+
+ if err := topotools.RebuildVSchema(agent.batchCtx, logutil.NewConsoleLogger(), agent.TopoServer, []string{cell}); err != nil {
+ log.Warningf("RebuildVSchema(%v) failed: %v, may need to run 'vtctl RebuildVSchemaGraph --cells %v", cell, err, cell)
}
}
diff --git a/go/vt/tabletmanager/orchestrator.go b/go/vt/vttablet/tabletmanager/orchestrator.go
similarity index 100%
rename from go/vt/tabletmanager/orchestrator.go
rename to go/vt/vttablet/tabletmanager/orchestrator.go
diff --git a/go/vt/tabletmanager/replication_reporter.go b/go/vt/vttablet/tabletmanager/replication_reporter.go
similarity index 100%
rename from go/vt/tabletmanager/replication_reporter.go
rename to go/vt/vttablet/tabletmanager/replication_reporter.go
diff --git a/go/vt/tabletmanager/replication_reporter_test.go b/go/vt/vttablet/tabletmanager/replication_reporter_test.go
similarity index 100%
rename from go/vt/tabletmanager/replication_reporter_test.go
rename to go/vt/vttablet/tabletmanager/replication_reporter_test.go
diff --git a/go/vt/tabletmanager/restore.go b/go/vt/vttablet/tabletmanager/restore.go
similarity index 100%
rename from go/vt/tabletmanager/restore.go
rename to go/vt/vttablet/tabletmanager/restore.go
diff --git a/go/vt/tabletmanager/rpc_actions.go b/go/vt/vttablet/tabletmanager/rpc_actions.go
similarity index 100%
rename from go/vt/tabletmanager/rpc_actions.go
rename to go/vt/vttablet/tabletmanager/rpc_actions.go
diff --git a/go/vt/tabletmanager/rpc_agent.go b/go/vt/vttablet/tabletmanager/rpc_agent.go
similarity index 100%
rename from go/vt/tabletmanager/rpc_agent.go
rename to go/vt/vttablet/tabletmanager/rpc_agent.go
diff --git a/go/vt/tabletmanager/rpc_backup.go b/go/vt/vttablet/tabletmanager/rpc_backup.go
similarity index 100%
rename from go/vt/tabletmanager/rpc_backup.go
rename to go/vt/vttablet/tabletmanager/rpc_backup.go
diff --git a/go/vt/tabletmanager/rpc_binlog_players.go b/go/vt/vttablet/tabletmanager/rpc_binlog_players.go
similarity index 100%
rename from go/vt/tabletmanager/rpc_binlog_players.go
rename to go/vt/vttablet/tabletmanager/rpc_binlog_players.go
diff --git a/go/vt/tabletmanager/rpc_external_reparent.go b/go/vt/vttablet/tabletmanager/rpc_external_reparent.go
similarity index 99%
rename from go/vt/tabletmanager/rpc_external_reparent.go
rename to go/vt/vttablet/tabletmanager/rpc_external_reparent.go
index 25ece48537d..7164d22e4aa 100644
--- a/go/vt/tabletmanager/rpc_external_reparent.go
+++ b/go/vt/vttablet/tabletmanager/rpc_external_reparent.go
@@ -15,7 +15,7 @@ import (
"github.com/youtube/vitess/go/stats"
"github.com/youtube/vitess/go/trace"
"github.com/youtube/vitess/go/vt/concurrency"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
"github.com/youtube/vitess/go/vt/topotools/events"
diff --git a/go/vt/tabletmanager/rpc_query.go b/go/vt/vttablet/tabletmanager/rpc_query.go
similarity index 100%
rename from go/vt/tabletmanager/rpc_query.go
rename to go/vt/vttablet/tabletmanager/rpc_query.go
diff --git a/go/vt/tabletmanager/rpc_replication.go b/go/vt/vttablet/tabletmanager/rpc_replication.go
similarity index 100%
rename from go/vt/tabletmanager/rpc_replication.go
rename to go/vt/vttablet/tabletmanager/rpc_replication.go
diff --git a/go/vt/tabletmanager/rpc_schema.go b/go/vt/vttablet/tabletmanager/rpc_schema.go
similarity index 100%
rename from go/vt/tabletmanager/rpc_schema.go
rename to go/vt/vttablet/tabletmanager/rpc_schema.go
diff --git a/go/vt/tabletmanager/rpc_server.go b/go/vt/vttablet/tabletmanager/rpc_server.go
similarity index 100%
rename from go/vt/tabletmanager/rpc_server.go
rename to go/vt/vttablet/tabletmanager/rpc_server.go
diff --git a/go/vt/tabletmanager/state_change.go b/go/vt/vttablet/tabletmanager/state_change.go
similarity index 96%
rename from go/vt/tabletmanager/state_change.go
rename to go/vt/vttablet/tabletmanager/state_change.go
index 11cd50d0d34..2996cef5fbf 100644
--- a/go/vt/tabletmanager/state_change.go
+++ b/go/vt/vttablet/tabletmanager/state_change.go
@@ -18,9 +18,9 @@ import (
"github.com/youtube/vitess/go/event"
"github.com/youtube/vitess/go/trace"
"github.com/youtube/vitess/go/vt/mysqlctl"
- "github.com/youtube/vitess/go/vt/tabletmanager/events"
- "github.com/youtube/vitess/go/vt/tabletserver"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletmanager/events"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/rules"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
@@ -45,7 +45,7 @@ const blacklistQueryRules string = "BlacklistQueryRules"
// loadBlacklistRules loads and builds the blacklist query rules
func (agent *ActionAgent) loadBlacklistRules(tablet *topodatapb.Tablet, blacklistedTables []string) (err error) {
- blacklistRules := tabletserver.NewQueryRules()
+ blacklistRules := rules.New()
if len(blacklistedTables) > 0 {
// tables, first resolve wildcards
tables, err := mysqlctl.ResolveTables(agent.MysqlDaemon, topoproto.TabletDbName(tablet), blacklistedTables)
@@ -57,7 +57,7 @@ func (agent *ActionAgent) loadBlacklistRules(tablet *topodatapb.Tablet, blacklis
// that we don't add a rule to blacklist all tables
if len(tables) > 0 {
log.Infof("Blacklisting tables %v", strings.Join(tables, ", "))
- qr := tabletserver.NewQueryRule("enforce blacklisted tables", "blacklisted_table", tabletserver.QRFailRetry)
+ qr := rules.NewQueryRule("enforce blacklisted tables", "blacklisted_table", rules.QRFailRetry)
for _, t := range tables {
qr.AddTableCond(t)
}
diff --git a/go/vt/tabletserver/codex.go b/go/vt/vttablet/tabletserver/codex.go
similarity index 82%
rename from go/vt/tabletserver/codex.go
rename to go/vt/vttablet/tabletserver/codex.go
index 6cb22d177db..eef0ac037c1 100644
--- a/go/vt/tabletserver/codex.go
+++ b/go/vt/vttablet/tabletserver/codex.go
@@ -7,8 +7,8 @@ package tabletserver
import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
+ "github.com/youtube/vitess/go/vt/vterrors"
querypb "github.com/youtube/vitess/go/vt/proto/query"
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
@@ -42,7 +42,7 @@ func resolvePKValues(table *schema.Table, pkValues []interface{}, bindVars map[s
if length == -1 {
length = len(list)
} else if len(list) != length {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "mismatched lengths for values %v", pkValues)
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "mismatched lengths for values %v", pkValues)
}
return nil
}
@@ -93,7 +93,7 @@ func resolvePKValues(table *schema.Table, pkValues []interface{}, bindVars map[s
func resolveListArg(col *schema.TableColumn, key string, bindVars map[string]interface{}) ([]sqltypes.Value, error) {
val, _, err := sqlparser.FetchBindVar(key, bindVars)
if err != nil {
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "%v", err)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%v", err)
}
switch list := val.(type) {
@@ -102,7 +102,7 @@ func resolveListArg(col *schema.TableColumn, key string, bindVars map[string]int
for i, v := range list {
sqlval, err := sqltypes.BuildConverted(col.Type, v)
if err != nil {
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "%v", err)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%v", err)
}
if err = validateValue(col, sqlval); err != nil {
return nil, err
@@ -112,7 +112,7 @@ func resolveListArg(col *schema.TableColumn, key string, bindVars map[string]int
return resolved, nil
case *querypb.BindVariable:
if list.Type != querypb.Type_TUPLE {
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "expecting list for bind var %s: %v", key, list)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "expecting list for bind var %s: %v", key, list)
}
resolved := make([]sqltypes.Value, len(list.Values))
for i, v := range list.Values {
@@ -120,7 +120,7 @@ func resolveListArg(col *schema.TableColumn, key string, bindVars map[string]int
sqlval := sqltypes.MakeTrusted(v.Type, v.Value)
sqlval, err := sqltypes.BuildConverted(col.Type, sqlval)
if err != nil {
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "%v", err)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%v", err)
}
if err = validateValue(col, sqlval); err != nil {
return nil, err
@@ -129,7 +129,7 @@ func resolveListArg(col *schema.TableColumn, key string, bindVars map[string]int
}
return resolved, nil
default:
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "unknown type for bind variable %v", key)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unknown type for bind variable %v", key)
}
}
@@ -159,12 +159,12 @@ func resolveValue(col *schema.TableColumn, value interface{}, bindVars map[strin
if v, ok := value.(string); ok {
value, _, err = sqlparser.FetchBindVar(v, bindVars)
if err != nil {
- return result, tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "%v", err)
+ return result, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%v", err)
}
}
result, err = sqltypes.BuildConverted(col.Type, value)
if err != nil {
- return result, tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "%v", err)
+ return result, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%v", err)
}
if err = validateValue(col, result); err != nil {
return result, err
@@ -178,23 +178,23 @@ func resolveNumber(value interface{}, bindVars map[string]interface{}) (int64, e
if v, ok := value.(string); ok {
value, _, err = sqlparser.FetchBindVar(v, bindVars)
if err != nil {
- return 0, tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "%v", err)
+ return 0, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%v", err)
}
}
v, err := sqltypes.BuildValue(value)
if err != nil {
- return 0, tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "%v", err)
+ return 0, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%v", err)
}
ret, err := v.ParseInt64()
if err != nil {
- return 0, tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "%v", err)
+ return 0, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%v", err)
}
return ret, nil
}
func validateRow(table *schema.Table, columnNumbers []int, row []sqltypes.Value) error {
if len(row) != len(columnNumbers) {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "data inconsistency %d vs %d", len(row), len(columnNumbers))
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "data inconsistency %d vs %d", len(row), len(columnNumbers))
}
for j, value := range row {
if err := validateValue(&table.Columns[columnNumbers[j]], value); err != nil {
@@ -211,11 +211,11 @@ func validateValue(col *schema.TableColumn, value sqltypes.Value) error {
}
if sqltypes.IsIntegral(col.Type) {
if !value.IsIntegral() {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "type mismatch, expecting numeric type for %v for column: %v", value, col)
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "type mismatch, expecting numeric type for %v for column: %v", value, col)
}
} else if col.Type == sqltypes.VarBinary {
if !value.IsQuoted() {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "type mismatch, expecting string type for %v for column: %v", value, col)
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "type mismatch, expecting string type for %v for column: %v", value, col)
}
}
return nil
diff --git a/go/vt/tabletserver/codex_test.go b/go/vt/vttablet/tabletserver/codex_test.go
similarity index 91%
rename from go/vt/tabletserver/codex_test.go
rename to go/vt/vttablet/tabletserver/codex_test.go
index 5f7d58afd6d..9d3107ccc2f 100644
--- a/go/vt/tabletserver/codex_test.go
+++ b/go/vt/vttablet/tabletserver/codex_test.go
@@ -14,7 +14,8 @@ import (
querypb "github.com/youtube/vitess/go/vt/proto/query"
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
+ "github.com/youtube/vitess/go/vt/vterrors"
)
func TestCodexBuildValuesList(t *testing.T) {
@@ -57,7 +58,7 @@ func TestCodexBuildValuesList(t *testing.T) {
// invalid value
bindVars["pk1"] = struct{}{}
pkValues = []interface{}{":pk1"}
- wantErr := "error: unexpected type struct {}: {}"
+ wantErr := "unexpected type struct {}: {}"
got, err := buildValueList(table, pkValues, bindVars)
@@ -68,7 +69,7 @@ func TestCodexBuildValuesList(t *testing.T) {
// type mismatch int
bindVars["pk1"] = "str"
pkValues = []interface{}{":pk1"}
- wantErr = "error: strconv.ParseInt"
+ wantErr = "strconv.ParseInt"
got, err = buildValueList(table, pkValues, bindVars)
if err == nil || !strings.Contains(err.Error(), wantErr) {
@@ -79,7 +80,7 @@ func TestCodexBuildValuesList(t *testing.T) {
bindVars["pk1"] = 1
bindVars["pk2"] = 1
pkValues = []interface{}{":pk1", ":pk2"}
- wantErr = "error: type mismatch, expecting string type for 1"
+ wantErr = "type mismatch, expecting string type for 1"
got, err = buildValueList(table, pkValues, bindVars)
if err == nil || !strings.Contains(err.Error(), wantErr) {
@@ -211,7 +212,7 @@ func TestCodexBuildValuesList(t *testing.T) {
pk1Val,
"::list",
}
- wantErr = "error: empty list supplied for list"
+ wantErr = "empty list supplied for list"
got, err = buildValueList(table, pkValues, bindVars)
if err == nil || !strings.Contains(err.Error(), wantErr) {
t.Fatalf("got %v, want %v", err, wantErr)
@@ -225,7 +226,7 @@ func TestCodexBuildValuesList(t *testing.T) {
pk1Val,
":list",
}
- wantErr = "error: unexpected arg type []interface {} for key list"
+ wantErr = "unexpected arg type []interface {} for key list"
got, err = buildValueList(table, pkValues, bindVars)
if err == nil || !strings.Contains(err.Error(), wantErr) {
t.Fatalf("got %v, want %v", err, wantErr)
@@ -233,7 +234,6 @@ func TestCodexBuildValuesList(t *testing.T) {
}
func TestCodexResolvePKValues(t *testing.T) {
- testUtils := newTestUtils()
table := createTable("Table",
[]string{"pk1", "pk2", "col1"},
[]querypb.Type{sqltypes.Int64, sqltypes.VarBinary, sqltypes.Int32},
@@ -257,7 +257,9 @@ func TestCodexResolvePKValues(t *testing.T) {
pkValues = make([]interface{}, 0, 10)
pkValues = append(pkValues, sqltypes.MakeString([]byte("type_mismatch")))
_, _, err = resolvePKValues(table, pkValues, nil)
- testUtils.checkTabletError(t, err, vtrpcpb.ErrorCode_BAD_INPUT, "strconv.ParseInt")
+ if code := vterrors.Code(err); code != vtrpcpb.Code_INVALID_ARGUMENT {
+ t.Errorf("resolvePKValues: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT)
+ }
// pkValues with different length
bindVariables = make(map[string]interface{})
bindVariables[key] = 1
@@ -269,7 +271,9 @@ func TestCodexResolvePKValues(t *testing.T) {
pkValues = append(pkValues, []interface{}{":" + key})
pkValues = append(pkValues, []interface{}{":" + key2, ":" + key3})
_, _, err = resolvePKValues(table, pkValues, bindVariables)
- testUtils.checkTabletError(t, err, vtrpcpb.ErrorCode_BAD_INPUT, "mismatched lengths")
+ if code := vterrors.Code(err); code != vtrpcpb.Code_INVALID_ARGUMENT {
+ t.Errorf("resolvePKValues: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT)
+ }
}
func TestCodexResolveListArg(t *testing.T) {
@@ -284,7 +288,9 @@ func TestCodexResolveListArg(t *testing.T) {
bindVariables[key] = []interface{}{fmt.Errorf("error is not supported")}
_, err := resolveListArg(table.GetPKColumn(0), "::"+key, bindVariables)
- testUtils.checkTabletError(t, err, vtrpcpb.ErrorCode_BAD_INPUT, "")
+ if code := vterrors.Code(err); code != vtrpcpb.Code_INVALID_ARGUMENT {
+ t.Errorf("resolvePKValues: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT)
+ }
// This should successfully convert.
bindVariables[key] = []interface{}{"1"}
@@ -322,19 +328,19 @@ func TestResolveNumber(t *testing.T) {
bv: map[string]interface{}{
"a": []interface{}{10},
},
- outErr: "error: unexpected type []interface {}: [10]",
+ outErr: "unexpected type []interface {}: [10]",
}, {
v: ":a",
- outErr: "error: missing bind var a",
+ outErr: "missing bind var a",
}, {
v: make(chan int),
- outErr: "error: unexpected type chan int",
+ outErr: "unexpected type chan int",
}, {
v: int64(1),
out: int64(1),
}, {
v: 1.2,
- outErr: "error: strconv.ParseInt",
+ outErr: "strconv.ParseInt",
}}
for _, tc := range testcases {
got, err := resolveNumber(tc.v, tc.bv)
@@ -406,17 +412,20 @@ func TestCodexBuildStreamComment(t *testing.T) {
}
func TestCodexValidateRow(t *testing.T) {
- testUtils := newTestUtils()
table := createTable("Table",
[]string{"pk1", "pk2", "col1"},
[]querypb.Type{sqltypes.Int64, sqltypes.VarBinary, sqltypes.Int32},
[]string{"pk1", "pk2"})
// #columns and #rows do not match
err := validateRow(table, []int{1}, []sqltypes.Value{})
- testUtils.checkTabletError(t, err, vtrpcpb.ErrorCode_BAD_INPUT, "data inconsistency")
+ if code := vterrors.Code(err); code != vtrpcpb.Code_INVALID_ARGUMENT {
+ t.Errorf("validateRow: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT)
+ }
// column 0 is int type but row is in string type
err = validateRow(table, []int{0}, []sqltypes.Value{sqltypes.MakeString([]byte("str"))})
- testUtils.checkTabletError(t, err, vtrpcpb.ErrorCode_BAD_INPUT, "type mismatch")
+ if code := vterrors.Code(err); code != vtrpcpb.Code_INVALID_ARGUMENT {
+ t.Errorf("validateRow: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT)
+ }
}
func TestCodexApplyFilterWithPKDefaults(t *testing.T) {
diff --git a/go/vt/tabletserver/comments.go b/go/vt/vttablet/tabletserver/comments.go
similarity index 100%
rename from go/vt/tabletserver/comments.go
rename to go/vt/vttablet/tabletserver/comments.go
diff --git a/go/vt/tabletserver/comments_test.go b/go/vt/vttablet/tabletserver/comments_test.go
similarity index 100%
rename from go/vt/tabletserver/comments_test.go
rename to go/vt/vttablet/tabletserver/comments_test.go
diff --git a/go/vt/tabletserver/connpool/dbconn.go b/go/vt/vttablet/tabletserver/connpool/dbconn.go
similarity index 88%
rename from go/vt/tabletserver/connpool/dbconn.go
rename to go/vt/vttablet/tabletserver/connpool/dbconn.go
index 698cc6cddab..ee3dac20a79 100644
--- a/go/vt/tabletserver/connpool/dbconn.go
+++ b/go/vt/vttablet/tabletserver/connpool/dbconn.go
@@ -10,14 +10,14 @@ import (
"time"
log "github.com/golang/glog"
+ "github.com/youtube/vitess/go/mysqlconn"
"github.com/youtube/vitess/go/sqldb"
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/sync2"
"github.com/youtube/vitess/go/trace"
"github.com/youtube/vitess/go/vt/dbconnpool"
querypb "github.com/youtube/vitess/go/vt/proto/query"
- vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
"golang.org/x/net/context"
)
@@ -62,14 +62,14 @@ func (dbc *DBConn) Exec(ctx context.Context, query string, maxrows int, wantfiel
switch {
case err == nil:
return r, nil
- case !tabletenv.IsConnErr(err):
+ case !mysqlconn.IsConnErr(err):
// MySQL error that isn't due to a connection issue
- return nil, tabletenv.NewTabletErrorSQL(vtrpcpb.ErrorCode_UNKNOWN_ERROR, err)
+ return nil, err
case attempt == 2:
// If the MySQL connection is bad, we assume that there is nothing wrong with
// the query itself, and retrying it might succeed. The MySQL connection might
// fix itself, or the query could succeed on a different VtTablet.
- return nil, tabletenv.NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, err)
+ return nil, err
}
// Connection error. Try to reconnect.
@@ -78,7 +78,7 @@ func (dbc *DBConn) Exec(ctx context.Context, query string, maxrows int, wantfiel
dbc.pool.checker.CheckMySQL()
// Return the error of the reconnect and not the original connection error.
// NOTE: We return a tryable error code here.
- return nil, tabletenv.NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, reconnectErr)
+ return nil, reconnectErr
}
// Reconnect succeeded. Retry query at second attempt.
@@ -130,7 +130,7 @@ func (dbc *DBConn) Stream(ctx context.Context, query string, callback func(*sqlt
switch {
case err == nil:
return nil
- case !tabletenv.IsConnErr(err) || resultSent || attempt == 2:
+ case !mysqlconn.IsConnErr(err) || resultSent || attempt == 2:
// MySQL error that isn't due to a connection issue
return err
}
@@ -190,16 +190,14 @@ func (dbc *DBConn) Kill(reason string) error {
killConn, err := dbc.pool.dbaPool.Get(context.TODO())
if err != nil {
log.Warningf("Failed to get conn from dba pool: %v", err)
- // TODO(aaijazi): Find the right error code for an internal error that we don't want to retry
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "Failed to get conn from dba pool: %v", err)
+ return err
}
defer killConn.Recycle()
sql := fmt.Sprintf("kill %d", dbc.conn.ID())
_, err = killConn.ExecuteFetch(sql, 10000, false)
if err != nil {
log.Errorf("Could not kill query %s: %v", dbc.Current(), err)
- // TODO(aaijazi): Find the right error code for an internal error that we don't want to retry
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "Could not kill query %s: %v", dbc.Current(), err)
+ return err
}
return nil
}
diff --git a/go/vt/tabletserver/connpool/dbconn_test.go b/go/vt/vttablet/tabletserver/connpool/dbconn_test.go
similarity index 98%
rename from go/vt/tabletserver/connpool/dbconn_test.go
rename to go/vt/vttablet/tabletserver/connpool/dbconn_test.go
index 31ea3a648cd..6b44cc6f90b 100644
--- a/go/vt/tabletserver/connpool/dbconn_test.go
+++ b/go/vt/vttablet/tabletserver/connpool/dbconn_test.go
@@ -80,7 +80,7 @@ func TestDBConnKill(t *testing.T) {
// Kill failed because we are not able to connect to the database
db.EnableConnFail()
err = dbConn.Kill("test kill")
- want := "Failed to get conn from dba pool"
+ want := "Lost connection"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("Exec: %v, want %s", err, want)
}
@@ -100,7 +100,7 @@ func TestDBConnKill(t *testing.T) {
// Kill failed because "kill query_id" failed
db.AddRejectedQuery(newKillQuery, errors.New("rejected"))
err = dbConn.Kill("test kill")
- want = "Could not kill query"
+ want = "rejected"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("Exec: %v, want %s", err, want)
}
diff --git a/go/vt/tabletserver/connpool/pool.go b/go/vt/vttablet/tabletserver/connpool/pool.go
similarity index 92%
rename from go/vt/tabletserver/connpool/pool.go
rename to go/vt/vttablet/tabletserver/connpool/pool.go
index 37369f74fbf..889ecef7510 100644
--- a/go/vt/tabletserver/connpool/pool.go
+++ b/go/vt/vttablet/tabletserver/connpool/pool.go
@@ -12,10 +12,15 @@ import (
"github.com/youtube/vitess/go/sqldb"
"github.com/youtube/vitess/go/stats"
"github.com/youtube/vitess/go/vt/dbconnpool"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vterrors"
"golang.org/x/net/context"
)
+// ErrConnPoolClosed is returned when the connection pool is closed.
+var ErrConnPoolClosed = vterrors.New(vtrpcpb.Code_INTERNAL, "internal error: unexpected: conn pool is closed")
+
// usedNames is for preventing expvar from panicking. Tests
// create pool objects multiple time. If a name was previously
// used, expvar initialization is skipped.
@@ -108,7 +113,7 @@ func (cp *Pool) Close() {
func (cp *Pool) Get(ctx context.Context) (*DBConn, error) {
p := cp.pool()
if p == nil {
- return nil, tabletenv.ErrConnPoolClosed
+ return nil, ErrConnPoolClosed
}
r, err := p.Get(ctx)
if err != nil {
@@ -121,7 +126,7 @@ func (cp *Pool) Get(ctx context.Context) (*DBConn, error) {
func (cp *Pool) Put(conn *DBConn) {
p := cp.pool()
if p == nil {
- panic(tabletenv.ErrConnPoolClosed)
+ panic(ErrConnPoolClosed)
}
if conn == nil {
p.Put(nil)
diff --git a/go/vt/tabletserver/connpool/pool_test.go b/go/vt/vttablet/tabletserver/connpool/pool_test.go
similarity index 100%
rename from go/vt/tabletserver/connpool/pool_test.go
rename to go/vt/vttablet/tabletserver/connpool/pool_test.go
diff --git a/go/vt/tabletserver/controller.go b/go/vt/vttablet/tabletserver/controller.go
similarity index 85%
rename from go/vt/tabletserver/controller.go
rename to go/vt/vttablet/tabletserver/controller.go
index 501a052ddfb..75d2f7e197f 100644
--- a/go/vt/tabletserver/controller.go
+++ b/go/vt/vttablet/tabletserver/controller.go
@@ -9,7 +9,9 @@ import (
"github.com/youtube/vitess/go/vt/dbconfigs"
"github.com/youtube/vitess/go/vt/mysqlctl"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/rules"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
@@ -51,11 +53,14 @@ type Controller interface {
UnRegisterQueryRuleSource(ruleSource string)
// SetQueryRules sets the query rules for this QueryService
- SetQueryRules(ruleSource string, qrs *QueryRules) error
+ SetQueryRules(ruleSource string, qrs *rules.Rules) error
// QueryService returns the QueryService object used by this Controller
QueryService() queryservice.QueryService
+ // SchemaEngine returns the SchemaEngine object used by this Controller
+ SchemaEngine() *schema.Engine
+
// BroadcastHealth sends the current health to all listeners
BroadcastHealth(terTimestamp int64, stats *querypb.RealtimeStats)
}
diff --git a/go/vt/tabletserver/messager_cache.go b/go/vt/vttablet/tabletserver/messager/cache.go
similarity index 85%
rename from go/vt/tabletserver/messager_cache.go
rename to go/vt/vttablet/tabletserver/messager/cache.go
index b27610d5e93..ac5bd7c137f 100644
--- a/go/vt/tabletserver/messager_cache.go
+++ b/go/vt/vttablet/tabletserver/messager/cache.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package tabletserver
+package messager
import (
"container/heap"
@@ -52,17 +52,17 @@ func (mh *messageHeap) Pop() interface{} {
//_______________________________________________
-// MessagerCache is the cache for the messager.
-type MessagerCache struct {
+// cache is the cache for the messager.
+type cache struct {
mu sync.Mutex
size int
sendQueue messageHeap
messages map[string]*MessageRow
}
-// NewMessagerCache creates a new MessagerCache.
-func NewMessagerCache(size int) *MessagerCache {
- mc := &MessagerCache{
+// NewMessagerCache creates a new cache.
+func newCache(size int) *cache {
+ mc := &cache{
size: size,
messages: make(map[string]*MessageRow),
}
@@ -70,7 +70,7 @@ func NewMessagerCache(size int) *MessagerCache {
}
// Clear clears the cache.
-func (mc *MessagerCache) Clear() {
+func (mc *cache) Clear() {
mc.mu.Lock()
defer mc.mu.Unlock()
mc.sendQueue = nil
@@ -79,7 +79,7 @@ func (mc *MessagerCache) Clear() {
// Add adds a MessageRow to the cache. It returns
// false if the cache is full.
-func (mc *MessagerCache) Add(mr *MessageRow) bool {
+func (mc *cache) Add(mr *MessageRow) bool {
mc.mu.Lock()
defer mc.mu.Unlock()
if len(mc.sendQueue) >= mc.size {
@@ -102,7 +102,7 @@ func (mc *MessagerCache) Add(mr *MessageRow) bool {
// to prevent the poller thread from repopulating the
// message while it's being sent.
// If the Cache is empty Pop returns nil.
-func (mc *MessagerCache) Pop() *MessageRow {
+func (mc *cache) Pop() *MessageRow {
mc.mu.Lock()
defer mc.mu.Unlock()
for {
@@ -125,7 +125,7 @@ func (mc *MessagerCache) Pop() *MessageRow {
}
// Discard forgets the specified id.
-func (mc *MessagerCache) Discard(ids []string) {
+func (mc *cache) Discard(ids []string) {
mc.mu.Lock()
defer mc.mu.Unlock()
for _, id := range ids {
@@ -140,8 +140,8 @@ func (mc *MessagerCache) Discard(ids []string) {
}
}
-// Size returns the max size of MessagerCache.
-func (mc *MessagerCache) Size() int {
+// Size returns the max size of cache.
+func (mc *cache) Size() int {
mc.mu.Lock()
defer mc.mu.Unlock()
return mc.size
diff --git a/go/vt/tabletserver/messager_cache_test.go b/go/vt/vttablet/tabletserver/messager/cache_test.go
similarity index 96%
rename from go/vt/tabletserver/messager_cache_test.go
rename to go/vt/vttablet/tabletserver/messager/cache_test.go
index 5eb0297b465..d7769c4b226 100644
--- a/go/vt/tabletserver/messager_cache_test.go
+++ b/go/vt/vttablet/tabletserver/messager/cache_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package tabletserver
+package messager
import (
"reflect"
@@ -12,7 +12,7 @@ import (
)
func TestMessagerCacheOrder(t *testing.T) {
- mc := NewMessagerCache(10)
+ mc := newCache(10)
if !mc.Add(&MessageRow{
TimeNext: 1,
Epoch: 0,
@@ -65,7 +65,7 @@ func TestMessagerCacheOrder(t *testing.T) {
}
func TestMessagerCacheDupKey(t *testing.T) {
- mc := NewMessagerCache(10)
+ mc := newCache(10)
if !mc.Add(&MessageRow{
TimeNext: 1,
Epoch: 0,
@@ -99,7 +99,7 @@ func TestMessagerCacheDupKey(t *testing.T) {
}
func TestMessagerCacheDiscard(t *testing.T) {
- mc := NewMessagerCache(10)
+ mc := newCache(10)
if !mc.Add(&MessageRow{
TimeNext: 1,
Epoch: 0,
@@ -149,7 +149,7 @@ func TestMessagerCacheDiscard(t *testing.T) {
}
func TestMessagerCacheFull(t *testing.T) {
- mc := NewMessagerCache(2)
+ mc := newCache(2)
if !mc.Add(&MessageRow{
TimeNext: 1,
Epoch: 0,
@@ -174,7 +174,7 @@ func TestMessagerCacheFull(t *testing.T) {
}
func TestMessagerCacheEmpty(t *testing.T) {
- mc := NewMessagerCache(2)
+ mc := newCache(2)
if !mc.Add(&MessageRow{
TimeNext: 1,
Epoch: 0,
diff --git a/go/vt/tabletserver/messager_engine.go b/go/vt/vttablet/tabletserver/messager/engine.go
similarity index 56%
rename from go/vt/tabletserver/messager_engine.go
rename to go/vt/vttablet/tabletserver/messager/engine.go
index f840d62a318..06551bcb561 100644
--- a/go/vt/tabletserver/messager_engine.go
+++ b/go/vt/vttablet/tabletserver/messager/engine.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package tabletserver
+package messager
import (
"fmt"
@@ -10,82 +10,102 @@ import (
"time"
log "github.com/golang/glog"
+ "golang.org/x/net/context"
+ "github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/dbconfigs"
- "github.com/youtube/vitess/go/vt/tabletserver/connpool"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/connpool"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vterrors"
+ querypb "github.com/youtube/vitess/go/vt/proto/query"
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)
-// MessagerEngine is the engine for handling messages.
-type MessagerEngine struct {
+// TabletService defines the functions of TabletServer
+// that the messager needs for callback.
+type TabletService interface {
+ CheckMySQL()
+ PostponeMessages(ctx context.Context, target *querypb.Target, name string, ids []string) (count int64, err error)
+ PurgeMessages(ctx context.Context, target *querypb.Target, name string, timeCutoff int64) (count int64, err error)
+}
+
+// Engine is the engine for handling messages.
+type Engine struct {
mu sync.Mutex
isOpen bool
- managers map[string]*MessageManager
+ managers map[string]*messageManager
- // TODO(sougou): This depndency should be cleaned up.
- tsv *TabletServer
+ tsv TabletService
+ se *schema.Engine
conns *connpool.Pool
}
-// NewMessagerEngine creates a new MessagerEngine.
-func NewMessagerEngine(tsv *TabletServer, config tabletenv.TabletConfig) *MessagerEngine {
- return &MessagerEngine{
+// NewEngine creates a new Engine.
+func NewEngine(tsv TabletService, se *schema.Engine, config tabletenv.TabletConfig) *Engine {
+ return &Engine{
tsv: tsv,
+ se: se,
conns: connpool.New(
config.PoolNamePrefix+"MessagerPool",
config.MessagePoolSize,
time.Duration(config.IdleTimeout*1e9),
tsv,
),
- managers: make(map[string]*MessageManager),
+ managers: make(map[string]*messageManager),
}
}
-// Open starts the MessagerEngine service.
-func (me *MessagerEngine) Open(dbconfigs dbconfigs.DBConfigs) error {
+// Open starts the Engine service.
+func (me *Engine) Open(dbconfigs dbconfigs.DBConfigs) error {
if me.isOpen {
return nil
}
me.conns.Open(&dbconfigs.App, &dbconfigs.Dba)
- me.tsv.se.RegisterNotifier("messages", me.schemaChanged)
+ me.se.RegisterNotifier("messages", me.schemaChanged)
me.isOpen = true
return nil
}
-// Close closes the MessagerEngine service.
-func (me *MessagerEngine) Close() {
+// Close closes the Engine service.
+func (me *Engine) Close() {
me.mu.Lock()
defer me.mu.Unlock()
if !me.isOpen {
return
}
me.isOpen = false
- me.tsv.se.UnregisterNotifier("messages")
+ me.se.UnregisterNotifier("messages")
for _, mm := range me.managers {
mm.Close()
}
- me.managers = make(map[string]*MessageManager)
+ me.managers = make(map[string]*messageManager)
me.conns.Close()
}
// Subscribe subscribes to messages from the requested table.
-func (me *MessagerEngine) Subscribe(name string, rcv *messageReceiver) error {
+// The function returns a done channel that will be closed when
+// the subscription ends, which can be initiated by the send function
+// returning io.EOF. The engine can also end a subscription which is
+// usually triggered by Close. It's the responsibility of the send
+// function to promptly return if the done channel is closed. Otherwise,
+// the engine's Close function will hang indefinitely.
+func (me *Engine) Subscribe(name string, send func(*sqltypes.Result) error) (done chan struct{}, err error) {
me.mu.Lock()
defer me.mu.Unlock()
mm := me.managers[name]
if mm == nil {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "message table %s not found", name)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "message table %s not found", name)
}
+ rcv, done := newMessageReceiver(send)
mm.Subscribe(rcv)
- return nil
+ return done, nil
}
// LockDB obtains db locks for all messages that need to
// be updated and returns the counterpart unlock function.
-func (me *MessagerEngine) LockDB(newMessages map[string][]*MessageRow, changedMessages map[string][]string) func() {
+func (me *Engine) LockDB(newMessages map[string][]*MessageRow, changedMessages map[string][]string) func() {
combined := make(map[string]struct{})
for name := range newMessages {
combined[name] = struct{}{}
@@ -93,7 +113,7 @@ func (me *MessagerEngine) LockDB(newMessages map[string][]*MessageRow, changedMe
for name := range changedMessages {
combined[name] = struct{}{}
}
- var mms []*MessageManager
+ var mms []*messageManager
// Don't do DBLock while holding lock on mu.
// It causes deadlocks.
func() {
@@ -116,7 +136,7 @@ func (me *MessagerEngine) LockDB(newMessages map[string][]*MessageRow, changedMe
}
// UpdateCaches updates the caches for the committed changes.
-func (me *MessagerEngine) UpdateCaches(newMessages map[string][]*MessageRow, changedMessages map[string][]string) {
+func (me *Engine) UpdateCaches(newMessages map[string][]*MessageRow, changedMessages map[string][]string) {
me.mu.Lock()
defer me.mu.Unlock()
now := time.Now().UnixNano()
@@ -143,7 +163,7 @@ func (me *MessagerEngine) UpdateCaches(newMessages map[string][]*MessageRow, cha
}
// GenerateAckQuery returns the query and bind vars for acking a message.
-func (me *MessagerEngine) GenerateAckQuery(name string, ids []string) (string, map[string]interface{}, error) {
+func (me *Engine) GenerateAckQuery(name string, ids []string) (string, map[string]interface{}, error) {
me.mu.Lock()
defer me.mu.Unlock()
mm := me.managers[name]
@@ -155,7 +175,7 @@ func (me *MessagerEngine) GenerateAckQuery(name string, ids []string) (string, m
}
// GeneratePostponeQuery returns the query and bind vars for postponing a message.
-func (me *MessagerEngine) GeneratePostponeQuery(name string, ids []string) (string, map[string]interface{}, error) {
+func (me *Engine) GeneratePostponeQuery(name string, ids []string) (string, map[string]interface{}, error) {
me.mu.Lock()
defer me.mu.Unlock()
mm := me.managers[name]
@@ -167,7 +187,7 @@ func (me *MessagerEngine) GeneratePostponeQuery(name string, ids []string) (stri
}
// GeneratePurgeQuery returns the query and bind vars for purging messages.
-func (me *MessagerEngine) GeneratePurgeQuery(name string, timeCutoff int64) (string, map[string]interface{}, error) {
+func (me *Engine) GeneratePurgeQuery(name string, timeCutoff int64) (string, map[string]interface{}, error) {
me.mu.Lock()
defer me.mu.Unlock()
mm := me.managers[name]
@@ -178,7 +198,7 @@ func (me *MessagerEngine) GeneratePurgeQuery(name string, timeCutoff int64) (str
return query, bv, nil
}
-func (me *MessagerEngine) schemaChanged(tables map[string]*schema.Table, created, altered, dropped []string) {
+func (me *Engine) schemaChanged(tables map[string]*schema.Table, created, altered, dropped []string) {
me.mu.Lock()
defer me.mu.Unlock()
for _, name := range created {
@@ -191,7 +211,7 @@ func (me *MessagerEngine) schemaChanged(tables map[string]*schema.Table, created
log.Errorf("Newly created table alread exists in messages: %s", name)
continue
}
- mm := NewMessageManager(me.tsv, t, me.conns)
+ mm := newMessageManager(me.tsv, t, me.conns)
me.managers[name] = mm
mm.Open()
}
diff --git a/go/vt/vttablet/tabletserver/messager/engine_test.go b/go/vt/vttablet/tabletserver/messager/engine_test.go
new file mode 100644
index 00000000000..367d6c7f1cb
--- /dev/null
+++ b/go/vt/vttablet/tabletserver/messager/engine_test.go
@@ -0,0 +1,233 @@
+// Copyright 2017, Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package messager
+
+import (
+ "fmt"
+ "math/rand"
+ "reflect"
+ "runtime"
+ "testing"
+ "time"
+
+ "github.com/youtube/vitess/go/mysqlconn/fakesqldb"
+ "github.com/youtube/vitess/go/sqltypes"
+ "github.com/youtube/vitess/go/sync2"
+ "github.com/youtube/vitess/go/vt/dbconfigs"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
+)
+
+var meTable = &schema.Table{
+ Type: schema.Message,
+ MessageInfo: mmTable.MessageInfo,
+}
+
+func TestEngineSchemaChanged(t *testing.T) {
+ db := fakesqldb.New(t)
+ defer db.Close()
+ engine := newTestEngine(db)
+ defer engine.Close()
+ tables := map[string]*schema.Table{
+ "t1": meTable,
+ "t2": {
+ Type: schema.NoType,
+ },
+ }
+ engine.schemaChanged(tables, []string{"t1", "t2"}, nil, nil)
+ got := extractManagerNames(engine.managers)
+ want := map[string]bool{"t1": true}
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("got: %+v, want %+v", got, want)
+ }
+ tables = map[string]*schema.Table{
+ "t1": meTable,
+ "t2": {
+ Type: schema.NoType,
+ },
+ "t3": meTable,
+ }
+ engine.schemaChanged(tables, []string{"t3"}, nil, nil)
+ got = extractManagerNames(engine.managers)
+ want = map[string]bool{"t1": true, "t3": true}
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("got: %+v, want %+v", got, want)
+ }
+ tables = map[string]*schema.Table{
+ "t1": meTable,
+ "t2": {
+ Type: schema.NoType,
+ },
+ "t4": meTable,
+ }
+ engine.schemaChanged(tables, []string{"t4"}, nil, []string{"t3", "t5"})
+ got = extractManagerNames(engine.managers)
+ // schemaChanged is only additive.
+ want = map[string]bool{"t1": true, "t4": true}
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("got: %+v, want %+v", got, want)
+ }
+}
+
+func extractManagerNames(in map[string]*messageManager) map[string]bool {
+ out := make(map[string]bool)
+ for k := range in {
+ out[k] = true
+ }
+ return out
+}
+
+func TestSubscribe(t *testing.T) {
+ db := fakesqldb.New(t)
+ defer db.Close()
+ engine := newTestEngine(db)
+ defer engine.Close()
+ tables := map[string]*schema.Table{
+ "t1": meTable,
+ "t2": meTable,
+ }
+ engine.schemaChanged(tables, []string{"t1", "t2"}, nil, nil)
+ f1, ch1 := newEngineReceiver()
+ f2, ch2 := newEngineReceiver()
+ // Each receiver is subscribed to different managers.
+ engine.Subscribe("t1", f1)
+ <-ch1
+ engine.Subscribe("t2", f2)
+ <-ch2
+ engine.managers["t1"].Add(&MessageRow{ID: sqltypes.MakeString([]byte("1"))})
+ engine.managers["t2"].Add(&MessageRow{ID: sqltypes.MakeString([]byte("2"))})
+ <-ch1
+ <-ch2
+
+ // Error case.
+ want := "message table t3 not found"
+ _, err := engine.Subscribe("t3", f1)
+ if err == nil || err.Error() != want {
+ t.Errorf("Subscribe: %v, want %s", err, want)
+ }
+}
+
+func TestLockDB(t *testing.T) {
+ db := fakesqldb.New(t)
+ defer db.Close()
+ engine := newTestEngine(db)
+ defer engine.Close()
+ tables := map[string]*schema.Table{
+ "t1": meTable,
+ "t2": meTable,
+ }
+ engine.schemaChanged(tables, []string{"t1", "t2"}, nil, nil)
+ f1, ch1 := newEngineReceiver()
+ engine.Subscribe("t1", f1)
+ <-ch1
+
+ row1 := &MessageRow{
+ ID: sqltypes.MakeString([]byte("1")),
+ }
+ row2 := &MessageRow{
+ TimeNext: time.Now().UnixNano() + int64(10*time.Minute),
+ ID: sqltypes.MakeString([]byte("2")),
+ }
+ newMessages := map[string][]*MessageRow{"t1": {row1, row2}, "t3": {row1}}
+ unlock := engine.LockDB(newMessages, nil)
+ engine.UpdateCaches(newMessages, nil)
+ unlock()
+ <-ch1
+ runtime.Gosched()
+ // row2 should not be sent.
+ select {
+ case mr := <-ch1:
+ t.Errorf("Unexpected message: %v", mr)
+ default:
+ }
+
+ ch2 := make(chan *sqltypes.Result)
+ var count sync2.AtomicInt64
+ engine.Subscribe("t2", func(qr *sqltypes.Result) error {
+ count.Add(1)
+ ch2 <- qr
+ return nil
+ })
+ <-ch2
+ mm := engine.managers["t2"]
+ mm.Add(&MessageRow{ID: sqltypes.MakeString([]byte("1"))})
+ // Make sure the message is enqueued.
+ for {
+ runtime.Gosched()
+ time.Sleep(10 * time.Millisecond)
+ if count.Get() == int64(2) {
+ break
+ }
+ }
+ // "2" will be in the cache.
+ mm.Add(&MessageRow{ID: sqltypes.MakeString([]byte("2"))})
+ changedMessages := map[string][]string{"t2": {"2"}, "t3": {"2"}}
+ unlock = engine.LockDB(nil, changedMessages)
+ // This should delete "2".
+ engine.UpdateCaches(nil, changedMessages)
+ unlock()
+ <-ch2
+ runtime.Gosched()
+ // There should be no more messages.
+ select {
+ case mr := <-ch2:
+ t.Errorf("Unexpected message: %v", mr)
+ default:
+ }
+}
+
+func TestEngineGenerate(t *testing.T) {
+ db := fakesqldb.New(t)
+ defer db.Close()
+ engine := newTestEngine(db)
+ defer engine.Close()
+ engine.schemaChanged(map[string]*schema.Table{
+ "t1": meTable,
+ }, []string{"t1"}, nil, nil)
+ if _, _, err := engine.GenerateAckQuery("t1", []string{"1"}); err != nil {
+ t.Error(err)
+ }
+ want := "message table t2 not found in schema"
+ if _, _, err := engine.GenerateAckQuery("t2", []string{"1"}); err == nil || err.Error() != want {
+ t.Errorf("engine.GenerateAckQuery(invalid): %v, want %s", err, want)
+ }
+
+ if _, _, err := engine.GeneratePostponeQuery("t1", []string{"1"}); err != nil {
+ t.Error(err)
+ }
+ if _, _, err := engine.GeneratePostponeQuery("t2", []string{"1"}); err == nil || err.Error() != want {
+ t.Errorf("engine.GeneratePostponeQuery(invalid): %v, want %s", err, want)
+ }
+
+ if _, _, err := engine.GeneratePurgeQuery("t1", 0); err != nil {
+ t.Error(err)
+ }
+ if _, _, err := engine.GeneratePurgeQuery("t2", 0); err == nil || err.Error() != want {
+ t.Errorf("engine.GeneratePurgeQuery(invalid): %v, want %s", err, want)
+ }
+}
+
+func newTestEngine(db *fakesqldb.DB) *Engine {
+ randID := rand.Int63()
+ config := tabletenv.DefaultQsConfig
+ config.PoolNamePrefix = fmt.Sprintf("Pool-%d-", randID)
+ tsv := newFakeTabletServer()
+ se := schema.NewEngine(tsv, config)
+ te := NewEngine(tsv, se, config)
+ dbconfigs := dbconfigs.DBConfigs{
+ App: *db.ConnParams(),
+ SidecarDBName: "_vt",
+ }
+ te.Open(dbconfigs)
+ return te
+}
+
+func newEngineReceiver() (f func(qr *sqltypes.Result) error, ch chan *sqltypes.Result) {
+ ch = make(chan *sqltypes.Result)
+ return func(qr *sqltypes.Result) error {
+ ch <- qr
+ return nil
+ }, ch
+}
diff --git a/go/vt/tabletserver/message_manager.go b/go/vt/vttablet/tabletserver/messager/message_manager.go
similarity index 77%
rename from go/vt/tabletserver/message_manager.go
rename to go/vt/vttablet/tabletserver/messager/message_manager.go
index 7956890b7d8..db606b72fcb 100644
--- a/go/vt/tabletserver/message_manager.go
+++ b/go/vt/vttablet/tabletserver/messager/message_manager.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package tabletserver
+package messager
import (
"io"
@@ -15,9 +15,9 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/timer"
"github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/connpool"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/connpool"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
)
type messageReceiver struct {
@@ -61,17 +61,17 @@ func (rcv *messageReceiver) Cancel() {
}
// receiverWithStatus is a separate struct to signify
-// that the busy flag is controlled by the MessageManager
+// that the busy flag is controlled by the messageManager
// mutex.
type receiverWithStatus struct {
receiver *messageReceiver
busy bool
}
-// MessageManager manages messages for a message table.
-type MessageManager struct {
+// messageManager manages messages for a message table.
+type messageManager struct {
DBLock sync.Mutex
- tsv *TabletServer
+ tsv TabletService
isOpen bool
@@ -89,7 +89,7 @@ type MessageManager struct {
// an item gets added to the cache, or if the manager is closed.
// The trigger wakes up the runSend thread.
cond sync.Cond
- cache *MessagerCache
+ cache *cache
receivers []*receiverWithStatus
curReceiver int
messagesPending bool
@@ -106,9 +106,11 @@ type MessageManager struct {
purgeQuery *sqlparser.ParsedQuery
}
-// NewMessageManager creates a new message manager.
-func NewMessageManager(tsv *TabletServer, table *schema.Table, conns *connpool.Pool) *MessageManager {
- mm := &MessageManager{
+// newMessageManager creates a new message manager.
+// Calls into tsv have to be made asynchronously. Otherwise,
+// it can lead to deadlocks.
+func newMessageManager(tsv TabletService, table *schema.Table, conns *connpool.Pool) *messageManager {
+ mm := &messageManager{
tsv: tsv,
name: table.Name,
fieldResult: &sqltypes.Result{
@@ -117,30 +119,30 @@ func NewMessageManager(tsv *TabletServer, table *schema.Table, conns *connpool.P
ackWaitTime: table.MessageInfo.AckWaitDuration,
purgeAfter: table.MessageInfo.PurgeAfterDuration,
batchSize: table.MessageInfo.BatchSize,
- cache: NewMessagerCache(table.MessageInfo.CacheSize),
+ cache: newCache(table.MessageInfo.CacheSize),
pollerTicks: timer.NewTimer(table.MessageInfo.PollInterval),
purgeTicks: timer.NewTimer(table.MessageInfo.PollInterval),
conns: conns,
}
mm.cond.L = &mm.mu
- mm.readByTimeNext = buildParsedQuery(
+ mm.readByTimeNext = sqlparser.BuildParsedQuery(
"select time_next, epoch, id, message from %v where time_next < %a order by time_next desc limit %a",
mm.name, ":time_next", ":max")
- mm.ackQuery = buildParsedQuery(
+ mm.ackQuery = sqlparser.BuildParsedQuery(
"update %v set time_acked = %a, time_next = null where id in %a and time_acked is null",
mm.name, ":time_acked", "::ids")
- mm.postponeQuery = buildParsedQuery(
+ mm.postponeQuery = sqlparser.BuildParsedQuery(
"update %v set time_next = %a+(%a< t.SequenceInfo.LastVal {
_, err := qre.execAsTransaction(func(conn *TxConnection) (*sqltypes.Result, error) {
- query := fmt.Sprintf("select next_id, cache from %s where id = 0 for update", sqlparser.String(qre.plan.TableName))
+ query := fmt.Sprintf("select next_id, cache from %s where id = 0 for update", sqlparser.String(tableName))
qr, err := qre.execSQL(conn, query, false)
if err != nil {
return nil, err
}
if len(qr.Rows) != 1 {
- return nil, fmt.Errorf("unexpected rows from reading sequence %s (possible mis-route): %d", qre.plan.TableName, len(qr.Rows))
+ return nil, fmt.Errorf("unexpected rows from reading sequence %s (possible mis-route): %d", tableName, len(qr.Rows))
}
nextID, err := qr.Rows[0][0].ParseInt64()
if err != nil {
- return nil, fmt.Errorf("error loading sequence %s: %v", qre.plan.TableName, err)
+ return nil, fmt.Errorf("error loading sequence %s: %v", tableName, err)
}
// Initialize SequenceInfo.NextVal if it wasn't already.
if t.SequenceInfo.NextVal == 0 {
@@ -347,16 +360,16 @@ func (qre *QueryExecutor) execNextval() (*sqltypes.Result, error) {
}
cache, err := qr.Rows[0][1].ParseInt64()
if err != nil {
- return nil, fmt.Errorf("error loading sequence %s: %v", qre.plan.TableName, err)
+ return nil, fmt.Errorf("error loading sequence %s: %v", tableName, err)
}
if cache < 1 {
- return nil, fmt.Errorf("invalid cache value for sequence %s: %d", qre.plan.TableName, cache)
+ return nil, fmt.Errorf("invalid cache value for sequence %s: %d", tableName, cache)
}
newLast := nextID + cache
for newLast <= t.SequenceInfo.NextVal+inc {
newLast += cache
}
- query = fmt.Sprintf("update %s set next_id = %d where id = 0", sqlparser.String(qre.plan.TableName), newLast)
+ query = fmt.Sprintf("update %s set next_id = %d where id = 0", sqlparser.String(tableName), newLast)
conn.RecordQuery(query)
_, err = qre.execSQL(conn, query, false)
if err != nil {
@@ -444,7 +457,7 @@ func (qre *QueryExecutor) execInsertMessage(conn *TxConnection) (*sqltypes.Resul
}
mrs := conn.NewMessages[qre.plan.Table.Name.String()]
for _, row := range readback.Rows {
- mr, err := BuildMessageRow(row)
+ mr, err := messager.BuildMessageRow(row)
if err != nil {
return nil, err
}
@@ -464,7 +477,7 @@ func (qre *QueryExecutor) execInsertSubquery(conn *TxConnection) (*sqltypes.Resu
return &sqltypes.Result{RowsAffected: 0}, nil
}
if len(qre.plan.ColumnNumbers) != len(innerRows[0]) {
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "Subquery length does not match column list")
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Subquery length does not match column list")
}
pkRows := make([][]sqltypes.Value, len(innerRows))
for i, innerRow := range innerRows {
@@ -494,15 +507,15 @@ func (qre *QueryExecutor) execUpsertPK(conn *TxConnection) (*sqltypes.Result, er
if err == nil {
return result, nil
}
- terr, ok := err.(*tabletenv.TabletError)
+ sqlErr, ok := err.(*sqldb.SQLError)
if !ok {
return result, err
}
- if terr.SQLError != mysqlconn.ERDupEntry {
+ if sqlErr.Number() != mysqlconn.ERDupEntry {
return nil, err
}
// If the error didn't match pk, just return the error without updating.
- if !strings.Contains(terr.Message, "'PRIMARY'") {
+ if !strings.Contains(sqlErr.Error(), "'PRIMARY'") {
return nil, err
}
// At this point, we know the insert failed due to a duplicate pk row.
@@ -597,10 +610,10 @@ func (qre *QueryExecutor) getConn(pool *connpool.Pool) (*connpool.DBConn, error)
case nil:
qre.logStats.WaitingForConnection += time.Now().Sub(start)
return conn, nil
- case tabletenv.ErrConnPoolClosed:
+ case connpool.ErrConnPoolClosed:
return nil, err
}
- return nil, tabletenv.NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, err)
+ return nil, err
}
func (qre *QueryExecutor) qFetch(logStats *tabletenv.LogStats, parsedQuery *sqlparser.ParsedQuery, bindVars map[string]interface{}) (*sqltypes.Result, error) {
@@ -615,7 +628,7 @@ func (qre *QueryExecutor) qFetch(logStats *tabletenv.LogStats, parsedQuery *sqlp
conn, err := qre.tsv.qe.conns.Get(qre.ctx)
logStats.WaitingForConnection += time.Now().Sub(waitingForConnectionStart)
if err != nil {
- q.Err = tabletenv.NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, err)
+ q.Err = err
} else {
defer conn.Recycle()
q.Result, q.Err = qre.execSQL(conn, sql, false)
@@ -671,7 +684,7 @@ func (qre *QueryExecutor) generateFinalSQL(parsedQuery *sqlparser.ParsedQuery, b
bindVars["#maxLimit"] = qre.tsv.qe.maxResultSize.Get() + 1
sql, err := parsedQuery.GenerateQuery(bindVars)
if err != nil {
- return "", tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "%s", err)
+ return "", vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%s", err)
}
if buildStreamComment != nil {
sql = append(sql, buildStreamComment...)
@@ -696,7 +709,7 @@ func (qre *QueryExecutor) execStreamSQL(conn *connpool.DBConn, sql string, inclu
qre.logStats.AddRewrittenSQL(sql, start)
if err != nil {
// MySQL error that isn't due to a connection issue
- return tabletenv.NewTabletErrorSQL(vtrpcpb.ErrorCode_UNKNOWN_ERROR, err)
+ return err
}
return nil
}
diff --git a/go/vt/tabletserver/query_executor_test.go b/go/vt/vttablet/tabletserver/query_executor_test.go
similarity index 93%
rename from go/vt/tabletserver/query_executor_test.go
rename to go/vt/vttablet/tabletserver/query_executor_test.go
index f4043bd6d5a..d4ff45d00f3 100644
--- a/go/vt/tabletserver/query_executor_test.go
+++ b/go/vt/vttablet/tabletserver/query_executor_test.go
@@ -6,6 +6,7 @@ package tabletserver
import (
"fmt"
+ "io"
"math/rand"
"reflect"
"strings"
@@ -22,9 +23,10 @@ import (
"github.com/youtube/vitess/go/vt/callinfo/fakecallinfo"
"github.com/youtube/vitess/go/vt/tableacl"
"github.com/youtube/vitess/go/vt/tableacl/simpleacl"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema"
- "github.com/youtube/vitess/go/vt/tabletserver/planbuilder"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/planbuilder"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/rules"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vterrors"
querypb "github.com/youtube/vitess/go/vt/proto/query"
tableaclpb "github.com/youtube/vitess/go/vt/proto/tableacl"
@@ -85,16 +87,9 @@ func TestQueryExecutorPlanPassDmlStrictMode(t *testing.T) {
defer tsv.StopService()
defer testCommitHelper(t, tsv, qre)
checkPlanID(t, planbuilder.PlanPassDML, qre.plan.PlanID)
- got, err = qre.Execute()
- if err == nil {
- t.Fatal("qre.Execute() = nil, want error")
- }
- tabletError, ok := err.(*tabletenv.TabletError)
- if !ok {
- t.Fatalf("got: %v, want: a tabletenv.TabletError", tabletError)
- }
- if tabletError.ErrorCode != vtrpcpb.ErrorCode_BAD_INPUT {
- t.Fatalf("got: %s, want: BAD_INPUT", tabletError.ErrorCode)
+ _, err = qre.Execute()
+ if code := vterrors.Code(err); code != vtrpcpb.Code_INVALID_ARGUMENT {
+ t.Fatalf("qre.Execute: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT)
}
}
@@ -125,15 +120,8 @@ func TestQueryExecutorPlanPassDmlStrictModeAutoCommit(t *testing.T) {
defer tsv.StopService()
checkPlanID(t, planbuilder.PlanPassDML, qre.plan.PlanID)
_, err = qre.Execute()
- if err == nil {
- t.Fatal("got: nil, want: error")
- }
- tabletError, ok := err.(*tabletenv.TabletError)
- if !ok {
- t.Fatalf("got: %v, want: *tabletenv.TabletError", tabletError)
- }
- if tabletError.ErrorCode != vtrpcpb.ErrorCode_BAD_INPUT {
- t.Fatalf("got: %s, want: BAD_INPUT", tabletError.ErrorCode)
+ if code := vterrors.Code(err); code != vtrpcpb.Code_INVALID_ARGUMENT {
+ t.Fatalf("qre.Execute: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT)
}
}
@@ -186,14 +174,17 @@ func TestQueryExecutorPlanInsertMessage(t *testing.T) {
qre := newTestQueryExecutor(ctx, tsv, query, 0)
defer tsv.StopService()
checkPlanID(t, planbuilder.PlanInsertMessage, qre.plan.PlanID)
- r1 := newTestReceiver(1)
- tsv.messager.schemaChanged(map[string]*schema.Table{
- "msg": {
- Type: schema.Message,
- },
- }, []string{"msg"}, nil, nil)
- tsv.messager.Subscribe("msg", r1.rcv)
- <-r1.ch
+ ch1 := make(chan *sqltypes.Result)
+ count := 0
+ tsv.messager.Subscribe("msg", func(qr *sqltypes.Result) error {
+ if count > 1 {
+ return io.EOF
+ }
+ count++
+ ch1 <- qr
+ return nil
+ })
+ <-ch1
got, err := qre.Execute()
if err != nil {
t.Fatalf("qre.Execute() = %v, want nil", err)
@@ -201,7 +192,7 @@ func TestQueryExecutorPlanInsertMessage(t *testing.T) {
if !reflect.DeepEqual(got, want) {
t.Fatalf("got: %v, want: %v", got, want)
}
- mr := <-r1.ch
+ mr := <-ch1
wantqr := &sqltypes.Result{
Rows: [][]sqltypes.Value{{
sqltypes.MakeTrusted(sqltypes.Int64, []byte("1")),
@@ -313,6 +304,7 @@ func TestQueryExecutorPlanUpsertPk(t *testing.T) {
txid := newTransaction(tsv)
qre := newTestQueryExecutor(ctx, tsv, query, txid)
defer tsv.StopService()
+ defer testCommitHelper(t, tsv, qre)
checkPlanID(t, planbuilder.PlanUpsertPK, qre.plan.PlanID)
got, err := qre.Execute()
if err != nil {
@@ -326,20 +318,19 @@ func TestQueryExecutorPlanUpsertPk(t *testing.T) {
if !reflect.DeepEqual(gotqueries, wantqueries) {
t.Errorf("queries: %v, want %v", gotqueries, wantqueries)
}
- testCommitHelper(t, tsv, qre)
db.AddRejectedQuery("insert into test_table values (1) /* _stream test_table (pk ) (1 ); */", errRejected)
txid = newTransaction(tsv)
qre = newTestQueryExecutor(ctx, tsv, query, txid)
+ defer testCommitHelper(t, tsv, qre)
_, err = qre.Execute()
- wantErr := "error: rejected"
+ wantErr := "rejected"
if err == nil || !strings.Contains(err.Error(), wantErr) {
t.Errorf("qre.Execute() = %v, want %v", err, wantErr)
}
if gotqueries = fetchRecordedQueries(qre); gotqueries != nil {
t.Errorf("queries: %v, want nil", gotqueries)
}
- testCommitHelper(t, tsv, qre)
db.AddRejectedQuery(
"insert into test_table values (1) /* _stream test_table (pk ) (1 ); */",
@@ -348,8 +339,9 @@ func TestQueryExecutorPlanUpsertPk(t *testing.T) {
db.AddQuery("update test_table set val = 1 where pk in (1) /* _stream test_table (pk ) (1 ); */", &sqltypes.Result{})
txid = newTransaction(tsv)
qre = newTestQueryExecutor(ctx, tsv, query, txid)
+ defer testCommitHelper(t, tsv, qre)
_, err = qre.Execute()
- wantErr = "error: err (errno 1062) (sqlstate 23000)"
+ wantErr = "err (errno 1062) (sqlstate 23000)"
if err == nil || !strings.Contains(err.Error(), wantErr) {
t.Errorf("qre.Execute() = %v, want %v", err, wantErr)
}
@@ -357,7 +349,6 @@ func TestQueryExecutorPlanUpsertPk(t *testing.T) {
if gotqueries = fetchRecordedQueries(qre); gotqueries != nil {
t.Errorf("queries: %v, want nil", gotqueries)
}
- testCommitHelper(t, tsv, qre)
db.AddRejectedQuery(
"insert into test_table values (1) /* _stream test_table (pk ) (1 ); */",
@@ -369,6 +360,7 @@ func TestQueryExecutorPlanUpsertPk(t *testing.T) {
)
txid = newTransaction(tsv)
qre = newTestQueryExecutor(ctx, tsv, query, txid)
+ defer testCommitHelper(t, tsv, qre)
got, err = qre.Execute()
if err != nil {
t.Fatalf("qre.Execute() = %v, want nil", err)
@@ -384,7 +376,6 @@ func TestQueryExecutorPlanUpsertPk(t *testing.T) {
if !reflect.DeepEqual(gotqueries, wantqueries) {
t.Errorf("queries: %v, want %v", gotqueries, wantqueries)
}
- testCommitHelper(t, tsv, qre)
}
func TestQueryExecutorPlanUpsertPkAutoCommit(t *testing.T) {
@@ -408,7 +399,7 @@ func TestQueryExecutorPlanUpsertPkAutoCommit(t *testing.T) {
db.AddRejectedQuery("insert into test_table values (1) /* _stream test_table (pk ) (1 ); */", errRejected)
_, err = qre.Execute()
- wantErr := "error: rejected"
+ wantErr := "rejected"
if err == nil || !strings.Contains(err.Error(), wantErr) {
t.Fatalf("qre.Execute() = %v, want %v", err, wantErr)
}
@@ -419,7 +410,7 @@ func TestQueryExecutorPlanUpsertPkAutoCommit(t *testing.T) {
)
db.AddQuery("update test_table set val = 1 where pk in (1) /* _stream test_table (pk ) (1 ); */", &sqltypes.Result{})
_, err = qre.Execute()
- wantErr = "error: err (errno 1062) (sqlstate 23000)"
+ wantErr = "err (errno 1062) (sqlstate 23000)"
if err == nil || !strings.Contains(err.Error(), wantErr) {
t.Fatalf("qre.Execute() = %v, want %v", err, wantErr)
}
@@ -683,15 +674,8 @@ func TestQueryExecutorPlanPassSelectWithLockOutsideATransaction(t *testing.T) {
defer tsv.StopService()
checkPlanID(t, planbuilder.PlanSelectLock, qre.plan.PlanID)
_, err := qre.Execute()
- if err == nil {
- t.Fatal("got: nil, want: error")
- }
- got, ok := err.(*tabletenv.TabletError)
- if !ok {
- t.Fatalf("got: %v, want: *tabletenv.TabletError", err)
- }
- if got.ErrorCode != vtrpcpb.ErrorCode_BAD_INPUT {
- t.Fatalf("got: %s, want: BAD_INPUT", got.ErrorCode)
+ if code := vterrors.Code(err); code != vtrpcpb.Code_INVALID_ARGUMENT {
+ t.Fatalf("qre.Execute: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT)
}
}
@@ -1031,12 +1015,8 @@ func TestQueryExecutorTableAclNoPermission(t *testing.T) {
if err == nil {
t.Fatal("got: nil, want: error")
}
- tabletError, ok := err.(*tabletenv.TabletError)
- if !ok {
- t.Fatalf("got: %v, want: *tabletenv.TabletError", err)
- }
- if tabletError.ErrorCode != vtrpcpb.ErrorCode_PERMISSION_DENIED {
- t.Fatalf("got: %s, want: PERMISSION_DENIED", tabletError.ErrorCode)
+ if code := vterrors.Code(err); code != vtrpcpb.Code_PERMISSION_DENIED {
+ t.Fatalf("qre.Execute: %v, want %v", code, vtrpcpb.Code_PERMISSION_DENIED)
}
}
@@ -1082,18 +1062,12 @@ func TestQueryExecutorTableAclExemptACL(t *testing.T) {
checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID)
// query should fail because current user do not have read permissions
_, err := qre.Execute()
- if err == nil {
- t.Fatal("got: nil, want: error")
- }
- tabletError, ok := err.(*tabletenv.TabletError)
- if !ok {
- t.Fatalf("got: %v, want: *tabletenv.TabletError", err)
+ if code := vterrors.Code(err); code != vtrpcpb.Code_PERMISSION_DENIED {
+ t.Fatalf("qre.Execute: %v, want %v", code, vtrpcpb.Code_PERMISSION_DENIED)
}
- if tabletError.ErrorCode != vtrpcpb.ErrorCode_PERMISSION_DENIED {
- t.Fatalf("got: %s, want: PERMISSION_DENIED", tabletError.ErrorCode)
- }
- if !strings.Contains(tabletError.Error(), "table acl error") {
- t.Fatalf("got %s, want tablet errorL table acl error", tabletError.Error())
+ wanterr := "table acl error"
+ if !strings.Contains(err.Error(), wanterr) {
+ t.Fatalf("qre.Execute: %v, want %s", err, wanterr)
}
// table acl should be ignored since this is an exempt user.
@@ -1191,7 +1165,7 @@ func TestQueryExecutorBlacklistQRFail(t *testing.T) {
bannedAddr := "127.0.0.1"
bannedUser := "u2"
- alterRule := NewQueryRule("disable update", "disable update", QRFail)
+ alterRule := rules.NewQueryRule("disable update", "disable update", rules.QRFail)
alterRule.SetIPCond(bannedAddr)
alterRule.SetUserCond(bannedUser)
alterRule.SetQueryCond("select.*")
@@ -1199,7 +1173,7 @@ func TestQueryExecutorBlacklistQRFail(t *testing.T) {
alterRule.AddTableCond("test_table")
rulesName := "blacklistedRulesQRFail"
- rules := NewQueryRules()
+ rules := rules.New()
rules.Add(alterRule)
callInfo := &fakecallinfo.FakeCallInfo{
@@ -1208,9 +1182,9 @@ func TestQueryExecutorBlacklistQRFail(t *testing.T) {
}
ctx := callinfo.NewContext(context.Background(), callInfo)
tsv := newTestTabletServer(ctx, enableStrict, db)
- tsv.qe.queryRuleSources.UnRegisterQueryRuleSource(rulesName)
- tsv.qe.queryRuleSources.RegisterQueryRuleSource(rulesName)
- defer tsv.qe.queryRuleSources.UnRegisterQueryRuleSource(rulesName)
+ tsv.qe.queryRuleSources.UnRegisterSource(rulesName)
+ tsv.qe.queryRuleSources.RegisterSource(rulesName)
+ defer tsv.qe.queryRuleSources.UnRegisterSource(rulesName)
if err := tsv.qe.queryRuleSources.SetRules(rulesName, rules); err != nil {
t.Fatalf("failed to set rule, error: %v", err)
@@ -1222,15 +1196,8 @@ func TestQueryExecutorBlacklistQRFail(t *testing.T) {
checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID)
// execute should fail because query has been blacklisted
_, err := qre.Execute()
- if err == nil {
- t.Fatal("got: nil, want: error")
- }
- got, ok := err.(*tabletenv.TabletError)
- if !ok {
- t.Fatalf("got: %v, want: *tabletenv.TabletError", err)
- }
- if got.ErrorCode != vtrpcpb.ErrorCode_BAD_INPUT {
- t.Fatalf("got: %s, want: BAD_INPUT", got.ErrorCode)
+ if code := vterrors.Code(err); code != vtrpcpb.Code_INVALID_ARGUMENT {
+ t.Fatalf("qre.Execute: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT)
}
}
@@ -1252,7 +1219,7 @@ func TestQueryExecutorBlacklistQRRetry(t *testing.T) {
bannedAddr := "127.0.0.1"
bannedUser := "x"
- alterRule := NewQueryRule("disable update", "disable update", QRFailRetry)
+ alterRule := rules.NewQueryRule("disable update", "disable update", rules.QRFailRetry)
alterRule.SetIPCond(bannedAddr)
alterRule.SetUserCond(bannedUser)
alterRule.SetQueryCond("select.*")
@@ -1260,7 +1227,7 @@ func TestQueryExecutorBlacklistQRRetry(t *testing.T) {
alterRule.AddTableCond("test_table")
rulesName := "blacklistedRulesQRRetry"
- rules := NewQueryRules()
+ rules := rules.New()
rules.Add(alterRule)
callInfo := &fakecallinfo.FakeCallInfo{
@@ -1269,9 +1236,9 @@ func TestQueryExecutorBlacklistQRRetry(t *testing.T) {
}
ctx := callinfo.NewContext(context.Background(), callInfo)
tsv := newTestTabletServer(ctx, enableStrict, db)
- tsv.qe.queryRuleSources.UnRegisterQueryRuleSource(rulesName)
- tsv.qe.queryRuleSources.RegisterQueryRuleSource(rulesName)
- defer tsv.qe.queryRuleSources.UnRegisterQueryRuleSource(rulesName)
+ tsv.qe.queryRuleSources.UnRegisterSource(rulesName)
+ tsv.qe.queryRuleSources.RegisterSource(rulesName)
+ defer tsv.qe.queryRuleSources.UnRegisterSource(rulesName)
if err := tsv.qe.queryRuleSources.SetRules(rulesName, rules); err != nil {
t.Fatalf("failed to set rule, error: %v", err)
@@ -1282,15 +1249,8 @@ func TestQueryExecutorBlacklistQRRetry(t *testing.T) {
checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID)
_, err := qre.Execute()
- if err == nil {
- t.Fatal("got: nil, want: error")
- }
- got, ok := err.(*tabletenv.TabletError)
- if !ok {
- t.Fatalf("got: %v, want: *tabletenv.TabletError", err)
- }
- if got.ErrorCode != vtrpcpb.ErrorCode_QUERY_NOT_SERVED {
- t.Fatalf("got: %s, want: QUERY_NOT_SERVED", got.ErrorCode)
+ if code := vterrors.Code(err); code != vtrpcpb.Code_FAILED_PRECONDITION {
+ t.Fatalf("tsv.qe.queryRuleSources.SetRules: %v, want %v", code, vtrpcpb.Code_FAILED_PRECONDITION)
}
}
@@ -1339,7 +1299,7 @@ func newTestTabletServer(ctx context.Context, flags executorFlags, db *fakesqldb
} else {
config.TwoPCAbandonAge = 10
}
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
testUtils := newTestUtils()
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
diff --git a/go/vt/tabletserver/query_list.go b/go/vt/vttablet/tabletserver/query_list.go
similarity index 100%
rename from go/vt/tabletserver/query_list.go
rename to go/vt/vttablet/tabletserver/query_list.go
diff --git a/go/vt/tabletserver/query_list_test.go b/go/vt/vttablet/tabletserver/query_list_test.go
similarity index 100%
rename from go/vt/tabletserver/query_list_test.go
rename to go/vt/vttablet/tabletserver/query_list_test.go
diff --git a/go/vt/tabletserver/querylogz.go b/go/vt/vttablet/tabletserver/querylogz.go
similarity index 98%
rename from go/vt/tabletserver/querylogz.go
rename to go/vt/vttablet/tabletserver/querylogz.go
index 158598c2d69..4197a226888 100644
--- a/go/vt/tabletserver/querylogz.go
+++ b/go/vt/vttablet/tabletserver/querylogz.go
@@ -16,7 +16,7 @@ import (
log "github.com/golang/glog"
"github.com/youtube/vitess/go/acl"
"github.com/youtube/vitess/go/vt/logz"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
)
var (
diff --git a/go/vt/tabletserver/querylogz_test.go b/go/vt/vttablet/tabletserver/querylogz_test.go
similarity index 96%
rename from go/vt/tabletserver/querylogz_test.go
rename to go/vt/vttablet/tabletserver/querylogz_test.go
index 62f95e36855..eb50bf2ff6d 100644
--- a/go/vt/tabletserver/querylogz_test.go
+++ b/go/vt/vttablet/tabletserver/querylogz_test.go
@@ -14,8 +14,8 @@ import (
"time"
"github.com/youtube/vitess/go/vt/callerid"
- "github.com/youtube/vitess/go/vt/tabletserver/planbuilder"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/planbuilder"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
"golang.org/x/net/context"
)
diff --git a/go/vt/tabletserver/querytypes/bound_query.go b/go/vt/vttablet/tabletserver/querytypes/bound_query.go
similarity index 80%
rename from go/vt/tabletserver/querytypes/bound_query.go
rename to go/vt/vttablet/tabletserver/querytypes/bound_query.go
index c687658c440..92e57d90b64 100644
--- a/go/vt/tabletserver/querytypes/bound_query.go
+++ b/go/vt/vttablet/tabletserver/querytypes/bound_query.go
@@ -7,9 +7,8 @@
package querytypes
import (
+ "bytes"
"fmt"
-
- "github.com/youtube/vitess/go/bytes2"
)
// This file defines the BoundQuery type.
@@ -34,14 +33,14 @@ type BoundQuery struct {
// QueryAsString prints a readable version of query+bind variables,
// and also truncates data if it's too long
func QueryAsString(sql string, bindVariables map[string]interface{}) string {
- buf := bytes2.NewChunkedWriter(1024)
- fmt.Fprintf(buf, "Sql: %#v, BindVars: {", sql)
+ buf := &bytes.Buffer{}
+ fmt.Fprintf(buf, "Sql: %q, BindVars: {", slimit(sql, 5000))
for k, v := range bindVariables {
switch val := v.(type) {
case []byte:
- fmt.Fprintf(buf, "%s: %#v, ", k, slimit(string(val)))
+ fmt.Fprintf(buf, "%s: %q, ", k, slimit(string(val), 256))
case string:
- fmt.Fprintf(buf, "%s: %#v, ", k, slimit(val))
+ fmt.Fprintf(buf, "%s: %q, ", k, slimit(val, 256))
default:
fmt.Fprintf(buf, "%s: %v, ", k, v)
}
@@ -50,10 +49,9 @@ func QueryAsString(sql string, bindVariables map[string]interface{}) string {
return string(buf.Bytes())
}
-func slimit(s string) string {
- l := len(s)
- if l > 256 {
- l = 256
+func slimit(s string, max int) string {
+ if l := len(s); l > max {
+ return s[:max]
}
- return s[:l]
+ return s
}
diff --git a/go/vt/tabletserver/querytypes/proto3.go b/go/vt/vttablet/tabletserver/querytypes/proto3.go
similarity index 100%
rename from go/vt/tabletserver/querytypes/proto3.go
rename to go/vt/vttablet/tabletserver/querytypes/proto3.go
diff --git a/go/vt/tabletserver/querytypes/proto3_test.go b/go/vt/vttablet/tabletserver/querytypes/proto3_test.go
similarity index 100%
rename from go/vt/tabletserver/querytypes/proto3_test.go
rename to go/vt/vttablet/tabletserver/querytypes/proto3_test.go
diff --git a/go/vt/tabletserver/querytypes/query_split.go b/go/vt/vttablet/tabletserver/querytypes/query_split.go
similarity index 100%
rename from go/vt/tabletserver/querytypes/query_split.go
rename to go/vt/vttablet/tabletserver/querytypes/query_split.go
diff --git a/go/vt/tabletserver/queryz.go b/go/vt/vttablet/tabletserver/queryz.go
similarity index 97%
rename from go/vt/tabletserver/queryz.go
rename to go/vt/vttablet/tabletserver/queryz.go
index 59bad70d344..20cf7d7be02 100644
--- a/go/vt/tabletserver/queryz.go
+++ b/go/vt/vttablet/tabletserver/queryz.go
@@ -14,7 +14,7 @@ import (
log "github.com/golang/glog"
"github.com/youtube/vitess/go/acl"
"github.com/youtube/vitess/go/vt/logz"
- "github.com/youtube/vitess/go/vt/tabletserver/planbuilder"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/planbuilder"
)
var (
@@ -138,7 +138,7 @@ func queryzHandler(qe *QueryEngine, w http.ResponseWriter, r *http.Request) {
}
Value := &queryzRow{
Query: logz.Wrappable(v),
- Table: plan.TableName.String(),
+ Table: plan.TableName().String(),
Plan: plan.PlanID,
Reason: plan.Reason,
}
diff --git a/go/vt/tabletserver/queryz_test.go b/go/vt/vttablet/tabletserver/queryz_test.go
similarity index 75%
rename from go/vt/tabletserver/queryz_test.go
rename to go/vt/vttablet/tabletserver/queryz_test.go
index f33bff4b118..874bf1ff965 100644
--- a/go/vt/tabletserver/queryz_test.go
+++ b/go/vt/vttablet/tabletserver/queryz_test.go
@@ -14,7 +14,8 @@ import (
"time"
"github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/planbuilder"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/planbuilder"
)
func TestQueryzHandler(t *testing.T) {
@@ -22,36 +23,36 @@ func TestQueryzHandler(t *testing.T) {
req, _ := http.NewRequest("GET", "/schemaz", nil)
qe := newTestQueryEngine(100, 10*time.Second, true)
- plan1 := &ExecPlan{
- ExecPlan: &planbuilder.ExecPlan{
- TableName: sqlparser.NewTableIdent("test_table"),
- PlanID: planbuilder.PlanPassSelect,
- Reason: planbuilder.ReasonTable,
+ plan1 := &TabletPlan{
+ Plan: &planbuilder.Plan{
+ Table: &schema.Table{Name: sqlparser.NewTableIdent("test_table")},
+ PlanID: planbuilder.PlanPassSelect,
+ Reason: planbuilder.ReasonTable,
},
}
plan1.AddStats(10, 2*time.Second, 1*time.Second, 2, 0)
qe.queries.Set("select name from test_table", plan1)
- plan2 := &ExecPlan{
- ExecPlan: &planbuilder.ExecPlan{
- TableName: sqlparser.NewTableIdent("test_table"),
- PlanID: planbuilder.PlanDDL,
- Reason: planbuilder.ReasonDefault,
+ plan2 := &TabletPlan{
+ Plan: &planbuilder.Plan{
+ Table: &schema.Table{Name: sqlparser.NewTableIdent("test_table")},
+ PlanID: planbuilder.PlanDDL,
+ Reason: planbuilder.ReasonDefault,
},
}
plan2.AddStats(1, 2*time.Millisecond, 1*time.Millisecond, 1, 0)
qe.queries.Set("insert into test_table values 1", plan2)
- plan3 := &ExecPlan{
- ExecPlan: &planbuilder.ExecPlan{
- TableName: sqlparser.NewTableIdent(""),
- PlanID: planbuilder.PlanOther,
- Reason: planbuilder.ReasonDefault,
+ plan3 := &TabletPlan{
+ Plan: &planbuilder.Plan{
+ Table: &schema.Table{Name: sqlparser.NewTableIdent("")},
+ PlanID: planbuilder.PlanOther,
+ Reason: planbuilder.ReasonDefault,
},
}
plan3.AddStats(1, 75*time.Millisecond, 50*time.Millisecond, 1, 0)
qe.queries.Set("show tables", plan3)
- qe.queries.Set("", (*ExecPlan)(nil))
+ qe.queries.Set("", (*TabletPlan)(nil))
queryzHandler(qe, resp, req)
body, _ := ioutil.ReadAll(resp.Body)
@@ -108,7 +109,7 @@ func TestQueryzHandler(t *testing.T) {
checkQueryzHasPlan(t, planPattern3, plan3, body)
}
-func checkQueryzHasPlan(t *testing.T, planPattern []string, plan *ExecPlan, page []byte) {
+func checkQueryzHasPlan(t *testing.T, planPattern []string, plan *TabletPlan, page []byte) {
matcher := regexp.MustCompile(strings.Join(planPattern, `\s*`))
if !matcher.Match(page) {
t.Fatalf("queryz page does not contain plan: %v, page: %s", plan, string(page))
diff --git a/go/vt/tabletserver/replication_watcher.go b/go/vt/vttablet/tabletserver/replication_watcher.go
similarity index 87%
rename from go/vt/tabletserver/replication_watcher.go
rename to go/vt/vttablet/tabletserver/replication_watcher.go
index 0a4709b412e..9650aa7019c 100644
--- a/go/vt/tabletserver/replication_watcher.go
+++ b/go/vt/vttablet/tabletserver/replication_watcher.go
@@ -17,8 +17,8 @@ import (
"github.com/youtube/vitess/go/vt/binlog/eventtoken"
"github.com/youtube/vitess/go/vt/dbconfigs"
"github.com/youtube/vitess/go/vt/mysqlctl"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
binlogdatapb "github.com/youtube/vitess/go/vt/proto/binlogdata"
querypb "github.com/youtube/vitess/go/vt/proto/query"
@@ -89,18 +89,21 @@ func (rpw *ReplicationWatcher) Close() {
// Process processes the replication stream.
func (rpw *ReplicationWatcher) Process(ctx context.Context, dbconfigs dbconfigs.DBConfigs, mysqld mysqlctl.MysqlDaemon) {
- defer rpw.wg.Done()
+ defer func() {
+ tabletenv.LogError()
+ rpw.wg.Done()
+ }()
for {
log.Infof("Starting a binlog Streamer from current replication position to monitor binlogs")
- streamer := binlog.NewStreamer(dbconfigs.App.DbName, mysqld, nil /*clientCharset*/, replication.Position{}, 0 /*timestamp*/, func(trans *binlogdatapb.BinlogTransaction) error {
+ streamer := binlog.NewStreamer(dbconfigs.App.DbName, mysqld, rpw.se, nil /*clientCharset*/, replication.Position{}, 0 /*timestamp*/, func(eventToken *querypb.EventToken, statements []binlog.FullBinlogStatement) error {
// Save the event token.
rpw.mu.Lock()
- rpw.eventToken = trans.EventToken
+ rpw.eventToken = eventToken
rpw.mu.Unlock()
// If it's a DDL, trigger a schema reload.
- for _, statement := range trans.Statements {
- if statement.Category != binlogdatapb.BinlogTransaction_Statement_BL_DDL {
+ for _, statement := range statements {
+ if statement.Statement.Category != binlogdatapb.BinlogTransaction_Statement_BL_DDL {
continue
}
err := rpw.se.Reload(ctx)
diff --git a/go/vt/vttablet/tabletserver/rules/map.go b/go/vt/vttablet/tabletserver/rules/map.go
new file mode 100644
index 00000000000..931a51c0ca8
--- /dev/null
+++ b/go/vt/vttablet/tabletserver/rules/map.go
@@ -0,0 +1,92 @@
+// Copyright 2014, Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package rules
+
+import (
+ "encoding/json"
+ "errors"
+ "sync"
+
+ log "github.com/golang/glog"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/planbuilder"
+)
+
+// Map is the maintainer of Rules from multiple sources
+type Map struct {
+ // mutex to protect following queryRulesMap
+ mu sync.Mutex
+ // queryRulesMap maps the names of different query rule sources to the actual Rules structure
+ queryRulesMap map[string]*Rules
+}
+
+// NewMap returns an empty Map object.
+func NewMap() *Map {
+ qri := &Map{
+ queryRulesMap: map[string]*Rules{},
+ }
+ return qri
+}
+
+// RegisterSource registers a query rule source name with Map.
+func (qri *Map) RegisterSource(ruleSource string) {
+ qri.mu.Lock()
+ defer qri.mu.Unlock()
+ if _, existed := qri.queryRulesMap[ruleSource]; existed {
+ log.Errorf("Query rule source " + ruleSource + " has been registered")
+ panic("Query rule source " + ruleSource + " has been registered")
+ }
+ qri.queryRulesMap[ruleSource] = New()
+}
+
+// UnRegisterSource removes a registered query rule source name.
+func (qri *Map) UnRegisterSource(ruleSource string) {
+ qri.mu.Lock()
+ defer qri.mu.Unlock()
+ delete(qri.queryRulesMap, ruleSource)
+}
+
+// SetRules takes an external Rules structure and overwrite one of the
+// internal Rules as designated by ruleSource parameter.
+func (qri *Map) SetRules(ruleSource string, newRules *Rules) error {
+ if newRules == nil {
+ newRules = New()
+ }
+ qri.mu.Lock()
+ defer qri.mu.Unlock()
+ if _, ok := qri.queryRulesMap[ruleSource]; ok {
+ qri.queryRulesMap[ruleSource] = newRules.Copy()
+ return nil
+ }
+ return errors.New("Rule source identifier " + ruleSource + " is not valid")
+}
+
+// Get returns the corresponding Rules as designated by ruleSource parameter.
+func (qri *Map) Get(ruleSource string) (*Rules, error) {
+ qri.mu.Lock()
+ defer qri.mu.Unlock()
+ if ruleset, ok := qri.queryRulesMap[ruleSource]; ok {
+ return ruleset.Copy(), nil
+ }
+ return New(), errors.New("Rule source identifier " + ruleSource + " is not valid")
+}
+
+// FilterByPlan creates a new Rules by prefiltering on all query rules that are contained in internal
+// Rules structures, in other words, query rules from all predefined sources will be applied.
+func (qri *Map) FilterByPlan(query string, planid planbuilder.PlanType, tableName string) (newqrs *Rules) {
+ qri.mu.Lock()
+ defer qri.mu.Unlock()
+ newqrs = New()
+ for _, rules := range qri.queryRulesMap {
+ newqrs.Append(rules.FilterByPlan(query, planid, tableName))
+ }
+ return newqrs
+}
+
+// MarshalJSON marshals to JSON.
+func (qri *Map) MarshalJSON() ([]byte, error) {
+ qri.mu.Lock()
+ defer qri.mu.Unlock()
+ return json.Marshal(qri.queryRulesMap)
+}
diff --git a/go/vt/tabletserver/query_rule_info_test.go b/go/vt/vttablet/tabletserver/rules/map_test.go
similarity index 71%
rename from go/vt/tabletserver/query_rule_info_test.go
rename to go/vt/vttablet/tabletserver/rules/map_test.go
index 8ff09ce354e..c88d6f8c094 100644
--- a/go/vt/tabletserver/query_rule_info_test.go
+++ b/go/vt/vttablet/tabletserver/rules/map_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package tabletserver
+package rules
import (
"fmt"
@@ -10,15 +10,15 @@ import (
"strings"
"testing"
- "github.com/youtube/vitess/go/vt/tabletserver/planbuilder"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/planbuilder"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
)
var (
- keyrangeRules *QueryRules
- blacklistRules *QueryRules
- otherRules *QueryRules
+ keyrangeRules *Rules
+ blacklistRules *Rules
+ otherRules *Rules
)
// mimic query rules from keyrange
@@ -30,10 +30,10 @@ const blacklistQueryRules string = "BLACKLIST_QUERY_RULES"
// mimic query rules from custom source
const customQueryRules string = "CUSTOM_QUERY_RULES"
-func setupQueryRules() {
- var qr *QueryRule
+func setupRules() {
+ var qr *Rule
// mock keyrange rules
- keyrangeRules = NewQueryRules()
+ keyrangeRules = New()
dmlPlans := []struct {
planID planbuilder.PlanType
onAbsent bool
@@ -56,7 +56,7 @@ func setupQueryRules() {
}
// mock blacklisted tables
- blacklistRules = NewQueryRules()
+ blacklistRules = New()
blacklistedTables := []string{"bannedtable1", "bannedtable2", "bannedtable3"}
qr = NewQueryRule("enforce blacklisted tables", "blacklisted_table", QRFailRetry)
for _, t := range blacklistedTables {
@@ -65,36 +65,36 @@ func setupQueryRules() {
blacklistRules.Add(qr)
// mock custom rules
- otherRules = NewQueryRules()
+ otherRules = New()
qr = NewQueryRule("sample custom rule", "customrule_ban_bindvar", QRFail)
qr.AddTableCond("t_customer")
qr.AddBindVarCond("bindvar1", true, false, QRNoOp, nil)
otherRules.Add(qr)
}
-func TestQueryRuleInfoRegisterARegisteredSource(t *testing.T) {
- setupQueryRules()
- qri := NewQueryRuleInfo()
- qri.RegisterQueryRuleSource(keyrangeQueryRules)
+func TestMapRegisterARegisteredSource(t *testing.T) {
+ setupRules()
+ qri := NewMap()
+ qri.RegisterSource(keyrangeQueryRules)
defer func() {
err := recover()
if err == nil {
t.Fatalf("should get an error for registering a registered query rule source ")
}
}()
- qri.RegisterQueryRuleSource(keyrangeQueryRules)
+ qri.RegisterSource(keyrangeQueryRules)
}
-func TestQueryRuleInfoSetRulesWithNil(t *testing.T) {
- setupQueryRules()
- qri := NewQueryRuleInfo()
+func TestMapSetRulesWithNil(t *testing.T) {
+ setupRules()
+ qri := NewMap()
- qri.RegisterQueryRuleSource(keyrangeQueryRules)
+ qri.RegisterSource(keyrangeQueryRules)
err := qri.SetRules(keyrangeQueryRules, keyrangeRules)
if err != nil {
- t.Errorf("Failed to set keyrange QueryRules : %s", err)
+ t.Errorf("Failed to set keyrange Rules : %s", err)
}
- qrs, err := qri.GetRules(keyrangeQueryRules)
+ qrs, err := qri.Get(keyrangeQueryRules)
if err != nil {
t.Errorf("GetRules failed to retrieve keyrangeQueryRules that has been set: %s", err)
}
@@ -104,57 +104,57 @@ func TestQueryRuleInfoSetRulesWithNil(t *testing.T) {
qri.SetRules(keyrangeQueryRules, nil)
- qrs, err = qri.GetRules(keyrangeQueryRules)
+ qrs, err = qri.Get(keyrangeQueryRules)
if err != nil {
t.Errorf("GetRules failed to retrieve keyrangeQueryRules that has been set: %s", err)
}
- if !reflect.DeepEqual(qrs, NewQueryRules()) {
+ if !reflect.DeepEqual(qrs, New()) {
t.Errorf("keyrangeQueryRules retrived is %v, but the expected value should be %v", qrs, keyrangeRules)
}
}
-func TestQueryRuleInfoGetSetQueryRules(t *testing.T) {
- setupQueryRules()
- qri := NewQueryRuleInfo()
+func TestMapGetSetQueryRules(t *testing.T) {
+ setupRules()
+ qri := NewMap()
- qri.RegisterQueryRuleSource(keyrangeQueryRules)
- qri.RegisterQueryRuleSource(blacklistQueryRules)
- qri.RegisterQueryRuleSource(customQueryRules)
+ qri.RegisterSource(keyrangeQueryRules)
+ qri.RegisterSource(blacklistQueryRules)
+ qri.RegisterSource(customQueryRules)
- // Test if we can get a QueryRules without a predefined rule set name
- qrs, err := qri.GetRules("Foo")
+ // Test if we can get a Rules without a predefined rule set name
+ qrs, err := qri.Get("Foo")
if err == nil {
t.Errorf("GetRules shouldn't succeed with 'Foo' as the rule set name")
}
if qrs == nil {
- t.Errorf("GetRules should always return empty QueryRules and never nil")
+ t.Errorf("GetRules should always return empty Rules and never nil")
}
- if !reflect.DeepEqual(qrs, NewQueryRules()) {
- t.Errorf("QueryRuleInfo contains only empty QueryRules at the beginning")
+ if !reflect.DeepEqual(qrs, New()) {
+ t.Errorf("Map contains only empty Rules at the beginning")
}
- // Test if we can set a QueryRules without a predefined rule set name
- err = qri.SetRules("Foo", NewQueryRules())
+ // Test if we can set a Rules without a predefined rule set name
+ err = qri.SetRules("Foo", New())
if err == nil {
t.Errorf("SetRules shouldn't succeed with 'Foo' as the rule set name")
}
- // Test if we can successfully set QueryRules previously mocked into QueryRuleInfo
+ // Test if we can successfully set Rules previously mocked into Map
err = qri.SetRules(keyrangeQueryRules, keyrangeRules)
if err != nil {
- t.Errorf("Failed to set keyrange QueryRules : %s", err)
+ t.Errorf("Failed to set keyrange Rules : %s", err)
}
err = qri.SetRules(blacklistQueryRules, blacklistRules)
if err != nil {
- t.Errorf("Failed to set blacklist QueryRules: %s", err)
+ t.Errorf("Failed to set blacklist Rules: %s", err)
}
err = qri.SetRules(customQueryRules, otherRules)
if err != nil {
- t.Errorf("Failed to set custom QueryRules: %s", err)
+ t.Errorf("Failed to set custom Rules: %s", err)
}
// Test if we can successfully retrive rules that've been set
- qrs, err = qri.GetRules(keyrangeQueryRules)
+ qrs, err = qri.Get(keyrangeQueryRules)
if err != nil {
t.Errorf("GetRules failed to retrieve keyrangeQueryRules that has been set: %s", err)
}
@@ -162,7 +162,7 @@ func TestQueryRuleInfoGetSetQueryRules(t *testing.T) {
t.Errorf("keyrangeQueryRules retrived is %v, but the expected value should be %v", qrs, keyrangeRules)
}
- qrs, err = qri.GetRules(blacklistQueryRules)
+ qrs, err = qri.Get(blacklistQueryRules)
if err != nil {
t.Errorf("GetRules failed to retrieve blacklistQueryRules that has been set: %s", err)
}
@@ -170,7 +170,7 @@ func TestQueryRuleInfoGetSetQueryRules(t *testing.T) {
t.Errorf("blacklistQueryRules retrived is %v, but the expected value should be %v", qrs, blacklistRules)
}
- qrs, err = qri.GetRules(customQueryRules)
+ qrs, err = qri.Get(customQueryRules)
if err != nil {
t.Errorf("GetRules failed to retrieve customQueryRules that has been set: %s", err)
}
@@ -179,21 +179,21 @@ func TestQueryRuleInfoGetSetQueryRules(t *testing.T) {
}
}
-func TestQueryRuleInfoFilterByPlan(t *testing.T) {
- var qrs *QueryRules
- setupQueryRules()
- qri := NewQueryRuleInfo()
+func TestMapFilterByPlan(t *testing.T) {
+ var qrs *Rules
+ setupRules()
+ qri := NewMap()
- qri.RegisterQueryRuleSource(keyrangeQueryRules)
- qri.RegisterQueryRuleSource(blacklistQueryRules)
- qri.RegisterQueryRuleSource(customQueryRules)
+ qri.RegisterSource(keyrangeQueryRules)
+ qri.RegisterSource(blacklistQueryRules)
+ qri.RegisterSource(customQueryRules)
qri.SetRules(keyrangeQueryRules, keyrangeRules)
qri.SetRules(blacklistQueryRules, blacklistRules)
qri.SetRules(customQueryRules, otherRules)
// Test filter by keyrange rule
- qrs = qri.filterByPlan("insert into t_test values(123, 456, 'abc')", planbuilder.PlanInsertPK, "t_test")
+ qrs = qri.FilterByPlan("insert into t_test values(123, 456, 'abc')", planbuilder.PlanInsertPK, "t_test")
if l := len(qrs.rules); l != 1 {
t.Errorf("Insert PK query matches %d rules, but we expect %d", l, 1)
}
@@ -202,7 +202,7 @@ func TestQueryRuleInfoFilterByPlan(t *testing.T) {
}
// Test filter by blacklist rule
- qrs = qri.filterByPlan("select * from bannedtable2", planbuilder.PlanPassSelect, "bannedtable2")
+ qrs = qri.FilterByPlan("select * from bannedtable2", planbuilder.PlanPassSelect, "bannedtable2")
if l := len(qrs.rules); l != 1 {
t.Errorf("Select from bannedtable matches %d rules, but we expect %d", l, 1)
}
@@ -211,7 +211,7 @@ func TestQueryRuleInfoFilterByPlan(t *testing.T) {
}
// Test filter by custom rule
- qrs = qri.filterByPlan("select cid from t_customer limit 10", planbuilder.PlanPassSelect, "t_customer")
+ qrs = qri.FilterByPlan("select cid from t_customer limit 10", planbuilder.PlanPassSelect, "t_customer")
if l := len(qrs.rules); l != 1 {
t.Errorf("Select from t_customer matches %d rules, but we expect %d", l, 1)
}
@@ -220,12 +220,12 @@ func TestQueryRuleInfoFilterByPlan(t *testing.T) {
}
// Test match two rules: both keyrange rule and custom rule will be matched
- otherRules = NewQueryRules()
+ otherRules = New()
qr := NewQueryRule("sample custom rule", "customrule_ban_bindvar", QRFail)
qr.AddBindVarCond("bindvar1", true, false, QRNoOp, nil)
otherRules.Add(qr)
qri.SetRules(customQueryRules, otherRules)
- qrs = qri.filterByPlan("insert into t_test values (:bindvar1, 123, 'test')", planbuilder.PlanInsertPK, "t_test")
+ qrs = qri.FilterByPlan("insert into t_test values (:bindvar1, 123, 'test')", planbuilder.PlanInsertPK, "t_test")
if l := len(qrs.rules); l != 2 {
t.Errorf("Insert into t_test matches %d rules: %v, but we expect %d rules to be matched", l, qrs.rules, 2)
}
@@ -242,12 +242,12 @@ func TestQueryRuleInfoFilterByPlan(t *testing.T) {
qrs.rules[0].Name, qrs.rules[1].Name, "keyspace_id_not_in_range", "customrule_ban_bindvar")
}
-func TestQueryRuleInfoJSON(t *testing.T) {
- setupQueryRules()
- qri := NewQueryRuleInfo()
- qri.RegisterQueryRuleSource(blacklistQueryRules)
+func TestMapJSON(t *testing.T) {
+ setupRules()
+ qri := NewMap()
+ qri.RegisterSource(blacklistQueryRules)
_ = qri.SetRules(blacklistQueryRules, blacklistRules)
- qri.RegisterQueryRuleSource(customQueryRules)
+ qri.RegisterSource(customQueryRules)
_ = qri.SetRules(customQueryRules, otherRules)
got := marshalled(qri)
want := compacted(`{
diff --git a/go/vt/tabletserver/query_rules.go b/go/vt/vttablet/tabletserver/rules/rules.go
similarity index 74%
rename from go/vt/tabletserver/query_rules.go
rename to go/vt/vttablet/tabletserver/rules/rules.go
index 692195029dd..a0cfb513f6d 100644
--- a/go/vt/tabletserver/query_rules.go
+++ b/go/vt/vttablet/tabletserver/rules/rules.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package tabletserver
+package rules
import (
"bytes"
@@ -13,8 +13,8 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/key"
- "github.com/youtube/vitess/go/vt/tabletserver/planbuilder"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/planbuilder"
+ "github.com/youtube/vitess/go/vt/vterrors"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
@@ -23,22 +23,22 @@ import (
//-----------------------------------------------
-// QueryRules is used to store and execute rules for the tabletserver.
-type QueryRules struct {
- rules []*QueryRule
+// Rules is used to store and execute rules for the tabletserver.
+type Rules struct {
+ rules []*Rule
}
-// NewQueryRules creates a new QueryRules.
-func NewQueryRules() *QueryRules {
- return &QueryRules{}
+// New creates a new Rules.
+func New() *Rules {
+ return &Rules{}
}
-// Copy performs a deep copy of QueryRules.
+// Copy performs a deep copy of Rules.
// A nil input produces a nil output.
-func (qrs *QueryRules) Copy() (newqrs *QueryRules) {
- newqrs = NewQueryRules()
+func (qrs *Rules) Copy() (newqrs *Rules) {
+ newqrs = New()
if qrs.rules != nil {
- newqrs.rules = make([]*QueryRule, 0, len(qrs.rules))
+ newqrs.rules = make([]*Rule, 0, len(qrs.rules))
for _, qr := range qrs.rules {
newqrs.rules = append(newqrs.rules, qr.Copy())
}
@@ -46,22 +46,22 @@ func (qrs *QueryRules) Copy() (newqrs *QueryRules) {
return newqrs
}
-// Append merges the rules from another QueryRules into the receiver
-func (qrs *QueryRules) Append(otherqrs *QueryRules) {
+// Append merges the rules from another Rules into the receiver
+func (qrs *Rules) Append(otherqrs *Rules) {
for _, qr := range otherqrs.rules {
qrs.rules = append(qrs.rules, qr)
}
}
-// Add adds a QueryRule to QueryRules. It does not check
+// Add adds a Rule to Rules. It does not check
// for duplicates.
-func (qrs *QueryRules) Add(qr *QueryRule) {
+func (qrs *Rules) Add(qr *Rule) {
qrs.rules = append(qrs.rules, qr)
}
-// Find finds the first occurrence of a QueryRule by matching
+// Find finds the first occurrence of a Rule by matching
// the Name field. It returns nil if the rule was not found.
-func (qrs *QueryRules) Find(name string) (qr *QueryRule) {
+func (qrs *Rules) Find(name string) (qr *Rule) {
for _, qr = range qrs.rules {
if qr.Name == name {
return qr
@@ -70,9 +70,9 @@ func (qrs *QueryRules) Find(name string) (qr *QueryRule) {
return nil
}
-// Delete deletes a QueryRule by name and returns the rule
+// Delete deletes a Rule by name and returns the rule
// that was deleted. It returns nil if the rule was not found.
-func (qrs *QueryRules) Delete(name string) (qr *QueryRule) {
+func (qrs *Rules) Delete(name string) (qr *Rule) {
for i, qr := range qrs.rules {
if qr.Name == name {
for j := i; j < len(qrs.rules)-i-1; j++ {
@@ -85,19 +85,14 @@ func (qrs *QueryRules) Delete(name string) (qr *QueryRule) {
return nil
}
-// UnmarshalJSON unmarshals QueryRules.
-func (qrs *QueryRules) UnmarshalJSON(data []byte) (err error) {
+// UnmarshalJSON unmarshals Rules.
+func (qrs *Rules) UnmarshalJSON(data []byte) (err error) {
var rulesInfo []map[string]interface{}
dec := json.NewDecoder(bytes.NewReader(data))
dec.UseNumber()
err = dec.Decode(&rulesInfo)
if err != nil {
- // TODO(aaijazi): There doesn't seem to be a better error code for this, but
- // we consider InternalErrors to be retriable (which this error shouldn't be).
- // Ideally, we should have an error code that means "This isn't the query's
- // fault, but don't retry either, as this will be a global problem".
- // (true for all INTERNAL_ERRORS in query_rules)
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "%v", err)
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%v", err)
}
for _, ruleInfo := range rulesInfo {
qr, err := BuildQueryRule(ruleInfo)
@@ -110,7 +105,7 @@ func (qrs *QueryRules) UnmarshalJSON(data []byte) (err error) {
}
// MarshalJSON marshals to JSON.
-func (qrs *QueryRules) MarshalJSON() ([]byte, error) {
+func (qrs *Rules) MarshalJSON() ([]byte, error) {
b := bytes.NewBuffer(nil)
_, _ = b.WriteString("[")
for i, rule := range qrs.rules {
@@ -123,22 +118,22 @@ func (qrs *QueryRules) MarshalJSON() ([]byte, error) {
return b.Bytes(), nil
}
-// filterByPlan creates a new QueryRules by prefiltering on the query and planId. This allows
-// us to create query plan specific QueryRules out of the original QueryRules. In the new rules,
+// FilterByPlan creates a new Rules by prefiltering on the query and planId. This allows
+// us to create query plan specific Rules out of the original Rules. In the new rules,
// query, plans and tableNames predicates are empty.
-func (qrs *QueryRules) filterByPlan(query string, planid planbuilder.PlanType, tableName string) (newqrs *QueryRules) {
- var newrules []*QueryRule
+func (qrs *Rules) FilterByPlan(query string, planid planbuilder.PlanType, tableName string) (newqrs *Rules) {
+ var newrules []*Rule
for _, qr := range qrs.rules {
- if newrule := qr.filterByPlan(query, planid, tableName); newrule != nil {
+ if newrule := qr.FilterByPlan(query, planid, tableName); newrule != nil {
newrules = append(newrules, newrule)
}
}
- return &QueryRules{newrules}
+ return &Rules{newrules}
}
-func (qrs *QueryRules) getAction(ip, user string, bindVars map[string]interface{}) (action Action, desc string) {
+func (qrs *Rules) GetAction(ip, user string, bindVars map[string]interface{}) (action Action, desc string) {
for _, qr := range qrs.rules {
- if act := qr.getAction(ip, user, bindVars); act != QRContinue {
+ if act := qr.GetAction(ip, user, bindVars); act != QRContinue {
return act, qr.Description
}
}
@@ -147,15 +142,15 @@ func (qrs *QueryRules) getAction(ip, user string, bindVars map[string]interface{
//-----------------------------------------------
-// QueryRule represents one rule (conditions-action).
+// Rule represents one rule (conditions-action).
// Name is meant to uniquely identify a rule.
// Description is a human readable comment that describes the rule.
-// For a QueryRule to fire, all conditions of the QueryRule
-// have to match. For example, an empty QueryRule will match
+// For a Rule to fire, all conditions of the Rule
+// have to match. For example, an empty Rule will match
// all requests.
-// Every QueryRule has an associated Action. If all the conditions
-// of the QueryRule are met, then the Action is triggerred.
-type QueryRule struct {
+// Every Rule has an associated Action. If all the conditions
+// of the Rule are met, then the Action is triggerred.
+type Rule struct {
Description string
Name string
@@ -187,15 +182,15 @@ func (nr namedRegexp) MarshalJSON() ([]byte, error) {
return json.Marshal(nr.name)
}
-// NewQueryRule creates a new QueryRule.
-func NewQueryRule(description, name string, act Action) (qr *QueryRule) {
+// NewQueryRule creates a new Rule.
+func NewQueryRule(description, name string, act Action) (qr *Rule) {
// We ignore act because there's only one action right now
- return &QueryRule{Description: description, Name: name, act: act}
+ return &Rule{Description: description, Name: name, act: act}
}
-// Copy performs a deep copy of a QueryRule.
-func (qr *QueryRule) Copy() (newqr *QueryRule) {
- newqr = &QueryRule{
+// Copy performs a deep copy of a Rule.
+func (qr *Rule) Copy() (newqr *Rule) {
+ newqr = &Rule{
Description: qr.Description,
Name: qr.Name,
requestIP: qr.requestIP,
@@ -219,7 +214,7 @@ func (qr *QueryRule) Copy() (newqr *QueryRule) {
}
// MarshalJSON marshals to JSON.
-func (qr *QueryRule) MarshalJSON() ([]byte, error) {
+func (qr *Rule) MarshalJSON() ([]byte, error) {
b := bytes.NewBuffer(nil)
safeEncode(b, `{"Description":`, qr.Description)
safeEncode(b, `,"Name":`, qr.Name)
@@ -250,7 +245,7 @@ func (qr *QueryRule) MarshalJSON() ([]byte, error) {
// SetIPCond adds a regular expression condition for the client IP.
// It has to be a full match (not substring).
-func (qr *QueryRule) SetIPCond(pattern string) (err error) {
+func (qr *Rule) SetIPCond(pattern string) (err error) {
qr.requestIP.name = pattern
qr.requestIP.Regexp, err = regexp.Compile(makeExact(pattern))
return err
@@ -258,7 +253,7 @@ func (qr *QueryRule) SetIPCond(pattern string) (err error) {
// SetUserCond adds a regular expression condition for the user name
// used by the client.
-func (qr *QueryRule) SetUserCond(pattern string) (err error) {
+func (qr *Rule) SetUserCond(pattern string) (err error) {
qr.user.name = pattern
qr.user.Regexp, err = regexp.Compile(makeExact(pattern))
return
@@ -267,19 +262,19 @@ func (qr *QueryRule) SetUserCond(pattern string) (err error) {
// AddPlanCond adds to the list of plans that can be matched for
// the rule to fire.
// This function acts as an OR: Any plan id match is considered a match.
-func (qr *QueryRule) AddPlanCond(planType planbuilder.PlanType) {
+func (qr *Rule) AddPlanCond(planType planbuilder.PlanType) {
qr.plans = append(qr.plans, planType)
}
// AddTableCond adds to the list of tableNames that can be matched for
// the rule to fire.
// This function acts as an OR: Any tableName match is considered a match.
-func (qr *QueryRule) AddTableCond(tableName string) {
+func (qr *Rule) AddTableCond(tableName string) {
qr.tableNames = append(qr.tableNames, tableName)
}
// SetQueryCond adds a regular expression condition for the query.
-func (qr *QueryRule) SetQueryCond(pattern string) (err error) {
+func (qr *Rule) SetQueryCond(pattern string) (err error) {
qr.query.name = pattern
qr.query.Regexp, err = regexp.Compile(makeExact(pattern))
return
@@ -290,8 +285,8 @@ func makeExact(pattern string) string {
return fmt.Sprintf("^%s$", pattern)
}
-// AddBindVarCond adds a bind variable restriction to the QueryRule.
-// All bind var conditions have to be satisfied for the QueryRule
+// AddBindVarCond adds a bind variable restriction to the Rule.
+// All bind var conditions have to be satisfied for the Rule
// to be a match.
// name represents the name (not regexp) of the bind variable.
// onAbsent specifies the value of the condition if the
@@ -308,7 +303,7 @@ func makeExact(pattern string) string {
// string ==, !=, <, >=, >, <=, MATCH, NOMATCH []byte, string
// KeyRange IN, NOTIN whole numbers
// whole numbers can be: int, int8, int16, int32, int64, uint64
-func (qr *QueryRule) AddBindVarCond(name string, onAbsent, onMismatch bool, op Operator, value interface{}) error {
+func (qr *Rule) AddBindVarCond(name string, onAbsent, onMismatch bool, op Operator, value interface{}) error {
var converted bvcValue
if op == QRNoOp {
qr.bindVarConds = append(qr.bindVarConds, BindVarCond{name, onAbsent, onMismatch, op, nil})
@@ -333,7 +328,7 @@ func (qr *QueryRule) AddBindVarCond(name string, onAbsent, onMismatch bool, op O
// Change the value to compiled regexp
re, err := regexp.Compile(makeExact(v))
if err != nil {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "processing %s: %v", v, err)
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "processing %s: %v", v, err)
}
converted = bvcre{re}
} else {
@@ -346,20 +341,20 @@ func (qr *QueryRule) AddBindVarCond(name string, onAbsent, onMismatch bool, op O
b := bvcKeyRange(*v)
converted = &b
default:
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "type %T not allowed as condition operand (%v)", value, value)
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "type %T not allowed as condition operand (%v)", value, value)
}
qr.bindVarConds = append(qr.bindVarConds, BindVarCond{name, onAbsent, onMismatch, op, converted})
return nil
Error:
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "invalid operator %s for type %T (%v)", op, value, value)
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid operator %v for type %T (%v)", op, value, value)
}
-// filterByPlan returns a new QueryRule if the query and planid match.
-// The new QueryRule will contain all the original constraints other
-// than the plan and query. If the plan and query don't match the QueryRule,
+// FilterByPlan returns a new Rule if the query and planid match.
+// The new Rule will contain all the original constraints other
+// than the plan and query. If the plan and query don't match the Rule,
// then it returns nil.
-func (qr *QueryRule) filterByPlan(query string, planid planbuilder.PlanType, tableName string) (newqr *QueryRule) {
+func (qr *Rule) FilterByPlan(query string, planid planbuilder.PlanType, tableName string) (newqr *Rule) {
if !reMatch(qr.query.Regexp, query) {
return nil
}
@@ -376,7 +371,7 @@ func (qr *QueryRule) filterByPlan(query string, planid planbuilder.PlanType, tab
return newqr
}
-func (qr *QueryRule) getAction(ip, user string, bindVars map[string]interface{}) Action {
+func (qr *Rule) GetAction(ip, user string, bindVars map[string]interface{}) Action {
if !reMatch(qr.requestIP.Regexp, ip) {
return QRContinue
}
@@ -431,10 +426,10 @@ func bvMatch(bvcond BindVarCond, bindVars map[string]interface{}) bool {
}
//-----------------------------------------------
-// Support types for QueryRule
+// Support types for Rule
// Action speficies the list of actions to perform
-// when a QueryRule is triggered.
+// when a Rule is triggered.
type Action int
// These are actions.
@@ -885,11 +880,11 @@ func MapStrOperator(strop string) (op Operator, err error) {
if op, ok := opmap[strop]; ok {
return op, nil
}
- return QRNoOp, tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "invalid Operator %s", strop)
+ return QRNoOp, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid Operator %s", strop)
}
// BuildQueryRule builds a query rule from a ruleInfo.
-func BuildQueryRule(ruleInfo map[string]interface{}) (qr *QueryRule, err error) {
+func BuildQueryRule(ruleInfo map[string]interface{}) (qr *Rule, err error) {
qr = NewQueryRule("", "", QRFail)
for k, v := range ruleInfo {
var sv string
@@ -899,15 +894,15 @@ func BuildQueryRule(ruleInfo map[string]interface{}) (qr *QueryRule, err error)
case "Name", "Description", "RequestIP", "User", "Query", "Action":
sv, ok = v.(string)
if !ok {
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "want string for %s", k)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "want string for %s", k)
}
case "Plans", "BindVarConds", "TableNames":
lv, ok = v.([]interface{})
if !ok {
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "want list for %s", k)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "want list for %s", k)
}
default:
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "unrecognized tag %s", k)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unrecognized tag %s", k)
}
switch k {
case "Name":
@@ -917,27 +912,27 @@ func BuildQueryRule(ruleInfo map[string]interface{}) (qr *QueryRule, err error)
case "RequestIP":
err = qr.SetIPCond(sv)
if err != nil {
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "could not set IP condition: %v", sv)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "could not set IP condition: %v", sv)
}
case "User":
err = qr.SetUserCond(sv)
if err != nil {
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "could not set User condition: %v", sv)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "could not set User condition: %v", sv)
}
case "Query":
err = qr.SetQueryCond(sv)
if err != nil {
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "could not set Query condition: %v", sv)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "could not set Query condition: %v", sv)
}
case "Plans":
for _, p := range lv {
pv, ok := p.(string)
if !ok {
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "want string for Plans")
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "want string for Plans")
}
pt, ok := planbuilder.PlanByName(pv)
if !ok {
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "invalid plan name: %s", pv)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid plan name: %s", pv)
}
qr.AddPlanCond(pt)
}
@@ -945,7 +940,7 @@ func BuildQueryRule(ruleInfo map[string]interface{}) (qr *QueryRule, err error)
for _, t := range lv {
tableName, ok := t.(string)
if !ok {
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "want string for TableNames")
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "want string for TableNames")
}
qr.AddTableCond(tableName)
}
@@ -967,7 +962,7 @@ func BuildQueryRule(ruleInfo map[string]interface{}) (qr *QueryRule, err error)
case "FAIL_RETRY":
qr.act = QRFailRetry
default:
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "invalid Action %s", sv)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid Action %s", sv)
}
}
}
@@ -977,41 +972,41 @@ func BuildQueryRule(ruleInfo map[string]interface{}) (qr *QueryRule, err error)
func buildBindVarCondition(bvc interface{}) (name string, onAbsent, onMismatch bool, op Operator, value interface{}, err error) {
bvcinfo, ok := bvc.(map[string]interface{})
if !ok {
- err = tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "want json object for bind var conditions")
+ err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "want json object for bind var conditions")
return
}
var v interface{}
v, ok = bvcinfo["Name"]
if !ok {
- err = tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "Name missing in BindVarConds")
+ err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Name missing in BindVarConds")
return
}
name, ok = v.(string)
if !ok {
- err = tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "want string for Name in BindVarConds")
+ err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "want string for Name in BindVarConds")
return
}
v, ok = bvcinfo["OnAbsent"]
if !ok {
- err = tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "OnAbsent missing in BindVarConds")
+ err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "OnAbsent missing in BindVarConds")
return
}
onAbsent, ok = v.(bool)
if !ok {
- err = tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "want bool for OnAbsent")
+ err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "want bool for OnAbsent")
return
}
v, ok = bvcinfo["Operator"]
if !ok {
- err = tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "Operator missing in BindVarConds")
+ err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Operator missing in BindVarConds")
return
}
strop, ok := v.(string)
if !ok {
- err = tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "want string for Operator")
+ err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "want string for Operator")
return
}
op, err = MapStrOperator(strop)
@@ -1023,7 +1018,7 @@ func buildBindVarCondition(bvc interface{}) (name string, onAbsent, onMismatch b
}
v, ok = bvcinfo["Value"]
if !ok {
- err = tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "Value missing in BindVarConds")
+ err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Value missing in BindVarConds")
return
}
if op >= QREqual && op <= QRLessEqual {
@@ -1034,50 +1029,50 @@ func buildBindVarCondition(bvc interface{}) (name string, onAbsent, onMismatch b
// Maybe uint64
value, err = strconv.ParseUint(string(v), 10, 64)
if err != nil {
- err = tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "want int64/uint64: %s", string(v))
+ err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "want int64/uint64: %s", string(v))
return
}
}
case string:
value = v
default:
- err = tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "want string or number: %v", v)
+ err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "want string or number: %v", v)
return
}
} else if op == QRMatch || op == QRNoMatch {
strvalue, ok := v.(string)
if !ok {
- err = tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "want string: %v", v)
+ err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "want string: %v", v)
return
}
value = strvalue
} else if op == QRIn || op == QRNotIn {
kr, ok := v.(map[string]interface{})
if !ok {
- err = tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "want keyrange for Value")
+ err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "want keyrange for Value")
return
}
keyrange := &topodatapb.KeyRange{}
strstart, ok := kr["Start"]
if !ok {
- err = tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "Start missing in KeyRange")
+ err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Start missing in KeyRange")
return
}
start, ok := strstart.(string)
if !ok {
- err = tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "want string for Start")
+ err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "want string for Start")
return
}
keyrange.Start = []byte(start)
strend, ok := kr["End"]
if !ok {
- err = tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "End missing in KeyRange")
+ err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "End missing in KeyRange")
return
}
end, ok := strend.(string)
if !ok {
- err = tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "want string for End")
+ err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "want string for End")
return
}
keyrange.End = []byte(end)
@@ -1086,12 +1081,12 @@ func buildBindVarCondition(bvc interface{}) (name string, onAbsent, onMismatch b
v, ok = bvcinfo["OnMismatch"]
if !ok {
- err = tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "OnMismatch missing in BindVarConds")
+ err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "OnMismatch missing in BindVarConds")
return
}
onMismatch, ok = v.(bool)
if !ok {
- err = tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "want bool for OnMismatch")
+ err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "want bool for OnMismatch")
return
}
return
diff --git a/go/vt/tabletserver/query_rules_test.go b/go/vt/vttablet/tabletserver/rules/rules_test.go
similarity index 96%
rename from go/vt/tabletserver/query_rules_test.go
rename to go/vt/vttablet/tabletserver/rules/rules_test.go
index dab9bd67b18..07eb0e23e4f 100644
--- a/go/vt/tabletserver/query_rules_test.go
+++ b/go/vt/vttablet/tabletserver/rules/rules_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package tabletserver
+package rules
import (
"bytes"
@@ -13,8 +13,8 @@ import (
"testing"
"github.com/youtube/vitess/go/vt/key"
- "github.com/youtube/vitess/go/vt/tabletserver/planbuilder"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/planbuilder"
+ "github.com/youtube/vitess/go/vt/vterrors"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
@@ -22,7 +22,7 @@ import (
)
func TestQueryRules(t *testing.T) {
- qrs := NewQueryRules()
+ qrs := New()
qr1 := NewQueryRule("rule 1", "r1", QRFail)
qr2 := NewQueryRule("rule 2", "r2", QRFail)
qrs.Add(qr1)
@@ -68,7 +68,7 @@ func TestQueryRules(t *testing.T) {
// TestCopy tests for deep copy
func TestCopy(t *testing.T) {
- qrs1 := NewQueryRules()
+ qrs1 := New()
qr1 := NewQueryRule("rule 1", "r1", QRFail)
qr1.AddPlanCond(planbuilder.PlanPassSelect)
qr1.AddTableCond("aa")
@@ -83,7 +83,7 @@ func TestCopy(t *testing.T) {
t.Errorf("qrs1: %+v, not equal to %+v", qrs2, qrs1)
}
- qrs1 = NewQueryRules()
+ qrs1 = New()
qrs2 = qrs1.Copy()
if !reflect.DeepEqual(qrs2, qrs1) {
t.Errorf("qrs1: %+v, not equal to %+v", qrs2, qrs1)
@@ -91,7 +91,7 @@ func TestCopy(t *testing.T) {
}
func TestFilterByPlan(t *testing.T) {
- qrs := NewQueryRules()
+ qrs := New()
qr1 := NewQueryRule("rule 1", "r1", QRFail)
qr1.SetIPCond("123")
@@ -117,7 +117,7 @@ func TestFilterByPlan(t *testing.T) {
qrs.Add(qr3)
qrs.Add(qr4)
- qrs1 := qrs.filterByPlan("select", planbuilder.PlanPassSelect, "a")
+ qrs1 := qrs.FilterByPlan("select", planbuilder.PlanPassSelect, "a")
want := compacted(`[{
"Description":"rule 1",
"Name":"r1",
@@ -152,7 +152,7 @@ func TestFilterByPlan(t *testing.T) {
t.Errorf("qrs1:\n%s, want\n%s", got, want)
}
- qrs1 = qrs.filterByPlan("insert", planbuilder.PlanPassSelect, "a")
+ qrs1 = qrs.FilterByPlan("insert", planbuilder.PlanPassSelect, "a")
want = compacted(`[{
"Description":"rule 2",
"Name":"r2",
@@ -168,13 +168,13 @@ func TestFilterByPlan(t *testing.T) {
t.Errorf("qrs1:\n%s, want\n%s", got, want)
}
- qrs1 = qrs.filterByPlan("insert", planbuilder.PlanSelectLock, "a")
+ qrs1 = qrs.FilterByPlan("insert", planbuilder.PlanSelectLock, "a")
got = marshalled(qrs1)
if got != want {
t.Errorf("qrs1:\n%s, want\n%s", got, want)
}
- qrs1 = qrs.filterByPlan("select", planbuilder.PlanInsertPK, "a")
+ qrs1 = qrs.FilterByPlan("select", planbuilder.PlanInsertPK, "a")
want = compacted(`[{
"Description":"rule 3",
"Name":"r3",
@@ -190,12 +190,12 @@ func TestFilterByPlan(t *testing.T) {
t.Errorf("qrs1:\n%s, want\n%s", got, want)
}
- qrs1 = qrs.filterByPlan("sel", planbuilder.PlanInsertPK, "a")
+ qrs1 = qrs.FilterByPlan("sel", planbuilder.PlanInsertPK, "a")
if qrs1.rules != nil {
t.Errorf("want nil, got non-nil")
}
- qrs1 = qrs.filterByPlan("table", planbuilder.PlanPassDML, "b")
+ qrs1 = qrs.FilterByPlan("table", planbuilder.PlanPassDML, "b")
want = compacted(`[{
"Description":"rule 4",
"Name":"r4",
@@ -209,7 +209,7 @@ func TestFilterByPlan(t *testing.T) {
qr5 := NewQueryRule("rule 5", "r5", QRFail)
qrs.Add(qr5)
- qrs1 = qrs.filterByPlan("sel", planbuilder.PlanInsertPK, "a")
+ qrs1 = qrs.FilterByPlan("sel", planbuilder.PlanInsertPK, "a")
want = compacted(`[{
"Description":"rule 5",
"Name":"r5",
@@ -220,8 +220,8 @@ func TestFilterByPlan(t *testing.T) {
t.Errorf("qrs1:\n%s, want\n%s", got, want)
}
- qrsnil1 := NewQueryRules()
- if qrsnil2 := qrsnil1.filterByPlan("", planbuilder.PlanPassSelect, "a"); qrsnil2.rules != nil {
+ qrsnil1 := New()
+ if qrsnil2 := qrsnil1.FilterByPlan("", planbuilder.PlanPassSelect, "a"); qrsnil2.rules != nil {
t.Errorf("want nil, got non-nil")
}
}
@@ -574,7 +574,7 @@ func TestBVConditions(t *testing.T) {
}
func TestAction(t *testing.T) {
- qrs := NewQueryRules()
+ qrs := New()
qr1 := NewQueryRule("rule 1", "r1", QRFail)
qr1.SetIPCond("123")
@@ -591,26 +591,26 @@ func TestAction(t *testing.T) {
bv := make(map[string]interface{})
bv["a"] = uint64(0)
- action, desc := qrs.getAction("123", "user1", bv)
+ action, desc := qrs.GetAction("123", "user1", bv)
if action != QRFail {
t.Errorf("want fail")
}
if desc != "rule 1" {
t.Errorf("want rule 1, got %s", desc)
}
- action, desc = qrs.getAction("1234", "user", bv)
+ action, desc = qrs.GetAction("1234", "user", bv)
if action != QRFailRetry {
t.Errorf("want fail_retry")
}
if desc != "rule 2" {
t.Errorf("want rule 2, got %s", desc)
}
- action, desc = qrs.getAction("1234", "user1", bv)
+ action, desc = qrs.GetAction("1234", "user1", bv)
if action != QRContinue {
t.Errorf("want continue")
}
bv["a"] = uint64(1)
- action, desc = qrs.getAction("1234", "user1", bv)
+ action, desc = qrs.GetAction("1234", "user1", bv)
if action != QRFail {
t.Errorf("want fail")
}
@@ -620,7 +620,7 @@ func TestAction(t *testing.T) {
}
func TestImport(t *testing.T) {
- var qrs = NewQueryRules()
+ var qrs = New()
jsondata := `[{
"Description": "desc1",
"Name": "name1",
@@ -703,7 +703,7 @@ var validjsons = []ValidJSONCase{
func TestValidJSON(t *testing.T) {
for i, tcase := range validjsons {
- qrs := NewQueryRules()
+ qrs := New()
err := qrs.UnmarshalJSON([]byte(tcase.input))
if err != nil {
t.Fatalf("Unexpected error for case %d: %v", i, err)
@@ -779,7 +779,7 @@ var invalidjsons = []InvalidJSONCase{
func TestInvalidJSON(t *testing.T) {
for _, tcase := range invalidjsons {
- qrs := NewQueryRules()
+ qrs := New()
err := qrs.UnmarshalJSON([]byte(tcase.input))
if err == nil {
t.Errorf("want error for case %q", tcase.input)
@@ -790,14 +790,10 @@ func TestInvalidJSON(t *testing.T) {
t.Errorf("invalid json: %s, want '%v', got '%v'", tcase.input, tcase.err, recvd)
}
}
- qrs := NewQueryRules()
+ qrs := New()
err := qrs.UnmarshalJSON([]byte(`{`))
- terr, ok := err.(*tabletenv.TabletError)
- if !ok {
- t.Fatalf("invalid json, should get a tablet error")
- }
- if terr.ErrorCode != vtrpcpb.ErrorCode_INTERNAL_ERROR {
- t.Fatalf("got: %v wanted: INTERNAL_ERROR", terr.ErrorCode)
+ if code := vterrors.Code(err); code != vtrpcpb.Code_INVALID_ARGUMENT {
+ t.Errorf("qrs.UnmarshalJSON: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT)
}
}
diff --git a/go/vt/tabletserver/engines/schema/schema_engine.go b/go/vt/vttablet/tabletserver/schema/engine.go
similarity index 86%
rename from go/vt/tabletserver/engines/schema/schema_engine.go
rename to go/vt/vttablet/tabletserver/schema/engine.go
index e67d62ccc8c..e987fa9075b 100644
--- a/go/vt/tabletserver/engines/schema/schema_engine.go
+++ b/go/vt/vttablet/tabletserver/schema/engine.go
@@ -21,12 +21,12 @@ import (
"github.com/youtube/vitess/go/sqldb"
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/stats"
- "github.com/youtube/vitess/go/sync2"
"github.com/youtube/vitess/go/timer"
"github.com/youtube/vitess/go/vt/concurrency"
"github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/connpool"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/connpool"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vterrors"
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)
@@ -48,9 +48,8 @@ type Engine struct {
// The following fields have their own synchronization
// and do not require locking mu.
- strictMode sync2.AtomicBool
- conns *connpool.Pool
- ticks *timer.Timer
+ conns *connpool.Pool
+ ticks *timer.Timer
}
var schemaOnce sync.Once
@@ -63,7 +62,6 @@ func NewEngine(checker tabletenv.MySQLChecker, config tabletenv.TabletConfig) *E
conns: connpool.New("", 3, idleTimeout, checker),
ticks: timer.NewTimer(reloadTime),
reloadTime: reloadTime,
- strictMode: sync2.NewAtomicBool(config.StrictMode),
}
schemaOnce.Do(func() {
stats.Publish("SchemaReloadTime", stats.DurationFunc(se.ticks.Interval))
@@ -96,7 +94,7 @@ func (se *Engine) Open(dbaParams *sqldb.ConnParams) error {
conn, err := se.conns.Get(ctx)
if err != nil {
- return tabletenv.NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, err)
+ return err
}
defer conn.Recycle()
@@ -105,15 +103,9 @@ func (se *Engine) Open(dbaParams *sqldb.ConnParams) error {
return err
}
- if se.strictMode.Get() {
- if err := conn.VerifyMode(); err != nil {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_UNKNOWN_ERROR, err.Error())
- }
- }
-
tableData, err := conn.Exec(ctx, mysqlconn.BaseShowTables, maxTableCount, false)
if err != nil {
- return tabletenv.PrefixTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, err, "Could not get table list: ")
+ return vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "could not get table list: %v", err)
}
tables := make(map[string]*Table, len(tableData.Rows)+1)
@@ -123,7 +115,10 @@ func (se *Engine) Open(dbaParams *sqldb.ConnParams) error {
for _, row := range tableData.Rows {
wg.Add(1)
go func(row []sqltypes.Value) {
- defer wg.Done()
+ defer func() {
+ tabletenv.LogError()
+ wg.Done()
+ }()
tableName := row[0].String()
conn, err := se.conns.Get(ctx)
@@ -155,7 +150,7 @@ func (se *Engine) Open(dbaParams *sqldb.ConnParams) error {
// Fail if we can't load the schema for any tables, but we know that some tables exist. This points to a configuration problem.
if len(tableData.Rows) != 0 && len(tables) == 1 { // len(tables) is always at least 1 because of the "dual" table
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_UNKNOWN_ERROR, "could not get schema for any tables")
+ return vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "could not get schema for any tables")
}
se.tables = tables
se.lastChange = curTime
@@ -198,7 +193,7 @@ func (se *Engine) Reload(ctx context.Context) error {
curTime, tableData, err := func() (int64, *sqltypes.Result, error) {
conn, err := se.conns.Get(ctx)
if err != nil {
- return 0, nil, tabletenv.NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, err)
+ return 0, nil, err
}
defer conn.Recycle()
curTime, err := se.mysqlTime(ctx, conn)
@@ -264,14 +259,14 @@ func (se *Engine) Reload(ctx context.Context) error {
func (se *Engine) mysqlTime(ctx context.Context, conn *connpool.DBConn) (int64, error) {
tm, err := conn.Exec(ctx, "select unix_timestamp()", 1, false)
if err != nil {
- return 0, tabletenv.PrefixTabletError(vtrpcpb.ErrorCode_UNKNOWN_ERROR, err, "Could not get MySQL time: ")
+ return 0, vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "could not get MySQL time: %v", err)
}
if len(tm.Rows) != 1 || len(tm.Rows[0]) != 1 || tm.Rows[0][0].IsNull() {
- return 0, tabletenv.NewTabletError(vtrpcpb.ErrorCode_UNKNOWN_ERROR, "Unexpected result for MySQL time: %+v", tm.Rows)
+ return 0, vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "unexpected result for MySQL time: %+v", tm.Rows)
}
t, err := strconv.ParseInt(tm.Rows[0][0].String(), 10, 64)
if err != nil {
- return 0, tabletenv.NewTabletError(vtrpcpb.ErrorCode_UNKNOWN_ERROR, "Could not parse time %+v: %v", tm, err)
+ return 0, vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "could not parse time %+v: %v", tm, err)
}
return t, nil
}
@@ -281,19 +276,18 @@ func (se *Engine) TableWasCreatedOrAltered(ctx context.Context, tableName string
se.mu.Lock()
defer se.mu.Unlock()
if !se.isOpen {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "DDL called on closed schema")
+ return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "DDL called on closed schema")
}
conn, err := se.conns.Get(ctx)
if err != nil {
- return tabletenv.NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, err)
+ return err
}
defer conn.Recycle()
tableData, err := conn.Exec(ctx, mysqlconn.BaseShowTablesForTable(tableName), 1, false)
if err != nil {
tabletenv.InternalErrors.Add("Schema", 1)
- return tabletenv.PrefixTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, err,
- fmt.Sprintf("TableWasCreatedOrAltered: information_schema query failed for table %s: ", tableName))
+ return vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "TableWasCreatedOrAltered: information_schema query failed for table %s: %v", tableName, err)
}
if len(tableData.Rows) != 1 {
// This can happen if DDLs race with each other.
@@ -308,8 +302,7 @@ func (se *Engine) TableWasCreatedOrAltered(ctx context.Context, tableName string
)
if err != nil {
tabletenv.InternalErrors.Add("Schema", 1)
- return tabletenv.PrefixTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, err,
- fmt.Sprintf("TableWasCreatedOrAltered: failed to load table %s: ", tableName))
+ return vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "TableWasCreatedOrAltered: failed to load table %s: %v", tableName, err)
}
// table_rows, data_length, index_length, max_data_length
table.SetMysqlStats(row[4], row[5], row[6], row[7], row[8])
@@ -330,19 +323,6 @@ func (se *Engine) TableWasCreatedOrAltered(ctx context.Context, tableName string
return nil
}
-// TableWasDropped must be called if a table was dropped.
-func (se *Engine) TableWasDropped(tableName sqlparser.TableIdent) {
- se.mu.Lock()
- defer se.mu.Unlock()
- if !se.isOpen {
- return
- }
-
- delete(se.tables, tableName.String())
- log.Infof("Table %s forgotten", tableName)
- se.broadcast(nil, nil, []string{tableName.String()})
-}
-
// RegisterNotifier registers the function for schema change notification.
// It also causes an immediate notification to the caller. The notified
// function must not change the map or its contents. The only exception
@@ -489,3 +469,23 @@ func (se *Engine) handleHTTPSchema(response http.ResponseWriter, request *http.R
json.HTMLEscape(buf, b)
response.Write(buf.Bytes())
}
+
+// Test methods. Do not use in non-test code.
+
+// NewEngineForTests creates a new engine, that can't query the
+// database, and will not send notifications. It starts opened, and
+// doesn't reload. Use SetTableForTests to set table schema.
+func NewEngineForTests() *Engine {
+ se := &Engine{
+ isOpen: true,
+ tables: make(map[string]*Table),
+ }
+ return se
+}
+
+// SetTableForTests puts a Table in the map directly.
+func (se *Engine) SetTableForTests(table *Table) {
+ se.mu.Lock()
+ defer se.mu.Unlock()
+ se.tables[table.Name.String()] = table
+}
diff --git a/go/vt/tabletserver/engines/schema/schema_engine_test.go b/go/vt/vttablet/tabletserver/schema/engine_test.go
similarity index 87%
rename from go/vt/tabletserver/engines/schema/schema_engine_test.go
rename to go/vt/vttablet/tabletserver/schema/engine_test.go
index b198f3e3b46..862eb2a11b1 100644
--- a/go/vt/tabletserver/engines/schema/schema_engine_test.go
+++ b/go/vt/vttablet/tabletserver/schema/engine_test.go
@@ -20,28 +20,12 @@ import (
"github.com/youtube/vitess/go/mysqlconn/fakesqldb"
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema/schematest"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema/schematest"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
querypb "github.com/youtube/vitess/go/vt/proto/query"
)
-func TestStrictMode(t *testing.T) {
- db := fakesqldb.New(t)
- defer db.Close()
- for query, result := range schematest.Queries() {
- db.AddQuery(query, result)
- }
- db.AddRejectedQuery("select @@global.sql_mode", errRejected)
- se := newEngine(10, 1*time.Second, 1*time.Second, true)
- t.Log(se)
- err := se.Open(db.ConnParams())
- want := "error: could not verify mode"
- if err == nil || !strings.Contains(err.Error(), want) {
- t.Errorf("se.Open: %v, must contain %s", err, want)
- }
-}
-
func TestOpenFailedDueToMissMySQLTime(t *testing.T) {
db := fakesqldb.New(t)
defer db.Close()
@@ -57,7 +41,7 @@ func TestOpenFailedDueToMissMySQLTime(t *testing.T) {
})
se := newEngine(10, 1*time.Second, 1*time.Second, false)
err := se.Open(db.ConnParams())
- want := "Could not get MySQL time"
+ want := "could not get MySQL time"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("se.Open: %v, want %s", err, want)
}
@@ -77,7 +61,7 @@ func TestOpenFailedDueToIncorrectMysqlRowNum(t *testing.T) {
})
se := newEngine(10, 1*time.Second, 1*time.Second, false)
err := se.Open(db.ConnParams())
- want := "Unexpected result for MySQL time"
+ want := "unexpected result for MySQL time"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("se.Open: %v, want %s", err, want)
}
@@ -97,7 +81,7 @@ func TestOpenFailedDueToInvalidTimeFormat(t *testing.T) {
})
se := newEngine(10, 1*time.Second, 1*time.Second, false)
err := se.Open(db.ConnParams())
- want := "Could not parse time"
+ want := "could not parse time"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("se.Open: %v, want %s", err, want)
}
@@ -112,7 +96,7 @@ func TestOpenFailedDueToExecErr(t *testing.T) {
db.AddRejectedQuery(mysqlconn.BaseShowTables, fmt.Errorf("injected error"))
se := newEngine(10, 1*time.Second, 1*time.Second, false)
err := se.Open(db.ConnParams())
- want := "Could not get table list"
+ want := "could not get table list"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("se.Open: %v, want %s", err, want)
}
@@ -296,45 +280,6 @@ func TestCreateOrUpdateTable(t *testing.T) {
}
}
-func TestDropTable(t *testing.T) {
- db := fakesqldb.New(t)
- defer db.Close()
- for query, result := range schematest.Queries() {
- db.AddQuery(query, result)
- }
- existingTable := sqlparser.NewTableIdent("test_table_01")
- se := newEngine(10, 1*time.Second, 1*time.Second, false)
- se.Open(db.ConnParams())
- defer se.Close()
- table := se.GetTable(existingTable)
- if table == nil {
- t.Fatalf("table: %s should exist", existingTable)
- }
- i := 0
- se.RegisterNotifier("test", func(schema map[string]*Table, created, altered, dropped []string) {
- switch i {
- case 0:
- // Ignore.
- case 1:
- want := []string{"test_table_01"}
- if !reflect.DeepEqual(dropped, want) {
- t.Errorf("callback 1: %v, want %v\n", dropped, want)
- }
- default:
- t.Fatal("unexpected")
- }
- i++
- })
- se.TableWasDropped(existingTable)
- table = se.GetTable(existingTable)
- if table != nil {
- t.Fatalf("table: %s should not exist", existingTable)
- }
- if i < 2 {
- t.Error("Notifier did not get called")
- }
-}
-
func TestExportVars(t *testing.T) {
db := fakesqldb.New(t)
defer db.Close()
diff --git a/go/vt/tabletserver/engines/schema/load_table.go b/go/vt/vttablet/tabletserver/schema/load_table.go
similarity index 97%
rename from go/vt/tabletserver/engines/schema/load_table.go
rename to go/vt/vttablet/tabletserver/schema/load_table.go
index 49ac181a706..a3570d76a34 100644
--- a/go/vt/tabletserver/engines/schema/load_table.go
+++ b/go/vt/vttablet/tabletserver/schema/load_table.go
@@ -13,8 +13,8 @@ import (
log "github.com/golang/glog"
"github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/connpool"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/connpool"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
querypb "github.com/youtube/vitess/go/vt/proto/query"
)
diff --git a/go/vt/tabletserver/engines/schema/load_table_test.go b/go/vt/vttablet/tabletserver/schema/load_table_test.go
similarity index 99%
rename from go/vt/tabletserver/engines/schema/load_table_test.go
rename to go/vt/vttablet/tabletserver/schema/load_table_test.go
index c7f06a1514c..5961902daaa 100644
--- a/go/vt/tabletserver/engines/schema/load_table_test.go
+++ b/go/vt/vttablet/tabletserver/schema/load_table_test.go
@@ -17,7 +17,7 @@ import (
"github.com/youtube/vitess/go/mysqlconn/fakesqldb"
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/connpool"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/connpool"
querypb "github.com/youtube/vitess/go/vt/proto/query"
)
diff --git a/go/vt/tabletserver/engines/schema/schema.go b/go/vt/vttablet/tabletserver/schema/schema.go
similarity index 100%
rename from go/vt/tabletserver/engines/schema/schema.go
rename to go/vt/vttablet/tabletserver/schema/schema.go
diff --git a/go/vt/tabletserver/engines/schema/schema_test.go b/go/vt/vttablet/tabletserver/schema/schema_test.go
similarity index 100%
rename from go/vt/tabletserver/engines/schema/schema_test.go
rename to go/vt/vttablet/tabletserver/schema/schema_test.go
diff --git a/go/vt/tabletserver/engines/schema/schematest/schematest.go b/go/vt/vttablet/tabletserver/schema/schematest/schematest.go
similarity index 100%
rename from go/vt/tabletserver/engines/schema/schematest/schematest.go
rename to go/vt/vttablet/tabletserver/schema/schematest/schematest.go
diff --git a/go/vt/tabletserver/engines/schema/schemaz.go b/go/vt/vttablet/tabletserver/schema/schemaz.go
similarity index 100%
rename from go/vt/tabletserver/engines/schema/schemaz.go
rename to go/vt/vttablet/tabletserver/schema/schemaz.go
diff --git a/go/vt/tabletserver/engines/schema/schemaz_test.go b/go/vt/vttablet/tabletserver/schema/schemaz_test.go
similarity index 100%
rename from go/vt/tabletserver/engines/schema/schemaz_test.go
rename to go/vt/vttablet/tabletserver/schema/schemaz_test.go
diff --git a/go/vt/tabletserver/splitquery/doc.go b/go/vt/vttablet/tabletserver/splitquery/doc.go
similarity index 100%
rename from go/vt/tabletserver/splitquery/doc.go
rename to go/vt/vttablet/tabletserver/splitquery/doc.go
diff --git a/go/vt/tabletserver/splitquery/equal_splits_algorithm.go b/go/vt/vttablet/tabletserver/splitquery/equal_splits_algorithm.go
similarity index 94%
rename from go/vt/tabletserver/splitquery/equal_splits_algorithm.go
rename to go/vt/vttablet/tabletserver/splitquery/equal_splits_algorithm.go
index 6bd1c9a7f6c..dc0e97f3b44 100644
--- a/go/vt/tabletserver/splitquery/equal_splits_algorithm.go
+++ b/go/vt/vttablet/tabletserver/splitquery/equal_splits_algorithm.go
@@ -9,9 +9,11 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema"
+ "github.com/youtube/vitess/go/vt/vterrors"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
querypb "github.com/youtube/vitess/go/vt/proto/query"
+ vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)
// EqualSplitsAlgorithm implements the SplitAlgorithmInterface and represents the equal-splits
@@ -51,12 +53,14 @@ func NewEqualSplitsAlgorithm(splitParams *SplitParams, sqlExecuter SQLExecuter)
// primary key columns, and there can be more than one primary key column for a table.
if !sqltypes.IsFloat(splitParams.splitColumns[0].Type) &&
!sqltypes.IsIntegral(splitParams.splitColumns[0].Type) {
- return nil, fmt.Errorf("using the EQUAL_SPLITS algorithm in SplitQuery requires having"+
- " a numeric (integral or float) split-column. Got type: %v", splitParams.splitColumns[0])
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT,
+ "using the EQUAL_SPLITS algorithm in SplitQuery requires having"+
+ " a numeric (integral or float) split-column. Got type: %v", splitParams.splitColumns[0])
}
if splitParams.splitCount <= 0 {
- return nil, fmt.Errorf("using the EQUAL_SPLITS algorithm in SplitQuery requires a positive"+
- " splitParams.splitCount. Got: %v", splitParams.splitCount)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT,
+ "using the EQUAL_SPLITS algorithm in SplitQuery requires a positive"+
+ " splitParams.splitCount. Got: %v", splitParams.splitCount)
}
result := &EqualSplitsAlgorithm{
splitParams: splitParams,
diff --git a/go/vt/tabletserver/splitquery/equal_splits_algorithm_test.go b/go/vt/vttablet/tabletserver/splitquery/equal_splits_algorithm_test.go
similarity index 97%
rename from go/vt/tabletserver/splitquery/equal_splits_algorithm_test.go
rename to go/vt/vttablet/tabletserver/splitquery/equal_splits_algorithm_test.go
index 5be07f70d96..f4e764e25c1 100644
--- a/go/vt/tabletserver/splitquery/equal_splits_algorithm_test.go
+++ b/go/vt/vttablet/tabletserver/splitquery/equal_splits_algorithm_test.go
@@ -9,8 +9,8 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
- "github.com/youtube/vitess/go/vt/tabletserver/splitquery/splitquery_testing"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/splitquery/splitquery_testing"
)
// Table-driven test for equal-splits algorithm.
diff --git a/go/vt/tabletserver/splitquery/example_test.go b/go/vt/vttablet/tabletserver/splitquery/example_test.go
similarity index 94%
rename from go/vt/tabletserver/splitquery/example_test.go
rename to go/vt/vttablet/tabletserver/splitquery/example_test.go
index 890568722df..19e9718ec3b 100644
--- a/go/vt/tabletserver/splitquery/example_test.go
+++ b/go/vt/vttablet/tabletserver/splitquery/example_test.go
@@ -4,8 +4,8 @@ import (
"fmt"
"github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
)
func Example() {
diff --git a/go/vt/tabletserver/splitquery/full_scan_algorithm.go b/go/vt/vttablet/tabletserver/splitquery/full_scan_algorithm.go
similarity index 95%
rename from go/vt/tabletserver/splitquery/full_scan_algorithm.go
rename to go/vt/vttablet/tabletserver/splitquery/full_scan_algorithm.go
index fce6294a523..b2c406895ce 100644
--- a/go/vt/tabletserver/splitquery/full_scan_algorithm.go
+++ b/go/vt/vttablet/tabletserver/splitquery/full_scan_algorithm.go
@@ -4,8 +4,11 @@ import (
"fmt"
"github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vterrors"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
+
+ vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)
// FullScanAlgorithm implements the SplitAlgorithmInterface and represents the full-scan algorithm
@@ -48,8 +51,9 @@ func NewFullScanAlgorithm(
splitParams *SplitParams, sqlExecuter SQLExecuter) (*FullScanAlgorithm, error) {
if !splitParams.areSplitColumnsPrimaryKey() {
- return nil, fmt.Errorf("Using the FULL_SCAN algorithm requires split columns to be"+
- " the primary key. Got: %+v", splitParams)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT,
+ "Using the FULL_SCAN algorithm requires split columns to be"+
+ " the primary key. Got: %+v", splitParams)
}
result := &FullScanAlgorithm{
splitParams: splitParams,
diff --git a/go/vt/tabletserver/splitquery/full_scan_algorithm_test.go b/go/vt/vttablet/tabletserver/splitquery/full_scan_algorithm_test.go
similarity index 97%
rename from go/vt/tabletserver/splitquery/full_scan_algorithm_test.go
rename to go/vt/vttablet/tabletserver/splitquery/full_scan_algorithm_test.go
index 7c2f7655783..c02f9ad6acf 100644
--- a/go/vt/tabletserver/splitquery/full_scan_algorithm_test.go
+++ b/go/vt/vttablet/tabletserver/splitquery/full_scan_algorithm_test.go
@@ -8,8 +8,8 @@ import (
"github.com/golang/mock/gomock"
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
- "github.com/youtube/vitess/go/vt/tabletserver/splitquery/splitquery_testing"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/splitquery/splitquery_testing"
)
func TestMultipleBoundaries(t *testing.T) {
diff --git a/go/vt/tabletserver/splitquery/split_algorithm_interface.go b/go/vt/vttablet/tabletserver/splitquery/split_algorithm_interface.go
similarity index 94%
rename from go/vt/tabletserver/splitquery/split_algorithm_interface.go
rename to go/vt/vttablet/tabletserver/splitquery/split_algorithm_interface.go
index 75dfa5c97e0..6433b5645cd 100644
--- a/go/vt/tabletserver/splitquery/split_algorithm_interface.go
+++ b/go/vt/vttablet/tabletserver/splitquery/split_algorithm_interface.go
@@ -2,7 +2,7 @@ package splitquery
import (
"github.com/youtube/vitess/go/sqltypes"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
)
type tuple []sqltypes.Value
diff --git a/go/vt/tabletserver/splitquery/split_params.go b/go/vt/vttablet/tabletserver/splitquery/split_params.go
similarity index 83%
rename from go/vt/tabletserver/splitquery/split_params.go
rename to go/vt/vttablet/tabletserver/splitquery/split_params.go
index a1435e47aaa..cf11688df56 100644
--- a/go/vt/tabletserver/splitquery/split_params.go
+++ b/go/vt/vttablet/tabletserver/splitquery/split_params.go
@@ -4,8 +4,11 @@ import (
"fmt"
"github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vterrors"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
+
+ vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)
// SplitParams stores the context for a splitquery computation. It is used by
@@ -63,8 +66,8 @@ func NewSplitParamsGivenNumRowsPerQueryPart(
schema map[string]*schema.Table,
) (*SplitParams, error) {
if numRowsPerQueryPart <= 0 {
- return nil, fmt.Errorf("numRowsPerQueryPart must be positive. Got: %v",
- numRowsPerQueryPart)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT,
+ "numRowsPerQueryPart must be positive. Got: %v", numRowsPerQueryPart)
}
result, err := newSplitParams(query, splitColumnNames, schema)
if err != nil {
@@ -104,8 +107,8 @@ func NewSplitParamsGivenSplitCount(
schema map[string]*schema.Table,
) (*SplitParams, error) {
if splitCount <= 0 {
- return nil, fmt.Errorf("splitCount must be positive. Got: %v",
- splitCount)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT,
+ "splitCount must be positive. Got: %v", splitCount)
}
result, err := newSplitParams(query, splitColumnNames, schema)
if err != nil {
@@ -130,31 +133,33 @@ func newSplitParams(
) (*SplitParams, error) {
statement, err := sqlparser.Parse(query.Sql)
if err != nil {
- return nil, fmt.Errorf("failed parsing query: '%v', err: '%v'", query.Sql, err)
+ return nil, vterrors.Errorf(
+ vtrpcpb.Code_INVALID_ARGUMENT, "failed parsing query: '%v', err: '%v'", query.Sql, err)
}
selectAST, ok := statement.(*sqlparser.Select)
if !ok {
- return nil, fmt.Errorf("not a select statement")
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "not a select statement")
}
if selectAST.Distinct != "" || selectAST.GroupBy != nil ||
selectAST.Having != nil || len(selectAST.From) != 1 ||
selectAST.OrderBy != nil || selectAST.Limit != nil ||
selectAST.Lock != "" {
- return nil, fmt.Errorf("unsupported query: %v", query.Sql)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unsupported query: %v", query.Sql)
}
var aliasedTableExpr *sqlparser.AliasedTableExpr
aliasedTableExpr, ok = selectAST.From[0].(*sqlparser.AliasedTableExpr)
if !ok {
- return nil, fmt.Errorf("unsupported FROM clause in query: %v", query.Sql)
+ return nil, vterrors.Errorf(
+ vtrpcpb.Code_INVALID_ARGUMENT, "unsupported FROM clause in query: %v", query.Sql)
}
tableName := sqlparser.GetTableName(aliasedTableExpr.Expr)
if tableName.IsEmpty() {
- return nil, fmt.Errorf("unsupported FROM clause in query"+
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unsupported FROM clause in query"+
" (must be a simple table expression): %v", query.Sql)
}
tableSchema, ok := schemaMap[tableName.String()]
if tableSchema == nil {
- return nil, fmt.Errorf("can't find table in schema")
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "can't find table in schema")
}
// Get the schema.TableColumn representation of each splitColumnName.
@@ -162,9 +167,10 @@ func newSplitParams(
if len(splitColumnNames) == 0 {
splitColumns = getPrimaryKeyColumns(tableSchema)
if len(splitColumns) == 0 {
- return nil, fmt.Errorf("no split columns where given and the queried table has"+
- " no primary key columns (is the table a view? Running SplitQuery on a view"+
- " is not supported). query: %v", query.Sql)
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT,
+ "no split columns where given and the queried table has"+
+ " no primary key columns (is the table a view? Running SplitQuery on a view"+
+ " is not supported). query: %v", query.Sql)
}
} else {
splitColumns, err = findSplitColumnsInSchema(splitColumnNames, tableSchema)
@@ -172,8 +178,10 @@ func newSplitParams(
return nil, err
}
if !areColumnsAPrefixOfAnIndex(splitColumns, tableSchema) {
- return nil, fmt.Errorf("split-columns must be a prefix of the columns composing"+
- " an index. Sql: %v, split-columns: %v", query.Sql, splitColumns)
+ return nil, vterrors.Errorf(
+ vtrpcpb.Code_INVALID_ARGUMENT,
+ "split-columns must be a prefix of the columns composing"+
+ " an index. Sql: %v, split-columns: %v", query.Sql, splitColumns)
}
}
@@ -199,7 +207,9 @@ func findSplitColumnsInSchema(
for _, splitColumnName := range splitColumnNames {
i := tableSchema.FindColumn(splitColumnName)
if i == -1 {
- return nil, fmt.Errorf("can't find split column: %v", splitColumnName)
+ return nil, vterrors.Errorf(
+ vtrpcpb.Code_INVALID_ARGUMENT,
+ "can't find split column: %v", splitColumnName)
}
result = append(result, &tableSchema.Columns[i])
}
diff --git a/go/vt/tabletserver/splitquery/split_params_test.go b/go/vt/vttablet/tabletserver/splitquery/split_params_test.go
similarity index 98%
rename from go/vt/tabletserver/splitquery/split_params_test.go
rename to go/vt/vttablet/tabletserver/splitquery/split_params_test.go
index 9f541c39659..7090edcea53 100644
--- a/go/vt/tabletserver/splitquery/split_params_test.go
+++ b/go/vt/vttablet/tabletserver/splitquery/split_params_test.go
@@ -6,8 +6,8 @@ import (
"testing"
"github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
)
var splitParamsTestCases = []struct {
diff --git a/go/vt/tabletserver/splitquery/splitquery_testing/mock_sqlexecuter.go b/go/vt/vttablet/tabletserver/splitquery/splitquery_testing/mock_sqlexecuter.go
similarity index 100%
rename from go/vt/tabletserver/splitquery/splitquery_testing/mock_sqlexecuter.go
rename to go/vt/vttablet/tabletserver/splitquery/splitquery_testing/mock_sqlexecuter.go
diff --git a/go/vt/tabletserver/splitquery/splitter.go b/go/vt/vttablet/tabletserver/splitquery/splitter.go
similarity index 98%
rename from go/vt/tabletserver/splitquery/splitter.go
rename to go/vt/vttablet/tabletserver/splitquery/splitter.go
index 4da0191e1a4..72db0954426 100644
--- a/go/vt/tabletserver/splitquery/splitter.go
+++ b/go/vt/vttablet/tabletserver/splitquery/splitter.go
@@ -4,8 +4,8 @@ import (
"fmt"
"github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
)
// Splitter is used to drive the splitting procedure.
diff --git a/go/vt/tabletserver/splitquery/splitter_test.go b/go/vt/vttablet/tabletserver/splitquery/splitter_test.go
similarity index 98%
rename from go/vt/tabletserver/splitquery/splitter_test.go
rename to go/vt/vttablet/tabletserver/splitquery/splitter_test.go
index e00ea55b236..40d7afdbd69 100644
--- a/go/vt/tabletserver/splitquery/splitter_test.go
+++ b/go/vt/vttablet/tabletserver/splitquery/splitter_test.go
@@ -8,9 +8,9 @@ import (
"github.com/golang/mock/gomock"
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
- "github.com/youtube/vitess/go/vt/tabletserver/splitquery/splitquery_testing"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/splitquery/splitquery_testing"
)
type FakeSplitAlgorithm struct {
diff --git a/go/vt/tabletserver/splitquery/sql_executer_interface.go b/go/vt/vttablet/tabletserver/splitquery/sql_executer_interface.go
similarity index 100%
rename from go/vt/tabletserver/splitquery/sql_executer_interface.go
rename to go/vt/vttablet/tabletserver/splitquery/sql_executer_interface.go
diff --git a/go/vt/tabletserver/splitquery/testutils_test.go b/go/vt/vttablet/tabletserver/splitquery/testutils_test.go
similarity index 98%
rename from go/vt/tabletserver/splitquery/testutils_test.go
rename to go/vt/vttablet/tabletserver/splitquery/testutils_test.go
index e74189c75b9..521b6bba364 100644
--- a/go/vt/tabletserver/splitquery/testutils_test.go
+++ b/go/vt/vttablet/tabletserver/splitquery/testutils_test.go
@@ -8,7 +8,7 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
)
// getSchema returns a fake schema object that can be given to SplitParams
diff --git a/go/vt/tabletserver/splitquery/utils.go b/go/vt/vttablet/tabletserver/splitquery/utils.go
similarity index 100%
rename from go/vt/tabletserver/splitquery/utils.go
rename to go/vt/vttablet/tabletserver/splitquery/utils.go
diff --git a/go/vt/tabletserver/status.go b/go/vt/vttablet/tabletserver/status.go
similarity index 98%
rename from go/vt/tabletserver/status.go
rename to go/vt/vttablet/tabletserver/status.go
index b493c04ee65..64edc18698e 100644
--- a/go/vt/tabletserver/status.go
+++ b/go/vt/vttablet/tabletserver/status.go
@@ -4,7 +4,7 @@ import (
"time"
"github.com/youtube/vitess/go/vt/servenv"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
)
// This file contains the status web page export for tabletserver
diff --git a/go/vt/tabletserver/stream_queryz.go b/go/vt/vttablet/tabletserver/stream_queryz.go
similarity index 100%
rename from go/vt/tabletserver/stream_queryz.go
rename to go/vt/vttablet/tabletserver/stream_queryz.go
diff --git a/go/vt/tabletserver/stream_queryz_test.go b/go/vt/vttablet/tabletserver/stream_queryz_test.go
similarity index 100%
rename from go/vt/tabletserver/stream_queryz_test.go
rename to go/vt/vttablet/tabletserver/stream_queryz_test.go
diff --git a/go/vt/tabletserver/tabletenv/config.go b/go/vt/vttablet/tabletserver/tabletenv/config.go
similarity index 100%
rename from go/vt/tabletserver/tabletenv/config.go
rename to go/vt/vttablet/tabletserver/tabletenv/config.go
diff --git a/go/vt/tabletserver/tabletenv/local_context.go b/go/vt/vttablet/tabletserver/tabletenv/local_context.go
similarity index 100%
rename from go/vt/tabletserver/tabletenv/local_context.go
rename to go/vt/vttablet/tabletserver/tabletenv/local_context.go
diff --git a/go/vt/tabletserver/tabletenv/logstats.go b/go/vt/vttablet/tabletserver/tabletenv/logstats.go
similarity index 99%
rename from go/vt/tabletserver/tabletenv/logstats.go
rename to go/vt/vttablet/tabletserver/tabletenv/logstats.go
index 5c87ebcba1d..db30558d6df 100644
--- a/go/vt/tabletserver/tabletenv/logstats.go
+++ b/go/vt/vttablet/tabletserver/tabletenv/logstats.go
@@ -47,7 +47,7 @@ type LogStats struct {
QuerySources byte
Rows [][]sqltypes.Value
TransactionID int64
- Error *TabletError
+ Error error
}
// NewLogStats constructs a new LogStats with supplied Method and ctx
diff --git a/go/vt/tabletserver/tabletenv/logstats_test.go b/go/vt/vttablet/tabletserver/tabletenv/logstats_test.go
similarity index 96%
rename from go/vt/tabletserver/tabletenv/logstats_test.go
rename to go/vt/vttablet/tabletserver/tabletenv/logstats_test.go
index 9878e5ca502..60175e8d2a1 100644
--- a/go/vt/tabletserver/tabletenv/logstats_test.go
+++ b/go/vt/vttablet/tabletserver/tabletenv/logstats_test.go
@@ -5,6 +5,7 @@
package tabletenv
import (
+ "errors"
"net/url"
"strings"
"testing"
@@ -15,8 +16,6 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/callinfo"
"github.com/youtube/vitess/go/vt/callinfo/fakecallinfo"
-
- vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)
func TestLogStats(t *testing.T) {
@@ -106,10 +105,7 @@ func TestLogStatsErrorStr(t *testing.T) {
t.Fatalf("should not get error in stats, but got: %s", logStats.ErrorStr())
}
errStr := "unknown error"
- logStats.Error = &TabletError{
- ErrorCode: vtrpcpb.ErrorCode_UNKNOWN_ERROR,
- Message: errStr,
- }
+ logStats.Error = errors.New(errStr)
if !strings.Contains(logStats.ErrorStr(), errStr) {
t.Fatalf("expect string '%s' in error message, but got: %s", errStr, logStats.ErrorStr())
}
diff --git a/go/vt/tabletserver/tabletenv/tabletenv.go b/go/vt/vttablet/tabletserver/tabletenv/tabletenv.go
similarity index 76%
rename from go/vt/tabletserver/tabletenv/tabletenv.go
rename to go/vt/vttablet/tabletserver/tabletenv/tabletenv.go
index 3c1c24df1ad..c399a78da9a 100644
--- a/go/vt/tabletserver/tabletenv/tabletenv.go
+++ b/go/vt/vttablet/tabletserver/tabletenv/tabletenv.go
@@ -10,14 +10,18 @@ import (
"context"
"time"
+ log "github.com/golang/glog"
+
"github.com/youtube/vitess/go/stats"
+ "github.com/youtube/vitess/go/tb"
"github.com/youtube/vitess/go/vt/callerid"
+ vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
"github.com/youtube/vitess/go/vt/sqlparser"
)
var (
// MySQLStats shows the time histogram for operations spent on mysql side.
- MySQLStats = stats.NewTimings("MySQL")
+ MySQLStats = stats.NewTimings("Mysql")
// QueryStats shows the time histogram for each type of queries.
QueryStats = stats.NewTimings("Queries")
// QPSRates shows the qps of QueryStats. Sample every 5 seconds and keep samples for up to 15 mins.
@@ -26,10 +30,27 @@ var (
WaitStats = stats.NewTimings("Waits")
// KillStats shows number of connections being killed.
KillStats = stats.NewCounters("Kills", "Transactions", "Queries")
- // InfoErrors shows number of various non critical errors happened.
- InfoErrors = stats.NewCounters("InfoErrors", "Retry", "DupKey")
// ErrorStats shows number of critial erros happened.
- ErrorStats = stats.NewCounters("Errors", "Fail", "TxPoolFull", "NotInTx", "Deadlock", "Fatal")
+ ErrorStats = stats.NewCounters(
+ "Errors",
+ vtrpcpb.Code_OK.String(),
+ vtrpcpb.Code_CANCELED.String(),
+ vtrpcpb.Code_UNKNOWN.String(),
+ vtrpcpb.Code_INVALID_ARGUMENT.String(),
+ vtrpcpb.Code_DEADLINE_EXCEEDED.String(),
+ vtrpcpb.Code_NOT_FOUND.String(),
+ vtrpcpb.Code_ALREADY_EXISTS.String(),
+ vtrpcpb.Code_PERMISSION_DENIED.String(),
+ vtrpcpb.Code_UNAUTHENTICATED.String(),
+ vtrpcpb.Code_RESOURCE_EXHAUSTED.String(),
+ vtrpcpb.Code_FAILED_PRECONDITION.String(),
+ vtrpcpb.Code_ABORTED.String(),
+ vtrpcpb.Code_OUT_OF_RANGE.String(),
+ vtrpcpb.Code_UNIMPLEMENTED.String(),
+ vtrpcpb.Code_INTERNAL.String(),
+ vtrpcpb.Code_UNAVAILABLE.String(),
+ vtrpcpb.Code_DATA_LOSS.String(),
+ )
// InternalErrors shows number of errors from internal components.
InternalErrors = stats.NewCounters("InternalErrors", "Task", "StrayTransactions", "Panic", "HungQuery", "Schema", "TwopcCommit", "TwopcResurrection", "WatchdogFail")
// Unresolved tracks unresolved items. For now it's just Prepares.
@@ -61,3 +82,11 @@ func RecordUserQuery(ctx context.Context, tableName sqlparser.TableIdent, queryT
UserTableQueryCount.Add([]string{tableName.String(), username, queryType}, 1)
UserTableQueryTimesNs.Add([]string{tableName.String(), username, queryType}, int64(duration))
}
+
+// LogError logs panics and increments InternalErrors.
+func LogError() {
+ if x := recover(); x != nil {
+ log.Errorf("Uncaught panic:\n%v\n%s", x, tb.Stack(4))
+ InternalErrors.Add("Panic", 1)
+ }
+}
diff --git a/go/vt/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go
similarity index 86%
rename from go/vt/tabletserver/tabletserver.go
rename to go/vt/vttablet/tabletserver/tabletserver.go
index 0ff4e3f5267..df68bf1b006 100644
--- a/go/vt/tabletserver/tabletserver.go
+++ b/go/vt/vttablet/tabletserver/tabletserver.go
@@ -20,6 +20,7 @@ import (
"github.com/youtube/vitess/go/history"
"github.com/youtube/vitess/go/mysqlconn"
"github.com/youtube/vitess/go/mysqlconn/replication"
+ "github.com/youtube/vitess/go/sqldb"
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/stats"
"github.com/youtube/vitess/go/sync2"
@@ -30,14 +31,18 @@ import (
"github.com/youtube/vitess/go/vt/logutil"
"github.com/youtube/vitess/go/vt/mysqlctl"
"github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/connpool"
- "github.com/youtube/vitess/go/vt/tabletserver/engines/schema"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
- "github.com/youtube/vitess/go/vt/tabletserver/splitquery"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
- "github.com/youtube/vitess/go/vt/tabletserver/txthrottler"
+ "github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/utils"
+ "github.com/youtube/vitess/go/vt/vterrors"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/connpool"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/messager"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/rules"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/splitquery"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/txthrottler"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
@@ -110,7 +115,7 @@ type TabletServer struct {
se *schema.Engine
qe *QueryEngine
te *TxEngine
- messager *MessagerEngine
+ messager *messager.Engine
watcher *ReplicationWatcher
updateStreamList *binlog.StreamList
@@ -120,6 +125,7 @@ type TabletServer struct {
// txThrottler is used to throttle transactions based on the observed replication lag.
txThrottler *txthrottler.TxThrottler
+ topoServer topo.Server
// streamHealthMutex protects all the following fields
streamHealthMutex sync.Mutex
@@ -148,15 +154,21 @@ type MySQLChecker interface {
}
// NewServer creates a new TabletServer based on the command line flags.
-func NewServer() *TabletServer {
- return NewTabletServer(tabletenv.Config)
+func NewServer(topoServer topo.Server) *TabletServer {
+ return NewTabletServer(tabletenv.Config, topoServer)
}
var tsOnce sync.Once
-// NewTabletServer creates an instance of TabletServer. Only one instance
-// of TabletServer can be created per process.
-func NewTabletServer(config tabletenv.TabletConfig) *TabletServer {
+// NewTabletServerWithNilTopoServer is typically used in tests that don't need a topoSever
+// member.
+func NewTabletServerWithNilTopoServer(config tabletenv.TabletConfig) *TabletServer {
+ return NewTabletServer(config, topo.Server{})
+}
+
+// NewTabletServer creates an instance of TabletServer. Only the first
+// instance of TabletServer will expose its state variables.
+func NewTabletServer(config tabletenv.TabletConfig, topoServer topo.Server) *TabletServer {
tsv := &TabletServer{
QueryTimeout: sync2.NewAtomicDuration(time.Duration(config.QueryTimeout * 1e9)),
BeginTimeout: sync2.NewAtomicDuration(time.Duration(config.TxPoolTimeout * 1e9)),
@@ -164,14 +176,18 @@ func NewTabletServer(config tabletenv.TabletConfig) *TabletServer {
checkMySQLThrottler: sync2.NewSemaphore(1, 0),
streamHealthMap: make(map[int]chan<- *querypb.StreamHealthResponse),
history: history.New(10),
+ topoServer: topoServer,
}
tsv.se = schema.NewEngine(tsv, config)
tsv.qe = NewQueryEngine(tsv, tsv.se, config)
tsv.te = NewTxEngine(tsv, config)
- tsv.txThrottler = txthrottler.CreateTxThrottlerFromTabletConfig()
- tsv.messager = NewMessagerEngine(tsv, config)
+ tsv.txThrottler = txthrottler.CreateTxThrottlerFromTabletConfig(topoServer)
+ tsv.messager = messager.NewEngine(tsv, tsv.se, config)
tsv.watcher = NewReplicationWatcher(tsv.se, config)
tsv.updateStreamList = &binlog.StreamList{}
+ // FIXME(alainjobart) could we move this to the Register method below?
+ // So that vtcombo doesn't even call it once, on the first tablet.
+ // And we can remove the tsOnce variable.
tsOnce.Do(func() {
stats.Publish("TabletState", stats.IntFunc(func() int64 {
tsv.mu.Lock()
@@ -200,16 +216,16 @@ func (tsv *TabletServer) Register() {
// RegisterQueryRuleSource registers ruleSource for setting query rules.
func (tsv *TabletServer) RegisterQueryRuleSource(ruleSource string) {
- tsv.qe.queryRuleSources.RegisterQueryRuleSource(ruleSource)
+ tsv.qe.queryRuleSources.RegisterSource(ruleSource)
}
// UnRegisterQueryRuleSource unregisters ruleSource from query rules.
func (tsv *TabletServer) UnRegisterQueryRuleSource(ruleSource string) {
- tsv.qe.queryRuleSources.UnRegisterQueryRuleSource(ruleSource)
+ tsv.qe.queryRuleSources.UnRegisterSource(ruleSource)
}
// SetQueryRules sets the query rules for a registered ruleSource.
-func (tsv *TabletServer) SetQueryRules(ruleSource string, qrs *QueryRules) error {
+func (tsv *TabletServer) SetQueryRules(ruleSource string, qrs *rules.Rules) error {
err := tsv.qe.queryRuleSources.SetRules(ruleSource, qrs)
if err != nil {
return err
@@ -259,7 +275,7 @@ func (tsv *TabletServer) InitDBConfig(target querypb.Target, dbconfigs dbconfigs
tsv.mu.Lock()
defer tsv.mu.Unlock()
if tsv.state != StateNotConnected {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_UNKNOWN_ERROR, "InitDBConfig failed, current state: %s", stateName[tsv.state])
+ return vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "InitDBConfig failed, current state: %s", stateName[tsv.state])
}
tsv.target = target
tsv.dbconfigs = dbconfigs
@@ -377,7 +393,7 @@ func (tsv *TabletServer) decideAction(tabletType topodatapb.TabletType, serving
tsv.setState(StateTransitioning)
return actionServeNewType, nil
case StateTransitioning, StateShuttingDown:
- return actionNone, tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "cannot SetServingType, current state: %s", stateName[tsv.state])
+ return actionNone, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "cannot SetServingType, current state: %s", stateName[tsv.state])
default:
panic("unreachable")
}
@@ -587,6 +603,11 @@ func (tsv *TabletServer) QueryService() queryservice.QueryService {
return tsv
}
+// SchemaEngine returns the SchemaEngine part of TabletServer.
+func (tsv *TabletServer) SchemaEngine() *schema.Engine {
+ return tsv.se
+}
+
// Begin starts a new transaction. This is allowed only if the state is StateServing.
func (tsv *TabletServer) Begin(ctx context.Context, target *querypb.Target) (transactionID int64, err error) {
err = tsv.execRequest(
@@ -596,7 +617,8 @@ func (tsv *TabletServer) Begin(ctx context.Context, target *querypb.Target) (tra
func(ctx context.Context, logStats *tabletenv.LogStats) error {
defer tabletenv.QueryStats.Record("BEGIN", time.Now())
if tsv.txThrottler.Throttle() {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_TRANSIENT_ERROR, "Transaction throttled")
+ // TODO(erez): I think this should be RESOURCE_EXHAUSTED.
+ return vterrors.Errorf(vtrpcpb.Code_UNAVAILABLE, "Transaction throttled")
}
transactionID, err = tsv.te.txPool.Begin(ctx)
logStats.TransactionID = transactionID
@@ -857,10 +879,10 @@ func (tsv *TabletServer) StreamExecute(ctx context.Context, target *querypb.Targ
// transaction. If AsTransaction is true, TransactionId must be 0.
func (tsv *TabletServer) ExecuteBatch(ctx context.Context, target *querypb.Target, queries []querytypes.BoundQuery, asTransaction bool, transactionID int64, options *querypb.ExecuteOptions) (results []sqltypes.Result, err error) {
if len(queries) == 0 {
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "Empty query list")
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Empty query list")
}
if asTransaction && transactionID != 0 {
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "cannot start a new transaction in the scope of an existing one")
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "cannot start a new transaction in the scope of an existing one")
}
allowOnShutdown := (transactionID != 0)
@@ -873,7 +895,7 @@ func (tsv *TabletServer) ExecuteBatch(ctx context.Context, target *querypb.Targe
if asTransaction {
transactionID, err = tsv.Begin(ctx, target)
if err != nil {
- return nil, tsv.handleError("batch", nil, err, nil)
+ return nil, tsv.convertAndLogError("batch", nil, err, nil)
}
// If transaction was not committed by the end, it means
// that there was an error, roll it back.
@@ -887,14 +909,14 @@ func (tsv *TabletServer) ExecuteBatch(ctx context.Context, target *querypb.Targe
for _, bound := range queries {
localReply, err := tsv.Execute(ctx, target, bound.Sql, bound.BindVariables, transactionID, options)
if err != nil {
- return nil, tsv.handleError("batch", nil, err, nil)
+ return nil, tsv.convertAndLogError("batch", nil, err, nil)
}
results = append(results, *localReply)
}
if asTransaction {
if err = tsv.Commit(ctx, target, transactionID); err != nil {
transactionID = 0
- return nil, tsv.handleError("batch", nil, err, nil)
+ return nil, tsv.convertAndLogError("batch", nil, err, nil)
}
transactionID = 0
}
@@ -931,7 +953,7 @@ func (tsv *TabletServer) MessageStream(ctx context.Context, target *querypb.Targ
target, false, false,
func(ctx context.Context, logStats *tabletenv.LogStats) error {
// TODO(sougou): perform ACL checks.
- rcv, done := newMessageReceiver(func(r *sqltypes.Result) error {
+ done, err := tsv.messager.Subscribe(name, func(r *sqltypes.Result) error {
select {
case <-ctx.Done():
return io.EOF
@@ -939,7 +961,7 @@ func (tsv *TabletServer) MessageStream(ctx context.Context, target *querypb.Targ
}
return callback(r)
})
- if err := tsv.messager.Subscribe(name, rcv); err != nil {
+ if err != nil {
return err
}
<-done
@@ -955,7 +977,7 @@ func (tsv *TabletServer) MessageAck(ctx context.Context, target *querypb.Target,
for _, val := range ids {
v, err := sqltypes.BuildConverted(val.Type, val.Value)
if err != nil {
- return 0, tsv.handleError("message_ack", nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "invalid type: %v", err), nil)
+ return 0, tsv.convertAndLogError("message_ack", nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid type: %v", err), nil)
}
sids = append(sids, v.String())
}
@@ -989,7 +1011,7 @@ func (tsv *TabletServer) execDML(ctx context.Context, target *querypb.Target, qu
query, bv, err := queryGenerator()
if err != nil {
- return 0, tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "%v", err)
+ return 0, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%v", err)
}
transactionID, err := tsv.Begin(ctx, target)
@@ -1067,11 +1089,11 @@ func (tsv *TabletServer) SplitQuery(
defer sqlExecuter.done()
algorithmObject, err := createSplitQueryAlgorithmObject(algorithm, splitParams, sqlExecuter)
if err != nil {
- return splitQueryToTabletError(err)
+ return err
}
splits, err = splitquery.NewSplitter(splitParams, algorithmObject).Split()
if err != nil {
- return splitQueryToTabletError(err)
+ return err
}
return nil
},
@@ -1103,7 +1125,7 @@ func (tsv *TabletServer) execRequest(
err = exec(ctx, logStats)
if err != nil {
- return tsv.handleError(sql, bindVariables, err, logStats)
+ return tsv.convertAndLogError(sql, bindVariables, err, logStats)
}
return nil
}
@@ -1121,7 +1143,7 @@ func (tsv *TabletServer) handlePanicAndSendLogStats(
x,
tb.Stack(4) /* Skip the last 4 boiler-plate frames. */)
log.Errorf(errorMessage)
- terr := tabletenv.NewTabletError(vtrpcpb.ErrorCode_UNKNOWN_ERROR, "%s", errorMessage)
+ terr := vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "%s", errorMessage)
*err = terr
tabletenv.InternalErrors.Add("Panic", 1)
if logStats != nil {
@@ -1133,89 +1155,86 @@ func (tsv *TabletServer) handlePanicAndSendLogStats(
}
}
-func (tsv *TabletServer) handleError(
- sql string,
- bindVariables map[string]interface{},
- err error,
- logStats *tabletenv.LogStats,
-) error {
- var terr *tabletenv.TabletError
- defer func() {
- if logStats != nil {
- logStats.Error = terr
- }
- }()
- terr, ok := err.(*tabletenv.TabletError)
- if !ok {
- terr = tabletenv.NewTabletError(vtrpcpb.ErrorCode_UNKNOWN_ERROR, "%v", err)
- // We only want to see TabletError here.
- tabletenv.InternalErrors.Add("UnknownError", 1)
+func (tsv *TabletServer) convertAndLogError(sql string, bindVariables map[string]interface{}, err error, logStats *tabletenv.LogStats) error {
+ if err == nil {
+ return nil
}
+ err = tsv.convertError(sql, bindVariables, err)
- // If TerseErrors is on, strip the error message returned by MySQL and only
- // keep the error number and sql state.
- // This avoids leaking PII which may be contained in the bind variables: Since
- // vttablet has to rewrite and include the bind variables in the query for
- // MySQL, the bind variables data would show up in the error message.
- //
- // If no bind variables are specified, we do not strip the error message and
- // the full user query may be included. We do this on purpose for use cases
- // where users manually write queries and need the error message to debug
- // e.g. syntax errors on the rewritten query.
- var myError error
- if tsv.TerseErrors && terr.SQLError != 0 && len(bindVariables) != 0 {
- switch {
- // Google internal flavor error only. Do not strip it because the vtgate
- // buffer starts buffering master traffic when it sees the full error.
- case terr.SQLError == 1227 && terr.Message == "failover in progress (errno 1227) (sqlstate 42000)":
- myError = terr
- default:
- // Non-whitelisted error. Strip the error message.
- myError = &tabletenv.TabletError{
- SQLError: terr.SQLError,
- SQLState: terr.SQLState,
- ErrorCode: terr.ErrorCode,
- Message: fmt.Sprintf("(errno %d) (sqlstate %s) during query: %s", terr.SQLError, terr.SQLState, sql),
- }
- }
- } else {
- myError = terr
+ if logStats != nil {
+ logStats.Error = err
}
+ errCode := vterrors.Code(err)
+ tabletenv.ErrorStats.Add(errCode.String(), 1)
- terr.RecordStats()
-
- logMethod := log.Infof
+ logMethod := log.Errorf
// Suppress or demote some errors in logs.
- switch terr.ErrorCode {
- case vtrpcpb.ErrorCode_QUERY_NOT_SERVED:
- return myError
- case vtrpcpb.ErrorCode_RESOURCE_EXHAUSTED:
+ switch errCode {
+ case vtrpcpb.Code_FAILED_PRECONDITION, vtrpcpb.Code_ALREADY_EXISTS:
+ return err
+ case vtrpcpb.Code_RESOURCE_EXHAUSTED:
logMethod = logTxPoolFull.Errorf
- case vtrpcpb.ErrorCode_INTERNAL_ERROR:
- logMethod = log.Errorf
- case vtrpcpb.ErrorCode_NOT_IN_TX:
+ case vtrpcpb.Code_ABORTED:
logMethod = log.Warningf
- default:
- // We want to suppress/demote some MySQL error codes.
- switch terr.SQLError {
- case mysqlconn.ERDupEntry:
- return myError
- case mysqlconn.ERLockWaitTimeout,
- mysqlconn.ERLockDeadlock,
- mysqlconn.ERDataTooLong,
- mysqlconn.ERDataOutOfRange,
- mysqlconn.ERBadNullError:
- logMethod = log.Infof
- case 0:
- if !strings.Contains(terr.Error(), "Row count exceeded") {
- logMethod = log.Errorf
- }
- default:
- logMethod = log.Errorf
+ case vtrpcpb.Code_INVALID_ARGUMENT, vtrpcpb.Code_DEADLINE_EXCEEDED:
+ logMethod = log.Infof
+ }
+ logMethod("%v: %v", err, querytypes.QueryAsString(sql, bindVariables))
+ return err
+}
+
+func (tsv *TabletServer) convertError(sql string, bindVariables map[string]interface{}, err error) error {
+ sqlErr, ok := err.(*sqldb.SQLError)
+ if !ok {
+ return err
+ }
+
+ errCode := vterrors.Code(err)
+ errstr := err.Error()
+ errnum := sqlErr.Number()
+ sqlState := sqlErr.SQLState()
+ switch errnum {
+ case mysqlconn.EROptionPreventsStatement:
+ // Special-case this error code. It's probably because
+ // there was a failover and there are old clients still connected.
+ if strings.Contains(errstr, "read-only") {
+ errCode = vtrpcpb.Code_FAILED_PRECONDITION
+ }
+ case 1227: // Google internal failover error code.
+ if strings.Contains(errstr, "failover in progress") {
+ errCode = vtrpcpb.Code_FAILED_PRECONDITION
}
+ case mysqlconn.ERDupEntry:
+ errCode = vtrpcpb.Code_ALREADY_EXISTS
+ case mysqlconn.ERDataTooLong, mysqlconn.ERDataOutOfRange, mysqlconn.ERBadNullError:
+ errCode = vtrpcpb.Code_INVALID_ARGUMENT
+ case mysqlconn.ERLockWaitTimeout:
+ errCode = vtrpcpb.Code_DEADLINE_EXCEEDED
+ case mysqlconn.ERLockDeadlock:
+ // A deadlock rolls back the transaction.
+ errCode = vtrpcpb.Code_ABORTED
+ case mysqlconn.CRServerLost:
+ // Query was killed.
+ errCode = vtrpcpb.Code_DEADLINE_EXCEEDED
+ case mysqlconn.CRServerGone:
+ errCode = vtrpcpb.Code_UNAVAILABLE
}
- logMethod("%v: %v", terr, querytypes.QueryAsString(sql, bindVariables))
- return myError
+
+ // If TerseErrors is on, strip the error message returned by MySQL and only
+ // keep the error number and sql state.
+ // We assume that bind variable have PII, which are included in the MySQL
+ // query and come back as part of the error message. Removing the MySQL
+ // error helps us avoid leaking PII.
+ // There are two exceptions:
+ // 1. If no bind vars were specified, it's likely that the query was issued
+ // by someone manually. So, we don't suppress the error.
+ // 2. FAILED_PRECONDITION errors. These are caused when a failover is in progress.
+ // If so, we don't want to suppress the error. This will allow VTGate to
+ // detect and perform buffering during failovers.
+ if tsv.TerseErrors && len(bindVariables) != 0 && errCode != vtrpcpb.Code_FAILED_PRECONDITION {
+ errstr = fmt.Sprintf("(errno %d) (sqlstate %s) during query: %s", errnum, sqlState, sql)
+ }
+ return vterrors.New(errCode, errstr)
}
// validateSplitQueryParameters perform some validations on the SplitQuery parameters
@@ -1231,29 +1250,29 @@ func validateSplitQueryParameters(
// Check that the caller requested a RDONLY tablet.
// Since we're called by VTGate this should not normally be violated.
if target.TabletType != topodatapb.TabletType_RDONLY {
- return tabletenv.NewTabletError(
- vtrpcpb.ErrorCode_BAD_INPUT,
+ return vterrors.Errorf(
+ vtrpcpb.Code_INVALID_ARGUMENT,
"SplitQuery must be called with a RDONLY tablet. TableType passed is: %v",
target.TabletType)
}
if numRowsPerQueryPart < 0 {
- return tabletenv.NewTabletError(
- vtrpcpb.ErrorCode_BAD_INPUT,
+ return vterrors.Errorf(
+ vtrpcpb.Code_INVALID_ARGUMENT,
"splitQuery: numRowsPerQueryPart must be non-negative. Got: %v. SQL: %v",
numRowsPerQueryPart,
querytypes.QueryAsString(query.Sql, query.BindVariables))
}
if splitCount < 0 {
- return tabletenv.NewTabletError(
- vtrpcpb.ErrorCode_BAD_INPUT,
+ return vterrors.Errorf(
+ vtrpcpb.Code_INVALID_ARGUMENT,
"splitQuery: splitCount must be non-negative. Got: %v. SQL: %v",
splitCount,
querytypes.QueryAsString(query.Sql, query.BindVariables))
}
if (splitCount == 0 && numRowsPerQueryPart == 0) ||
(splitCount != 0 && numRowsPerQueryPart != 0) {
- return tabletenv.NewTabletError(
- vtrpcpb.ErrorCode_BAD_INPUT,
+ return vterrors.Errorf(
+ vtrpcpb.Code_INVALID_ARGUMENT,
"splitQuery: exactly one of {numRowsPerQueryPart, splitCount} must be"+
" non zero. Got: numRowsPerQueryPart=%v, splitCount=%v. SQL: %v",
numRowsPerQueryPart,
@@ -1262,8 +1281,8 @@ func validateSplitQueryParameters(
}
if algorithm != querypb.SplitQueryRequest_EQUAL_SPLITS &&
algorithm != querypb.SplitQueryRequest_FULL_SCAN {
- return tabletenv.NewTabletError(
- vtrpcpb.ErrorCode_BAD_INPUT,
+ return vterrors.Errorf(
+ vtrpcpb.Code_INVALID_ARGUMENT,
"splitquery: unsupported algorithm: %v. SQL: %v",
algorithm,
querytypes.QueryAsString(query.Sql, query.BindVariables))
@@ -1280,13 +1299,11 @@ func createSplitParams(
) (*splitquery.SplitParams, error) {
switch {
case numRowsPerQueryPart != 0 && splitCount == 0:
- splitParams, err := splitquery.NewSplitParamsGivenNumRowsPerQueryPart(
+ return splitquery.NewSplitParamsGivenNumRowsPerQueryPart(
query, splitColumns, numRowsPerQueryPart, schema)
- return splitParams, splitQueryToTabletError(err)
case numRowsPerQueryPart == 0 && splitCount != 0:
- splitParams, err := splitquery.NewSplitParamsGivenSplitCount(
+ return splitquery.NewSplitParamsGivenSplitCount(
query, splitColumns, splitCount, schema)
- return splitParams, splitQueryToTabletError(err)
default:
panic(fmt.Errorf("Exactly one of {numRowsPerQueryPart, splitCount} must be"+
" non zero. This should have already been caught by 'validateSplitQueryParameters' and "+
@@ -1367,16 +1384,6 @@ func createSplitQueryAlgorithmObject(
}
}
-// splitQueryToTabletError converts the given error assumed to be returned from the
-// splitquery-package into a TabletError suitable to be returned to the caller.
-// It returns nil if 'err' is nil.
-func splitQueryToTabletError(err error) error {
- if err == nil {
- return nil
- }
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "splitquery: %v", err)
-}
-
// StreamHealth streams the health status to callback.
// At the beginning, if TabletServer has a valid health
// state, that response is immediately sent.
@@ -1462,11 +1469,11 @@ func (tsv *TabletServer) UpdateStream(ctx context.Context, target *querypb.Targe
if position != "" {
p, err = replication.DecodePosition(position)
if err != nil {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "cannot parse position: %v", err)
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "cannot parse position: %v", err)
}
}
} else if position != "" {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "at most one of position and timestamp should be specified")
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "at most one of position and timestamp should be specified")
}
// Validate proper target is used.
@@ -1475,7 +1482,7 @@ func (tsv *TabletServer) UpdateStream(ctx context.Context, target *querypb.Targe
}
defer tsv.endRequest(false)
- s := binlog.NewEventStreamer(tsv.dbconfigs.App.DbName, tsv.mysqld, p, timestamp, callback)
+ s := binlog.NewEventStreamer(tsv.dbconfigs.App.DbName, tsv.mysqld, tsv.se, p, timestamp, callback)
// Create a cancelable wrapping context.
streamCtx, streamCancel := context.WithCancel(ctx)
@@ -1486,11 +1493,11 @@ func (tsv *TabletServer) UpdateStream(ctx context.Context, target *querypb.Targe
err = s.Stream(streamCtx)
switch err {
case mysqlctl.ErrBinlogUnavailable:
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_QUERY_NOT_SERVED, "%v", err)
+ return vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "%v", err)
case nil, io.EOF:
return nil
default:
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "%v", err)
+ return vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "%v", err)
}
}
@@ -1523,30 +1530,28 @@ func (tsv *TabletServer) startRequest(ctx context.Context, target *querypb.Targe
if allowOnShutdown && tsv.state == StateShuttingDown {
goto verifyTarget
}
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_QUERY_NOT_SERVED, "operation not allowed in state %s", stateName[tsv.state])
+ return vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "operation not allowed in state %s", stateName[tsv.state])
verifyTarget:
if target != nil {
// a valid target needs to be used
- if target.Keyspace != tsv.target.Keyspace {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_QUERY_NOT_SERVED, "Invalid keyspace %v", target.Keyspace)
- }
- if target.Shard != tsv.target.Shard {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_QUERY_NOT_SERVED, "Invalid shard %v", target.Shard)
- }
- if isTx && tsv.target.TabletType != topodatapb.TabletType_MASTER {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_QUERY_NOT_SERVED, "transactional statement disallowed on non-master tablet: %v", tsv.target.TabletType)
- }
- if target.TabletType != tsv.target.TabletType {
+ switch {
+ case target.Keyspace != tsv.target.Keyspace:
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid keyspace %v", target.Keyspace)
+ case target.Shard != tsv.target.Shard:
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid shard %v", target.Shard)
+ case isTx && tsv.target.TabletType != topodatapb.TabletType_MASTER:
+ return vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "transactional statement disallowed on non-master tablet: %v", tsv.target.TabletType)
+ case target.TabletType != tsv.target.TabletType:
for _, otherType := range tsv.alsoAllow {
if target.TabletType == otherType {
goto ok
}
}
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_QUERY_NOT_SERVED, "Invalid tablet type: %v, want: %v or %v", target.TabletType, tsv.target.TabletType, tsv.alsoAllow)
+ return vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "invalid tablet type: %v, want: %v or %v", target.TabletType, tsv.target.TabletType, tsv.alsoAllow)
}
} else if !tabletenv.IsLocalContext(ctx) {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_QUERY_NOT_SERVED, "No target")
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "No target")
}
ok:
diff --git a/go/vt/tabletserver/tabletserver_test.go b/go/vt/vttablet/tabletserver/tabletserver_test.go
similarity index 93%
rename from go/vt/tabletserver/tabletserver_test.go
rename to go/vt/vttablet/tabletserver/tabletserver_test.go
index f04d63f2e0c..1d1f80590a3 100644
--- a/go/vt/tabletserver/tabletserver_test.go
+++ b/go/vt/vttablet/tabletserver/tabletserver_test.go
@@ -21,9 +21,12 @@ import (
"github.com/golang/protobuf/proto"
"github.com/youtube/vitess/go/mysqlconn"
"github.com/youtube/vitess/go/mysqlconn/fakesqldb"
+ "github.com/youtube/vitess/go/sqldb"
"github.com/youtube/vitess/go/sqltypes"
- "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vterrors"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/messager"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/querytypes"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
@@ -48,7 +51,7 @@ func TestTabletServerGetState(t *testing.T) {
}
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
for i, state := range states {
tsv.setState(state)
if stateName := tsv.GetState(); stateName != names[i] {
@@ -67,7 +70,7 @@ func TestTabletServerAllowQueriesFailBadConn(t *testing.T) {
db.EnableConnFail()
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
checkTabletServerState(t, tsv, StateNotConnected)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
@@ -83,7 +86,7 @@ func TestTabletServerAllowQueries(t *testing.T) {
defer db.Close()
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
checkTabletServerState(t, tsv, StateNotConnected)
dbconfigs := testUtils.newDBConfigs(db)
tsv.setState(StateServing)
@@ -107,7 +110,7 @@ func TestTabletServerInitDBConfig(t *testing.T) {
defer db.Close()
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
tsv.setState(StateServing)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
dbconfigs := testUtils.newDBConfigs(db)
@@ -128,7 +131,7 @@ func TestDecideAction(t *testing.T) {
defer db.Close()
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
dbconfigs := testUtils.newDBConfigs(db)
err := tsv.InitDBConfig(target, dbconfigs, nil)
@@ -235,7 +238,7 @@ func TestSetServingType(t *testing.T) {
defer db.Close()
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.InitDBConfig(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -317,7 +320,7 @@ func TestTabletServerSingleSchemaFailure(t *testing.T) {
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
originalSchemaErrorCount := tabletenv.InternalErrors.Counts()["Schema"]
@@ -349,13 +352,16 @@ func TestTabletServerAllSchemaFailure(t *testing.T) {
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
defer tsv.StopService()
// tabletsever shouldn't start if it can't access schema for any tables
- testUtils.checkTabletError(t, err, vtrpcpb.ErrorCode_UNKNOWN_ERROR, "could not get schema for any tables")
+ wantErr := "could not get schema for any tables"
+ if err == nil || err.Error() != wantErr {
+ t.Errorf("tsv.StartService: %v, want %s", err, wantErr)
+ }
}
func TestTabletServerCheckMysql(t *testing.T) {
@@ -363,7 +369,7 @@ func TestTabletServerCheckMysql(t *testing.T) {
defer db.Close()
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -392,7 +398,7 @@ func TestTabletServerCheckMysqlFailInvalidConn(t *testing.T) {
defer db.Close()
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -410,7 +416,7 @@ func TestTabletServerCheckMysqlFailInvalidConn(t *testing.T) {
func TestTabletServerCheckMysqlInUnintialized(t *testing.T) {
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
// TabletServer start request fail because we are in StateNotConnected;
// however, isMySQLReachable should return true. Here, we always assume
// MySQL is healthy unless we've verified it is not.
@@ -439,7 +445,7 @@ func TestTabletServerReconnect(t *testing.T) {
db.AddQuery("select addr from test_table where 1 != 1", &sqltypes.Result{})
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -487,7 +493,7 @@ func TestTabletServerTarget(t *testing.T) {
defer db.Close()
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target1 := querypb.Target{
Keyspace: "test_keyspace",
@@ -512,7 +518,7 @@ func TestTabletServerTarget(t *testing.T) {
target2 := proto.Clone(&target1).(*querypb.Target)
target2.TabletType = topodatapb.TabletType_REPLICA
_, err = tsv.Execute(ctx, target2, "select * from test_table limit 1000", nil, 0, nil)
- want := "Invalid tablet type"
+ want := "invalid tablet type"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("err: %v, must contain %s", err, want)
}
@@ -532,7 +538,7 @@ func TestTabletServerTarget(t *testing.T) {
target2 = proto.Clone(&target1).(*querypb.Target)
target2.Keyspace = "bad"
_, err = tsv.Execute(ctx, target2, "select * from test_table limit 1000", nil, 0, nil)
- want = "Invalid keyspace bad"
+ want = "invalid keyspace bad"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("err: %v, must contain %s", err, want)
}
@@ -541,7 +547,7 @@ func TestTabletServerTarget(t *testing.T) {
target2 = proto.Clone(&target1).(*querypb.Target)
target2.Shard = "bad"
_, err = tsv.Execute(ctx, target2, "select * from test_table limit 1000", nil, 0, nil)
- want = "Invalid shard bad"
+ want = "invalid shard bad"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("err: %v, must contain %s", err, want)
}
@@ -786,7 +792,7 @@ func TestTabletServerStartCommit(t *testing.T) {
db.AddQuery(commitTransition, &sqltypes.Result{})
txid = newTxForPrep(tsv)
err = tsv.StartCommit(ctx, &target, txid, "aa")
- want := "error: could not transition to COMMIT: aa"
+ want := "could not transition to COMMIT: aa"
if err == nil || err.Error() != want {
t.Errorf("Prepare err: %v, want %s", err, want)
}
@@ -810,7 +816,7 @@ func TestTabletserverSetRollback(t *testing.T) {
db.AddQuery(rollbackTransition, &sqltypes.Result{})
txid = newTxForPrep(tsv)
err = tsv.SetRollback(ctx, &target, "aa", txid)
- want := "error: could not transition to ROLLBACK: aa"
+ want := "could not transition to ROLLBACK: aa"
if err == nil || err.Error() != want {
t.Errorf("Prepare err: %v, want %s", err, want)
}
@@ -947,7 +953,7 @@ func TestTabletServerBeginFail(t *testing.T) {
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
config.TransactionCap = 1
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -959,7 +965,7 @@ func TestTabletServerBeginFail(t *testing.T) {
defer cancel()
tsv.Begin(ctx, &target)
_, err = tsv.Begin(ctx, &target)
- want := "tx_pool_full: Transaction pool connection limit exceeded"
+ want := "transaction pool connection limit exceeded"
if err == nil || err.Error() != want {
t.Fatalf("Begin err: %v, want %v", err, want)
}
@@ -982,7 +988,7 @@ func TestTabletServerCommitTransaction(t *testing.T) {
}
db.AddQuery(executeSQL, executeSQLResult)
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -1008,7 +1014,7 @@ func TestTabletServerCommiRollbacktFail(t *testing.T) {
defer db.Close()
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -1018,7 +1024,7 @@ func TestTabletServerCommiRollbacktFail(t *testing.T) {
defer tsv.StopService()
ctx := context.Background()
err = tsv.Commit(ctx, &target, -1)
- want := "not_in_tx: Transaction -1: not found"
+ want := "transaction -1: not found"
if err == nil || err.Error() != want {
t.Fatalf("Commit err: %v, want %v", err, want)
}
@@ -1045,7 +1051,7 @@ func TestTabletServerRollback(t *testing.T) {
}
db.AddQuery(executeSQL, executeSQLResult)
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -1149,7 +1155,7 @@ func TestTabletServerStreamExecute(t *testing.T) {
db.AddQuery(executeSQL, executeSQLResult)
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -1176,7 +1182,7 @@ func TestTabletServerExecuteBatch(t *testing.T) {
db.AddQuery(sql, sqlResult)
db.AddQuery(expanedSQL, sqlResult)
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -1201,7 +1207,7 @@ func TestTabletServerExecuteBatchFailEmptyQueryList(t *testing.T) {
defer db.Close()
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -1222,7 +1228,7 @@ func TestTabletServerExecuteBatchFailAsTransaction(t *testing.T) {
defer db.Close()
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -1250,7 +1256,7 @@ func TestTabletServerExecuteBatchBeginFail(t *testing.T) {
// make "begin" query fail
db.AddRejectedQuery("begin", errRejected)
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -1276,7 +1282,7 @@ func TestTabletServerExecuteBatchCommitFail(t *testing.T) {
// make "commit" query fail
db.AddRejectedQuery("commit", errRejected)
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -1315,7 +1321,7 @@ func TestTabletServerExecuteBatchSqlExecFailInTransaction(t *testing.T) {
db.AddRejectedQuery(expanedSQL, errRejected)
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -1358,7 +1364,7 @@ func TestTabletServerExecuteBatchSqlSucceedInTransaction(t *testing.T) {
config := testUtils.newQueryServiceConfig()
config.EnableAutoCommit = true
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -1382,7 +1388,7 @@ func TestTabletServerExecuteBatchCallCommitWithoutABegin(t *testing.T) {
defer db.Close()
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -1412,7 +1418,7 @@ func TestExecuteBatchNestedTransaction(t *testing.T) {
db.AddQuery(sql, sqlResult)
db.AddQuery(expanedSQL, sqlResult)
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -1455,7 +1461,7 @@ func TestMessageStream(t *testing.T) {
ctx := context.Background()
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
- wanterr := "error: message table nomsg not found"
+ wanterr := "message table nomsg not found"
if err := tsv.MessageStream(ctx, &target, "nomsg", func(qr *sqltypes.Result) error {
return nil
}); err == nil || err.Error() != wanterr {
@@ -1478,9 +1484,9 @@ func TestMessageStream(t *testing.T) {
close(done)
}()
// Skip first result (field info).
- newMessages := map[string][]*MessageRow{
+ newMessages := map[string][]*messager.MessageRow{
"msg": {
- &MessageRow{ID: sqltypes.MakeString([]byte("1"))},
+ &messager.MessageRow{ID: sqltypes.MakeString([]byte("1"))},
},
}
// We may have to iterate a few times before the stream kicks in.
@@ -1525,13 +1531,13 @@ func TestMessageAck(t *testing.T) {
Value: []byte("2"),
}}
_, err := tsv.MessageAck(ctx, &target, "nonmsg", ids)
- want := "error: message table nonmsg not found in schema"
+ want := "message table nonmsg not found in schema"
if err == nil || err.Error() != want {
t.Errorf("tsv.MessageAck(invalid): %v, want %s", err, want)
}
_, err = tsv.MessageAck(ctx, &target, "msg", ids)
- want = "error: query: select time_scheduled, id from msg where id in ('1', '2') and time_acked is null limit 10001 for update is not supported on fakesqldb"
+ want = "query: select time_scheduled, id from msg where id in ('1', '2') and time_acked is null limit 10001 for update is not supported on fakesqldb"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("tsv.MessageAck(invalid): %v, want %s", err, want)
}
@@ -1568,13 +1574,13 @@ func TestRescheduleMessages(t *testing.T) {
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
_, err := tsv.PostponeMessages(ctx, &target, "nonmsg", []string{"1", "2"})
- want := "error: message table nonmsg not found in schema"
+ want := "message table nonmsg not found in schema"
if err == nil || err.Error() != want {
t.Errorf("tsv.PostponeMessages(invalid): %v, want %s", err, want)
}
_, err = tsv.PostponeMessages(ctx, &target, "msg", []string{"1", "2"})
- want = "error: query: select time_scheduled, id from msg where id in ('1', '2') and time_acked is null limit 10001 for update is not supported"
+ want = "query: select time_scheduled, id from msg where id in ('1', '2') and time_acked is null limit 10001 for update is not supported"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("tsv.PostponeMessages(invalid):\n%v, want\n%s", err, want)
}
@@ -1611,13 +1617,13 @@ func TestPurgeMessages(t *testing.T) {
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
_, err := tsv.PurgeMessages(ctx, &target, "nonmsg", 0)
- want := "error: message table nonmsg not found in schema"
+ want := "message table nonmsg not found in schema"
if err == nil || err.Error() != want {
t.Errorf("tsv.PurgeMessages(invalid): %v, want %s", err, want)
}
_, err = tsv.PurgeMessages(ctx, &target, "msg", 0)
- want = "error: query: select time_scheduled, id from msg where time_scheduled < 0 and time_acked is not null limit 500 for update is not supported"
+ want = "query: select time_scheduled, id from msg where time_scheduled < 0 and time_acked is not null limit 500 for update is not supported"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("tsv.PurgeMessages(invalid):\n%v, want\n%s", err, want)
}
@@ -1664,7 +1670,7 @@ func TestTabletServerSplitQuery(t *testing.T) {
})
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_RDONLY}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -1695,7 +1701,7 @@ func TestTabletServerSplitQueryInvalidQuery(t *testing.T) {
defer db.Close()
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_RDONLY}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -1725,7 +1731,7 @@ func TestTabletServerSplitQueryInvalidParams(t *testing.T) {
defer db.Close()
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_RDONLY}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -1754,7 +1760,7 @@ func TestTabletServerSplitQueryEqualSplitsOnStringColumn(t *testing.T) {
defer db.Close()
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_RDONLY}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
@@ -1774,7 +1780,7 @@ func TestTabletServerSplitQueryEqualSplitsOnStringColumn(t *testing.T) {
0, /* numRowsPerQueryPart */
querypb.SplitQueryRequest_EQUAL_SPLITS)
want :=
- "error: splitquery: using the EQUAL_SPLITS algorithm in SplitQuery" +
+ "using the EQUAL_SPLITS algorithm in SplitQuery" +
" requires having a numeric (integral or float) split-column." +
" Got type: {Name: 'name_string', Type: VARCHAR}"
if err.Error() != want {
@@ -1788,7 +1794,7 @@ func TestHandleExecUnknownError(t *testing.T) {
var err error
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
defer tsv.handlePanicAndSendLogStats("select * from test_table", nil, &err, logStats)
panic("unknown exec error")
}
@@ -1796,16 +1802,15 @@ func TestHandleExecUnknownError(t *testing.T) {
func TestHandleExecTabletError(t *testing.T) {
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
- err := tsv.handleError(
+ tsv := NewTabletServerWithNilTopoServer(config)
+ err := tsv.convertError(
"select * from test_table",
nil,
- tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "tablet error"),
- nil,
+ vterrors.Errorf(vtrpcpb.Code_INTERNAL, "tablet error"),
)
- want := "fatal: tablet error"
+ want := "tablet error"
if err == nil || err.Error() != want {
- t.Errorf("Error: %v, want '%s'", err, want)
+ t.Errorf("%v, want '%s'", err, want)
}
}
@@ -1813,16 +1818,15 @@ func TestTerseErrorsNonSQLError(t *testing.T) {
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
config.TerseErrors = true
- tsv := NewTabletServer(config)
- err := tsv.handleError(
+ tsv := NewTabletServerWithNilTopoServer(config)
+ err := tsv.convertError(
"select * from test_table",
nil,
- tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "tablet error"),
- nil,
+ vterrors.Errorf(vtrpcpb.Code_INTERNAL, "tablet error"),
)
- want := "fatal: tablet error"
+ want := "tablet error"
if err == nil || err.Error() != want {
- t.Errorf("Error: %v, want '%s'", err, want)
+ t.Errorf("%v, want '%s'", err, want)
}
}
@@ -1830,21 +1834,15 @@ func TestTerseErrorsBindVars(t *testing.T) {
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
config.TerseErrors = true
- tsv := NewTabletServer(config)
- err := tsv.handleError(
+ tsv := NewTabletServerWithNilTopoServer(config)
+ err := tsv.convertError(
"select * from test_table",
map[string]interface{}{"a": 1},
- &tabletenv.TabletError{
- ErrorCode: vtrpcpb.ErrorCode_DEADLINE_EXCEEDED,
- Message: "msg",
- SQLError: 10,
- SQLState: "HY000",
- },
- nil,
+ sqldb.NewSQLError(10, "HY000", "msg"),
)
- want := "error: (errno 10) (sqlstate HY000) during query: select * from test_table"
+ want := "(errno 10) (sqlstate HY000) during query: select * from test_table"
if err == nil || err.Error() != want {
- t.Errorf("Error: %v, want '%s'", err, want)
+ t.Errorf("%v, want '%s'", err, want)
}
}
@@ -1852,11 +1850,11 @@ func TestTerseErrorsNoBindVars(t *testing.T) {
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
config.TerseErrors = true
- tsv := NewTabletServer(config)
- err := tsv.handleError("", nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_DEADLINE_EXCEEDED, "msg"), nil)
- want := "error: msg"
+ tsv := NewTabletServerWithNilTopoServer(config)
+ err := tsv.convertError("", nil, vterrors.Errorf(vtrpcpb.Code_DEADLINE_EXCEEDED, "msg"))
+ want := "msg"
if err == nil || err.Error() != want {
- t.Errorf("Error: %v, want '%s'", err, want)
+ t.Errorf("%v, want '%s'", err, want)
}
}
@@ -1864,18 +1862,13 @@ func TestTerseErrorsIgnoreFailoverInProgress(t *testing.T) {
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
config.TerseErrors = true
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
- err := tsv.handleError("select * from test_table where id = :a",
+ err := tsv.convertError("select * from test_table where id = :a",
map[string]interface{}{"a": 1},
- &tabletenv.TabletError{
- ErrorCode: vtrpcpb.ErrorCode_INTERNAL_ERROR,
- Message: "failover in progress (errno 1227) (sqlstate 42000)",
- SQLError: 1227,
- SQLState: "42000",
- },
- nil /* logStats */)
- if got, want := err.Error(), "fatal: failover in progress (errno 1227) (sqlstate 42000)"; got != want {
+ sqldb.NewSQLError(1227, "42000", "failover in progress"),
+ )
+ if got, want := err.Error(), "failover in progress (errno 1227) (sqlstate 42000)"; got != want {
t.Fatalf("'failover in progress' text must never be stripped: got = %v, want = %v", got, want)
}
}
@@ -1885,7 +1878,7 @@ func TestConfigChanges(t *testing.T) {
defer db.Close()
testUtils := newTestUtils()
config := testUtils.newQueryServiceConfig()
- tsv := NewTabletServer(config)
+ tsv := NewTabletServerWithNilTopoServer(config)
dbconfigs := testUtils.newDBConfigs(db)
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs))
diff --git a/go/vt/tabletserver/testutils_test.go b/go/vt/vttablet/tabletserver/testutils_test.go
similarity index 68%
rename from go/vt/tabletserver/testutils_test.go
rename to go/vt/vttablet/tabletserver/testutils_test.go
index 7845c6ea10a..b2ad54cc0e4 100644
--- a/go/vt/tabletserver/testutils_test.go
+++ b/go/vt/vttablet/tabletserver/testutils_test.go
@@ -9,15 +9,12 @@ import (
"fmt"
"math/rand"
"reflect"
- "strings"
"testing"
"github.com/youtube/vitess/go/mysqlconn/fakesqldb"
"github.com/youtube/vitess/go/vt/dbconfigs"
"github.com/youtube/vitess/go/vt/mysqlctl"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
-
- vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
)
var errRejected = errors.New("rejected")
@@ -41,19 +38,6 @@ func (util *testUtils) checkEqual(t *testing.T, expected interface{}, result int
}
}
-func (util *testUtils) checkTabletError(t *testing.T, err interface{}, tabletErrCode vtrpcpb.ErrorCode, tabletErrStr string) {
- tabletError, ok := err.(*tabletenv.TabletError)
- if !ok {
- t.Fatalf("should return a TabletError, but got err: %v", err)
- }
- if tabletError.ErrorCode != tabletErrCode {
- t.Fatalf("got a TabletError with error code %s but wanted: %s", tabletError.ErrorCode, tabletErrCode)
- }
- if !strings.Contains(tabletError.Error(), tabletErrStr) {
- t.Fatalf("expect the tablet error should contain string: '%s', but it does not. Got tablet error: '%s'", tabletErrStr, tabletError.Error())
- }
-}
-
func (util *testUtils) newMysqld(dbcfgs *dbconfigs.DBConfigs) mysqlctl.MysqlDaemon {
cnf := mysqlctl.NewMycnf(11111, 6802)
// Assigning ServerID to be different from tablet UID to make sure that there are no
diff --git a/go/vt/tabletserver/twopc.go b/go/vt/vttablet/tabletserver/twopc.go
similarity index 93%
rename from go/vt/tabletserver/twopc.go
rename to go/vt/vttablet/tabletserver/twopc.go
index 621b8cca3df..5b39257f42d 100644
--- a/go/vt/tabletserver/twopc.go
+++ b/go/vt/vttablet/tabletserver/twopc.go
@@ -19,8 +19,8 @@ import (
"github.com/youtube/vitess/go/vt/dbconfigs"
"github.com/youtube/vitess/go/vt/dbconnpool"
"github.com/youtube/vitess/go/vt/sqlparser"
- "github.com/youtube/vitess/go/vt/tabletserver/connpool"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/connpool"
+ "github.com/youtube/vitess/go/vt/vterrors"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
@@ -137,51 +137,51 @@ func (tpc *TwoPC) Init(sidecarDBName string, dbaparams *sqldb.ConnParams) error
}
for _, s := range statements {
if _, err := conn.ExecuteFetch(s, 0, false); err != nil {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, err.Error())
+ return err
}
}
- tpc.insertRedoTx = buildParsedQuery(
+ tpc.insertRedoTx = sqlparser.BuildParsedQuery(
"insert into %s.redo_state(dtid, state, time_created) values (%a, %a, %a)",
dbname, ":dtid", ":state", ":time_created")
- tpc.insertRedoStmt = buildParsedQuery(
+ tpc.insertRedoStmt = sqlparser.BuildParsedQuery(
"insert into %s.redo_statement(dtid, id, statement) values %a",
dbname, ":vals")
- tpc.updateRedoTx = buildParsedQuery(
+ tpc.updateRedoTx = sqlparser.BuildParsedQuery(
"update %s.redo_state set state = %a where dtid = %a",
dbname, ":state", ":dtid")
- tpc.deleteRedoTx = buildParsedQuery(
+ tpc.deleteRedoTx = sqlparser.BuildParsedQuery(
"delete from %s.redo_state where dtid = %a",
dbname, ":dtid")
- tpc.deleteRedoStmt = buildParsedQuery(
+ tpc.deleteRedoStmt = sqlparser.BuildParsedQuery(
"delete from %s.redo_statement where dtid = %a",
dbname, ":dtid")
tpc.readAllRedo = fmt.Sprintf(sqlReadAllRedo, dbname, dbname)
- tpc.countUnresolvedRedo = buildParsedQuery(
+ tpc.countUnresolvedRedo = sqlparser.BuildParsedQuery(
"select count(*) from %s.redo_state where time_created < %a",
dbname, ":time_created")
- tpc.insertTransaction = buildParsedQuery(
+ tpc.insertTransaction = sqlparser.BuildParsedQuery(
"insert into %s.dt_state(dtid, state, time_created) values (%a, %a, %a)",
dbname, ":dtid", ":state", ":cur_time")
- tpc.insertParticipants = buildParsedQuery(
+ tpc.insertParticipants = sqlparser.BuildParsedQuery(
"insert into %s.dt_participant(dtid, id, keyspace, shard) values %a",
dbname, ":vals")
- tpc.transition = buildParsedQuery(
+ tpc.transition = sqlparser.BuildParsedQuery(
"update %s.dt_state set state = %a where dtid = %a and state = %a",
dbname, ":state", ":dtid", ":prepare")
- tpc.deleteTransaction = buildParsedQuery(
+ tpc.deleteTransaction = sqlparser.BuildParsedQuery(
"delete from %s.dt_state where dtid = %a",
dbname, ":dtid")
- tpc.deleteParticipants = buildParsedQuery(
+ tpc.deleteParticipants = sqlparser.BuildParsedQuery(
"delete from %s.dt_participant where dtid = %a",
dbname, ":dtid")
- tpc.readTransaction = buildParsedQuery(
+ tpc.readTransaction = sqlparser.BuildParsedQuery(
"select dtid, state, time_created from %s.dt_state where dtid = %a",
dbname, ":dtid")
- tpc.readParticipants = buildParsedQuery(
+ tpc.readParticipants = sqlparser.BuildParsedQuery(
"select keyspace, shard from %s.dt_participant where dtid = %a",
dbname, ":dtid")
- tpc.readAbandoned = buildParsedQuery(
+ tpc.readAbandoned = sqlparser.BuildParsedQuery(
"select dtid, time_created from %s.dt_state where time_created < %a",
dbname, ":time_created")
tpc.readAllTransactions = fmt.Sprintf(sqlReadAllTransactions, dbname, dbname)
@@ -198,12 +198,6 @@ func (tpc *TwoPC) Close() {
tpc.readPool.Close()
}
-func buildParsedQuery(in string, vars ...interface{}) *sqlparser.ParsedQuery {
- buf := sqlparser.NewTrackedBuffer(nil)
- buf.Myprintf(in, vars...)
- return buf.ParsedQuery()
-}
-
// SaveRedo saves the statements in the redo log using the supplied connection.
func (tpc *TwoPC) SaveRedo(ctx context.Context, conn *TxConnection, dtid string, queries []string) error {
bindVars := map[string]interface{}{
@@ -368,7 +362,7 @@ func (tpc *TwoPC) Transition(ctx context.Context, conn *TxConnection, dtid strin
return err
}
if qr.RowsAffected != 1 {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "could not transition to %v: %s", state, dtid)
+ return vterrors.Errorf(vtrpcpb.Code_NOT_FOUND, "could not transition to %v: %s", state, dtid)
}
return nil
}
diff --git a/go/vt/tabletserver/twopc_test.go b/go/vt/vttablet/tabletserver/twopc_test.go
similarity index 100%
rename from go/vt/tabletserver/twopc_test.go
rename to go/vt/vttablet/tabletserver/twopc_test.go
diff --git a/go/vt/tabletserver/twopcz.go b/go/vt/vttablet/tabletserver/twopcz.go
similarity index 100%
rename from go/vt/tabletserver/twopcz.go
rename to go/vt/vttablet/tabletserver/twopcz.go
diff --git a/go/vt/tabletserver/tx_engine.go b/go/vt/vttablet/tabletserver/tx_engine.go
similarity index 97%
rename from go/vt/tabletserver/tx_engine.go
rename to go/vt/vttablet/tabletserver/tx_engine.go
index e39c398cc68..6e5a93f6f0c 100644
--- a/go/vt/tabletserver/tx_engine.go
+++ b/go/vt/vttablet/tabletserver/tx_engine.go
@@ -15,8 +15,8 @@ import (
"github.com/youtube/vitess/go/vt/concurrency"
"github.com/youtube/vitess/go/vt/dbconfigs"
"github.com/youtube/vitess/go/vt/dtids"
- "github.com/youtube/vitess/go/vt/tabletserver/connpool"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/connpool"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
"github.com/youtube/vitess/go/vt/vtgate/vtgateconn"
)
@@ -130,7 +130,10 @@ func (te *TxEngine) Close(immediate bool) {
// the function closes rollbackDone, which can be
// verified to make sure it won't kick in later.
go func() {
- defer close(rollbackDone)
+ defer func() {
+ tabletenv.LogError()
+ close(rollbackDone)
+ }()
if immediate {
// Immediately rollback everything and return.
log.Info("Immediate shutdown: rolling back now.")
diff --git a/go/vt/tabletserver/tx_engine_test.go b/go/vt/vttablet/tabletserver/tx_engine_test.go
similarity index 97%
rename from go/vt/tabletserver/tx_engine_test.go
rename to go/vt/vttablet/tabletserver/tx_engine_test.go
index a7b957949ea..ebdc6e21244 100644
--- a/go/vt/tabletserver/tx_engine_test.go
+++ b/go/vt/vttablet/tabletserver/tx_engine_test.go
@@ -8,7 +8,7 @@ import (
"testing"
"time"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
"golang.org/x/net/context"
)
diff --git a/go/vt/tabletserver/tx_executor.go b/go/vt/vttablet/tabletserver/tx_executor.go
similarity index 86%
rename from go/vt/tabletserver/tx_executor.go
rename to go/vt/vttablet/tabletserver/tx_executor.go
index 92f5dd88189..cc0f5605f46 100644
--- a/go/vt/tabletserver/tx_executor.go
+++ b/go/vt/vttablet/tabletserver/tx_executor.go
@@ -14,7 +14,9 @@ import (
querypb "github.com/youtube/vitess/go/vt/proto/query"
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/messager"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vterrors"
)
// TxExecutor is used for executing a transactional request.
@@ -23,7 +25,7 @@ type TxExecutor struct {
ctx context.Context
logStats *tabletenv.LogStats
te *TxEngine
- messager *MessagerEngine
+ messager *messager.Engine
}
// Prepare performs a prepare on a connection including the redo log work.
@@ -32,7 +34,7 @@ type TxExecutor struct {
// protocol, will perform all the cleanup.
func (txe *TxExecutor) Prepare(transactionID int64, dtid string) error {
if !txe.te.twopcEnabled {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "2pc is not enabled")
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "2pc is not enabled")
}
defer tabletenv.QueryStats.Record("PREPARE", time.Now())
txe.logStats.TransactionID = transactionID
@@ -51,7 +53,7 @@ func (txe *TxExecutor) Prepare(transactionID int64, dtid string) error {
err = txe.te.preparedPool.Put(conn, dtid)
if err != nil {
txe.te.txPool.localRollback(txe.ctx, conn)
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_RESOURCE_EXHAUSTED, "prepare failed for transaction %d: %v", transactionID, err)
+ return vterrors.Errorf(vtrpcpb.Code_RESOURCE_EXHAUSTED, "prepare failed for transaction %d: %v", transactionID, err)
}
localConn, err := txe.te.txPool.LocalBegin(txe.ctx)
@@ -78,12 +80,12 @@ func (txe *TxExecutor) Prepare(transactionID int64, dtid string) error {
// marked as failed in the redo log.
func (txe *TxExecutor) CommitPrepared(dtid string) error {
if !txe.te.twopcEnabled {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "2pc is not enabled")
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "2pc is not enabled")
}
defer tabletenv.QueryStats.Record("COMMIT_PREPARED", time.Now())
conn, err := txe.te.preparedPool.FetchForCommit(dtid)
if err != nil {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "cannot commit dtid %s, state: %v", dtid, err)
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "cannot commit dtid %s, state: %v", dtid, err)
}
if conn == nil {
return nil
@@ -153,7 +155,7 @@ func (txe *TxExecutor) markFailed(ctx context.Context, dtid string) {
// killer will be the one to eventually roll it back.
func (txe *TxExecutor) RollbackPrepared(dtid string, originalID int64) error {
if !txe.te.twopcEnabled {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "2pc is not enabled")
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "2pc is not enabled")
}
defer tabletenv.QueryStats.Record("ROLLBACK_PREPARED", time.Now())
conn, err := txe.te.txPool.LocalBegin(txe.ctx)
@@ -183,7 +185,7 @@ returnConn:
// CreateTransaction creates the metadata for a 2PC transaction.
func (txe *TxExecutor) CreateTransaction(dtid string, participants []*querypb.Target) error {
if !txe.te.twopcEnabled {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "2pc is not enabled")
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "2pc is not enabled")
}
defer tabletenv.QueryStats.Record("CREATE_TRANSACTION", time.Now())
conn, err := txe.te.txPool.LocalBegin(txe.ctx)
@@ -203,7 +205,7 @@ func (txe *TxExecutor) CreateTransaction(dtid string, participants []*querypb.Ta
// decision to commit the associated 2pc transaction.
func (txe *TxExecutor) StartCommit(transactionID int64, dtid string) error {
if !txe.te.twopcEnabled {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "2pc is not enabled")
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "2pc is not enabled")
}
defer tabletenv.QueryStats.Record("START_COMMIT", time.Now())
txe.logStats.TransactionID = transactionID
@@ -225,7 +227,7 @@ func (txe *TxExecutor) StartCommit(transactionID int64, dtid string) error {
// If a transaction id is provided, that transaction is also rolled back.
func (txe *TxExecutor) SetRollback(dtid string, transactionID int64) error {
if !txe.te.twopcEnabled {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "2pc is not enabled")
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "2pc is not enabled")
}
defer tabletenv.QueryStats.Record("SET_ROLLBACK", time.Now())
txe.logStats.TransactionID = transactionID
@@ -257,7 +259,7 @@ func (txe *TxExecutor) SetRollback(dtid string, transactionID int64) error {
// essentially resolving it.
func (txe *TxExecutor) ConcludeTransaction(dtid string) error {
if !txe.te.twopcEnabled {
- return tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "2pc is not enabled")
+ return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "2pc is not enabled")
}
defer tabletenv.QueryStats.Record("RESOLVE", time.Now())
@@ -277,7 +279,7 @@ func (txe *TxExecutor) ConcludeTransaction(dtid string) error {
// ReadTransaction returns the metadata for the sepcified dtid.
func (txe *TxExecutor) ReadTransaction(dtid string) (*querypb.TransactionMetadata, error) {
if !txe.te.twopcEnabled {
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "2pc is not enabled")
+ return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "2pc is not enabled")
}
return txe.te.twoPC.ReadTransaction(txe.ctx, dtid)
}
@@ -285,15 +287,15 @@ func (txe *TxExecutor) ReadTransaction(dtid string) (*querypb.TransactionMetadat
// ReadTwopcInflight returns info about all in-flight 2pc transactions.
func (txe *TxExecutor) ReadTwopcInflight() (distributed []*DistributedTx, prepared, failed []*PreparedTx, err error) {
if !txe.te.twopcEnabled {
- return nil, nil, nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "2pc is not enabled")
+ return nil, nil, nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "2pc is not enabled")
}
prepared, failed, err = txe.te.twoPC.ReadAllRedo(txe.ctx)
if err != nil {
- return nil, nil, nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "Could not read redo: %v", err)
+ return nil, nil, nil, vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "Could not read redo: %v", err)
}
distributed, err = txe.te.twoPC.ReadAllTransactions(txe.ctx)
if err != nil {
- return nil, nil, nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "Could not read redo: %v", err)
+ return nil, nil, nil, vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "Could not read redo: %v", err)
}
return distributed, prepared, failed, nil
}
diff --git a/go/vt/tabletserver/tx_executor_test.go b/go/vt/vttablet/tabletserver/tx_executor_test.go
similarity index 97%
rename from go/vt/tabletserver/tx_executor_test.go
rename to go/vt/vttablet/tabletserver/tx_executor_test.go
index 0cc84bb8596..4d1e1ffae25 100644
--- a/go/vt/tabletserver/tx_executor_test.go
+++ b/go/vt/vttablet/tabletserver/tx_executor_test.go
@@ -19,7 +19,7 @@ import (
"github.com/youtube/vitess/go/sqltypes"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
"github.com/youtube/vitess/go/vt/vtgate/fakerpcvtgateconn"
"github.com/youtube/vitess/go/vt/vtgate/vtgateconn"
)
@@ -69,7 +69,7 @@ func TestTxExecutorPrepareNotInTx(t *testing.T) {
defer db.Close()
defer tsv.StopService()
err := txe.Prepare(0, "aa")
- want := "not_in_tx: Transaction 0: not found"
+ want := "transaction 0: not found"
if err == nil || err.Error() != want {
t.Errorf("Prepare err: %v, want %s", err, want)
}
@@ -101,7 +101,7 @@ func TestTxExecutorPrepareRedoBeginFail(t *testing.T) {
db.AddRejectedQuery("begin", errors.New("begin fail"))
err := txe.Prepare(txid, "aa")
defer txe.RollbackPrepared("aa", 0)
- want := "error: begin fail"
+ want := "begin fail"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("Prepare err: %v, want %s", err, want)
}
@@ -128,7 +128,7 @@ func TestTxExecutorPrepareRedoCommitFail(t *testing.T) {
db.AddRejectedQuery("commit", errors.New("commit fail"))
err := txe.Prepare(txid, "aa")
defer txe.RollbackPrepared("aa", 0)
- want := "error: commit fail"
+ want := "commit fail"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("Prepare err: %v, want %s", err, want)
}
@@ -192,7 +192,7 @@ func TestTxExecutorCommitRedoCommitFail(t *testing.T) {
defer txe.RollbackPrepared("aa", 0)
db.AddRejectedQuery("commit", errors.New("commit fail"))
err = txe.CommitPrepared("aa")
- want := "error: commit fail"
+ want := "commit fail"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("Prepare err: %v, want %s", err, want)
}
@@ -209,7 +209,7 @@ func TestTxExecutorRollbackBeginFail(t *testing.T) {
}
db.AddRejectedQuery("begin", errors.New("begin fail"))
err = txe.RollbackPrepared("aa", txid)
- want := "error: begin fail"
+ want := "begin fail"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("Prepare err: %v, want %s", err, want)
}
@@ -265,7 +265,7 @@ func TestExecutorStartCommit(t *testing.T) {
db.AddQuery(commitTransition, &sqltypes.Result{})
txid = newTxForPrep(tsv)
err = txe.StartCommit(txid, "aa")
- want := "error: could not transition to COMMIT: aa"
+ want := "could not transition to COMMIT: aa"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("Prepare err: %v, want %s", err, want)
}
@@ -287,7 +287,7 @@ func TestExecutorSetRollback(t *testing.T) {
db.AddQuery(rollbackTransition, &sqltypes.Result{})
txid = newTxForPrep(tsv)
err = txe.SetRollback("aa", txid)
- want := "error: could not transition to ROLLBACK: aa"
+ want := "could not transition to ROLLBACK: aa"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("Prepare err: %v, want %s", err, want)
}
@@ -541,7 +541,7 @@ func TestNoTwopc(t *testing.T) {
},
}}
- want := "error: 2pc is not enabled"
+ want := "2pc is not enabled"
for _, tc := range testcases {
err := tc.fun()
if err == nil || err.Error() != want {
diff --git a/go/vt/tabletserver/tx_pool.go b/go/vt/vttablet/tabletserver/tx_pool.go
similarity index 89%
rename from go/vt/tabletserver/tx_pool.go
rename to go/vt/vttablet/tabletserver/tx_pool.go
index 5b0539ec902..acb24f9772a 100644
--- a/go/vt/tabletserver/tx_pool.go
+++ b/go/vt/vttablet/tabletserver/tx_pool.go
@@ -14,6 +14,7 @@ import (
log "github.com/golang/glog"
"golang.org/x/net/context"
+ "github.com/youtube/vitess/go/mysqlconn"
"github.com/youtube/vitess/go/pools"
"github.com/youtube/vitess/go/sqldb"
"github.com/youtube/vitess/go/sqltypes"
@@ -21,8 +22,10 @@ import (
"github.com/youtube/vitess/go/sync2"
"github.com/youtube/vitess/go/timer"
"github.com/youtube/vitess/go/vt/callerid"
- "github.com/youtube/vitess/go/vt/tabletserver/connpool"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/connpool"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/messager"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vterrors"
querypb "github.com/youtube/vitess/go/vt/proto/query"
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
@@ -142,22 +145,17 @@ func (axp *TxPool) Begin(ctx context.Context) (int64, error) {
conn, err := axp.conns.Get(ctx)
if err != nil {
switch err {
- case tabletenv.ErrConnPoolClosed:
+ case connpool.ErrConnPoolClosed:
return 0, err
case pools.ErrTimeout:
axp.LogActive()
- return 0, tabletenv.NewTabletError(vtrpcpb.ErrorCode_RESOURCE_EXHAUSTED, "Transaction pool connection limit exceeded")
+ return 0, vterrors.Errorf(vtrpcpb.Code_RESOURCE_EXHAUSTED, "transaction pool connection limit exceeded")
}
- return 0, tabletenv.NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, err)
+ return 0, err
}
if _, err := conn.Exec(ctx, "begin", 1, false); err != nil {
conn.Recycle()
- if _, ok := err.(*tabletenv.TabletError); ok {
- // Exec() already returned a TabletError. Don't wrap err into another
- // TabletError and instead preserve the error code.
- return 0, err
- }
- return 0, tabletenv.NewTabletErrorSQL(vtrpcpb.ErrorCode_UNKNOWN_ERROR, err)
+ return 0, err
}
transactionID := axp.lastID.Add(1)
axp.activePool.Register(
@@ -174,7 +172,7 @@ func (axp *TxPool) Begin(ctx context.Context) (int64, error) {
}
// Commit commits the specified transaction.
-func (axp *TxPool) Commit(ctx context.Context, transactionID int64, messager *MessagerEngine) error {
+func (axp *TxPool) Commit(ctx context.Context, transactionID int64, messager *messager.Engine) error {
conn, err := axp.Get(transactionID, "for commit")
if err != nil {
return err
@@ -196,7 +194,7 @@ func (axp *TxPool) Rollback(ctx context.Context, transactionID int64) error {
func (axp *TxPool) Get(transactionID int64, reason string) (*TxConnection, error) {
v, err := axp.activePool.Get(transactionID, reason)
if err != nil {
- return nil, tabletenv.NewTabletError(vtrpcpb.ErrorCode_NOT_IN_TX, "Transaction %d: %v", transactionID, err)
+ return nil, vterrors.Errorf(vtrpcpb.Code_ABORTED, "transaction %d: %v", transactionID, err)
}
return v.(*TxConnection), nil
}
@@ -213,13 +211,13 @@ func (axp *TxPool) LocalBegin(ctx context.Context) (*TxConnection, error) {
}
// LocalCommit is the commit function for LocalBegin.
-func (axp *TxPool) LocalCommit(ctx context.Context, conn *TxConnection, messager *MessagerEngine) error {
+func (axp *TxPool) LocalCommit(ctx context.Context, conn *TxConnection, messager *messager.Engine) error {
defer conn.conclude(TxCommit)
defer messager.LockDB(conn.NewMessages, conn.ChangedMessages)()
txStats.Add("Completed", time.Now().Sub(conn.StartTime))
if _, err := conn.Exec(ctx, "commit", 1, false); err != nil {
conn.Close()
- return tabletenv.NewTabletErrorSQL(vtrpcpb.ErrorCode_UNKNOWN_ERROR, err)
+ return err
}
messager.UpdateCaches(conn.NewMessages, conn.ChangedMessages)
return nil
@@ -238,7 +236,7 @@ func (axp *TxPool) localRollback(ctx context.Context, conn *TxConnection) error
txStats.Add("Aborted", time.Now().Sub(conn.StartTime))
if _, err := conn.Exec(ctx, "rollback", 1, false); err != nil {
conn.Close()
- return tabletenv.NewTabletErrorSQL(vtrpcpb.ErrorCode_UNKNOWN_ERROR, err)
+ return err
}
return nil
}
@@ -279,7 +277,7 @@ type TxConnection struct {
StartTime time.Time
EndTime time.Time
Queries []string
- NewMessages map[string][]*MessageRow
+ NewMessages map[string][]*messager.MessageRow
ChangedMessages map[string][]string
Conclusion string
LogToFile sync2.AtomicInt32
@@ -293,7 +291,7 @@ func newTxConnection(conn *connpool.DBConn, transactionID int64, pool *TxPool, i
TransactionID: transactionID,
pool: pool,
StartTime: time.Now(),
- NewMessages: make(map[string][]*MessageRow),
+ NewMessages: make(map[string][]*messager.MessageRow),
ChangedMessages: make(map[string][]string),
ImmediateCallerID: immediate,
EffectiveCallerID: effective,
@@ -304,15 +302,25 @@ func newTxConnection(conn *connpool.DBConn, transactionID int64, pool *TxPool, i
func (txc *TxConnection) Exec(ctx context.Context, query string, maxrows int, wantfields bool) (*sqltypes.Result, error) {
r, err := txc.DBConn.ExecOnce(ctx, query, maxrows, wantfields)
if err != nil {
- if tabletenv.IsConnErr(err) {
+ if mysqlconn.IsConnErr(err) {
txc.pool.checker.CheckMySQL()
- return nil, tabletenv.NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, err)
}
- return nil, tabletenv.NewTabletErrorSQL(vtrpcpb.ErrorCode_UNKNOWN_ERROR, err)
+ return nil, err
}
return r, nil
}
+// BeginAgain commits the existing transaction and begins a new one
+func (txc *TxConnection) BeginAgain(ctx context.Context) error {
+ if _, err := txc.DBConn.Exec(ctx, "commit", 1, false); err != nil {
+ return err
+ }
+ if _, err := txc.DBConn.Exec(ctx, "begin", 1, false); err != nil {
+ return err
+ }
+ return nil
+}
+
// Recycle returns the connection to the pool. The transaction remains
// active.
func (txc *TxConnection) Recycle() {
diff --git a/go/vt/tabletserver/tx_pool_test.go b/go/vt/vttablet/tabletserver/tx_pool_test.go
similarity index 89%
rename from go/vt/tabletserver/tx_pool_test.go
rename to go/vt/vttablet/tabletserver/tx_pool_test.go
index 391e3041c05..4186174433e 100644
--- a/go/vt/tabletserver/tx_pool_test.go
+++ b/go/vt/vttablet/tabletserver/tx_pool_test.go
@@ -13,9 +13,11 @@ import (
"golang.org/x/net/context"
+ "github.com/youtube/vitess/go/mysqlconn"
"github.com/youtube/vitess/go/mysqlconn/fakesqldb"
+ "github.com/youtube/vitess/go/sqldb"
"github.com/youtube/vitess/go/sqltypes"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
"github.com/youtube/vitess/go/vt/vterrors"
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
@@ -117,25 +119,6 @@ func TestTxPoolTransactionKiller(t *testing.T) {
}
}
-func TestTxPoolBeginAfterConnPoolClosed(t *testing.T) {
- db := fakesqldb.New(t)
- defer db.Close()
- txPool := newTxPool()
- txPool.SetTimeout(time.Duration(10))
- txPool.Open(db.ConnParams(), db.ConnParams())
-
- txPool.Close()
-
- _, err := txPool.Begin(context.Background())
- if err == nil {
- t.Fatalf("expect to get an error")
- }
- terr, ok := err.(*tabletenv.TabletError)
- if !ok || terr != tabletenv.ErrConnPoolClosed {
- t.Fatalf("get error: %v, but expect: %v", terr, tabletenv.ErrConnPoolClosed)
- }
-}
-
// TestTxPoolBeginWithPoolConnectionError_TransientErrno2006 tests the case
// where we see a transient errno 2006 e.g. because MySQL killed the
// db connection. DBConn.Exec() is going to reconnect and retry automatically
@@ -190,8 +173,12 @@ func TestTxPoolBeginWithPoolConnectionError_Errno2006_Permanent(t *testing.T) {
if err == nil || !strings.Contains(err.Error(), "Lost connection to MySQL server") || !strings.Contains(err.Error(), "(errno 2013)") {
t.Fatalf("Begin did not return the reconnect error: %v", err)
}
- if got, want := vterrors.RecoverVtErrorCode(err), vtrpcpb.ErrorCode_INTERNAL_ERROR; got != want {
- t.Errorf("wrong error code for reconnect error after Begin: got = %v, want = %v", got, want)
+ sqlErr, ok := err.(*sqldb.SQLError)
+ if !ok {
+ t.Fatalf("Unexpected error type: %T, want %T", err, &sqldb.SQLError{})
+ }
+ if got, want := sqlErr.Number(), mysqlconn.CRServerLost; got != want {
+ t.Errorf("Unexpected error code: %d, want %d", got, want)
}
}
@@ -212,7 +199,7 @@ func TestTxPoolBeginWithPoolConnectionError_Errno2013(t *testing.T) {
if err == nil || !strings.Contains(err.Error(), "(errno 2013)") {
t.Fatalf("Begin must return connection error with MySQL errno 2013: %v", err)
}
- if got, want := vterrors.RecoverVtErrorCode(err), vtrpcpb.ErrorCode_UNKNOWN_ERROR; got != want {
+ if got, want := vterrors.Code(err), vtrpcpb.Code_UNKNOWN; got != want {
t.Errorf("wrong error code for Begin error: got = %v, want = %v", got, want)
}
}
@@ -253,7 +240,7 @@ func TestTxPoolBeginWithError(t *testing.T) {
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("Begin: %v, want %s", err, want)
}
- if got, want := vterrors.RecoverVtErrorCode(err), vtrpcpb.ErrorCode_UNKNOWN_ERROR; got != want {
+ if got, want := vterrors.Code(err), vtrpcpb.Code_UNKNOWN; got != want {
t.Errorf("wrong error code for Begin error: got = %v, want = %v", got, want)
}
}
@@ -298,7 +285,7 @@ func TestTxPoolGetConnNonExistentTransaction(t *testing.T) {
txPool.Open(db.ConnParams(), db.ConnParams())
defer txPool.Close()
_, err := txPool.Get(12345, "for query")
- want := "not_in_tx: Transaction 12345: not found"
+ want := "transaction 12345: not found"
if err == nil || err.Error() != want {
t.Errorf("Get: %v, want %s", err, want)
}
@@ -333,8 +320,12 @@ func TestTxPoolExecFailDueToConnFail_Errno2006(t *testing.T) {
if err == nil || !strings.Contains(err.Error(), "(errno 2006)") {
t.Fatalf("Exec must return connection error with MySQL errno 2006: %v", err)
}
- if got, want := vterrors.RecoverVtErrorCode(err), vtrpcpb.ErrorCode_INTERNAL_ERROR; got != want {
- t.Errorf("wrong error code for Exec error: got = %v, want = %v", got, want)
+ sqlErr, ok := err.(*sqldb.SQLError)
+ if !ok {
+ t.Fatalf("Unexpected error type: %T, want %T", err, &sqldb.SQLError{})
+ }
+ if num := sqlErr.Number(); num != mysqlconn.CRServerGone {
+ t.Errorf("Unexpected error code: %d, want %d", num, mysqlconn.CRServerGone)
}
}
@@ -366,7 +357,7 @@ func TestTxPoolExecFailDueToConnFail_Errno2013(t *testing.T) {
if err == nil || !strings.Contains(err.Error(), "(errno 2013)") {
t.Fatalf("Exec must return connection error with MySQL errno 2013: %v", err)
}
- if got, want := vterrors.RecoverVtErrorCode(err), vtrpcpb.ErrorCode_UNKNOWN_ERROR; got != want {
+ if got, want := vterrors.Code(err), vtrpcpb.Code_UNKNOWN; got != want {
t.Errorf("wrong error code for Exec error: got = %v, want = %v", got, want)
}
}
diff --git a/go/vt/tabletserver/tx_prep_pool.go b/go/vt/vttablet/tabletserver/tx_prep_pool.go
similarity index 100%
rename from go/vt/tabletserver/tx_prep_pool.go
rename to go/vt/vttablet/tabletserver/tx_prep_pool.go
diff --git a/go/vt/tabletserver/tx_prep_pool_test.go b/go/vt/vttablet/tabletserver/tx_prep_pool_test.go
similarity index 100%
rename from go/vt/tabletserver/tx_prep_pool_test.go
rename to go/vt/vttablet/tabletserver/tx_prep_pool_test.go
diff --git a/go/vt/tabletserver/txlogz.go b/go/vt/vttablet/tabletserver/txlogz.go
similarity index 97%
rename from go/vt/tabletserver/txlogz.go
rename to go/vt/vttablet/tabletserver/txlogz.go
index 63854fab190..9ed9b6800ce 100644
--- a/go/vt/tabletserver/txlogz.go
+++ b/go/vt/vttablet/tabletserver/txlogz.go
@@ -15,7 +15,7 @@ import (
"github.com/youtube/vitess/go/acl"
"github.com/youtube/vitess/go/vt/callerid"
"github.com/youtube/vitess/go/vt/logz"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
querypb "github.com/youtube/vitess/go/vt/proto/query"
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
diff --git a/go/vt/tabletserver/txlogz_test.go b/go/vt/vttablet/tabletserver/txlogz_test.go
similarity index 96%
rename from go/vt/tabletserver/txlogz_test.go
rename to go/vt/vttablet/tabletserver/txlogz_test.go
index 9208539fc55..41019403c9e 100644
--- a/go/vt/tabletserver/txlogz_test.go
+++ b/go/vt/vttablet/tabletserver/txlogz_test.go
@@ -13,7 +13,7 @@ import (
"github.com/youtube/vitess/go/sync2"
"github.com/youtube/vitess/go/vt/callerid"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
)
func testHandler(req *http.Request, t *testing.T) {
diff --git a/go/vt/tabletserver/txthrottler/mock_healthcheck_test.go b/go/vt/vttablet/tabletserver/txthrottler/mock_healthcheck_test.go
similarity index 97%
rename from go/vt/tabletserver/txthrottler/mock_healthcheck_test.go
rename to go/vt/vttablet/tabletserver/txthrottler/mock_healthcheck_test.go
index 41e48d951fd..d6dbc21ce3c 100644
--- a/go/vt/tabletserver/txthrottler/mock_healthcheck_test.go
+++ b/go/vt/vttablet/tabletserver/txthrottler/mock_healthcheck_test.go
@@ -7,7 +7,7 @@ import (
gomock "github.com/golang/mock/gomock"
discovery "github.com/youtube/vitess/go/vt/discovery"
topodata "github.com/youtube/vitess/go/vt/proto/topodata"
- queryservice "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
+ queryservice "github.com/youtube/vitess/go/vt/vttablet/queryservice"
)
// Mock of HealthCheck interface
diff --git a/go/vt/tabletserver/txthrottler/mock_server_test.go b/go/vt/vttablet/tabletserver/txthrottler/mock_server_test.go
similarity index 100%
rename from go/vt/tabletserver/txthrottler/mock_server_test.go
rename to go/vt/vttablet/tabletserver/txthrottler/mock_server_test.go
diff --git a/go/vt/tabletserver/txthrottler/mock_throttler_test.go b/go/vt/vttablet/tabletserver/txthrottler/mock_throttler_test.go
similarity index 100%
rename from go/vt/tabletserver/txthrottler/mock_throttler_test.go
rename to go/vt/vttablet/tabletserver/txthrottler/mock_throttler_test.go
diff --git a/go/vt/tabletserver/txthrottler/mock_topology_watcher_test.go b/go/vt/vttablet/tabletserver/txthrottler/mock_topology_watcher_test.go
similarity index 100%
rename from go/vt/tabletserver/txthrottler/mock_topology_watcher_test.go
rename to go/vt/vttablet/tabletserver/txthrottler/mock_topology_watcher_test.go
diff --git a/go/vt/tabletserver/txthrottler/mock_toposerver_impl_test.go b/go/vt/vttablet/tabletserver/txthrottler/mock_toposerver_impl_test.go
similarity index 100%
rename from go/vt/tabletserver/txthrottler/mock_toposerver_impl_test.go
rename to go/vt/vttablet/tabletserver/txthrottler/mock_toposerver_impl_test.go
diff --git a/go/vt/tabletserver/txthrottler/tx_throttler.go b/go/vt/vttablet/tabletserver/txthrottler/tx_throttler.go
similarity index 94%
rename from go/vt/tabletserver/txthrottler/tx_throttler.go
rename to go/vt/vttablet/tabletserver/txthrottler/tx_throttler.go
index 30d567c1ab2..3e927d42f9b 100644
--- a/go/vt/tabletserver/txthrottler/tx_throttler.go
+++ b/go/vt/vttablet/tabletserver/txthrottler/tx_throttler.go
@@ -10,7 +10,7 @@ import (
"github.com/golang/protobuf/proto"
"github.com/youtube/vitess/go/vt/discovery"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
"github.com/youtube/vitess/go/vt/throttler"
"github.com/youtube/vitess/go/vt/topo"
@@ -23,7 +23,8 @@ import (
// It uses a discovery.HealthCheck to send replication-lag updates to the wrapped throttler.
//
// Intended Usage:
-// t := CreateTxThrottlerFromTabletConfig()
+// // Assuming topoServer is a topo.Server variable pointing to a Vitess topology server.
+// t := CreateTxThrottlerFromTabletConfig(topoServer)
//
// // A transaction throttler must be opened before its first use:
// if err := t.Open(keyspace, shard); err != nil {
@@ -59,8 +60,8 @@ type TxThrottler struct {
// any error occurs.
// This function calls tryCreateTxThrottler that does the actual creation work
// and returns an error if one occurred.
-func CreateTxThrottlerFromTabletConfig() *TxThrottler {
- txThrottler, err := tryCreateTxThrottler()
+func CreateTxThrottlerFromTabletConfig(topoServer topo.Server) *TxThrottler {
+ txThrottler, err := tryCreateTxThrottler(topoServer)
if err != nil {
log.Errorf("Error creating transaction throttler. Transaction throttling will"+
" be disabled. Error: %v", err)
@@ -74,7 +75,7 @@ func CreateTxThrottlerFromTabletConfig() *TxThrottler {
return txThrottler
}
-func tryCreateTxThrottler() (*TxThrottler, error) {
+func tryCreateTxThrottler(topoServer topo.Server) (*TxThrottler, error) {
if !tabletenv.Config.EnableTxThrottler {
return newTxThrottler(&txThrottlerConfig{enabled: false})
}
@@ -91,6 +92,7 @@ func tryCreateTxThrottler() (*TxThrottler, error) {
return newTxThrottler(&txThrottlerConfig{
enabled: true,
+ topoServer: topoServer,
throttlerConfig: &throttlerConfig,
healthCheckCells: healthCheckCells,
})
@@ -104,6 +106,7 @@ type txThrottlerConfig struct {
// returns false.
enabled bool
+ topoServer topo.Server
throttlerConfig *throttlerdatapb.Configuration
// healthCheckCells stores the cell names in which running vttablets will be monitored for
// replication lag.
@@ -139,7 +142,6 @@ type txThrottlerState struct {
throttleMu sync.Mutex
throttler ThrottlerInterface
- topoServer topo.Server
healthCheck discovery.HealthCheck
topologyWatchers []TopologyWatcherInterface
}
@@ -147,13 +149,11 @@ type txThrottlerState struct {
// These vars store the functions used to create the topo server, healthcheck,
// topology watchers and go/vt/throttler. These are provided here so that they can be overridden
// in tests to generate mocks.
-type topoServerFactoryFunc func() topo.Server
type healthCheckFactoryFunc func() discovery.HealthCheck
type topologyWatcherFactoryFunc func(topoServer topo.Server, tr discovery.TabletRecorder, cell, keyspace, shard string, refreshInterval time.Duration, topoReadConcurrency int) TopologyWatcherInterface
type throttlerFactoryFunc func(name, unit string, threadCount int, maxRate, maxReplicationLag int64) (ThrottlerInterface, error)
var (
- topoServerFactory topoServerFactoryFunc
healthCheckFactory healthCheckFactoryFunc
topologyWatcherFactory topologyWatcherFactoryFunc
throttlerFactory throttlerFactoryFunc
@@ -164,7 +164,6 @@ func init() {
}
func resetTxThrottlerFactories() {
- topoServerFactory = topo.Open
healthCheckFactory = discovery.NewDefaultHealthCheck
topologyWatcherFactory = func(topoServer topo.Server, tr discovery.TabletRecorder, cell, keyspace, shard string, refreshInterval time.Duration, topoReadConcurrency int) TopologyWatcherInterface {
return discovery.NewShardReplicationWatcher(
@@ -248,14 +247,13 @@ func newTxThrottlerState(config *txThrottlerConfig, keyspace, shard string,
if err != nil {
return nil, err
}
- if t.UpdateConfiguration(config.throttlerConfig, true /* copyZeroValues */); err != nil {
+ if err := t.UpdateConfiguration(config.throttlerConfig, true /* copyZeroValues */); err != nil {
t.Close()
return nil, err
}
result := &txThrottlerState{
throttler: t,
}
- result.topoServer = topoServerFactory()
result.healthCheck = healthCheckFactory()
result.healthCheck.SetListener(result, false /* sendDownEvents */)
result.topologyWatchers = make(
@@ -264,7 +262,7 @@ func newTxThrottlerState(config *txThrottlerConfig, keyspace, shard string,
result.topologyWatchers = append(
result.topologyWatchers,
topologyWatcherFactory(
- result.topoServer,
+ config.topoServer,
result.healthCheck, /* TabletRecorder */
cell,
keyspace,
@@ -297,8 +295,6 @@ func (ts *txThrottlerState) deallocateResources() {
ts.healthCheck.Close()
ts.healthCheck = nil
- ts.topoServer.Close()
-
// After ts.healthCheck is closed txThrottlerState.StatsUpdate() is guaranteed not
// to be executing, so we can safely close the throttler.
ts.throttler.Close()
diff --git a/go/vt/tabletserver/txthrottler/tx_throttler_test.go b/go/vt/vttablet/tabletserver/txthrottler/tx_throttler_test.go
similarity index 94%
rename from go/vt/tabletserver/txthrottler/tx_throttler_test.go
rename to go/vt/vttablet/tabletserver/txthrottler/tx_throttler_test.go
index 047f09b74fd..d13c61cb202 100644
--- a/go/vt/tabletserver/txthrottler/tx_throttler_test.go
+++ b/go/vt/vttablet/tabletserver/txthrottler/tx_throttler_test.go
@@ -6,7 +6,7 @@ import (
"github.com/golang/mock/gomock"
"github.com/youtube/vitess/go/vt/discovery"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
"github.com/youtube/vitess/go/vt/topo"
querypb "github.com/youtube/vitess/go/vt/proto/query"
@@ -17,7 +17,7 @@ func TestDisabledThrottler(t *testing.T) {
oldConfig := tabletenv.Config
defer func() { tabletenv.Config = oldConfig }()
tabletenv.Config.EnableTxThrottler = false
- throttler := CreateTxThrottlerFromTabletConfig()
+ throttler := CreateTxThrottlerFromTabletConfig(topo.Server{})
if err := throttler.Open("keyspace", "shard"); err != nil {
t.Fatalf("want: nil, got: %v", err)
}
@@ -32,9 +32,7 @@ func TestEnabledThrottler(t *testing.T) {
defer mockCtrl.Finish()
defer resetTxThrottlerFactories()
- mockTopoServer, mockImpl := NewMockServer(mockCtrl)
- mockImpl.EXPECT().Close()
- topoServerFactory = func() topo.Server { return mockTopoServer }
+ mockTopoServer, _ := NewMockServer(mockCtrl)
mockHealthCheck := NewMockHealthCheck(mockCtrl)
var hcListener discovery.HealthCheckStatsListener
@@ -95,7 +93,7 @@ func TestEnabledThrottler(t *testing.T) {
tabletenv.Config.EnableTxThrottler = true
tabletenv.Config.TxThrottlerHealthCheckCells = []string{"cell1", "cell2"}
- throttler, err := tryCreateTxThrottler()
+ throttler, err := tryCreateTxThrottler(mockTopoServer)
if err != nil {
t.Fatalf("want: nil, got: %v", err)
}
diff --git a/go/vt/tabletserver/tabletservermock/controller.go b/go/vt/vttablet/tabletservermock/controller.go
similarity index 92%
rename from go/vt/tabletserver/tabletservermock/controller.go
rename to go/vt/vttablet/tabletservermock/controller.go
index 5daf993603a..5bbf8f0ab28 100644
--- a/go/vt/tabletserver/tabletservermock/controller.go
+++ b/go/vt/vttablet/tabletservermock/controller.go
@@ -14,8 +14,9 @@ import (
"github.com/youtube/vitess/go/vt/mysqlctl"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
- "github.com/youtube/vitess/go/vt/tabletserver"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/rules"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
)
// BroadcastData is used by the mock Controller to send data
@@ -146,7 +147,7 @@ func (tqsc *Controller) UnRegisterQueryRuleSource(ruleSource string) {
}
// SetQueryRules is part of the tabletserver.Controller interface
-func (tqsc *Controller) SetQueryRules(ruleSource string, qrs *tabletserver.QueryRules) error {
+func (tqsc *Controller) SetQueryRules(ruleSource string, qrs *rules.Rules) error {
return nil
}
@@ -155,6 +156,11 @@ func (tqsc *Controller) QueryService() queryservice.QueryService {
return nil
}
+// SchemaEngine is part of the tabletserver.Controller interface
+func (tqsc *Controller) SchemaEngine() *schema.Engine {
+ return nil
+}
+
// BroadcastHealth is part of the tabletserver.Controller interface
func (tqsc *Controller) BroadcastHealth(terTimestamp int64, stats *querypb.RealtimeStats) {
tqsc.mu.Lock()
diff --git a/go/vt/tabletmanager/tmclient/rpc_client_api.go b/go/vt/vttablet/tmclient/rpc_client_api.go
similarity index 100%
rename from go/vt/tabletmanager/tmclient/rpc_client_api.go
rename to go/vt/vttablet/tmclient/rpc_client_api.go
diff --git a/go/vt/vttest/local_cluster.go b/go/vt/vttest/local_cluster.go
index ec650ccd8c3..df7a14cf9af 100644
--- a/go/vt/vttest/local_cluster.go
+++ b/go/vt/vttest/local_cluster.go
@@ -176,6 +176,17 @@ func Schema(schema string) VitessOption {
}
}
+// ExtraMyCnf adds one or more 'my.cnf'-style config files to MySQL.
+// (if more than one, the ':' separator should be used).
+func ExtraMyCnf(extraMyCnf string) VitessOption {
+ return VitessOption{
+ beforeRun: func(hdl *Handle) error {
+ hdl.cmd.Args = append(hdl.cmd.Args, "--extra_my_cnf", extraMyCnf)
+ return nil
+ },
+ }
+}
+
// InitDataOptions contain the command line arguments that configure
// initialization of vttest with random data. See the documentation of
// the corresponding command line flags in py/vttest/run_local_database.py
diff --git a/go/vt/worker/command.go b/go/vt/worker/command.go
index 222e4a9b455..981d1066042 100644
--- a/go/vt/worker/command.go
+++ b/go/vt/worker/command.go
@@ -126,7 +126,7 @@ func (wi *Instance) RunCommand(ctx context.Context, args []string, wr *wrangler.
}
done, err := wi.setAndStartWorker(ctx, wrk, wr)
if err != nil {
- return nil, nil, vterrors.WithPrefix("cannot set worker: ", err)
+ return nil, nil, vterrors.Errorf(vterrors.Code(err), "cannot set worker: %v", err)
}
return wrk, done, nil
}
diff --git a/go/vt/worker/diff_utils.go b/go/vt/worker/diff_utils.go
index 3b5f5aa5a0b..bfff38a0baa 100644
--- a/go/vt/worker/diff_utils.go
+++ b/go/vt/worker/diff_utils.go
@@ -20,11 +20,11 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/key"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
"github.com/youtube/vitess/go/vt/vtgate/vindexes"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletconn"
querypb "github.com/youtube/vitess/go/vt/proto/query"
tabletmanagerdatapb "github.com/youtube/vitess/go/vt/proto/tabletmanagerdata"
diff --git a/go/vt/worker/grpcvtworkerclient/client.go b/go/vt/worker/grpcvtworkerclient/client.go
index 7e120dea354..afbc5b8cd79 100644
--- a/go/vt/worker/grpcvtworkerclient/client.go
+++ b/go/vt/worker/grpcvtworkerclient/client.go
@@ -43,7 +43,7 @@ func gRPCVtworkerClientFactory(addr string, dialTimeout time.Duration) (vtworker
}
cc, err := grpc.Dial(addr, opt, grpc.WithBlock(), grpc.WithTimeout(dialTimeout))
if err != nil {
- return nil, vterrors.NewVitessError(vtrpcpb.ErrorCode_DEADLINE_EXCEEDED, err, "grpc.Dial() err: %v", err)
+ return nil, vterrors.Errorf(vtrpcpb.Code_DEADLINE_EXCEEDED, "grpc.Dial() err: %v", err)
}
c := vtworkerservicepb.NewVtworkerClient(cc)
@@ -60,7 +60,7 @@ type eventStreamAdapter struct {
func (e *eventStreamAdapter) Recv() (*logutilpb.Event, error) {
le, err := e.stream.Recv()
if err != nil {
- return nil, vterrors.FromGRPCError(err)
+ return nil, vterrors.FromGRPC(err)
}
return le.Event, nil
}
@@ -73,7 +73,7 @@ func (client *gRPCVtworkerClient) ExecuteVtworkerCommand(ctx context.Context, ar
stream, err := client.c.ExecuteVtworkerCommand(ctx, query)
if err != nil {
- return nil, vterrors.FromGRPCError(err)
+ return nil, vterrors.FromGRPC(err)
}
return &eventStreamAdapter{stream}, nil
}
diff --git a/go/vt/worker/grpcvtworkerserver/server.go b/go/vt/worker/grpcvtworkerserver/server.go
index 5206c45d592..9331564f299 100644
--- a/go/vt/worker/grpcvtworkerserver/server.go
+++ b/go/vt/worker/grpcvtworkerserver/server.go
@@ -57,7 +57,7 @@ func (s *VtworkerServer) ExecuteVtworkerCommand(args *vtworkerdatapb.ExecuteVtwo
err = s.wi.WaitForCommand(worker, done)
}
- return vterrors.ToGRPCError(err)
+ return vterrors.ToGRPC(err)
}
// StartServer registers the VtworkerServer for RPCs
diff --git a/go/vt/worker/instance.go b/go/vt/worker/instance.go
index 91a74879d42..4ff5e38d3db 100644
--- a/go/vt/worker/instance.go
+++ b/go/vt/worker/instance.go
@@ -20,9 +20,9 @@ import (
"github.com/youtube/vitess/go/tb"
"github.com/youtube/vitess/go/vt/logutil"
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/vterrors"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
)
@@ -75,8 +75,7 @@ func (wi *Instance) setAndStartWorker(ctx context.Context, wrk Worker, wr *wrang
defer wi.currentWorkerMutex.Unlock()
if wi.currentContext != nil {
- return nil, vterrors.FromError(vtrpcpb.ErrorCode_TRANSIENT_ERROR,
- fmt.Errorf("A worker job is already in progress: %v", wi.currentWorker.StatusAsText()))
+ return nil, vterrors.Errorf(vtrpcpb.Code_UNAVAILABLE, "A worker job is already in progress: %v", wi.currentWorker.StatusAsText())
}
if wi.currentWorker != nil {
@@ -84,17 +83,14 @@ func (wi *Instance) setAndStartWorker(ctx context.Context, wrk Worker, wr *wrang
const gracePeriod = 1 * time.Minute
gracePeriodEnd := time.Now().Add(gracePeriod)
if wi.lastRunStopTime.Before(gracePeriodEnd) {
- return nil, vterrors.FromError(vtrpcpb.ErrorCode_TRANSIENT_ERROR,
- fmt.Errorf("A worker job was recently stopped (%f seconds ago): %v",
- time.Now().Sub(wi.lastRunStopTime).Seconds(),
- wi.currentWorker))
+ return nil, vterrors.Errorf(vtrpcpb.Code_UNAVAILABLE, "A worker job was recently stopped (%f seconds ago): %v", time.Now().Sub(wi.lastRunStopTime).Seconds(), wi.currentWorker)
}
// QUERY_NOT_SERVED = FailedPrecondition => manual resolution required.
- return nil, vterrors.FromError(vtrpcpb.ErrorCode_QUERY_NOT_SERVED,
- fmt.Errorf("The worker job was stopped %.1f minutes ago, but not reset. You have to reset it manually. Job: %v",
- time.Now().Sub(wi.lastRunStopTime).Minutes(),
- wi.currentWorker))
+ return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION,
+ "The worker job was stopped %.1f minutes ago, but not reset. You have to reset it manually. Job: %v",
+ time.Now().Sub(wi.lastRunStopTime).Minutes(),
+ wi.currentWorker)
}
wi.currentWorker = wrk
@@ -141,7 +137,7 @@ func (wi *Instance) setAndStartWorker(ctx context.Context, wrk Worker, wr *wrang
case <-wi.currentContext.Done():
// Context is done i.e. probably canceled.
if wi.currentContext.Err() == context.Canceled {
- err = vterrors.NewVitessError(vtrpcpb.ErrorCode_CANCELLED, err, "vtworker command was canceled: %v", err)
+ err = vterrors.Errorf(vtrpcpb.Code_CANCELED, "vtworker command was canceled: %v", err)
}
default:
}
diff --git a/go/vt/worker/legacy_split_clone_test.go b/go/vt/worker/legacy_split_clone_test.go
index 0ff613c644a..01198614edc 100644
--- a/go/vt/worker/legacy_split_clone_test.go
+++ b/go/vt/worker/legacy_split_clone_test.go
@@ -19,11 +19,11 @@ import (
"github.com/youtube/vitess/go/mysqlconn/replication"
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/mysqlctl/tmutils"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
- "github.com/youtube/vitess/go/vt/tabletserver/grpcqueryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice/fakes"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
+ "github.com/youtube/vitess/go/vt/vttablet/grpcqueryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice/fakes"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler/testlib"
querypb "github.com/youtube/vitess/go/vt/proto/query"
diff --git a/go/vt/worker/restartable_result_reader.go b/go/vt/worker/restartable_result_reader.go
index 1a5b52383b9..6d725669494 100644
--- a/go/vt/worker/restartable_result_reader.go
+++ b/go/vt/worker/restartable_result_reader.go
@@ -17,9 +17,9 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
"github.com/youtube/vitess/go/vt/topo/topoproto"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletconn"
querypb "github.com/youtube/vitess/go/vt/proto/query"
tabletmanagerdatapb "github.com/youtube/vitess/go/vt/proto/tabletmanagerdata"
diff --git a/go/vt/worker/restartable_result_reader_test.go b/go/vt/worker/restartable_result_reader_test.go
index 0906b1e4a66..7b08e135ef9 100644
--- a/go/vt/worker/restartable_result_reader_test.go
+++ b/go/vt/worker/restartable_result_reader_test.go
@@ -16,10 +16,10 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletconn"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
tabletmanagerdatapb "github.com/youtube/vitess/go/vt/proto/tabletmanagerdata"
diff --git a/go/vt/worker/split_clone_test.go b/go/vt/worker/split_clone_test.go
index 87b4c95113e..569630b868d 100644
--- a/go/vt/worker/split_clone_test.go
+++ b/go/vt/worker/split_clone_test.go
@@ -20,12 +20,12 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/concurrency"
"github.com/youtube/vitess/go/vt/mysqlctl/tmutils"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
- "github.com/youtube/vitess/go/vt/tabletserver/grpcqueryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice/fakes"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
+ "github.com/youtube/vitess/go/vt/vttablet/grpcqueryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice/fakes"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler/testlib"
querypb "github.com/youtube/vitess/go/vt/proto/query"
diff --git a/go/vt/worker/split_diff_test.go b/go/vt/worker/split_diff_test.go
index f274f6c6b9d..47ec8ae1210 100644
--- a/go/vt/worker/split_diff_test.go
+++ b/go/vt/worker/split_diff_test.go
@@ -15,9 +15,9 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/logutil"
"github.com/youtube/vitess/go/vt/mysqlctl/tmutils"
- "github.com/youtube/vitess/go/vt/tabletserver/grpcqueryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice/fakes"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
+ "github.com/youtube/vitess/go/vt/vttablet/grpcqueryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice/fakes"
"github.com/youtube/vitess/go/vt/wrangler"
"github.com/youtube/vitess/go/vt/wrangler/testlib"
diff --git a/go/vt/worker/utils_test.go b/go/vt/worker/utils_test.go
index f15cf908cea..875994dfd37 100644
--- a/go/vt/worker/utils_test.go
+++ b/go/vt/worker/utils_test.go
@@ -13,9 +13,9 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/dbconnpool"
- "github.com/youtube/vitess/go/vt/tabletmanager/faketmclient"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo"
+ "github.com/youtube/vitess/go/vt/vttablet/faketmclient"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
querypb "github.com/youtube/vitess/go/vt/proto/query"
diff --git a/go/vt/worker/vertical_split_clone_test.go b/go/vt/worker/vertical_split_clone_test.go
index 2cb1b64c761..20384b6917c 100644
--- a/go/vt/worker/vertical_split_clone_test.go
+++ b/go/vt/worker/vertical_split_clone_test.go
@@ -12,10 +12,10 @@ import (
"github.com/youtube/vitess/go/mysqlconn/replication"
"github.com/youtube/vitess/go/vt/mysqlctl/tmutils"
- "github.com/youtube/vitess/go/vt/tabletserver/grpcqueryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice/fakes"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
+ "github.com/youtube/vitess/go/vt/vttablet/grpcqueryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice/fakes"
"github.com/youtube/vitess/go/vt/wrangler/testlib"
tabletmanagerdatapb "github.com/youtube/vitess/go/vt/proto/tabletmanagerdata"
diff --git a/go/vt/worker/vertical_split_diff_test.go b/go/vt/worker/vertical_split_diff_test.go
index ded37d84b59..b8a66da5354 100644
--- a/go/vt/worker/vertical_split_diff_test.go
+++ b/go/vt/worker/vertical_split_diff_test.go
@@ -15,9 +15,9 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/logutil"
"github.com/youtube/vitess/go/vt/mysqlctl/tmutils"
- "github.com/youtube/vitess/go/vt/tabletserver/grpcqueryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice/fakes"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
+ "github.com/youtube/vitess/go/vt/vttablet/grpcqueryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice/fakes"
"github.com/youtube/vitess/go/vt/wrangler"
"github.com/youtube/vitess/go/vt/wrangler/testlib"
diff --git a/go/vt/worker/vtworkerclient/wrapper.go b/go/vt/worker/vtworkerclient/wrapper.go
index f7f13990c57..963f787f417 100644
--- a/go/vt/worker/vtworkerclient/wrapper.go
+++ b/go/vt/worker/vtworkerclient/wrapper.go
@@ -25,14 +25,14 @@ func RunCommandAndWait(ctx context.Context, server string, args []string, recv f
// TODO(mberlin): vtctlclient exposes dialTimeout as flag. If there are no use cases, remove it there as well to be consistent?
client, err := New(server, 30*time.Second /* dialTimeout */)
if err != nil {
- return vterrors.WithPrefix("cannot dial to server "+server+": ", err)
+ return vterrors.Errorf(vterrors.Code(err), "cannot dial to server "+server+": %v", err)
}
defer client.Close()
// run the command
stream, err := client.ExecuteVtworkerCommand(ctx, args)
if err != nil {
- return vterrors.WithPrefix("cannot execute remote command: ", err)
+ return vterrors.Errorf(vterrors.Code(err), "cannot execute remote command: %v", err)
}
for {
@@ -43,7 +43,7 @@ func RunCommandAndWait(ctx context.Context, server string, args []string, recv f
case io.EOF:
return nil
default:
- return vterrors.WithPrefix("stream error: ", err)
+ return vterrors.Errorf(vterrors.Code(err), "stream error: %v", err)
}
}
}
diff --git a/go/vt/worker/vtworkerclienttest/client_testsuite.go b/go/vt/worker/vtworkerclienttest/client_testsuite.go
index 4a4ea767c24..d180b454744 100644
--- a/go/vt/worker/vtworkerclienttest/client_testsuite.go
+++ b/go/vt/worker/vtworkerclienttest/client_testsuite.go
@@ -24,15 +24,15 @@ import (
"golang.org/x/net/context"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
"github.com/youtube/vitess/go/vt/vterrors"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/worker"
"github.com/youtube/vitess/go/vt/worker/vtworkerclient"
// Import the gRPC client implementation for tablet manager because the real
// vtworker implementation requires it.
- _ "github.com/youtube/vitess/go/vt/tabletmanager/grpctmclient"
+ _ "github.com/youtube/vitess/go/vt/vttablet/grpctmclient"
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)
@@ -101,7 +101,7 @@ func runVtworkerCommand(client vtworkerclient.Client, args []string) error {
case io.EOF:
return nil
default:
- return vterrors.WithPrefix("unexpected error when reading the stream: ", err)
+ return vterrors.Errorf(vterrors.Code(err), "unexpected error when reading the stream: %v", err)
}
}
}
@@ -129,7 +129,7 @@ func commandErrorsBecauseBusy(t *testing.T, client vtworkerclient.Client, server
if _, err := stream.Recv(); err != nil {
// We see CANCELED from the RPC client (client side cancelation) or
// from vtworker itself (server side cancelation).
- if vterrors.RecoverVtErrorCode(err) != vtrpcpb.ErrorCode_CANCELLED {
+ if vterrors.Code(err) != vtrpcpb.Code_CANCELED {
errorCodeCheck = fmt.Errorf("Block command should only error due to canceled context: %v", err)
}
// Stream has finished.
@@ -150,8 +150,8 @@ func commandErrorsBecauseBusy(t *testing.T, client vtworkerclient.Client, server
// vtworker should send an error back that it's busy and we should retry later.
<-blockCommandStarted
gotErr := runVtworkerCommand(client, []string{"Ping", "Are you busy?"})
- wantCode := vtrpcpb.ErrorCode_TRANSIENT_ERROR
- if gotCode := vterrors.RecoverVtErrorCode(gotErr); gotCode != wantCode {
+ wantCode := vtrpcpb.Code_UNAVAILABLE
+ if gotCode := vterrors.Code(gotErr); gotCode != wantCode {
t.Fatalf("wrong error code for second cmd: got = %v, want = %v, err: %v", gotCode, wantCode, gotErr)
}
@@ -174,8 +174,8 @@ func commandErrorsBecauseBusy(t *testing.T, client vtworkerclient.Client, server
// canceled but not reset yet. New commands are still failing with a
// retryable error.
gotErr2 := runVtworkerCommand(client, []string{"Ping", "canceled and still busy?"})
- wantCode2 := vtrpcpb.ErrorCode_TRANSIENT_ERROR
- if gotCode2 := vterrors.RecoverVtErrorCode(gotErr2); gotCode2 != wantCode2 {
+ wantCode2 := vtrpcpb.Code_UNAVAILABLE
+ if gotCode2 := vterrors.Code(gotErr2); gotCode2 != wantCode2 {
t.Fatalf("wrong error code for second cmd before reset: got = %v, want = %v, err: %v", gotCode2, wantCode2, gotErr2)
}
diff --git a/go/vt/workflow/manager.go b/go/vt/workflow/manager.go
index 1d48516912c..f3170678576 100644
--- a/go/vt/workflow/manager.go
+++ b/go/vt/workflow/manager.go
@@ -38,13 +38,16 @@ type Factory interface {
// The passed in workflow will have its Uuid, FactoryName and State
// variable filled it. This Init method should fill in the
// Name and Data attributes, based on the provided args.
- // This is called during the Manager.Create phase.
- Init(w *workflowpb.Workflow, args []string) error
+ // This is called during the Manager.Create phase and will initially
+ // checkpoint the workflow in the topology.
+ // The Manager object is passed to Init method since the resharding workflow
+ // will use the topology server in Manager.
+ Init(m *Manager, w *workflowpb.Workflow, args []string) error
// Instantiate loads a workflow from the proto representation
// into an in-memory Workflow object. rootNode is the root UI node
// representing the workflow.
- Instantiate(w *workflowpb.Workflow, rootNode *Node) (Workflow, error)
+ Instantiate(m *Manager, w *workflowpb.Workflow, rootNode *Node) (Workflow, error)
}
// Manager is the main Workflow manager object.
@@ -247,7 +250,7 @@ func (m *Manager) Create(ctx context.Context, factoryName string, args []string)
// Let the factory parse the parameters and initialize the
// object.
- if err := factory.Init(w, args); err != nil {
+ if err := factory.Init(m, w, args); err != nil {
return "", err
}
rw, err := m.instantiateWorkflow(w)
@@ -284,7 +287,7 @@ func (m *Manager) instantiateWorkflow(w *workflowpb.Workflow) (*runningWorkflow,
return nil, fmt.Errorf("no factory named %v is registered", w.FactoryName)
}
var err error
- rw.workflow, err = factory.Instantiate(w, rw.rootNode)
+ rw.workflow, err = factory.Instantiate(m, w, rw.rootNode)
if err != nil {
return nil, err
}
@@ -441,7 +444,7 @@ func (m *Manager) Delete(ctx context.Context, uuid string) error {
// Wait waits for the provided workflow to end.
func (m *Manager) Wait(ctx context.Context, uuid string) error {
// Find the workflow.
- rw, err := m.getRunningWorkflow(uuid)
+ rw, err := m.runningWorkflow(uuid)
if err != nil {
return err
}
@@ -456,8 +459,29 @@ func (m *Manager) Wait(ctx context.Context, uuid string) error {
return nil
}
-// getRunningWorkflow returns a runningWorkflow by uuid.
-func (m *Manager) getRunningWorkflow(uuid string) (*runningWorkflow, error) {
+// WorkflowForTesting returns the Workflow object of the running workflow
+// identified by uuid. The method is used in unit tests to inject mocks.
+func (m *Manager) WorkflowForTesting(uuid string) (Workflow, error) {
+ rw, err := m.runningWorkflow(uuid)
+ if err != nil {
+ return nil, err
+ }
+ return rw.workflow, nil
+}
+
+// WorkflowInfoForTesting returns the WorkflowInfo object of the running
+// workflow identified by uuid. The method is used in unit tests to manipulate
+// checkpoint.
+func (m *Manager) WorkflowInfoForTesting(uuid string) (*topo.WorkflowInfo, error) {
+ rw, err := m.runningWorkflow(uuid)
+ if err != nil {
+ return nil, err
+ }
+ return rw.wi, nil
+}
+
+// runningWorkflow returns a runningWorkflow by uuid.
+func (m *Manager) runningWorkflow(uuid string) (*runningWorkflow, error) {
m.mu.Lock()
defer m.mu.Unlock()
diff --git a/go/vt/workflow/node.go b/go/vt/workflow/node.go
index 7f06fee31af..11aaa3bf951 100644
--- a/go/vt/workflow/node.go
+++ b/go/vt/workflow/node.go
@@ -220,6 +220,30 @@ func (n *Node) deepCopyFrom(otherNode *Node, copyChildren bool) error {
return nil
}
+// GetChildByPath returns the child node given the relative path to this node.
+// The caller must ensure that the node tree is not modified during the call.
+func (n *Node) GetChildByPath(subPath string) (*Node, error) {
+ // Find the subnode if needed.
+ parts := strings.Split(subPath, "/")
+
+ currentNode := n
+ for i := 0; i < len(parts); i++ {
+ childPathName := parts[i]
+ found := false
+ for _, child := range currentNode.Children {
+ if child.PathName == childPathName {
+ found = true
+ currentNode = child
+ break
+ }
+ }
+ if !found {
+ return nil, fmt.Errorf("node %v has no children named %v", currentNode.Path, childPathName)
+ }
+ }
+ return currentNode, nil
+}
+
// ActionParameters describe an action initiated by the user.
type ActionParameters struct {
// Path is the path of the Node the action was performed on.
@@ -355,6 +379,7 @@ func (m *NodeManager) updateNodeAndBroadcastLocked(userNode *Node, updateChildre
if err != nil {
return err
}
+
userNode.LastChanged = time.Now().Unix()
if err := savedNode.deepCopyFrom(userNode, updateChildren); err != nil {
return err
diff --git a/go/vt/workflow/node_test.go b/go/vt/workflow/node_test.go
index 12a667b442d..5c8152070c8 100644
--- a/go/vt/workflow/node_test.go
+++ b/go/vt/workflow/node_test.go
@@ -85,7 +85,7 @@ func TestNodeManagerWithRoot(t *testing.T) {
t.Errorf("unexpected Action error: %v", err)
}
if len(tw.actions) != 1 || tw.actions[0].Path != n.Path || tw.actions[0].Name != "action" {
- t.Errorf("unexpected Ation callback values: %v", tw.actions)
+ t.Errorf("unexpected Action callback values: %v", tw.actions)
}
// Delete root node, make sure we get notified.
diff --git a/go/vt/workflow/resharding/checkpoint.go b/go/vt/workflow/resharding/checkpoint.go
new file mode 100644
index 00000000000..7392675cb82
--- /dev/null
+++ b/go/vt/workflow/resharding/checkpoint.go
@@ -0,0 +1,56 @@
+package resharding
+
+import (
+ "context"
+ "sync"
+
+ "github.com/golang/protobuf/proto"
+ "github.com/youtube/vitess/go/vt/topo"
+
+ workflowpb "github.com/youtube/vitess/go/vt/proto/workflow"
+)
+
+// CheckpointWriter saves the checkpoint data into topology server.
+type CheckpointWriter struct {
+ topoServer topo.Server
+
+ // checkpointMu is used for protecting data access during checkpointing.
+ mu sync.Mutex
+ checkpoint *workflowpb.WorkflowCheckpoint
+ wi *topo.WorkflowInfo
+}
+
+// NewCheckpointWriter creates a CheckpointWriter.
+func NewCheckpointWriter(ts topo.Server, checkpoint *workflowpb.WorkflowCheckpoint, wi *topo.WorkflowInfo) *CheckpointWriter {
+ return &CheckpointWriter{
+ topoServer: ts,
+ checkpoint: checkpoint,
+ wi: wi,
+ }
+}
+
+// UpdateTask updates the task status in the checkpointing copy and
+// saves the full checkpoint to the topology server.
+func (c *CheckpointWriter) UpdateTask(taskID string, status workflowpb.TaskState, err error) error {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ errorMessage := ""
+ if err != nil {
+ errorMessage = err.Error()
+ }
+
+ t := c.checkpoint.Tasks[taskID]
+ t.State = status
+ t.Error = errorMessage
+ return c.saveLocked()
+}
+
+func (c *CheckpointWriter) saveLocked() error {
+ var err error
+ c.wi.Data, err = proto.Marshal(c.checkpoint)
+ if err != nil {
+ return err
+ }
+ return c.topoServer.SaveWorkflow(context.TODO(), c.wi)
+}
diff --git a/go/vt/workflow/resharding/horizontal_resharding_workflow.go b/go/vt/workflow/resharding/horizontal_resharding_workflow.go
index 0103bd1eddb..5975f2fd4a7 100644
--- a/go/vt/workflow/resharding/horizontal_resharding_workflow.go
+++ b/go/vt/workflow/resharding/horizontal_resharding_workflow.go
@@ -3,24 +3,20 @@ package resharding
// Package resharding contains a workflow for automatic horizontal resharding.
// The workflow assumes that there are as many vtworker processes running as source shards.
// Plus, these vtworker processes must be reachable via RPC.
-// TO DO: it can be used to save checkpointer
import (
- "encoding/json"
"flag"
"fmt"
+ "strconv"
"strings"
- "sync"
log "github.com/golang/glog"
+ "github.com/golang/protobuf/proto"
"golang.org/x/net/context"
- "github.com/youtube/vitess/go/vt/automation"
- "github.com/youtube/vitess/go/vt/concurrency"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo"
- "github.com/youtube/vitess/go/vt/topo/topoproto"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/topotools"
"github.com/youtube/vitess/go/vt/workflow"
@@ -31,350 +27,368 @@ import (
)
const (
+ codeVersion = 1
+
horizontalReshardingFactoryName = "horizontal_resharding"
)
-// HorizontalReshardingData is the data structure to store resharding arguments.
-type HorizontalReshardingData struct {
- Keyspace string
- Vtworkers []string
-}
-
-// HorizontalReshardingWorkflow contains meta-information and methods to control horizontal resharding workflow.
-type HorizontalReshardingWorkflow struct {
- // ctx is the context of the whole horizontal resharding process. Once this context is canceled,
- // the horizontal resharding process stops.
- ctx context.Context
- wr ReshardingWrangler
- manager *workflow.Manager
- topoServer topo.Server
-
- // logger is the logger we export UI logs from.
- logger *logutil.MemoryLogger
-
- // rootUINode is the root node representing the workflow in the UI.
- rootUINode *workflow.Node
- copySchemaUINode *workflow.Node
- splitCloneUINode *workflow.Node
- splitDiffUINode *workflow.Node
- migrateUINode *workflow.Node
-
- keyspace string
- vtworkers []string
+// PhaseType is used to store the phase name in a workflow.
+type PhaseType string
- subWorkflows []*PerShardHorizontalResharding
-}
+const (
+ phaseCopySchema PhaseType = "copy_schema"
+ phaseClone PhaseType = "clone"
+ phaseWaitForFilteredReplication PhaseType = "wait_for_filtered_replication"
+ phaseDiff PhaseType = "diff"
+ phaseMigrateRdonly PhaseType = "migrate_rdonly"
+ phaseMigrateReplica PhaseType = "migrate_replica"
+ phaseMigrateMaster PhaseType = "migrate_master"
+)
-// PerShardHorizontalReshardingData is the data structure to store the resharding arguments for each shard.
-type PerShardHorizontalReshardingData struct {
- Keyspace string
- SourceShard string
- DestinationShards []string
- Vtworker string
+// Register registers the HorizontalReshardingWorkflowFactory as a factory
+// in the workflow framework.
+func Register() {
+ workflow.Register(horizontalReshardingFactoryName, &HorizontalReshardingWorkflowFactory{})
}
-// PerShardHorizontalResharding contains the data and method for horizontal resharding from a single source shard.
-type PerShardHorizontalResharding struct {
- PerShardHorizontalReshardingData
- parent *HorizontalReshardingWorkflow
-
- copySchemaShardUINode *workflow.Node
- splitCloneShardUINode *workflow.Node
- splitDiffShardUINode *workflow.Node
- migrateShardUINode *workflow.Node
+// HorizontalReshardingWorkflowFactory is the factory to create
+// a horizontal resharding workflow.
+type HorizontalReshardingWorkflowFactory struct{}
- shardUILogger *logutil.MemoryLogger
-}
-
-// Run executes the horizontal resharding process and updates the UI message.
-// It implements the workflow.Workflow interface.
-func (hw *HorizontalReshardingWorkflow) Run(ctx context.Context, manager *workflow.Manager, wi *topo.WorkflowInfo) error {
- hw.ctx = ctx
- hw.topoServer = manager.TopoServer()
- hw.wr = wrangler.New(logutil.NewConsoleLogger(), manager.TopoServer(), tmclient.NewTabletManagerClient())
-
- hw.createSubWorkflows()
-
- hw.setUIMessage("Horizontal resharding: workflow created successfully.")
-
- hw.rootUINode.Display = workflow.NodeDisplayDeterminate
- hw.rootUINode.BroadcastChanges(true /* updateChildren */)
+// Init is part of the workflow.Factory interface.
+func (*HorizontalReshardingWorkflowFactory) Init(m *workflow.Manager, w *workflowpb.Workflow, args []string) error {
+ subFlags := flag.NewFlagSet(horizontalReshardingFactoryName, flag.ContinueOnError)
+ keyspace := subFlags.String("keyspace", "", "Name of keyspace to perform horizontal resharding")
+ vtworkersStr := subFlags.String("vtworkers", "", "A comma-separated list of vtworker addresses")
+ enableApprovals := subFlags.Bool("enable_approvals", true, "If true, executions of tasks require user's approvals on the UI.")
- // TODO(yipeiw): Support action button to allow retry, stop, restart.
- if err := hw.executeWorkflow(); err != nil {
+ if err := subFlags.Parse(args); err != nil {
return err
}
+ if *keyspace == "" || *vtworkersStr == "" {
+ return fmt.Errorf("Keyspace name, vtworkers information must be provided for horizontal resharding")
+ }
- hw.setUIMessage(fmt.Sprintf("Horizontal Resharding on %v: finished sucessfully.", hw.keyspace))
-
- return nil
-}
+ vtworkers := strings.Split(*vtworkersStr, ",")
+ w.Name = fmt.Sprintf("Horizontal resharding on keyspace %s", *keyspace)
-// createSubWorkflows creates a per source shard horizontal resharding workflow for each source shard in the keyspace.
-func (hw *HorizontalReshardingWorkflow) createSubWorkflows() error {
- overlappingShards, err := topotools.FindOverlappingShards(hw.ctx, hw.topoServer, hw.keyspace)
+ checkpoint, err := initCheckpoint(m.TopoServer(), *keyspace, vtworkers)
if err != nil {
- hw.logger.Infof("Horizontal Resharding: createSubWorkflows error in finding overlapping shards: %v.", err)
return err
}
- for i, os := range overlappingShards {
- var sourceShard *topo.ShardInfo
- var destinationShards []*topo.ShardInfo
- // Judge which side is source shard by checking the number of servedTypes.
- if len(os.Left[0].ServedTypes) > 0 {
- sourceShard = os.Left[0]
- destinationShards = os.Right
- } else {
- sourceShard = os.Right[0]
- destinationShards = os.Left
- }
+ checkpoint.Settings["enable_approvals"] = fmt.Sprintf("%v", *enableApprovals)
- if err := hw.createWorkflowPerShard(sourceShard, destinationShards, hw.vtworkers[i]); err != nil {
- return err
- }
+ w.Data, err = proto.Marshal(checkpoint)
+ if err != nil {
+ return err
}
return nil
}
-func (hw *HorizontalReshardingWorkflow) createWorkflowPerShard(sourceShard *topo.ShardInfo, destinationShards []*topo.ShardInfo, vtworker string) error {
- sourceShardName := sourceShard.ShardName()
- var destShardNames []string
- for _, s := range destinationShards {
- destShardNames = append(destShardNames, s.ShardName())
+// Instantiate is part the workflow.Factory interface.
+func (*HorizontalReshardingWorkflowFactory) Instantiate(m *workflow.Manager, w *workflowpb.Workflow, rootNode *workflow.Node) (workflow.Workflow, error) {
+ rootNode.Message = "This is a workflow to execute horizontal resharding automatically."
+
+ checkpoint := &workflowpb.WorkflowCheckpoint{}
+ if err := proto.Unmarshal(w.Data, checkpoint); err != nil {
+ return nil, err
}
- perShard := &PerShardHorizontalResharding{
- PerShardHorizontalReshardingData: PerShardHorizontalReshardingData{
- Keyspace: hw.keyspace,
- SourceShard: sourceShardName,
- DestinationShards: destShardNames,
- Vtworker: vtworker,
- },
- copySchemaShardUINode: &workflow.Node{
- Name: "Shard " + sourceShardName,
- PathName: "shard_" + sourceShardName,
- },
- splitCloneShardUINode: &workflow.Node{
- Name: "Shard " + sourceShardName,
- PathName: "shard_" + sourceShardName,
- },
- splitDiffShardUINode: &workflow.Node{
- Name: "Shard " + sourceShardName,
- PathName: "shard_" + sourceShardName,
- },
- migrateShardUINode: &workflow.Node{
- Name: "Shard " + sourceShardName,
- PathName: "shard_" + sourceShardName,
- },
- shardUILogger: logutil.NewMemoryLogger(),
+ enableApprovals, err := strconv.ParseBool(checkpoint.Settings["enable_approvals"])
+ if err != nil {
+ return nil, err
+ }
+
+ hw := &HorizontalReshardingWorkflow{
+ checkpoint: checkpoint,
+ rootUINode: rootNode,
+ logger: logutil.NewMemoryLogger(),
+ wr: wrangler.New(logutil.NewConsoleLogger(), m.TopoServer(), tmclient.NewTabletManagerClient()),
+ topoServer: m.TopoServer(),
+ manager: m,
+ enableApprovals: enableApprovals,
+ }
+ copySchemaUINode := &workflow.Node{
+ Name: "CopySchemaShard",
+ PathName: string(phaseCopySchema),
+ }
+ cloneUINode := &workflow.Node{
+ Name: "SplitClone",
+ PathName: string(phaseClone),
+ }
+ waitForFilteredReplicationUINode := &workflow.Node{
+ Name: "WaitForFilteredReplication",
+ PathName: string(phaseWaitForFilteredReplication),
+ }
+ diffUINode := &workflow.Node{
+ Name: "SplitDiff",
+ PathName: string(phaseDiff),
+ }
+ migrateRdonlyUINode := &workflow.Node{
+ Name: "MigrateServedTypeRDONLY",
+ PathName: string(phaseMigrateRdonly),
+ }
+ migrateReplicaUINode := &workflow.Node{
+ Name: "MigrateServedTypeREPLICA",
+ PathName: string(phaseMigrateReplica),
+ }
+ migrateMasterUINode := &workflow.Node{
+ Name: "MigrateServedTypeMASTER",
+ PathName: string(phaseMigrateMaster),
}
- perShard.parent = hw
- hw.copySchemaUINode.Children = append(hw.copySchemaUINode.Children, perShard.copySchemaShardUINode)
- hw.splitCloneUINode.Children = append(hw.splitCloneUINode.Children, perShard.splitCloneShardUINode)
- hw.splitDiffUINode.Children = append(hw.splitDiffUINode.Children, perShard.splitDiffShardUINode)
- hw.migrateUINode.Children = append(hw.migrateUINode.Children, perShard.migrateShardUINode)
+ hw.rootUINode.Children = []*workflow.Node{
+ copySchemaUINode,
+ cloneUINode,
+ waitForFilteredReplicationUINode,
+ diffUINode,
+ migrateRdonlyUINode,
+ migrateReplicaUINode,
+ migrateMasterUINode,
+ }
- hw.subWorkflows = append(hw.subWorkflows, perShard)
- return nil
-}
+ sourceShards := strings.Split(hw.checkpoint.Settings["source_shards"], ",")
+ destinationShards := strings.Split(hw.checkpoint.Settings["destination_shards"], ",")
-func (hw *HorizontalReshardingWorkflow) executeWorkflow() error {
- if err := hw.runAllSubWorkflows(hw.executeCopySchemaPerShard); err != nil {
- hw.logger.Infof("Horizontal Resharding: error in CopySchemaShard: %v.", err)
- return err
+ if err := createUINodes(hw.rootUINode, phaseCopySchema, destinationShards); err != nil {
+ return hw, err
}
- if err := hw.runAllSubWorkflows(hw.executeSplitClonePerShard); err != nil {
- hw.logger.Infof("Horizontal Resharding: error in SplitClone: %v.", err)
- return err
+ if err := createUINodes(hw.rootUINode, phaseClone, sourceShards); err != nil {
+ return hw, err
}
- if err := hw.runAllSubWorkflows(hw.executeSplitDiffPerShard); err != nil {
- hw.logger.Infof("Horizontal Resharding: error in SplitDiff: %v.", err)
- return err
+ if err := createUINodes(hw.rootUINode, phaseWaitForFilteredReplication, destinationShards); err != nil {
+ return hw, err
}
- if err := hw.runAllSubWorkflows(hw.executeMigratePerShard); err != nil {
- hw.logger.Infof("Horizontal Resharding: error in MigratedServedType: %v.", err)
- return err
+ if err := createUINodes(hw.rootUINode, phaseDiff, destinationShards); err != nil {
+ return hw, err
+ }
+ if err := createUINodes(hw.rootUINode, phaseMigrateRdonly, sourceShards); err != nil {
+ return hw, err
+ }
+ if err := createUINodes(hw.rootUINode, phaseMigrateReplica, sourceShards); err != nil {
+ return hw, err
+ }
+ if err := createUINodes(hw.rootUINode, phaseMigrateMaster, sourceShards); err != nil {
+ return hw, err
}
- return nil
-}
-// runAllSubWorkflows runs jobs in parallel.
-func (hw *HorizontalReshardingWorkflow) runAllSubWorkflows(executeFunc func(subWorkflow *PerShardHorizontalResharding) error) error {
- ec := concurrency.AllErrorRecorder{}
- wg := sync.WaitGroup{}
- for _, sw := range hw.subWorkflows {
- wg.Add(1)
- go func(s *PerShardHorizontalResharding) {
- defer wg.Done()
- ec.RecordError(executeFunc(s))
- }(sw)
- }
- wg.Wait()
- return ec.Error()
+ return hw, nil
}
-// executeCopySchemaPerShard runs CopySchemaShard to copy the schema of a source shard to all its destination shards.
-// TODO(yipeiw): excludeTable information can be added to UI input parameters, s.t the user can customize excluded tables during resharding.
-func (hw *HorizontalReshardingWorkflow) executeCopySchemaPerShard(perhw *PerShardHorizontalResharding) error {
- sourceKeyspaceShard := topoproto.KeyspaceShardString(perhw.Keyspace, perhw.SourceShard)
- for _, d := range perhw.DestinationShards {
- err := hw.wr.CopySchemaShardFromShard(hw.ctx, nil /* tableArray*/, nil /* excludeTableArray */, true /*includeViews*/, perhw.Keyspace, perhw.SourceShard, perhw.Keyspace, d, wrangler.DefaultWaitSlaveTimeout)
- if err != nil {
- hw.logger.Infof("Horizontal Resharding: error in CopySchemaShardFromShard from %s to %s: %v.", sourceKeyspaceShard, d, err)
- return err
+func createUINodes(rootNode *workflow.Node, phaseName PhaseType, shards []string) error {
+ phaseNode, err := rootNode.GetChildByPath(string(phaseName))
+ if err != nil {
+ return fmt.Errorf("fails to find phase node for: %v", phaseName)
+ }
+
+ for _, shard := range shards {
+ taskUINode := &workflow.Node{
+ Name: "Shard " + shard,
+ PathName: shard,
}
- hw.logger.Infof("Horizontal Resharding: CopySchemaShardFromShard from %s to %s is finished.", sourceKeyspaceShard, d)
+ phaseNode.Children = append(phaseNode.Children, taskUINode)
}
return nil
}
-// executeSplitClonePerShard runs SplitClone to clone the data within a keyspace from a source shard to its destination shards.
-func (hw *HorizontalReshardingWorkflow) executeSplitClonePerShard(perhw *PerShardHorizontalResharding) error {
- sourceKeyspaceShard := topoproto.KeyspaceShardString(perhw.Keyspace, perhw.SourceShard)
- var destinationKeyspaceShards []string
- for _, destShard := range perhw.DestinationShards {
- destinationKeyspaceShards = append(destinationKeyspaceShards, topoproto.KeyspaceShardString(perhw.Keyspace, destShard))
- }
-
- // Reset the vtworker to avoid error if vtworker command has been called elsewhere.
- // This is because vtworker class doesn't cleanup the environment after execution.
- automation.ExecuteVtworker(hw.ctx, perhw.Vtworker, []string{"Reset"})
- // The flag min_healthy_rdonly_tablets is set to 1 (default value is 2).
- // Therefore, we can reuse the normal end to end test setting, which has only 1 rdonly tablet.
- // TODO(yipeiw): Add min_healthy_rdonly_tablets as an input argument in UI.
- args := []string{"SplitClone", "--min_healthy_rdonly_tablets=1", sourceKeyspaceShard}
- if _, err := automation.ExecuteVtworker(hw.ctx, perhw.Vtworker, args); err != nil {
- hw.logger.Infof("Horizontal resharding: error in SplitClone in keyspace %s: %v.", perhw.Keyspace, err)
- return err
+// initCheckpoint initialize the checkpoint for the horizontal workflow.
+func initCheckpoint(ts topo.Server, keyspace string, vtworkers []string) (*workflowpb.WorkflowCheckpoint, error) {
+ sourceShards, destinationShards, err := findSourceAndDestinationShards(ts, keyspace)
+ if err != nil {
+ return nil, err
}
- hw.logger.Infof("Horizontal resharding: SplitClone is finished.")
- // Wait for filtered replication task.
- for _, d := range perhw.DestinationShards {
- if err := hw.wr.WaitForFilteredReplication(hw.ctx, perhw.Keyspace, d, wrangler.DefaultWaitForFilteredReplicationMaxDelay); err != nil {
- hw.logger.Infof("Horizontal Resharding: error in WaitForFilteredReplication: %v.", err)
- return err
+ return initCheckpointFromShards(keyspace, vtworkers, sourceShards, destinationShards)
+}
+
+func findSourceAndDestinationShards(ts topo.Server, keyspace string) ([]string, []string, error) {
+ overlappingShards, err := topotools.FindOverlappingShards(context.Background(), ts, keyspace)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ var sourceShards, destinationShards []string
+
+ for _, os := range overlappingShards {
+ var sourceShardInfo *topo.ShardInfo
+ var destinationShardInfos []*topo.ShardInfo
+ // Judge which side is source shard by checking the number of servedTypes.
+ if len(os.Left[0].ServedTypes) > 0 {
+ sourceShardInfo = os.Left[0]
+ destinationShardInfos = os.Right
+ } else {
+ sourceShardInfo = os.Right[0]
+ destinationShardInfos = os.Left
+ }
+ sourceShards = append(sourceShards, sourceShardInfo.ShardName())
+ for _, d := range destinationShardInfos {
+ destinationShards = append(destinationShards, d.ShardName())
}
- hw.logger.Infof("Horizontal Resharding:WaitForFilteredReplication is finished on " + d)
}
- return nil
+ return sourceShards, destinationShards, nil
}
-// executeSplitDiffPerShard runs SplitDiff for every destination shard to the source and destination
-// to ensure all the data is present and correct.
-func (hw *HorizontalReshardingWorkflow) executeSplitDiffPerShard(perhw *PerShardHorizontalResharding) error {
- var destinationKeyspaceShards []string
- for _, destShard := range perhw.DestinationShards {
- destinationKeyspaceShards = append(destinationKeyspaceShards, topoproto.KeyspaceShardString(perhw.Keyspace, destShard))
+func initCheckpointFromShards(keyspace string, vtworkers, sourceShards, destinationShards []string) (*workflowpb.WorkflowCheckpoint, error) {
+ if len(vtworkers) != len(sourceShards) {
+ return nil, fmt.Errorf("there are %v vtworkers, %v source shards: the number should be same", len(vtworkers), len(sourceShards))
}
- for _, d := range destinationKeyspaceShards {
- automation.ExecuteVtworker(hw.ctx, perhw.Vtworker, []string{"Reset"})
- args := []string{"SplitDiff", "--min_healthy_rdonly_tablets=1", d}
- _, err := automation.ExecuteVtworker(hw.ctx, perhw.Vtworker, args)
- if err != nil {
- return err
+ tasks := make(map[string]*workflowpb.Task)
+ initTasks(tasks, phaseCopySchema, destinationShards, func(i int, shard string) map[string]string {
+ return map[string]string{
+ "keyspace": keyspace,
+ "source_shard": sourceShards[0],
+ "destination_shard": shard,
}
- }
- hw.logger.Infof("Horizontal resharding: SplitDiff is finished.")
- return nil
+ })
+ initTasks(tasks, phaseClone, sourceShards, func(i int, shard string) map[string]string {
+ return map[string]string{
+ "keyspace": keyspace,
+ "source_shard": shard,
+ "vtworker": vtworkers[i],
+ }
+ })
+ initTasks(tasks, phaseWaitForFilteredReplication, destinationShards, func(i int, shard string) map[string]string {
+ return map[string]string{
+ "keyspace": keyspace,
+ "destination_shard": shard,
+ }
+ })
+ initTasks(tasks, phaseDiff, destinationShards, func(i int, shard string) map[string]string {
+ return map[string]string{
+ "keyspace": keyspace,
+ "destination_shard": shard,
+ "vtworker": vtworkers[0],
+ }
+ })
+ initTasks(tasks, phaseMigrateRdonly, sourceShards, func(i int, shard string) map[string]string {
+ return map[string]string{
+ "keyspace": keyspace,
+ "source_shard": shard,
+ "served_type": topodatapb.TabletType_RDONLY.String(),
+ }
+ })
+ initTasks(tasks, phaseMigrateReplica, sourceShards, func(i int, shard string) map[string]string {
+ return map[string]string{
+ "keyspace": keyspace,
+ "source_shard": shard,
+ "served_type": topodatapb.TabletType_REPLICA.String(),
+ }
+ })
+ initTasks(tasks, phaseMigrateMaster, sourceShards, func(i int, shard string) map[string]string {
+ return map[string]string{
+ "keyspace": keyspace,
+ "source_shard": shard,
+ "served_type": topodatapb.TabletType_MASTER.String(),
+ }
+ })
+
+ return &workflowpb.WorkflowCheckpoint{
+ CodeVersion: codeVersion,
+ Tasks: tasks,
+ Settings: map[string]string{
+ "source_shards": strings.Join(sourceShards, ","),
+ "destination_shards": strings.Join(destinationShards, ","),
+ },
+ }, nil
}
-// executeMigratePerShard runs MigrateServedTypes to switch over to serving from the new shards.
-func (hw *HorizontalReshardingWorkflow) executeMigratePerShard(perhw *PerShardHorizontalResharding) error {
- sourceKeyspaceShard := topoproto.KeyspaceShardString(perhw.Keyspace, perhw.SourceShard)
- servedTypeParams := []topodatapb.TabletType{topodatapb.TabletType_RDONLY,
- topodatapb.TabletType_REPLICA,
- topodatapb.TabletType_MASTER}
- for _, servedType := range servedTypeParams {
- err := hw.wr.MigrateServedTypes(hw.ctx, perhw.Keyspace, perhw.SourceShard, nil /* cells */, servedType, false /* reverse */, false /* skipReFreshState */, wrangler.DefaultFilteredReplicationWaitTime)
- if err != nil {
- hw.logger.Infof("Horizontal Resharding: error in MigrateServedTypes on servedType %s: %v.", servedType, err)
- return err
+func initTasks(tasks map[string]*workflowpb.Task, phase PhaseType, shards []string, getAttributes func(int, string) map[string]string) {
+ for i, shard := range shards {
+ taskID := createTaskID(phase, shard)
+ tasks[taskID] = &workflowpb.Task{
+ Id: taskID,
+ State: workflowpb.TaskState_TaskNotStarted,
+ Attributes: getAttributes(i, shard),
}
- hw.logger.Infof("Horizontal Resharding: MigrateServedTypes is finished on tablet %s serve type %s.", sourceKeyspaceShard, servedType)
}
- return nil
}
-func (hw *HorizontalReshardingWorkflow) setUIMessage(message string) {
- log.Infof("Horizontal resharding on keyspace %v: %v.", hw.keyspace, message)
- hw.rootUINode.Log = hw.logger.String()
- hw.rootUINode.Message = message
- hw.rootUINode.BroadcastChanges(false /* updateChildren */)
-}
+// HorizontalReshardingWorkflow contains meta-information and methods to
+// control the horizontal resharding workflow.
+type HorizontalReshardingWorkflow struct {
+ ctx context.Context
+ wr ReshardingWrangler
+ manager *workflow.Manager
+ topoServer topo.Server
+ wi *topo.WorkflowInfo
+ // logger is the logger we export UI logs from.
+ logger *logutil.MemoryLogger
-// WorkflowFactory is the factory to register the HorizontalReshard Workflow.
-type WorkflowFactory struct{}
+ // rootUINode is the root node representing the workflow in the UI.
+ rootUINode *workflow.Node
-// Register registers horizontal_resharding as a valid factory in the workflow framework.
-func Register() {
- workflow.Register(horizontalReshardingFactoryName, &WorkflowFactory{})
+ checkpoint *workflowpb.WorkflowCheckpoint
+ checkpointWriter *CheckpointWriter
+
+ enableApprovals bool
}
-// Init is part of the workflow.Factory interface.
-func (*WorkflowFactory) Init(workflowProto *workflowpb.Workflow, args []string) error {
- subFlags := flag.NewFlagSet(horizontalReshardingFactoryName, flag.ContinueOnError)
- keyspace := subFlags.String("keyspace", "", "Name of keyspace to perform horizontal resharding")
- vtworkersStr := subFlags.String("vtworkers", "", "A comma-separated list of vtworker addresses")
+// Run executes the horizontal resharding process.
+// It implements the workflow.Workflow interface.
+func (hw *HorizontalReshardingWorkflow) Run(ctx context.Context, manager *workflow.Manager, wi *topo.WorkflowInfo) error {
+ hw.ctx = ctx
+ hw.wi = wi
+ hw.checkpointWriter = NewCheckpointWriter(hw.topoServer, hw.checkpoint, hw.wi)
+ hw.rootUINode.Display = workflow.NodeDisplayDeterminate
+ hw.rootUINode.BroadcastChanges(true /* updateChildren */)
- if err := subFlags.Parse(args); err != nil {
+ if err := hw.runWorkflow(); err != nil {
return err
}
- if *keyspace == "" || *vtworkersStr == "" {
- return fmt.Errorf("Keyspace name, vtworkers information must be provided for horizontal resharding")
+ hw.setUIMessage(fmt.Sprintf("Horizontal Resharding is finished sucessfully."))
+ return nil
+}
+
+func (hw *HorizontalReshardingWorkflow) runWorkflow() error {
+ copySchemaTasks := hw.GetTasks(phaseCopySchema)
+ copySchemaRunner := NewParallelRunner(hw.ctx, hw.rootUINode, hw.checkpointWriter, copySchemaTasks, hw.runCopySchema, Parallel, hw.enableApprovals)
+ if err := copySchemaRunner.Run(); err != nil {
+ return err
}
- vtworkers := strings.Split(*vtworkersStr, ",")
- workflowProto.Name = fmt.Sprintf("Horizontal resharding on keyspace %s", *keyspace)
- data := &HorizontalReshardingData{
- Keyspace: *keyspace,
- Vtworkers: vtworkers,
+ cloneTasks := hw.GetTasks(phaseClone)
+ cloneRunner := NewParallelRunner(hw.ctx, hw.rootUINode, hw.checkpointWriter, cloneTasks, hw.runSplitClone, Parallel, hw.enableApprovals)
+ if err := cloneRunner.Run(); err != nil {
+ return err
}
- var err error
- workflowProto.Data, err = json.Marshal(data)
- if err != nil {
+
+ waitForFilteredReplicationTasks := hw.GetTasks(phaseWaitForFilteredReplication)
+ waitForFilteredReplicationRunner := NewParallelRunner(hw.ctx, hw.rootUINode, hw.checkpointWriter, waitForFilteredReplicationTasks, hw.runWaitForFilteredReplication, Parallel, hw.enableApprovals)
+ if err := waitForFilteredReplicationRunner.Run(); err != nil {
return err
}
- return nil
-}
-// Instantiate is part of the workflow.Factory interface.
-func (*WorkflowFactory) Instantiate(workflowProto *workflowpb.Workflow, rootNode *workflow.Node) (workflow.Workflow, error) {
- rootNode.Message = "This is a workflow to execute horizontal resharding automatically."
- data := &HorizontalReshardingData{}
- if err := json.Unmarshal(workflowProto.Data, data); err != nil {
- return nil, err
+ diffTasks := hw.GetTasks(phaseDiff)
+ diffRunner := NewParallelRunner(hw.ctx, hw.rootUINode, hw.checkpointWriter, diffTasks, hw.runSplitDiff, Sequential, hw.enableApprovals)
+ if err := diffRunner.Run(); err != nil {
+ return err
}
- hw := &HorizontalReshardingWorkflow{
- keyspace: data.Keyspace,
- vtworkers: data.Vtworkers,
- rootUINode: rootNode,
- copySchemaUINode: &workflow.Node{
- Name: "CopySchemaShard",
- PathName: "copy_schema",
- },
- splitCloneUINode: &workflow.Node{
- Name: "SplitClone",
- PathName: "clone",
- },
- splitDiffUINode: &workflow.Node{
- Name: "SplitDiff",
- PathName: "diff",
- },
- migrateUINode: &workflow.Node{
- Name: "MigrateServedType",
- PathName: "migrate",
- },
- logger: logutil.NewMemoryLogger(),
+ migrateRdonlyTasks := hw.GetTasks(phaseMigrateRdonly)
+ migrateRdonlyRunner := NewParallelRunner(hw.ctx, hw.rootUINode, hw.checkpointWriter, migrateRdonlyTasks, hw.runMigrate, Sequential, hw.enableApprovals)
+ if err := migrateRdonlyRunner.Run(); err != nil {
+ return err
}
- hw.rootUINode.Children = []*workflow.Node{
- hw.copySchemaUINode,
- hw.splitCloneUINode,
- hw.splitDiffUINode,
- hw.migrateUINode,
+
+ migrateReplicaTasks := hw.GetTasks(phaseMigrateReplica)
+ migrateReplicaRunner := NewParallelRunner(hw.ctx, hw.rootUINode, hw.checkpointWriter, migrateReplicaTasks, hw.runMigrate, Sequential, hw.enableApprovals)
+ if err := migrateReplicaRunner.Run(); err != nil {
+ return err
}
- return hw, nil
+
+ migrateMasterTasks := hw.GetTasks(phaseMigrateMaster)
+ migrateMasterRunner := NewParallelRunner(hw.ctx, hw.rootUINode, hw.checkpointWriter, migrateMasterTasks, hw.runMigrate, Sequential, hw.enableApprovals)
+ if err := migrateMasterRunner.Run(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hw *HorizontalReshardingWorkflow) setUIMessage(message string) {
+ log.Infof("Horizontal resharding : %v.", message)
+ hw.rootUINode.Log = hw.logger.String()
+ hw.rootUINode.Message = message
+ hw.rootUINode.BroadcastChanges(false /* updateChildren */)
}
diff --git a/go/vt/workflow/resharding/horizontal_resharding_workflow_test.go b/go/vt/workflow/resharding/horizontal_resharding_workflow_test.go
index efc445e5e8f..1c29c9a52df 100644
--- a/go/vt/workflow/resharding/horizontal_resharding_workflow_test.go
+++ b/go/vt/workflow/resharding/horizontal_resharding_workflow_test.go
@@ -1,95 +1,119 @@
package resharding
import (
+ "context"
"flag"
"testing"
"github.com/golang/mock/gomock"
- "github.com/youtube/vitess/go/vt/logutil"
+ "github.com/youtube/vitess/go/vt/topo"
+ "github.com/youtube/vitess/go/vt/topo/memorytopo"
"github.com/youtube/vitess/go/vt/worker/fakevtworkerclient"
"github.com/youtube/vitess/go/vt/worker/vtworkerclient"
+ "github.com/youtube/vitess/go/vt/workflow"
"github.com/youtube/vitess/go/vt/wrangler"
+ // import the gRPC client implementation for tablet manager
+ _ "github.com/youtube/vitess/go/vt/vttablet/grpctmclient"
+
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
)
+var (
+ testKeyspace = "test_keyspace"
+ testVtworkers = "localhost:15032"
+)
+
+func init() {
+ Register()
+}
+
+// TestHorizontalResharding runs the happy path of HorizontalReshardingWorkflow.
func TestHorizontalResharding(t *testing.T) {
- // Create fake wrangler using mock interface, which is used for the unit test in steps CopySchema and MigratedServedType.
+ ctx := context.Background()
+
+ // Set up the mock wrangler. It is used for the CopySchema,
+ // WaitforFilteredReplication and Migrate phase.
ctrl := gomock.NewController(t)
defer ctrl.Finish()
- mockWranglerInterface := NewMockReshardingWrangler(ctrl)
+ mockWranglerInterface := setupMockWrangler(ctrl, testKeyspace)
+
+ // Set up the fakeworkerclient. It is used at SplitClone and SplitDiff phase.
+ fakeVtworkerClient := setupFakeVtworker(testKeyspace, testVtworkers)
+ vtworkerclient.RegisterFactory("fake", fakeVtworkerClient.FakeVtworkerClientFactory)
+ defer vtworkerclient.UnregisterFactoryForTest("fake")
+
+ // Initialize the topology.
+ ts := setupTopology(ctx, t, testKeyspace)
+ m := workflow.NewManager(ts)
+ // Run the manager in the background.
+ wg, _, cancel := startManager(m)
+ // Create the workflow.
+ uuid, err := m.Create(ctx, horizontalReshardingFactoryName, []string{"-keyspace=" + testKeyspace, "-vtworkers=" + testVtworkers, "-enable_approvals=false"})
+ if err != nil {
+ t.Fatalf("cannot create resharding workflow: %v", err)
+ }
+ // Inject the mock wranger into the workflow.
+ w, err := m.WorkflowForTesting(uuid)
+ if err != nil {
+ t.Fatalf("fail to get workflow from manager: %v", err)
+ }
+ hw := w.(*HorizontalReshardingWorkflow)
+ hw.wr = mockWranglerInterface
+
+ // Start the job.
+ if err := m.Start(ctx, uuid); err != nil {
+ t.Fatalf("cannot start resharding workflow: %v", err)
+ }
- // Create the workflow (ignore the node construction since we don't test the front-end part in this unit test).
- hw := &HorizontalReshardingWorkflow{
- keyspace: "test_keyspace",
- vtworkers: []string{"localhost:15032"},
- wr: mockWranglerInterface,
- logger: logutil.NewMemoryLogger(),
+ // Wait for the workflow to end.
+ m.Wait(ctx, uuid)
+ if err := verifyAllTasksDone(ctx, ts, uuid); err != nil {
+ t.Fatal(err)
}
- perShard := &PerShardHorizontalResharding{
- PerShardHorizontalReshardingData: PerShardHorizontalReshardingData{
- Keyspace: "test_keyspace",
- SourceShard: "0",
- DestinationShards: []string{"-80", "80-"},
- Vtworker: "localhost:15032",
- },
+ // Stop the manager.
+ if err := m.Stop(ctx, uuid); err != nil {
+ t.Fatalf("cannot stop resharding workflow: %v", err)
}
- perShard.parent = hw
- hw.subWorkflows = append(hw.subWorkflows, perShard)
+ cancel()
+ wg.Wait()
+}
+func setupFakeVtworker(keyspace, vtworkers string) *fakevtworkerclient.FakeVtworkerClient {
+ flag.Set("vtworker_client_protocol", "fake")
+ fakeVtworkerClient := fakevtworkerclient.NewFakeVtworkerClient()
+ fakeVtworkerClient.RegisterResultForAddr(vtworkers, []string{"SplitClone", "--min_healthy_rdonly_tablets=1", keyspace + "/0"}, "", nil)
+ fakeVtworkerClient.RegisterResultForAddr(vtworkers, []string{"SplitDiff", "--min_healthy_rdonly_tablets=1", keyspace + "/-80"}, "", nil)
+ fakeVtworkerClient.RegisterResultForAddr(vtworkers, []string{"SplitDiff", "--min_healthy_rdonly_tablets=1", keyspace + "/80-"}, "", nil)
+ return fakeVtworkerClient
+}
+
+func setupMockWrangler(ctrl *gomock.Controller, keyspace string) *MockReshardingWrangler {
+ mockWranglerInterface := NewMockReshardingWrangler(ctrl)
// Set the expected behaviors for mock wrangler.
- mockWranglerInterface.EXPECT().CopySchemaShardFromShard(
- hw.ctx,
- nil, /* tableArray*/
- nil, /* excludeTableArray */
- true, /*includeViews*/
- "test_keyspace",
- "0",
- "test_keyspace",
- "-80",
- wrangler.DefaultWaitSlaveTimeout).Return(nil)
-
- mockWranglerInterface.EXPECT().CopySchemaShardFromShard(
- hw.ctx,
- nil, /* tableArray*/
- nil, /* excludeTableArray */
- true, /*includeViews*/
- "test_keyspace",
- "0",
- "test_keyspace",
- "80-",
- wrangler.DefaultWaitSlaveTimeout).Return(nil)
-
- mockWranglerInterface.EXPECT().WaitForFilteredReplication(hw.ctx, "test_keyspace", "-80", wrangler.DefaultWaitForFilteredReplicationMaxDelay).Return(nil)
- mockWranglerInterface.EXPECT().WaitForFilteredReplication(hw.ctx, "test_keyspace", "80-", wrangler.DefaultWaitForFilteredReplicationMaxDelay).Return(nil)
+ mockWranglerInterface.EXPECT().CopySchemaShardFromShard(gomock.Any(), nil /* tableArray*/, nil /* excludeTableArray */, true /*includeViews*/, keyspace, "0", keyspace, "-80", wrangler.DefaultWaitSlaveTimeout).Return(nil)
+ mockWranglerInterface.EXPECT().CopySchemaShardFromShard(gomock.Any(), nil /* tableArray*/, nil /* excludeTableArray */, true /*includeViews*/, keyspace, "0", keyspace, "80-", wrangler.DefaultWaitSlaveTimeout).Return(nil)
+
+ mockWranglerInterface.EXPECT().WaitForFilteredReplication(gomock.Any(), keyspace, "-80", wrangler.DefaultWaitForFilteredReplicationMaxDelay).Return(nil)
+ mockWranglerInterface.EXPECT().WaitForFilteredReplication(gomock.Any(), keyspace, "80-", wrangler.DefaultWaitForFilteredReplicationMaxDelay).Return(nil)
servedTypeParams := []topodatapb.TabletType{topodatapb.TabletType_RDONLY,
topodatapb.TabletType_REPLICA,
topodatapb.TabletType_MASTER}
for _, servedType := range servedTypeParams {
- mockWranglerInterface.EXPECT().MigrateServedTypes(
- hw.ctx,
- "test_keyspace",
- "0",
- nil, /* cells */
- servedType,
- false, /* reverse */
- false, /* skipReFreshState */
- wrangler.DefaultFilteredReplicationWaitTime).Return(nil)
+ mockWranglerInterface.EXPECT().MigrateServedTypes(gomock.Any(), keyspace, "0", nil /* cells */, servedType, false /* reverse */, false /* skipReFreshState */, wrangler.DefaultFilteredReplicationWaitTime).Return(nil)
}
+ return mockWranglerInterface
+}
- // Create fakeworkerclient, which is used for the unit test in steps SplitClone and SplitDiff.
- fakeVtworkerClient := fakevtworkerclient.NewFakeVtworkerClient()
- vtworkerclient.RegisterFactory("fake", fakeVtworkerClient.FakeVtworkerClientFactory)
- defer vtworkerclient.UnregisterFactoryForTest("fake")
- flag.Set("vtworker_client_protocol", "fake")
- fakeVtworkerClient.RegisterResultForAddr("localhost:15032", []string{"SplitClone", "--min_healthy_rdonly_tablets=1", "test_keyspace/0"}, "", nil)
- fakeVtworkerClient.RegisterResultForAddr("localhost:15032", []string{"SplitDiff", "--min_healthy_rdonly_tablets=1", "test_keyspace/-80"}, "", nil)
- fakeVtworkerClient.RegisterResultForAddr("localhost:15032", []string{"SplitDiff", "--min_healthy_rdonly_tablets=1", "test_keyspace/80-"}, "", nil)
-
- // Test the execution of horizontal resharding.
- if err := hw.executeWorkflow(); err != nil {
- t.Errorf("%s: Horizontal resharding workflow should not fail", err)
+func setupTopology(ctx context.Context, t *testing.T, keyspace string) topo.Server {
+ ts := memorytopo.NewServer("cell")
+ if err := ts.CreateKeyspace(ctx, keyspace, &topodatapb.Keyspace{}); err != nil {
+ t.Fatalf("CreateKeyspace: %v", err)
}
+ ts.CreateShard(ctx, keyspace, "0")
+ ts.CreateShard(ctx, keyspace, "-80")
+ ts.CreateShard(ctx, keyspace, "80-")
+ return ts
}
diff --git a/go/vt/workflow/resharding/mock_resharding_wrangler.go b/go/vt/workflow/resharding/mock_resharding_wrangler_test.go
similarity index 100%
rename from go/vt/workflow/resharding/mock_resharding_wrangler.go
rename to go/vt/workflow/resharding/mock_resharding_wrangler_test.go
diff --git a/go/vt/workflow/resharding/parallel_runner.go b/go/vt/workflow/resharding/parallel_runner.go
new file mode 100644
index 00000000000..2adc0786355
--- /dev/null
+++ b/go/vt/workflow/resharding/parallel_runner.go
@@ -0,0 +1,403 @@
+package resharding
+
+import (
+ "errors"
+ "fmt"
+ "path"
+ "strings"
+ "sync"
+
+ log "github.com/golang/glog"
+ "golang.org/x/net/context"
+
+ "github.com/youtube/vitess/go/vt/logutil"
+ "github.com/youtube/vitess/go/vt/workflow"
+
+ workflowpb "github.com/youtube/vitess/go/vt/proto/workflow"
+)
+
+type level int
+
+const (
+ // Sequential means that the tasks will run sequentially.
+ Sequential level = iota
+ //Parallel means that the tasks will run in parallel.
+ Parallel
+)
+
+const (
+ actionNameRetry = "Retry"
+ actionNameApproveFirstTask = "Approve first shard"
+ actionNameApproveFirstTaskDone = "First shard approved"
+ actionNameApproveRemainingTasks = "Approve remaining shards"
+ actionNameApproveRemainingTasksDone = "Remaining shards approved"
+)
+
+const taskFinishedMessage = "task finished"
+
+// ParallelRunner is used to control executing tasks concurrently.
+// Each phase has its own ParallelRunner object.
+type ParallelRunner struct {
+ ctx context.Context
+ uiLogger *logutil.MemoryLogger
+ rootUINode *workflow.Node
+ phaseUINode *workflow.Node
+ checkpointWriter *CheckpointWriter
+ // tasks stores selected tasks for the phase with expected execution order.
+ tasks []*workflowpb.Task
+ concurrencyLevel level
+ executeFunc func(context.Context, *workflowpb.Task) error
+ enableApprovals bool
+
+ // mu is used to protect the access to retryActionRegistry, channels for task
+ // approvals and serialize UI node changes.
+ mu sync.Mutex
+ // retryActionRegistry stores the data for retry actions.
+ // Each task can retrieve the channel for synchronizing retrying
+ // through its UI node path.
+ retryActionRegistry map[string]chan struct{}
+ firstTaskApproved chan struct{}
+ remainingTasksApproved chan struct{}
+}
+
+// NewParallelRunner returns a new ParallelRunner.
+func NewParallelRunner(ctx context.Context, rootUINode *workflow.Node, cp *CheckpointWriter, tasks []*workflowpb.Task, executeFunc func(context.Context, *workflowpb.Task) error, concurrencyLevel level, enableApprovals bool) *ParallelRunner {
+ if len(tasks) < 1 {
+ log.Fatal(errors.New("BUG: No tasks passed into ParallelRunner"))
+ }
+
+ phaseID := path.Dir(tasks[0].Id)
+ phaseUINode, err := rootUINode.GetChildByPath(phaseID)
+ if err != nil {
+ panic(fmt.Errorf("BUG: nodepath %v not found", phaseID))
+ }
+
+ p := &ParallelRunner{
+ ctx: ctx,
+ uiLogger: logutil.NewMemoryLogger(),
+ rootUINode: rootUINode,
+ phaseUINode: phaseUINode,
+ checkpointWriter: cp,
+ tasks: tasks,
+ executeFunc: executeFunc,
+ concurrencyLevel: concurrencyLevel,
+ retryActionRegistry: make(map[string]chan struct{}),
+ enableApprovals: enableApprovals,
+ }
+
+ if p.enableApprovals {
+ p.initApprovalActions()
+ }
+
+ return p
+}
+
+// Run is the entry point for controling task executions.
+func (p *ParallelRunner) Run() error {
+ // default value is 0. The task will not run in this case.
+ var parallelNum int
+ switch p.concurrencyLevel {
+ case Sequential:
+ parallelNum = 1
+ case Parallel:
+ parallelNum = len(p.tasks)
+ default:
+ panic(fmt.Sprintf("BUG: Invalid concurrency level: %v", p.concurrencyLevel))
+ }
+ // sem is a channel used to control the level of concurrency.
+ sem := make(chan bool, parallelNum)
+ wg := sync.WaitGroup{}
+ for i, task := range p.tasks {
+ if isTaskSucceeded(task) {
+ continue
+ }
+
+ sem <- true
+ wg.Add(1)
+ if p.enableApprovals && !isTaskRunning(task) {
+ p.waitForApproval(i)
+ }
+
+ go func(t *workflowpb.Task) {
+ p.setUIMessage(fmt.Sprintf("Launch task: %v.", t.Id))
+ defer func() { <-sem }()
+ defer wg.Done()
+ p.executeTask(t)
+ }(task)
+ }
+ wg.Wait()
+
+ if p.enableApprovals {
+ p.clearPhaseActions()
+ }
+ // TODO(yipeiw): collect error message from tasks.Error instead,
+ // s.t. if the task is retried, we can update the error
+ return nil
+}
+
+func (p *ParallelRunner) executeTask(t *workflowpb.Task) {
+ taskID := t.Id
+ for {
+ // Update the task status to running in the checkpoint.
+ if updateErr := p.checkpointWriter.UpdateTask(taskID, workflowpb.TaskState_TaskRunning, nil); updateErr != nil {
+ // Only logging the error rather then passing it to ErrorRecorder.
+ // Errors in ErrorRecorder will lead to the stop of a workflow. We
+ // don't want to stop the workflow if only checkpointing fails.
+ log.Errorf("%v", updateErr)
+ }
+ err := p.executeFunc(p.ctx, t)
+ // Update the task status to done in the checkpoint.
+ if updateErr := p.checkpointWriter.UpdateTask(taskID, workflowpb.TaskState_TaskDone, err); updateErr != nil {
+ log.Errorf("%v", updateErr)
+ }
+
+ // The function returns if the task is executed successfully.
+ if err == nil {
+ p.setFinishUIMessage(t.Id)
+ p.setUIMessage(fmt.Sprintf("Task %v has finished.", t.Id))
+ return
+ }
+ // When task fails, first check whether the context is canceled.
+ // If so, return right away. If not, enable the retry action.
+ select {
+ case <-p.ctx.Done():
+ return
+ default:
+ }
+ retryChannel := p.addRetryAction(taskID)
+
+ // Block the task execution until the retry action is triggered
+ // or the context is canceled.
+ select {
+ case <-retryChannel:
+ continue
+ case <-p.ctx.Done():
+ return
+ }
+ }
+}
+
+// Action handles retrying, approval of the first task and approval of the
+// remaining tasks actions. It implements the interface ActionListener.
+func (p *ParallelRunner) Action(ctx context.Context, path, name string) error {
+ switch name {
+ case actionNameRetry:
+ // Extract the path relative to the root node.
+ parts := strings.Split(path, "/")
+ taskID := strings.Join(parts[2:], "/")
+ return p.triggerRetry(taskID)
+ case actionNameApproveFirstTask:
+ p.mu.Lock()
+ defer p.mu.Unlock()
+
+ if p.firstTaskApproved != nil {
+ close(p.firstTaskApproved)
+ p.firstTaskApproved = nil
+ return nil
+ }
+ return fmt.Errorf("ignored the approval action %v because no pending approval found: it might be already approved before", actionNameApproveFirstTask)
+ case actionNameApproveRemainingTasks:
+ p.mu.Lock()
+ defer p.mu.Unlock()
+
+ if p.remainingTasksApproved != nil {
+ close(p.remainingTasksApproved)
+ p.remainingTasksApproved = nil
+ return nil
+ }
+ return fmt.Errorf("ignored the approval action %v because no pending approval found: it might be already approved before", actionNameApproveRemainingTasks)
+ default:
+ return fmt.Errorf("Unknown action: %v", name)
+ }
+}
+
+func (p *ParallelRunner) triggerRetry(taskID string) error {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+
+ // Unregister the retry channel.
+ retryChannel, ok := p.retryActionRegistry[taskID]
+ if !ok {
+ return fmt.Errorf("Unregistered action for node: %v", taskID)
+ }
+ delete(p.retryActionRegistry, taskID)
+
+ // Disable the retry action and synchronize for retrying the job.
+ node, err := p.rootUINode.GetChildByPath(taskID)
+ if err != nil {
+ panic(fmt.Sprintf("BUG: node on child path %v not found", taskID))
+ }
+ if len(node.Actions) == 0 {
+ panic(fmt.Sprintf("BUG: node actions should not be empty"))
+ }
+ node.Actions = []*workflow.Action{}
+ node.BroadcastChanges(false /* updateChildren */)
+ close(retryChannel)
+ return nil
+}
+
+func (p *ParallelRunner) addRetryAction(taskID string) chan struct{} {
+ node, err := p.rootUINode.GetChildByPath(taskID)
+ if err != nil {
+ panic(fmt.Sprintf("BUG: node on child path %v not found", taskID))
+ }
+
+ p.mu.Lock()
+ defer p.mu.Unlock()
+
+ // Register the channel for synchronizing retrying job.
+ if _, ok := p.retryActionRegistry[taskID]; ok {
+ panic(fmt.Sprintf("BUG: duplicate retry action for node: %v", taskID))
+ }
+ retryChannel := make(chan struct{})
+ p.retryActionRegistry[taskID] = retryChannel
+
+ // Enable retry action on the node.
+ retryAction := &workflow.Action{
+ Name: actionNameRetry,
+ State: workflow.ActionStateEnabled,
+ Style: workflow.ActionStyleWaiting,
+ }
+ node.Actions = []*workflow.Action{retryAction}
+ node.Listener = p
+ node.BroadcastChanges(false /* updateChildren */)
+ return retryChannel
+}
+
+func (p *ParallelRunner) initApprovalActions() {
+ // If all tasks have succeeded, no action is added.
+ allDone := true
+ for _, task := range p.tasks {
+ if !isTaskSucceeded(task) {
+ allDone = false
+ break
+ }
+ }
+ if allDone {
+ return
+ }
+
+ actionFirstApproval := &workflow.Action{
+ Name: actionNameApproveFirstTask,
+ State: workflow.ActionStateDisabled,
+ Style: workflow.ActionStyleTriggered,
+ }
+ if isTaskSucceeded(p.tasks[0]) || isTaskRunning(p.tasks[0]) {
+ // Reset the action name if the first task is running or has succeeded.
+ actionFirstApproval.Name = actionNameApproveFirstTaskDone
+ }
+
+ p.mu.Lock()
+ defer p.mu.Unlock()
+
+ p.phaseUINode.Actions = []*workflow.Action{actionFirstApproval}
+ // Add the approval action for the remaining tasks,
+ // if there are more than one tasks.
+ if len(p.tasks) > 1 {
+ actionRemainingTasksApproval := &workflow.Action{
+ Name: actionNameApproveRemainingTasks,
+ State: workflow.ActionStateDisabled,
+ Style: workflow.ActionStyleTriggered,
+ }
+ if isTaskSucceeded(p.tasks[1]) || isTaskRunning(p.tasks[1]) {
+ // Reset the action name if the second task is running or has succeeded.
+ actionRemainingTasksApproval.Name = actionNameApproveRemainingTasksDone
+ }
+ p.phaseUINode.Actions = append(p.phaseUINode.Actions, actionRemainingTasksApproval)
+ }
+ p.phaseUINode.Listener = p
+ p.phaseUINode.BroadcastChanges(false /* updateChildren */)
+}
+
+func isTaskSucceeded(task *workflowpb.Task) bool {
+ if task.State == workflowpb.TaskState_TaskDone && task.Error == "" {
+ return true
+ }
+ return false
+}
+
+func isTaskRunning(task *workflowpb.Task) bool {
+ if task.State == workflowpb.TaskState_TaskRunning {
+ return true
+ }
+ return false
+}
+
+func (p *ParallelRunner) waitForApproval(taskIndex int) {
+ if taskIndex == 0 {
+ p.mu.Lock()
+ p.firstTaskApproved = make(chan struct{})
+ firstTaskApproved := p.firstTaskApproved
+ p.updateApprovalActionLocked(0, actionNameApproveFirstTask, workflow.ActionStateEnabled, workflow.ActionStyleWaiting)
+ p.mu.Unlock()
+
+ p.setUIMessage(fmt.Sprintf("approve first task enabled: %v", taskIndex))
+
+ select {
+ case <-firstTaskApproved:
+ p.mu.Lock()
+ defer p.mu.Unlock()
+ p.updateApprovalActionLocked(0, actionNameApproveFirstTaskDone, workflow.ActionStateDisabled, workflow.ActionStyleTriggered)
+ case <-p.ctx.Done():
+ return
+ }
+ } else if taskIndex == 1 {
+ p.mu.Lock()
+ p.remainingTasksApproved = make(chan struct{})
+
+ remainingTasksApproved := p.remainingTasksApproved
+ p.updateApprovalActionLocked(1, actionNameApproveRemainingTasks, workflow.ActionStateEnabled, workflow.ActionStyleWaiting)
+ p.mu.Unlock()
+
+ p.setUIMessage(fmt.Sprintf("approve remaining task enabled: %v", taskIndex))
+
+ select {
+ case <-remainingTasksApproved:
+ p.mu.Lock()
+ defer p.mu.Unlock()
+ p.updateApprovalActionLocked(1, actionNameApproveRemainingTasksDone, workflow.ActionStateDisabled, workflow.ActionStyleTriggered)
+ case <-p.ctx.Done():
+ return
+ }
+ }
+}
+
+func (p *ParallelRunner) updateApprovalActionLocked(index int, name string, state workflow.ActionState, style workflow.ActionStyle) {
+ action := p.phaseUINode.Actions[index]
+ action.Name = name
+ action.State = state
+ action.Style = style
+ p.phaseUINode.BroadcastChanges(false /* updateChildren */)
+}
+
+func (p *ParallelRunner) clearPhaseActions() {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+
+ if len(p.phaseUINode.Actions) != 0 {
+ p.phaseUINode.Actions = []*workflow.Action{}
+ p.phaseUINode.BroadcastChanges(false /* updateChildren */)
+ }
+}
+
+func (p *ParallelRunner) setFinishUIMessage(taskID string) {
+ taskNode, err := p.rootUINode.GetChildByPath(taskID)
+ if err != nil {
+ panic(fmt.Errorf("BUG: nodepath %v not found", taskID))
+ }
+
+ p.mu.Lock()
+ defer p.mu.Unlock()
+ taskNode.Message = taskFinishedMessage
+ taskNode.BroadcastChanges(false /* updateChildren */)
+}
+
+func (p *ParallelRunner) setUIMessage(message string) {
+ p.uiLogger.Infof(message)
+
+ p.mu.Lock()
+ defer p.mu.Unlock()
+ p.phaseUINode.Log = p.uiLogger.String()
+ p.phaseUINode.Message = message
+ p.phaseUINode.BroadcastChanges(false /* updateChildren */)
+}
diff --git a/go/vt/workflow/resharding/parallel_runner_test.go b/go/vt/workflow/resharding/parallel_runner_test.go
new file mode 100644
index 00000000000..88b506b7e1d
--- /dev/null
+++ b/go/vt/workflow/resharding/parallel_runner_test.go
@@ -0,0 +1,756 @@
+package resharding
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "path"
+ "reflect"
+ "sync"
+ "testing"
+
+ "github.com/golang/protobuf/proto"
+ "github.com/youtube/vitess/go/vt/topo"
+ "github.com/youtube/vitess/go/vt/topo/memorytopo"
+ "github.com/youtube/vitess/go/vt/workflow"
+
+ workflowpb "github.com/youtube/vitess/go/vt/proto/workflow"
+)
+
+func TestParallelRunner(t *testing.T) {
+ ctx := context.Background()
+ ts := memorytopo.NewServer("cell")
+
+ m, uuid, wg, cancel, err := setupTestWorkflow(ctx, ts, false /* enableApprovals*/, false /* retry */)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Start the job
+ if err := m.Start(ctx, uuid); err != nil {
+ t.Fatalf("cannot start testworkflow: %v", err)
+ }
+
+ // Wait for the workflow to end.
+ m.Wait(ctx, uuid)
+
+ if err := verifyAllTasksDone(ctx, ts, uuid); err != nil {
+ t.Fatal(err)
+ }
+ // Stop the manager.
+ if err := m.Stop(ctx, uuid); err != nil {
+ t.Fatalf("cannot stop testworkflow: %v", err)
+ }
+ cancel()
+ wg.Wait()
+}
+
+func TestParallelRunnerApproval(t *testing.T) {
+ ctx := context.Background()
+ ts := memorytopo.NewServer("cell")
+
+ m, uuid, wg, cancel, err := setupTestWorkflow(ctx, ts, true /* enableApprovals*/, false /* retry */)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ notifications, index, err := setupNotifications(m)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer m.NodeManager().CloseWatcher(index)
+ defer close(notifications)
+
+ // Start the job
+ if err := m.Start(ctx, uuid); err != nil {
+ t.Fatalf("cannot start testworkflow: %v", err)
+ }
+
+ if err := checkUIChangeFromNoneStarted(m, uuid, notifications); err != nil {
+ t.Fatal(err)
+ }
+
+ // Wait for the workflow to end.
+ m.Wait(context.Background(), uuid)
+
+ if err := verifyAllTasksDone(ctx, ts, uuid); err != nil {
+ t.Fatal(err)
+ }
+ // Stop the manager.
+ if err := m.Stop(ctx, uuid); err != nil {
+ t.Fatalf("cannot stop testworkflow: %v", err)
+ }
+ cancel()
+ wg.Wait()
+}
+
+func TestParallelRunnerApprovalFromFirstDone(t *testing.T) {
+ ctx := context.Background()
+ ts := memorytopo.NewServer("cell")
+ m, uuid, wg, cancel, err := setupTestWorkflow(ctx, ts, true /* enableApprovals*/, false /* retry */)
+ if err != nil {
+ t.Fatal(err)
+ }
+ tw, err := testworkflow(m, uuid)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Change the checkpoint of the workflow to let task1 succeed
+ // before starting the workflow.
+ wi, err := m.WorkflowInfoForTesting(uuid)
+ if err != nil {
+ t.Fatalf("fail to get workflow info from manager: %v", err)
+ }
+ checkpointWriter := NewCheckpointWriter(ts, tw.checkpoint, wi)
+ task1ID := createTestTaskID(phaseSimple, 0)
+ checkpointWriter.UpdateTask(task1ID, workflowpb.TaskState_TaskDone, nil)
+
+ notifications, index, err := setupNotifications(m)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer m.NodeManager().CloseWatcher(index)
+ defer close(notifications)
+
+ // Start the job
+ if err := m.Start(ctx, uuid); err != nil {
+ t.Fatalf("cannot start testworkflow: %v", err)
+ }
+ if err := checkUIChangeFirstApproved(m, uuid, notifications); err != nil {
+ t.Fatal(err)
+ }
+ // Wait for the workflow to end.
+ m.Wait(ctx, uuid)
+
+ if err := verifyAllTasksDone(ctx, ts, uuid); err != nil {
+ t.Fatal(err)
+ }
+ // Stop the manager.
+ if err := m.Stop(ctx, uuid); err != nil {
+ t.Fatalf("cannot stop testworkflow: %v", err)
+ }
+ cancel()
+ wg.Wait()
+}
+
+func TestParallelRunnerApprovalFromFirstRunning(t *testing.T) {
+ ctx := context.Background()
+ ts := memorytopo.NewServer("cell")
+ m, uuid, wg, cancel, err := setupTestWorkflow(ctx, ts, true /* enableApprovals*/, false /* retry */)
+ if err != nil {
+ t.Fatal(err)
+ }
+ tw, err := testworkflow(m, uuid)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Change the checkpoint of the workflow to let task1 succeeded
+ // before starting the workflow.
+ wi, err := m.WorkflowInfoForTesting(uuid)
+ if err != nil {
+ t.Fatalf("fail to get workflow info from manager: %v", err)
+ }
+ checkpointWriter := NewCheckpointWriter(ts, tw.checkpoint, wi)
+ task1ID := createTestTaskID(phaseSimple, 0)
+ checkpointWriter.UpdateTask(task1ID, workflowpb.TaskState_TaskRunning, nil)
+
+ notifications, index, err := setupNotifications(m)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer m.NodeManager().CloseWatcher(index)
+ defer close(notifications)
+
+ // Start the job
+ if err := m.Start(ctx, uuid); err != nil {
+ t.Fatalf("cannot start testworkflow: %v", err)
+ }
+
+ if err := checkUIChangeFirstApproved(m, uuid, notifications); err != nil {
+ t.Fatal(err)
+ }
+
+ // Wait for the workflow to end.
+ m.Wait(ctx, uuid)
+
+ if err := verifyAllTasksDone(ctx, ts, uuid); err != nil {
+ t.Fatal(err)
+ }
+ // Stop the manager.
+ if err := m.Stop(ctx, uuid); err != nil {
+ t.Fatalf("cannot stop testworkflow: %v", err)
+ }
+ cancel()
+ wg.Wait()
+}
+
+func TestParallelRunnerApprovalFromFirstDoneSecondRunning(t *testing.T) {
+ ctx := context.Background()
+ ts := memorytopo.NewServer("cell")
+ m, uuid, wg, cancel, err := setupTestWorkflow(ctx, ts, true /* enableApprovals*/, false /* retry */)
+ if err != nil {
+ t.Fatal(err)
+ }
+ tw, err := testworkflow(m, uuid)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Change the checkpoint of the workflow to let task1 succeeded
+ // before starting the workflow.
+ wi, err := m.WorkflowInfoForTesting(uuid)
+ if err != nil {
+ t.Fatalf("fail to get workflow info from manager: %v", err)
+ }
+ checkpointWriter := NewCheckpointWriter(ts, tw.checkpoint, wi)
+ task1ID := createTestTaskID(phaseSimple, 0)
+ checkpointWriter.UpdateTask(task1ID, workflowpb.TaskState_TaskDone, nil)
+ task2ID := createTestTaskID(phaseSimple, 1)
+ checkpointWriter.UpdateTask(task2ID, workflowpb.TaskState_TaskRunning, nil)
+
+ notifications, index, err := setupNotifications(m)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer m.NodeManager().CloseWatcher(index)
+ defer close(notifications)
+
+ // Start the job
+ if err := m.Start(ctx, uuid); err != nil {
+ t.Fatalf("cannot start testworkflow: %v", err)
+ }
+
+ if err := checkUIChangeAllApproved(notifications); err != nil {
+ t.Fatal(err)
+ }
+
+ // Wait for the workflow to end.
+ m.Wait(ctx, uuid)
+
+ if err := verifyAllTasksDone(ctx, ts, uuid); err != nil {
+ t.Fatal(err)
+ }
+ // Stop the manager.
+ if err := m.Stop(ctx, uuid); err != nil {
+ t.Fatalf("cannot stop testworkflow: %v", err)
+ }
+ cancel()
+ wg.Wait()
+}
+
+func TestParallelRunnerApprovalFirstRunningSecondRunning(t *testing.T) {
+ ctx := context.Background()
+ ts := memorytopo.NewServer("cell")
+ m, uuid, wg, cancel, err := setupTestWorkflow(ctx, ts, true /* enableApprovals*/, false /* retry */)
+ if err != nil {
+ t.Fatal(err)
+ }
+ tw, err := testworkflow(m, uuid)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Change the checkpoint in TestWorkflow to let task1 succeeded
+ // before starting the workflow.
+ wi, err := m.WorkflowInfoForTesting(uuid)
+ if err != nil {
+ t.Fatalf("fail to get workflow info from manager: %v", err)
+ }
+ checkpointWriter := NewCheckpointWriter(ts, tw.checkpoint, wi)
+ task1ID := createTestTaskID(phaseSimple, 0)
+ checkpointWriter.UpdateTask(task1ID, workflowpb.TaskState_TaskRunning, nil)
+ task2ID := createTestTaskID(phaseSimple, 1)
+ checkpointWriter.UpdateTask(task2ID, workflowpb.TaskState_TaskRunning, nil)
+
+ notifications, index, err := setupNotifications(m)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer m.NodeManager().CloseWatcher(index)
+ defer close(notifications)
+
+ // Start the job
+ if err := m.Start(ctx, uuid); err != nil {
+ t.Fatalf("cannot start testworkflow: %v", err)
+ }
+
+ if err := checkUIChangeAllApproved(notifications); err != nil {
+ t.Fatal(err)
+ }
+
+ // Wait for the workflow to end.
+ m.Wait(ctx, uuid)
+
+ if err := verifyAllTasksDone(ctx, ts, uuid); err != nil {
+ t.Fatal(err)
+ }
+
+ // Stop the manager.
+ if err := m.Stop(ctx, uuid); err != nil {
+ t.Fatalf("cannot stop testworkflow: %v", err)
+ }
+ cancel()
+ wg.Wait()
+}
+
+func TestParallelRunnerApprovalFromAllDone(t *testing.T) {
+ ctx := context.Background()
+ ts := memorytopo.NewServer("cell")
+ m, uuid, wg, cancel, err := setupTestWorkflow(ctx, ts, true /* enableApprovals*/, false /* retry */)
+ if err != nil {
+ t.Fatal(err)
+ }
+ tw, err := testworkflow(m, uuid)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Change the checkpoint in TestWorkflow to let all tasks succeeded
+ // before starting the workflow.
+ wi, err := m.WorkflowInfoForTesting(uuid)
+ if err != nil {
+ t.Fatalf("fail to get workflow info from manager: %v", err)
+ }
+ checkpointWriter := NewCheckpointWriter(ts, tw.checkpoint, wi)
+ task1ID := createTestTaskID(phaseSimple, 0)
+ checkpointWriter.UpdateTask(task1ID, workflowpb.TaskState_TaskDone, nil)
+ task2ID := createTestTaskID(phaseSimple, 1)
+ checkpointWriter.UpdateTask(task2ID, workflowpb.TaskState_TaskDone, nil)
+
+ if err := verifyAllTasksDone(ctx, ts, uuid); err != nil {
+ t.Fatal(err)
+ }
+
+ notifications, index, err := setupNotifications(m)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer m.NodeManager().CloseWatcher(index)
+
+ // Start the job
+ if err := m.Start(ctx, uuid); err != nil {
+ t.Fatalf("cannot start testworkflow: %v", err)
+ }
+
+ // Wait for the workflow to end.
+ m.Wait(ctx, uuid)
+ close(notifications)
+ // Check notification about phase node if exists, make sure no actions added.
+
+ if message, err := checkNoActions(notifications, string(phaseSimple)); err != nil {
+ t.Fatalf("there should be no actions for node %v: %v, %v", phaseSimple, message, err)
+ }
+
+ if err := verifyAllTasksDone(ctx, ts, uuid); err != nil {
+ t.Fatal(err)
+ }
+ // Stop the manager.
+ if err := m.Stop(ctx, uuid); err != nil {
+ t.Fatalf("cannot stop testworkflow: %v", err)
+ }
+ cancel()
+ wg.Wait()
+}
+
+func TestParallelRunnerRetry(t *testing.T) {
+ // Tasks in the workflow are forced to fail at the first attempt. Then we
+ // retry task1, after it is finished successfully, we retry task2.
+ ctx := context.Background()
+ ts := memorytopo.NewServer("cell")
+ m, uuid, wg, cancel, err := setupTestWorkflow(ctx, ts, false /* enableApprovals*/, true /* retry */)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ notifications, index, err := setupNotifications(m)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer m.NodeManager().CloseWatcher(index)
+ defer close(notifications)
+
+ // Start the job
+ if err := m.Start(ctx, uuid); err != nil {
+ t.Fatalf("cannot start testworkflow: %v", err)
+ }
+
+ task1ID := createTestTaskID(phaseSimple, 0)
+ task2ID := createTestTaskID(phaseSimple, 1)
+ task1Node := &workflow.Node{
+ PathName: "0",
+ Actions: []*workflow.Action{
+ {
+ Name: actionNameRetry,
+ State: workflow.ActionStateEnabled,
+ Style: workflow.ActionStyleWaiting,
+ },
+ },
+ }
+ task2Node := &workflow.Node{
+ PathName: "1",
+ Actions: []*workflow.Action{
+ {
+ Name: actionNameRetry,
+ State: workflow.ActionStateEnabled,
+ Style: workflow.ActionStyleWaiting,
+ },
+ },
+ }
+
+ // Wait for retry actions enabled after tasks failed at the first attempt.
+ if err := consumeNotificationsUntil(notifications, task1Node, task2Node); err != nil {
+ t.Fatalf("Should get expected update of nodes: %v", task1Node)
+ }
+ if err := verifyTask(context.Background(), ts, uuid, task1ID, workflowpb.TaskState_TaskDone, errMessage); err != nil {
+ t.Errorf("verify task %v failed: %v", task1ID, err)
+ }
+ if err := verifyTask(context.Background(), ts, uuid, task2ID, workflowpb.TaskState_TaskDone, errMessage); err != nil {
+ t.Errorf("verify task %v failed: %v", task2ID, err)
+ }
+
+ // Retry task1. Task1 is launched after removing actions.
+ if err := triggerAction(ctx, m, path.Join("/", uuid, task1ID), actionNameRetry); err != nil {
+ t.Fatal(err)
+ }
+ // Check the retry action is removed.
+ task1Node.Actions = []*workflow.Action{}
+ if err := consumeNotificationsUntil(notifications, task1Node); err != nil {
+ t.Fatalf("Should get expected update of nodes: %v", task1Node)
+ }
+ // Verify task1 has succeed.
+ if err := waitForFinished(notifications, "0", taskFinishedMessage); err != nil {
+ t.Fatal(err)
+ }
+ if err := verifyTask(context.Background(), ts, uuid, task1ID, workflowpb.TaskState_TaskDone, ""); err != nil {
+ t.Errorf("verify task %v failed: %v", task1ID, err)
+ }
+
+ // Retry task2
+ if err := triggerAction(ctx, m, path.Join("/", uuid, task2ID), actionNameRetry); err != nil {
+ t.Fatal(err)
+ }
+ // Check the retry action is removed. Task2 is launched after removing actions.
+ task2Node.Actions = []*workflow.Action{}
+ if err := consumeNotificationsUntil(notifications, task2Node); err != nil {
+ t.Fatalf("Should get expected update of nodes: %v", task2Node)
+ }
+ // Verify task2 has succeed.
+ if err := waitForFinished(notifications, "1", taskFinishedMessage); err != nil {
+ t.Fatal(err)
+ }
+ if err := verifyTask(context.Background(), ts, uuid, task2ID, workflowpb.TaskState_TaskDone, ""); err != nil {
+ t.Errorf("verify task %v failed: %v", task2ID, err)
+ }
+
+ // Wait for the workflow to end.
+ m.Wait(ctx, uuid)
+
+ if err := verifyAllTasksDone(ctx, ts, uuid); err != nil {
+ t.Fatal(err)
+ }
+ // Stop the manager.
+ if err := m.Stop(ctx, uuid); err != nil {
+ t.Fatalf("cannot stop testworkflow: %v", err)
+ }
+ cancel()
+ wg.Wait()
+}
+
+func setupTestWorkflow(ctx context.Context, ts topo.Server, enableApprovals, retry bool) (*workflow.Manager, string, *sync.WaitGroup, context.CancelFunc, error) {
+ m := workflow.NewManager(ts)
+ // Run the manager in the background.
+ wg, _, cancel := startManager(m)
+
+ // Create a testworkflow.
+ enableApprovalsFlag := fmt.Sprintf("-enable_approvals=%v", enableApprovals)
+ retryFlag := fmt.Sprintf("-retry=%v", retry)
+ uuid, err := m.Create(ctx, testWorkflowFactoryName, []string{retryFlag, "-count=2", enableApprovalsFlag})
+ if err != nil {
+ return nil, "", nil, nil, fmt.Errorf("cannot create testworkflow: %v", err)
+ }
+
+ return m, uuid, wg, cancel, nil
+}
+
+func testworkflow(m *workflow.Manager, uuid string) (*TestWorkflow, error) {
+ w, err := m.WorkflowForTesting(uuid)
+ if err != nil {
+ return nil, fmt.Errorf("fail to get workflow from manager: %v", err)
+ }
+ tw := w.(*TestWorkflow)
+ return tw, nil
+}
+
+func startManager(m *workflow.Manager) (*sync.WaitGroup, context.Context, context.CancelFunc) {
+ // Run the manager in the background.
+ ctx, cancel := context.WithCancel(context.Background())
+ wg := &sync.WaitGroup{}
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ m.Run(ctx)
+ }()
+
+ m.WaitUntilRunning()
+ return wg, ctx, cancel
+}
+
+func triggerAction(ctx context.Context, m *workflow.Manager, nodePath, actionName string) error {
+ return m.NodeManager().Action(ctx, &workflow.ActionParameters{
+ Path: nodePath,
+ Name: actionName,
+ })
+}
+
+func setupNotifications(m *workflow.Manager) (chan []byte, int, error) {
+ // Set up notifications channel to monitor UI updates.
+ notifications := make(chan []byte, 10)
+ _, index, err := m.NodeManager().GetAndWatchFullTree(notifications)
+ if err != nil {
+ return nil, -1, fmt.Errorf("GetAndWatchTree Failed: %v", err)
+ }
+ return notifications, index, nil
+}
+
+func checkUIChangeFromNoneStarted(m *workflow.Manager, uuid string, notifications chan []byte) error {
+ wantNode := &workflow.Node{
+ PathName: string(phaseSimple),
+ Actions: []*workflow.Action{
+ {
+ Name: actionNameApproveFirstTask,
+ State: workflow.ActionStateDisabled,
+ Style: workflow.ActionStyleTriggered,
+ },
+ {
+ Name: actionNameApproveRemainingTasks,
+ State: workflow.ActionStateDisabled,
+ Style: workflow.ActionStyleTriggered,
+ },
+ },
+ }
+
+ // Approval buttons are initially disabled.
+ if err := consumeNotificationsUntil(notifications, wantNode); err != nil {
+ return fmt.Errorf("should get expected update of node: %v", wantNode)
+ }
+
+ // First task is ready and approval button is enabled.
+ wantNode.Actions[0].State = workflow.ActionStateEnabled
+ wantNode.Actions[0].Style = workflow.ActionStyleWaiting
+ if err := consumeNotificationsUntil(notifications, wantNode); err != nil {
+ return fmt.Errorf("should get expected update of node: %v", wantNode)
+ }
+ // Trigger the approval button.
+ // It becomes disabled and shows approved message.
+ if err := triggerAction(context.Background(), m, path.Join("/", uuid, string(phaseSimple)), actionNameApproveFirstTask); err != nil {
+ return err
+ }
+ // Trigger the approval button again to test the code is robust against
+ // duplicate requests.
+ if err := triggerAction(context.Background(), m, path.Join("/", uuid, string(phaseSimple)), actionNameApproveFirstTask); err == nil {
+ return fmt.Errorf("triggering the approval action %v again should fail", actionNameApproveFirstTask)
+ }
+ return checkUIChangeFirstApproved(m, uuid, notifications)
+}
+
+// checkUIChangeFirstApproved observes the UI change for 2 scenarios:
+// 1. the first and second tasks are all running.
+// 2. the first and second tasks have succeeded, but remaining tasks haven't
+// succeeded yet.
+func checkUIChangeAllApproved(notifications chan []byte) error {
+ wantNode := &workflow.Node{
+ PathName: string(phaseSimple),
+ Actions: []*workflow.Action{
+ {
+ Name: actionNameApproveFirstTaskDone,
+ State: workflow.ActionStateDisabled,
+ Style: workflow.ActionStyleTriggered,
+ },
+ {
+ Name: actionNameApproveRemainingTasksDone,
+ State: workflow.ActionStateDisabled,
+ Style: workflow.ActionStyleTriggered,
+ },
+ },
+ }
+
+ // Approval buttons are disabled and show approved messages.
+ if err := consumeNotificationsUntil(notifications, wantNode); err != nil {
+ return fmt.Errorf("should get expected update of node: %v", wantNode)
+ }
+
+ // Approval buttons are cleared after the phase is finished.
+ wantNode.Actions = []*workflow.Action{}
+ if err := consumeNotificationsUntil(notifications, wantNode); err != nil {
+ return fmt.Errorf("should get expected update of node: %v", wantNode)
+ }
+ return nil
+}
+
+// checkUIChangeFirstApproved observes the UI change for 2 scenarios:
+// 1. the first task is running and the second hasn't started.
+// 2. the first task is done and the second hasn't started.
+func checkUIChangeFirstApproved(m *workflow.Manager, uuid string, notifications chan []byte) error {
+ wantNode := &workflow.Node{
+ PathName: string(phaseSimple),
+ Actions: []*workflow.Action{
+ {
+ Name: actionNameApproveFirstTaskDone,
+ State: workflow.ActionStateDisabled,
+ Style: workflow.ActionStyleTriggered,
+ },
+ {
+ Name: actionNameApproveRemainingTasks,
+ State: workflow.ActionStateDisabled,
+ Style: workflow.ActionStyleTriggered,
+ },
+ },
+ }
+
+ // Approval buttons are initially disabled.
+ // The approval button for the first task shows the approved message.
+ if err := consumeNotificationsUntil(notifications, wantNode); err != nil {
+ return fmt.Errorf("should get expected update of node: %v", wantNode)
+ }
+
+ // The second task is ready and approval button for remaining tasks is
+ // enabled.
+ wantNode.Actions[1].State = workflow.ActionStateEnabled
+ wantNode.Actions[1].Style = workflow.ActionStyleWaiting
+ if err := consumeNotificationsUntil(notifications, wantNode); err != nil {
+ return fmt.Errorf("should get expected update of node: %v", wantNode)
+ }
+ // Trigger this approval button. It becomes disabled and shows the
+ // approved message.
+ if err := triggerAction(context.Background(), m, path.Join("/", uuid, string(phaseSimple)), actionNameApproveRemainingTasks); err != nil {
+ return err
+ }
+ return checkUIChangeAllApproved(notifications)
+}
+
+// consumeNotificationsUntil waits for all wantNodes to be seen from the
+// notifications of UI change.
+func consumeNotificationsUntil(notifications chan []byte, wantNodes ...*workflow.Node) error {
+ wantSet := make(map[*workflow.Node]bool)
+ for _, n := range wantNodes {
+ wantSet[n] = true
+ }
+
+ for monitor := range notifications {
+ update := &workflow.Update{}
+ if err := json.Unmarshal(monitor, update); err != nil {
+ return err
+ }
+
+ if update.Nodes == nil || len(update.Nodes) != 1 {
+ // Ignore unrelated UI updates. For example, the UI update often includes
+ // multiple nodes when the workflow initialize its UI.
+ continue
+ }
+ for _, n := range wantNodes {
+ if checkNode(update.Nodes[0], n) {
+ if wantSet[n] {
+ delete(wantSet, n)
+ }
+ }
+ if len(wantSet) == 0 {
+ return nil
+ }
+ }
+ }
+ return fmt.Errorf("notifications channel is closed unexpectedly when waiting for expected nodes")
+}
+
+func checkNode(gotNode *workflow.Node, wantNode *workflow.Node) bool {
+ if gotNode.PathName != wantNode.PathName || len(gotNode.Actions) != len(wantNode.Actions) {
+ return false
+ }
+
+ for i := 0; i < len(wantNode.Actions); i++ {
+ if !reflect.DeepEqual(gotNode.Actions[i], wantNode.Actions[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+func checkNoActions(notifications chan []byte, nodePath string) (string, error) {
+ for monitor := range notifications {
+ update := &workflow.Update{}
+ if err := json.Unmarshal(monitor, update); err != nil {
+ return "", err
+ }
+
+ if update.Nodes == nil {
+ continue
+ }
+
+ for _, n := range update.Nodes {
+ if n.PathName == nodePath && len(n.Actions) > 0 {
+ return string(monitor), fmt.Errorf("actions detected unexpectedly")
+ }
+ }
+ }
+ return "", nil
+}
+
+func waitForFinished(notifications chan []byte, path, message string) error {
+ for monitor := range notifications {
+ update := &workflow.Update{}
+ if err := json.Unmarshal(monitor, update); err != nil {
+ return err
+ }
+
+ if update.Nodes == nil || len(update.Nodes) != 1 {
+ // Ignore unrelated UI updates. For example, the UI update often includes
+ // multiple nodes when the workflow initialize its UI.
+ continue
+ }
+
+ if update.Nodes[0].PathName == path && update.Nodes[0].Message == taskFinishedMessage {
+ return nil
+ }
+ }
+ return fmt.Errorf("notifications channel is closed unexpectedly when waiting for expected nodes")
+}
+
+func verifyAllTasksDone(ctx context.Context, ts topo.Server, uuid string) error {
+ checkpoint, err := checkpoint(ctx, ts, uuid)
+ if err != nil {
+ return err
+ }
+
+ for _, task := range checkpoint.Tasks {
+ if task.State != workflowpb.TaskState_TaskDone || task.Error != "" {
+ return fmt.Errorf("task: %v should succeed: task status: %v, %v", task.Id, task.State, task.Attributes)
+ }
+ }
+ return nil
+}
+
+func verifyTask(ctx context.Context, ts topo.Server, uuid, taskID string, taskState workflowpb.TaskState, taskError string) error {
+ checkpoint, err := checkpoint(ctx, ts, uuid)
+ if err != nil {
+ return err
+ }
+ task := checkpoint.Tasks[taskID]
+
+ if task.State != taskState || task.Error != taskError {
+ return fmt.Errorf("task status: %v, %v fails to match expected status: %v, %v", task.State, task.Error, taskState, taskError)
+ }
+ return nil
+}
+
+func checkpoint(ctx context.Context, ts topo.Server, uuid string) (*workflowpb.WorkflowCheckpoint, error) {
+ wi, err := ts.GetWorkflow(ctx, uuid)
+ if err != nil {
+ return nil, fmt.Errorf("fail to get workflow for: %v", uuid)
+ }
+ checkpoint := &workflowpb.WorkflowCheckpoint{}
+ if err := proto.Unmarshal(wi.Workflow.Data, checkpoint); err != nil {
+ return nil, fmt.Errorf("fails to get checkpoint for the workflow: %v", err)
+ }
+ return checkpoint, nil
+}
diff --git a/go/vt/workflow/resharding/tasks.go b/go/vt/workflow/resharding/tasks.go
new file mode 100644
index 00000000000..7c624dfe70c
--- /dev/null
+++ b/go/vt/workflow/resharding/tasks.go
@@ -0,0 +1,101 @@
+package resharding
+
+import (
+ "fmt"
+ "strings"
+
+ "golang.org/x/net/context"
+
+ "github.com/youtube/vitess/go/vt/automation"
+ "github.com/youtube/vitess/go/vt/topo/topoproto"
+ "github.com/youtube/vitess/go/vt/wrangler"
+
+ topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
+ workflowpb "github.com/youtube/vitess/go/vt/proto/workflow"
+)
+
+func createTaskID(phase PhaseType, shardName string) string {
+ return fmt.Sprintf("%s/%s", phase, shardName)
+}
+
+// GetTasks returns selected tasks for a phase from the checkpoint
+// with expected execution order.
+func (hw *HorizontalReshardingWorkflow) GetTasks(phase PhaseType) []*workflowpb.Task {
+ var shards []string
+ switch phase {
+ case phaseCopySchema, phaseWaitForFilteredReplication, phaseDiff:
+ shards = strings.Split(hw.checkpoint.Settings["destination_shards"], ",")
+ case phaseClone, phaseMigrateRdonly, phaseMigrateReplica, phaseMigrateMaster:
+ shards = strings.Split(hw.checkpoint.Settings["source_shards"], ",")
+ default:
+ panic(fmt.Sprintf("BUG: unknown phase type: %v", phase))
+ }
+
+ var tasks []*workflowpb.Task
+ for _, s := range shards {
+ taskID := createTaskID(phase, s)
+ tasks = append(tasks, hw.checkpoint.Tasks[taskID])
+ }
+ return tasks
+}
+
+func (hw *HorizontalReshardingWorkflow) runCopySchema(ctx context.Context, t *workflowpb.Task) error {
+ keyspace := t.Attributes["keyspace"]
+ sourceShard := t.Attributes["source_shard"]
+ destShard := t.Attributes["destination_shard"]
+ return hw.wr.CopySchemaShardFromShard(ctx, nil /* tableArray*/, nil /* excludeTableArray */, true, /*includeViews*/
+ keyspace, sourceShard, keyspace, destShard, wrangler.DefaultWaitSlaveTimeout)
+}
+
+func (hw *HorizontalReshardingWorkflow) runSplitClone(ctx context.Context, t *workflowpb.Task) error {
+ keyspace := t.Attributes["keyspace"]
+ sourceShard := t.Attributes["source_shard"]
+ worker := t.Attributes["vtworker"]
+
+ sourceKeyspaceShard := topoproto.KeyspaceShardString(keyspace, sourceShard)
+ // Reset the vtworker to avoid error if vtworker command has been called elsewhere.
+ // This is because vtworker class doesn't cleanup the environment after execution.
+ automation.ExecuteVtworker(ctx, worker, []string{"Reset"})
+ // The flag min_healthy_rdonly_tablets is set to 1 (default value is 2).
+ // Therefore, we can reuse the normal end to end test setting, which has only 1 rdonly tablet.
+ // TODO(yipeiw): Add min_healthy_rdonly_tablets as an input argument in UI.
+ args := []string{"SplitClone", "--min_healthy_rdonly_tablets=1", sourceKeyspaceShard}
+ _, err := automation.ExecuteVtworker(hw.ctx, worker, args)
+ return err
+}
+
+func (hw *HorizontalReshardingWorkflow) runWaitForFilteredReplication(ctx context.Context, t *workflowpb.Task) error {
+ keyspace := t.Attributes["keyspace"]
+ destShard := t.Attributes["destination_shard"]
+ return hw.wr.WaitForFilteredReplication(ctx, keyspace, destShard, wrangler.DefaultWaitForFilteredReplicationMaxDelay)
+}
+
+func (hw *HorizontalReshardingWorkflow) runSplitDiff(ctx context.Context, t *workflowpb.Task) error {
+ keyspace := t.Attributes["keyspace"]
+ destShard := t.Attributes["destination_shard"]
+ worker := t.Attributes["vtworker"]
+
+ automation.ExecuteVtworker(hw.ctx, worker, []string{"Reset"})
+ args := []string{"SplitDiff", "--min_healthy_rdonly_tablets=1", topoproto.KeyspaceShardString(keyspace, destShard)}
+ _, err := automation.ExecuteVtworker(ctx, worker, args)
+ return err
+}
+
+func (hw *HorizontalReshardingWorkflow) runMigrate(ctx context.Context, t *workflowpb.Task) error {
+ keyspace := t.Attributes["keyspace"]
+ sourceShard := t.Attributes["source_shard"]
+ servedTypeStr := t.Attributes["served_type"]
+
+ servedType, err := topoproto.ParseTabletType(servedTypeStr)
+ if err != nil {
+ return fmt.Errorf("unknown tablet type: %v", servedTypeStr)
+ }
+
+ if servedType != topodatapb.TabletType_RDONLY &&
+ servedType != topodatapb.TabletType_REPLICA &&
+ servedType != topodatapb.TabletType_MASTER {
+ return fmt.Errorf("wrong served type to be migrated: %v", servedTypeStr)
+ }
+
+ return hw.wr.MigrateServedTypes(ctx, keyspace, sourceShard, nil /* cells */, servedType, false /* reverse */, false /* skipReFreshState */, wrangler.DefaultFilteredReplicationWaitTime)
+}
diff --git a/go/vt/workflow/resharding/test_workflow_test.go b/go/vt/workflow/resharding/test_workflow_test.go
new file mode 100644
index 00000000000..9c47cd2d935
--- /dev/null
+++ b/go/vt/workflow/resharding/test_workflow_test.go
@@ -0,0 +1,195 @@
+package resharding
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "strconv"
+ "sync"
+
+ log "github.com/golang/glog"
+ "golang.org/x/net/context"
+
+ "github.com/golang/protobuf/proto"
+ "github.com/youtube/vitess/go/vt/logutil"
+ "github.com/youtube/vitess/go/vt/topo"
+ "github.com/youtube/vitess/go/vt/workflow"
+
+ workflowpb "github.com/youtube/vitess/go/vt/proto/workflow"
+)
+
+const (
+ testWorkflowFactoryName = "test_workflow"
+
+ phaseSimple PhaseType = "simple"
+
+ errMessage = "fake error for testing retry"
+)
+
+func createTestTaskID(phase PhaseType, count int) string {
+ return fmt.Sprintf("%s/%v", phase, count)
+}
+
+func init() {
+ workflow.Register(testWorkflowFactoryName, &TestWorkflowFactory{})
+}
+
+// TestWorkflowFactory is the factory to create a test workflow.
+type TestWorkflowFactory struct{}
+
+// Init is part of the workflow.Factory interface.
+func (*TestWorkflowFactory) Init(_ *workflow.Manager, w *workflowpb.Workflow, args []string) error {
+ subFlags := flag.NewFlagSet(testWorkflowFactoryName, flag.ContinueOnError)
+ retryFlag := subFlags.Bool("retry", false, "The retry flag should be true if the retry action should be tested")
+ count := subFlags.Int("count", 0, "The number of simple tasks")
+ enableApprovals := subFlags.Bool("enable_approvals", false, "If true, executions of tasks require user's approvals on the UI.")
+ if err := subFlags.Parse(args); err != nil {
+ return err
+ }
+
+ // Initialize the checkpoint.
+ taskMap := make(map[string]*workflowpb.Task)
+ for i := 0; i < *count; i++ {
+ taskID := createTestTaskID(phaseSimple, i)
+ taskMap[taskID] = &workflowpb.Task{
+ Id: taskID,
+ State: workflowpb.TaskState_TaskNotStarted,
+ Attributes: map[string]string{"number": fmt.Sprintf("%v", i)},
+ }
+ }
+ checkpoint := &workflowpb.WorkflowCheckpoint{
+ CodeVersion: 0,
+ Tasks: taskMap,
+ Settings: map[string]string{"count": fmt.Sprintf("%v", *count), "retry": fmt.Sprintf("%v", *retryFlag), "enable_approvals": fmt.Sprintf("%v", *enableApprovals)},
+ }
+ var err error
+ w.Data, err = proto.Marshal(checkpoint)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// Instantiate is part the workflow.Factory interface.
+func (*TestWorkflowFactory) Instantiate(m *workflow.Manager, w *workflowpb.Workflow, rootNode *workflow.Node) (workflow.Workflow, error) {
+ checkpoint := &workflowpb.WorkflowCheckpoint{}
+ if err := proto.Unmarshal(w.Data, checkpoint); err != nil {
+ return nil, err
+ }
+ // Get the retry flags for all tasks from the checkpoint.
+ retry, err := strconv.ParseBool(checkpoint.Settings["retry"])
+ if err != nil {
+ log.Errorf("converting retry in checkpoint.Settings to bool fails: %v", checkpoint.Settings["retry"])
+ return nil, err
+ }
+ retryFlags := make(map[string]bool)
+ for _, task := range checkpoint.Tasks {
+ retryFlags[task.Id] = retry
+ }
+
+ // Get the user control flags from the checkpoint.
+ enableApprovals, err := strconv.ParseBool(checkpoint.Settings["enable_approvals"])
+ if err != nil {
+ log.Errorf("converting retry in checkpoint.Settings to bool fails: %v", checkpoint.Settings["user_control"])
+ return nil, err
+ }
+
+ tw := &TestWorkflow{
+ topoServer: m.TopoServer(),
+ manager: m,
+ checkpoint: checkpoint,
+ rootUINode: rootNode,
+ logger: logutil.NewMemoryLogger(),
+ retryFlags: retryFlags,
+ enableApprovals: enableApprovals,
+ }
+
+ count, err := strconv.Atoi(checkpoint.Settings["count"])
+ if err != nil {
+ log.Errorf("converting count in checkpoint.Settings to int fails: %v", checkpoint.Settings["count"])
+ return nil, err
+ }
+
+ phaseNode := &workflow.Node{
+ Name: string(phaseSimple),
+ PathName: string(phaseSimple),
+ }
+ tw.rootUINode.Children = append(tw.rootUINode.Children, phaseNode)
+
+ for i := 0; i < count; i++ {
+ taskName := fmt.Sprintf("%v", i)
+ taskUINode := &workflow.Node{
+ Name: taskName,
+ PathName: taskName,
+ }
+ phaseNode.Children = append(phaseNode.Children, taskUINode)
+ }
+ return tw, nil
+}
+
+// TestWorkflow is used to unit test the ParallelRunner object. It is a
+// simplified workflow of one phase. To test the ParallelRunner's retry
+// behavior, we can let the tasks explicitly fail initially and succeed
+// after a retry.
+type TestWorkflow struct {
+ ctx context.Context
+ manager *workflow.Manager
+ topoServer topo.Server
+ wi *topo.WorkflowInfo
+ logger *logutil.MemoryLogger
+
+ retryMu sync.Mutex
+ // retryFlags stores the retry flag for all tasks.
+ retryFlags map[string]bool
+
+ rootUINode *workflow.Node
+
+ checkpoint *workflowpb.WorkflowCheckpoint
+ checkpointWriter *CheckpointWriter
+
+ enableApprovals bool
+}
+
+// Run implements the workflow.Workflow interface.
+func (tw *TestWorkflow) Run(ctx context.Context, manager *workflow.Manager, wi *topo.WorkflowInfo) error {
+ tw.ctx = ctx
+ tw.wi = wi
+ tw.checkpointWriter = NewCheckpointWriter(tw.topoServer, tw.checkpoint, tw.wi)
+
+ tw.rootUINode.Display = workflow.NodeDisplayDeterminate
+ tw.rootUINode.BroadcastChanges(true /* updateChildren */)
+
+ simpleTasks := tw.getTasks(phaseSimple)
+ simpleRunner := NewParallelRunner(tw.ctx, tw.rootUINode, tw.checkpointWriter, simpleTasks, tw.runSimple, Parallel, tw.enableApprovals)
+ if err := simpleRunner.Run(); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (tw *TestWorkflow) getTasks(phaseName PhaseType) []*workflowpb.Task {
+ count, err := strconv.Atoi(tw.checkpoint.Settings["count"])
+ if err != nil {
+ log.Info("converting count in checkpoint.Settings to int failed: %v", tw.checkpoint.Settings["count"])
+ return nil
+ }
+ var tasks []*workflowpb.Task
+ for i := 0; i < count; i++ {
+ taskID := createTestTaskID(phaseName, i)
+ tasks = append(tasks, tw.checkpoint.Tasks[taskID])
+ }
+ return tasks
+}
+
+func (tw *TestWorkflow) runSimple(ctx context.Context, t *workflowpb.Task) error {
+ log.Info("The number passed to me is %v", t.Attributes["number"])
+
+ tw.retryMu.Lock()
+ defer tw.retryMu.Unlock()
+ if tw.retryFlags[t.Id] {
+ log.Info("I will fail at this time since retry flag is true.")
+ tw.retryFlags[t.Id] = false
+ return errors.New(errMessage)
+ }
+ return nil
+}
diff --git a/go/vt/workflow/sleep_workflow.go b/go/vt/workflow/sleep_workflow.go
index a3c4b4c3955..bd5eba54925 100644
--- a/go/vt/workflow/sleep_workflow.go
+++ b/go/vt/workflow/sleep_workflow.go
@@ -190,7 +190,7 @@ func (sw *SleepWorkflow) checkpointLocked(ctx context.Context) error {
type SleepWorkflowFactory struct{}
// Init is part of the workflow.Factory interface.
-func (f *SleepWorkflowFactory) Init(w *workflowpb.Workflow, args []string) error {
+func (f *SleepWorkflowFactory) Init(_ *Manager, w *workflowpb.Workflow, args []string) error {
// Parse the flags.
subFlags := flag.NewFlagSet(sleepFactoryName, flag.ContinueOnError)
duration := subFlags.Int("duration", 30, "How long to sleep")
@@ -215,7 +215,7 @@ func (f *SleepWorkflowFactory) Init(w *workflowpb.Workflow, args []string) error
}
// Instantiate is part of the workflow.Factory interface.
-func (f *SleepWorkflowFactory) Instantiate(w *workflowpb.Workflow, rootNode *Node) (Workflow, error) {
+func (f *SleepWorkflowFactory) Instantiate(_ *Manager, w *workflowpb.Workflow, rootNode *Node) (Workflow, error) {
rootNode.Message = "This workflow is a test workflow that just sleeps for the provided amount of time."
data := &SleepWorkflowData{}
diff --git a/go/vt/workflow/topovalidator/validator.go b/go/vt/workflow/topovalidator/validator.go
index bf15060f551..3cc98e91570 100644
--- a/go/vt/workflow/topovalidator/validator.go
+++ b/go/vt/workflow/topovalidator/validator.go
@@ -193,7 +193,7 @@ func (f *workflowFixer) Action(ctx context.Context, path, name string) error {
type WorkflowFactory struct{}
// Init is part of the workflow.Factory interface.
-func (f *WorkflowFactory) Init(w *workflowpb.Workflow, args []string) error {
+func (f *WorkflowFactory) Init(_ *workflow.Manager, w *workflowpb.Workflow, args []string) error {
// No parameters to parse.
if len(args) > 0 {
return fmt.Errorf("%v doesn't take any parameter", topoValidatorFactoryName)
@@ -203,7 +203,7 @@ func (f *WorkflowFactory) Init(w *workflowpb.Workflow, args []string) error {
}
// Instantiate is part of the workflow.Factory interface.
-func (f *WorkflowFactory) Instantiate(w *workflowpb.Workflow, rootNode *workflow.Node) (workflow.Workflow, error) {
+func (f *WorkflowFactory) Instantiate(_ *workflow.Manager, w *workflowpb.Workflow, rootNode *workflow.Node) (workflow.Workflow, error) {
rootNode.Message = "Validates the Topology and proposes fixes for known issues."
return &Workflow{
diff --git a/go/vt/wrangler/split.go b/go/vt/wrangler/split.go
index e5786a95b9b..ca33e2bab86 100644
--- a/go/vt/wrangler/split.go
+++ b/go/vt/wrangler/split.go
@@ -11,8 +11,8 @@ import (
"golang.org/x/net/context"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
"github.com/youtube/vitess/go/vt/topo"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletconn"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
diff --git a/go/vt/wrangler/testlib/apply_schema_test.go b/go/vt/wrangler/testlib/apply_schema_test.go
index 3b2eaebc2e8..917f6b6b4e6 100644
--- a/go/vt/wrangler/testlib/apply_schema_test.go
+++ b/go/vt/wrangler/testlib/apply_schema_test.go
@@ -14,8 +14,8 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/logutil"
"github.com/youtube/vitess/go/vt/mysqlctl/tmutils"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
tabletmanagerdatapb "github.com/youtube/vitess/go/vt/proto/tabletmanagerdata"
diff --git a/go/vt/wrangler/testlib/backup_test.go b/go/vt/wrangler/testlib/backup_test.go
index 73ccffe87eb..d5e5c3fc9b3 100644
--- a/go/vt/wrangler/testlib/backup_test.go
+++ b/go/vt/wrangler/testlib/backup_test.go
@@ -20,9 +20,9 @@ import (
"github.com/youtube/vitess/go/vt/mysqlctl"
"github.com/youtube/vitess/go/vt/mysqlctl/backupstorage"
"github.com/youtube/vitess/go/vt/mysqlctl/filebackupstorage"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
diff --git a/go/vt/wrangler/testlib/copy_schema_shard_test.go b/go/vt/wrangler/testlib/copy_schema_shard_test.go
index b80b144cc93..3388c962dba 100644
--- a/go/vt/wrangler/testlib/copy_schema_shard_test.go
+++ b/go/vt/wrangler/testlib/copy_schema_shard_test.go
@@ -13,9 +13,9 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/logutil"
"github.com/youtube/vitess/go/vt/mysqlctl/tmutils"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
querypb "github.com/youtube/vitess/go/vt/proto/query"
diff --git a/go/vt/wrangler/testlib/emergency_reparent_shard_test.go b/go/vt/wrangler/testlib/emergency_reparent_shard_test.go
index ccaf27f06c2..51a78af4b5d 100644
--- a/go/vt/wrangler/testlib/emergency_reparent_shard_test.go
+++ b/go/vt/wrangler/testlib/emergency_reparent_shard_test.go
@@ -14,9 +14,9 @@ import (
"github.com/youtube/vitess/go/mysqlconn/replication"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
diff --git a/go/vt/wrangler/testlib/fake_tablet.go b/go/vt/wrangler/testlib/fake_tablet.go
index a76a04c601e..4eb3f79e1a3 100644
--- a/go/vt/wrangler/testlib/fake_tablet.go
+++ b/go/vt/wrangler/testlib/fake_tablet.go
@@ -20,21 +20,21 @@ import (
"github.com/youtube/vitess/go/mysqlconn/fakesqldb"
"github.com/youtube/vitess/go/vt/mysqlctl"
- "github.com/youtube/vitess/go/vt/tabletmanager"
- "github.com/youtube/vitess/go/vt/tabletmanager/grpctmserver"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
"github.com/youtube/vitess/go/vt/topo"
+ "github.com/youtube/vitess/go/vt/vttablet/grpctmserver"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletconn"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletmanager"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
querypb "github.com/youtube/vitess/go/vt/proto/query"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
// import the gRPC client implementation for tablet manager
- _ "github.com/youtube/vitess/go/vt/tabletmanager/grpctmclient"
+ _ "github.com/youtube/vitess/go/vt/vttablet/grpctmclient"
// import the gRPC client implementation for query service
- _ "github.com/youtube/vitess/go/vt/tabletserver/grpctabletconn"
+ _ "github.com/youtube/vitess/go/vt/vttablet/grpctabletconn"
)
// This file contains utility methods for unit tests.
diff --git a/go/vt/wrangler/testlib/init_shard_master_test.go b/go/vt/wrangler/testlib/init_shard_master_test.go
index 29c8bead46c..3ae75749f86 100644
--- a/go/vt/wrangler/testlib/init_shard_master_test.go
+++ b/go/vt/wrangler/testlib/init_shard_master_test.go
@@ -16,10 +16,10 @@ import (
"github.com/youtube/vitess/go/mysqlconn/replication"
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
diff --git a/go/vt/wrangler/testlib/migrate_served_from_test.go b/go/vt/wrangler/testlib/migrate_served_from_test.go
index 08a0b14432b..ee8df6e7ed0 100644
--- a/go/vt/wrangler/testlib/migrate_served_from_test.go
+++ b/go/vt/wrangler/testlib/migrate_served_from_test.go
@@ -13,8 +13,8 @@ import (
"github.com/youtube/vitess/go/mysqlconn/replication"
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
diff --git a/go/vt/wrangler/testlib/migrate_served_types_test.go b/go/vt/wrangler/testlib/migrate_served_types_test.go
index b8d96c17095..4a4fae4ccfc 100644
--- a/go/vt/wrangler/testlib/migrate_served_types_test.go
+++ b/go/vt/wrangler/testlib/migrate_served_types_test.go
@@ -14,9 +14,9 @@ import (
"github.com/youtube/vitess/go/mysqlconn/replication"
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
diff --git a/go/vt/wrangler/testlib/permissions_test.go b/go/vt/wrangler/testlib/permissions_test.go
index 6cf545d06c1..3d31af71f10 100644
--- a/go/vt/wrangler/testlib/permissions_test.go
+++ b/go/vt/wrangler/testlib/permissions_test.go
@@ -12,9 +12,9 @@ import (
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
querypb "github.com/youtube/vitess/go/vt/proto/query"
diff --git a/go/vt/wrangler/testlib/planned_reparent_shard_test.go b/go/vt/wrangler/testlib/planned_reparent_shard_test.go
index b9361b4a657..4225392aae8 100644
--- a/go/vt/wrangler/testlib/planned_reparent_shard_test.go
+++ b/go/vt/wrangler/testlib/planned_reparent_shard_test.go
@@ -11,10 +11,10 @@ import (
"github.com/youtube/vitess/go/mysqlconn/replication"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletservermock"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletservermock"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
diff --git a/go/vt/wrangler/testlib/reparent_external_test.go b/go/vt/wrangler/testlib/reparent_external_test.go
index a2495767c7d..80b893f43a6 100644
--- a/go/vt/wrangler/testlib/reparent_external_test.go
+++ b/go/vt/wrangler/testlib/reparent_external_test.go
@@ -14,13 +14,13 @@ import (
"github.com/youtube/vitess/go/event"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
"github.com/youtube/vitess/go/vt/topotools"
"github.com/youtube/vitess/go/vt/topotools/events"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletmanager"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
diff --git a/go/vt/wrangler/testlib/reparent_utils_test.go b/go/vt/wrangler/testlib/reparent_utils_test.go
index 6e06b360e0c..0ed311e3e03 100644
--- a/go/vt/wrangler/testlib/reparent_utils_test.go
+++ b/go/vt/wrangler/testlib/reparent_utils_test.go
@@ -12,10 +12,10 @@ import (
"github.com/youtube/vitess/go/mysqlconn/replication"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
"github.com/youtube/vitess/go/vt/topo/topoproto"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
diff --git a/go/vt/wrangler/testlib/shard_test.go b/go/vt/wrangler/testlib/shard_test.go
index 6dc1bd61e8a..9eee7327520 100644
--- a/go/vt/wrangler/testlib/shard_test.go
+++ b/go/vt/wrangler/testlib/shard_test.go
@@ -7,9 +7,9 @@ import (
"golang.org/x/net/context"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
diff --git a/go/vt/wrangler/testlib/version_test.go b/go/vt/wrangler/testlib/version_test.go
index 4fbe4099347..48a678c7c54 100644
--- a/go/vt/wrangler/testlib/version_test.go
+++ b/go/vt/wrangler/testlib/version_test.go
@@ -12,8 +12,8 @@ import (
"testing"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
diff --git a/go/vt/wrangler/testlib/wait_for_drain_test.go b/go/vt/wrangler/testlib/wait_for_drain_test.go
index 2cac39de502..08fa710a1b3 100644
--- a/go/vt/wrangler/testlib/wait_for_drain_test.go
+++ b/go/vt/wrangler/testlib/wait_for_drain_test.go
@@ -13,10 +13,10 @@ import (
"golang.org/x/net/context"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
- "github.com/youtube/vitess/go/vt/tabletserver/grpcqueryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/queryservice/fakes"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
+ "github.com/youtube/vitess/go/vt/vttablet/grpcqueryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/queryservice/fakes"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
logutilpb "github.com/youtube/vitess/go/vt/proto/logutil"
diff --git a/go/vt/wrangler/testlib/wait_for_filtered_replication_test.go b/go/vt/wrangler/testlib/wait_for_filtered_replication_test.go
index 14ea0dccb32..223eaee06ab 100644
--- a/go/vt/wrangler/testlib/wait_for_filtered_replication_test.go
+++ b/go/vt/wrangler/testlib/wait_for_filtered_replication_test.go
@@ -12,12 +12,12 @@ import (
"golang.org/x/net/context"
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
- "github.com/youtube/vitess/go/vt/tabletserver"
- "github.com/youtube/vitess/go/vt/tabletserver/grpcqueryservice"
- "github.com/youtube/vitess/go/vt/tabletserver/tabletenv"
"github.com/youtube/vitess/go/vt/topo/memorytopo"
+ "github.com/youtube/vitess/go/vt/vttablet/grpcqueryservice"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletmanager"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver"
+ "github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
"github.com/youtube/vitess/go/vt/wrangler"
querypb "github.com/youtube/vitess/go/vt/proto/query"
@@ -111,7 +111,7 @@ func waitForFilteredReplication(t *testing.T, expectedErr string, initialStats *
dest.Agent.BinlogPlayerMap = tabletmanager.NewBinlogPlayerMap(ts, nil, nil)
// Use real, but trimmed down QueryService.
- qs := tabletserver.NewTabletServer(tabletenv.DefaultQsConfig)
+ qs := tabletserver.NewTabletServerWithNilTopoServer(tabletenv.DefaultQsConfig)
grpcqueryservice.Register(dest.RPCServer, qs)
qs.BroadcastHealth(42, initialStats)
diff --git a/go/vt/wrangler/wrangler.go b/go/vt/wrangler/wrangler.go
index 0fe75cfe15c..e53952c4783 100644
--- a/go/vt/wrangler/wrangler.go
+++ b/go/vt/wrangler/wrangler.go
@@ -8,8 +8,8 @@ package wrangler
import (
"github.com/youtube/vitess/go/vt/logutil"
- "github.com/youtube/vitess/go/vt/tabletmanager/tmclient"
"github.com/youtube/vitess/go/vt/topo"
+ "github.com/youtube/vitess/go/vt/vttablet/tmclient"
)
var (
diff --git a/helm/vitess/templates/_helpers.tpl b/helm/vitess/templates/_helpers.tpl
index b4d28ebf8a1..49d7185a642 100644
--- a/helm/vitess/templates/_helpers.tpl
+++ b/helm/vitess/templates/_helpers.tpl
@@ -14,14 +14,17 @@
{{- range . }}{{template "format-flags" .}}{{end -}}
{{- end -}}
-# Format a shard name, making sure it starts and ends with [A-Za-z0-9].
-{{- define "format-shard-name" -}}
+# Clean labels, making sure it starts and ends with [A-Za-z0-9].
+# This is especially important for shard names, which can start or end with
+# '-' (like -80 or 80-), which would be an invalid kubernetes label.
+{{- define "clean-label" -}}
+{{- $replaced_label := . | replace "_" "-"}}
{{- if hasPrefix "-" . -}}
-x{{.}}
+x{{$replaced_label}}
{{- else if hasSuffix "-" . -}}
-{{.}}x
+{{$replaced_label}}x
{{- else -}}
-{{.}}
+{{$replaced_label}}
{{- end -}}
{{- end -}}
diff --git a/helm/vitess/templates/_vttablet.tpl b/helm/vitess/templates/_vttablet.tpl
index fb48e440f7c..620ca956e8e 100644
--- a/helm/vitess/templates/_vttablet.tpl
+++ b/helm/vitess/templates/_vttablet.tpl
@@ -176,9 +176,10 @@ volumes:
{{- $tablet := index . 4 -}}
{{- with $tablet.vttablet -}}
{{- $0 := $.Values.vttablet -}}
-{{- $keyspaceClean := $keyspace.name | replace "_" "-" -}}
-{{- $shardClean := include "format-shard-name" $shard.name -}}
-{{- $setName := printf "%s-%s-%s" $keyspaceClean $shardClean $tablet.type | lower -}}
+{{- $cellClean := include "clean-label" $cell.name -}}
+{{- $keyspaceClean := include "clean-label" $keyspace.name -}}
+{{- $shardClean := include "clean-label" $shard.name -}}
+{{- $setName := printf "%s-%s-%s-%s" $cellClean $keyspaceClean $shardClean $tablet.type | lower -}}
{{- $uid := "$(cat $VTDATAROOT/init/tablet-uid)" }}
# vttablet StatefulSet
apiVersion: apps/v1beta1
@@ -193,6 +194,7 @@ spec:
labels:
app: vitess
component: vttablet
+ cell: {{$cellClean | quote}}
keyspace: {{$keyspace.name | quote}}
shard: {{$shardClean | quote}}
type: {{$tablet.type | quote}}
@@ -222,6 +224,7 @@ spec:
{{- $cell := index . 1 -}}
{{- $keyspace := index . 2 -}}
{{- $shard := index . 3 -}}
+{{- $shardClean := include "clean-label" $shard.name -}}
{{- $tablet := index . 4 -}}
{{- $uid := index . 5 -}}
{{- with $tablet.vttablet -}}
@@ -235,7 +238,7 @@ metadata:
app: vitess
component: vttablet
keyspace: {{$keyspace.name | quote}}
- shard: {{$shard.name | quote}}
+ shard: {{$shardClean | quote}}
type: {{$tablet.type | quote}}
annotations:
pod.beta.kubernetes.io/init-containers: '[
diff --git a/java/client/pom.xml b/java/client/pom.xml
index e84540e7a45..242ba2904bb 100644
--- a/java/client/pom.xml
+++ b/java/client/pom.xml
@@ -4,7 +4,7 @@
com.youtube.vitess
vitess-parent
- 1.0-SNAPSHOT
+ 1.1.0-SNAPSHOT
../pom.xml
client
diff --git a/java/client/src/main/java/com/youtube/vitess/client/Proto.java b/java/client/src/main/java/com/youtube/vitess/client/Proto.java
index 3663ed48fb7..2cc85df1085 100644
--- a/java/client/src/main/java/com/youtube/vitess/client/Proto.java
+++ b/java/client/src/main/java/com/youtube/vitess/client/Proto.java
@@ -51,19 +51,39 @@ public static void checkError(RPCError error) throws SQLException {
String sqlState = getSQLState(error.getMessage());
switch (error.getCode()) {
- case SUCCESS:
+ case OK:
break;
- case BAD_INPUT:
+ case INVALID_ARGUMENT:
throw new SQLSyntaxErrorException(error.toString(), sqlState, errno);
case DEADLINE_EXCEEDED:
throw new SQLTimeoutException(error.toString(), sqlState, errno);
- case INTEGRITY_ERROR:
+ case ALREADY_EXISTS:
throw new SQLIntegrityConstraintViolationException(error.toString(), sqlState, errno);
- case TRANSIENT_ERROR:
+ case UNAVAILABLE:
throw new SQLTransientException(error.toString(), sqlState, errno);
case UNAUTHENTICATED:
throw new SQLInvalidAuthorizationSpecException(error.toString(), sqlState, errno);
- case NOT_IN_TX:
+ case ABORTED:
+ throw new SQLRecoverableException(error.toString(), sqlState, errno);
+ default:
+ throw new SQLNonTransientException("Vitess RPC error: " + error.toString(), sqlState,
+ errno);
+ }
+
+ switch (error.getLegacyCode()) {
+ case SUCCESS_LEGACY:
+ break;
+ case BAD_INPUT_LEGACY:
+ throw new SQLSyntaxErrorException(error.toString(), sqlState, errno);
+ case DEADLINE_EXCEEDED_LEGACY:
+ throw new SQLTimeoutException(error.toString(), sqlState, errno);
+ case INTEGRITY_ERROR_LEGACY:
+ throw new SQLIntegrityConstraintViolationException(error.toString(), sqlState, errno);
+ case TRANSIENT_ERROR_LEGACY:
+ throw new SQLTransientException(error.toString(), sqlState, errno);
+ case UNAUTHENTICATED_LEGACY:
+ throw new SQLInvalidAuthorizationSpecException(error.toString(), sqlState, errno);
+ case NOT_IN_TX_LEGACY:
throw new SQLRecoverableException(error.toString(), sqlState, errno);
default:
throw new SQLNonTransientException("Vitess RPC error: " + error.toString(), sqlState,
diff --git a/java/client/src/main/java/com/youtube/vitess/client/VTGateConn.java b/java/client/src/main/java/com/youtube/vitess/client/VTGateConn.java
index 1427cfc0dbd..91fdd08f054 100644
--- a/java/client/src/main/java/com/youtube/vitess/client/VTGateConn.java
+++ b/java/client/src/main/java/com/youtube/vitess/client/VTGateConn.java
@@ -1,5 +1,9 @@
package com.youtube.vitess.client;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.util.concurrent.Futures.transformAsync;
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
+
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures;
@@ -39,8 +43,6 @@
import com.youtube.vitess.proto.Vtgate.StreamExecuteKeyspaceIdsRequest;
import com.youtube.vitess.proto.Vtgate.StreamExecuteRequest;
import com.youtube.vitess.proto.Vtgate.StreamExecuteShardsRequest;
-
-import javax.annotation.Nullable;
import java.io.Closeable;
import java.io.IOException;
import java.sql.SQLDataException;
@@ -48,8 +50,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-
-import static com.google.common.base.Preconditions.checkNotNull;
+import javax.annotation.Nullable;
/**
* An asynchronous VTGate connection.
@@ -108,14 +109,17 @@ public SQLFuture execute(Context ctx, String query, @Nullable Map(Futures.transformAsync(client.execute(ctx, requestBuilder.build()),
- new AsyncFunction() {
- @Override
- public ListenableFuture apply(ExecuteResponse response) throws Exception {
- Proto.checkError(response.getError());
- return Futures.immediateFuture(new SimpleCursor(response.getResult()));
- }
- }));
+ return new SQLFuture(
+ transformAsync(
+ client.execute(ctx, requestBuilder.build()),
+ new AsyncFunction() {
+ @Override
+ public ListenableFuture apply(ExecuteResponse response) throws Exception {
+ Proto.checkError(response.getError());
+ return Futures.immediateFuture(new SimpleCursor(response.getResult()));
+ }
+ },
+ directExecutor()));
}
public SQLFuture executeShards(Context ctx, String query, String keyspace,
@@ -136,7 +140,8 @@ public SQLFuture executeShards(Context ctx, String query, String keyspac
}
return new SQLFuture(
- Futures.transformAsync(client.executeShards(ctx, requestBuilder.build()),
+ transformAsync(
+ client.executeShards(ctx, requestBuilder.build()),
new AsyncFunction() {
@Override
public ListenableFuture apply(ExecuteShardsResponse response)
@@ -144,7 +149,8 @@ public ListenableFuture apply(ExecuteShardsResponse response)
Proto.checkError(response.getError());
return Futures.immediateFuture(new SimpleCursor(response.getResult()));
}
- }));
+ },
+ directExecutor()));
}
public SQLFuture executeKeyspaceIds(Context ctx, String query, String keyspace,
@@ -165,7 +171,8 @@ public SQLFuture executeKeyspaceIds(Context ctx, String query, String ke
}
return new SQLFuture(
- Futures.transformAsync(client.executeKeyspaceIds(ctx, requestBuilder.build()),
+ transformAsync(
+ client.executeKeyspaceIds(ctx, requestBuilder.build()),
new AsyncFunction() {
@Override
public ListenableFuture apply(ExecuteKeyspaceIdsResponse response)
@@ -173,7 +180,8 @@ public ListenableFuture apply(ExecuteKeyspaceIdsResponse response)
Proto.checkError(response.getError());
return Futures.immediateFuture(new SimpleCursor(response.getResult()));
}
- }));
+ },
+ directExecutor()));
}
public SQLFuture executeKeyRanges(Context ctx, String query, String keyspace,
@@ -192,7 +200,8 @@ public SQLFuture executeKeyRanges(Context ctx, String query, String keys
}
return new SQLFuture(
- Futures.transformAsync(client.executeKeyRanges(ctx, requestBuilder.build()),
+ transformAsync(
+ client.executeKeyRanges(ctx, requestBuilder.build()),
new AsyncFunction() {
@Override
public ListenableFuture apply(ExecuteKeyRangesResponse response)
@@ -200,7 +209,8 @@ public ListenableFuture apply(ExecuteKeyRangesResponse response)
Proto.checkError(response.getError());
return Futures.immediateFuture(new SimpleCursor(response.getResult()));
}
- }));
+ },
+ directExecutor()));
}
public SQLFuture executeEntityIds(Context ctx, String query, String keyspace,
@@ -221,7 +231,8 @@ public SQLFuture executeEntityIds(Context ctx, String query, String keys
}
return new SQLFuture(
- Futures.transformAsync(client.executeEntityIds(ctx, requestBuilder.build()),
+ transformAsync(
+ client.executeEntityIds(ctx, requestBuilder.build()),
new AsyncFunction() {
@Override
public ListenableFuture apply(ExecuteEntityIdsResponse response)
@@ -229,7 +240,8 @@ public ListenableFuture apply(ExecuteEntityIdsResponse response)
Proto.checkError(response.getError());
return Futures.immediateFuture(new SimpleCursor(response.getResult()));
}
- }));
+ },
+ directExecutor()));
}
public SQLFuture> executeBatch(Context ctx, List queryList,
@@ -266,16 +278,19 @@ public SQLFuture> executeBatch(Context ctx, List q
requestBuilder.setCallerId(ctx.getCallerId());
}
- return new SQLFuture<>(Futures
- .transformAsync(client.executeBatch(ctx, requestBuilder.build()),
- new AsyncFunction>() {
- @Override public ListenableFuture> apply(
- Vtgate.ExecuteBatchResponse response) throws Exception {
- Proto.checkError(response.getError());
- return Futures.immediateFuture(
- Proto.fromQueryResponsesToCursorList(response.getResultsList()));
- }
- }));
+ return new SQLFuture<>(
+ transformAsync(
+ client.executeBatch(ctx, requestBuilder.build()),
+ new AsyncFunction>() {
+ @Override
+ public ListenableFuture> apply(
+ Vtgate.ExecuteBatchResponse response) throws Exception {
+ Proto.checkError(response.getError());
+ return Futures.immediateFuture(
+ Proto.fromQueryResponsesToCursorList(response.getResultsList()));
+ }
+ },
+ directExecutor()));
}
/**
@@ -301,16 +316,18 @@ public SQLFuture> executeBatchShards(Context ctx,
}
return new SQLFuture>(
- Futures.transformAsync(client.executeBatchShards(ctx, requestBuilder.build()),
+ transformAsync(
+ client.executeBatchShards(ctx, requestBuilder.build()),
new AsyncFunction>() {
@Override
public ListenableFuture> apply(ExecuteBatchShardsResponse response)
throws Exception {
Proto.checkError(response.getError());
- return Futures
- .>immediateFuture(Proto.toCursorList(response.getResultsList()));
+ return Futures.>immediateFuture(
+ Proto.toCursorList(response.getResultsList()));
}
- }));
+ },
+ directExecutor()));
}
/**
@@ -335,16 +352,18 @@ public SQLFuture> executeBatchKeyspaceIds(Context ctx,
}
return new SQLFuture>(
- Futures.transformAsync(client.executeBatchKeyspaceIds(ctx, requestBuilder.build()),
+ transformAsync(
+ client.executeBatchKeyspaceIds(ctx, requestBuilder.build()),
new AsyncFunction>() {
@Override
public ListenableFuture> apply(ExecuteBatchKeyspaceIdsResponse response)
throws Exception {
Proto.checkError(response.getError());
- return Futures
- .>immediateFuture(Proto.toCursorList(response.getResultsList()));
+ return Futures.>immediateFuture(
+ Proto.toCursorList(response.getResultsList()));
}
- }));
+ },
+ directExecutor()));
}
public Cursor streamExecute(Context ctx, String query, @Nullable Map bindVars,
@@ -432,14 +451,17 @@ public SQLFuture begin(Context ctx, boolean singleDB) throws SQLExcept
if (ctx.getCallerId() != null) {
requestBuilder.setCallerId(ctx.getCallerId());
}
- return new SQLFuture(Futures.transformAsync(client.begin(ctx, requestBuilder.build()),
- new AsyncFunction() {
- @Override
- public ListenableFuture apply(BeginResponse response) throws Exception {
- return Futures
- .immediateFuture(new VTGateTx(client, response.getSession(), keyspace));
- }
- }));
+ return new SQLFuture(
+ transformAsync(
+ client.begin(ctx, requestBuilder.build()),
+ new AsyncFunction() {
+ @Override
+ public ListenableFuture apply(BeginResponse response) throws Exception {
+ return Futures.immediateFuture(
+ new VTGateTx(client, response.getSession(), keyspace));
+ }
+ },
+ directExecutor()));
}
public SQLFuture> splitQuery(Context ctx, String keyspace,
@@ -458,7 +480,8 @@ public SQLFuture> splitQuery(Context ctx, String k
requestBuilder.setCallerId(ctx.getCallerId());
}
return new SQLFuture>(
- Futures.transformAsync(client.splitQuery(ctx, requestBuilder.build()),
+ transformAsync(
+ client.splitQuery(ctx, requestBuilder.build()),
new AsyncFunction>() {
@Override
public ListenableFuture> apply(
@@ -466,21 +489,24 @@ public ListenableFuture> apply(
return Futures.>immediateFuture(
response.getSplitsList());
}
- }));
+ },
+ directExecutor()));
}
public SQLFuture getSrvKeyspace(Context ctx, String keyspace) throws SQLException {
GetSrvKeyspaceRequest.Builder requestBuilder =
GetSrvKeyspaceRequest.newBuilder().setKeyspace(checkNotNull(keyspace));
return new SQLFuture(
- Futures.transformAsync(client.getSrvKeyspace(ctx, requestBuilder.build()),
+ transformAsync(
+ client.getSrvKeyspace(ctx, requestBuilder.build()),
new AsyncFunction() {
@Override
public ListenableFuture apply(GetSrvKeyspaceResponse response)
throws Exception {
return Futures.immediateFuture(response.getSrvKeyspace());
}
- }));
+ },
+ directExecutor()));
}
@Override
diff --git a/java/client/src/main/java/com/youtube/vitess/client/VTGateTx.java b/java/client/src/main/java/com/youtube/vitess/client/VTGateTx.java
index 5def7381d73..023089672ee 100644
--- a/java/client/src/main/java/com/youtube/vitess/client/VTGateTx.java
+++ b/java/client/src/main/java/com/youtube/vitess/client/VTGateTx.java
@@ -1,5 +1,9 @@
package com.youtube.vitess.client;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.util.concurrent.Futures.transformAsync;
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
+
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures;
@@ -32,37 +36,31 @@
import com.youtube.vitess.proto.Vtgate.RollbackRequest;
import com.youtube.vitess.proto.Vtgate.RollbackResponse;
import com.youtube.vitess.proto.Vtgate.Session;
-
-import javax.annotation.Nullable;
import java.sql.SQLDataException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-
-import static com.google.common.base.Preconditions.checkNotNull;
+import javax.annotation.Nullable;
/**
* An asynchronous VTGate transaction session.
*
- *
- * Because {@code VTGateTx} manages a session cookie, only one operation can be in flight at a time
- * on a given instance. The methods are {@code synchronized} only because the session cookie is
+ *
Because {@code VTGateTx} manages a session cookie, only one operation can be in flight at a
+ * time on a given instance. The methods are {@code synchronized} only because the session cookie is
* updated asynchronously when the RPC response comes back.
*
- *
- * After calling any method that returns a {@link SQLFuture}, you must wait for that future to
- * complete before calling any other methods on that {@code VTGateTx} instance. An
- * {@link IllegalStateException} will be thrown if this constraint is violated.
+ *
After calling any method that returns a {@link SQLFuture}, you must wait for that future to
+ * complete before calling any other methods on that {@code VTGateTx} instance. An {@link
+ * IllegalStateException} will be thrown if this constraint is violated.
*
- *
- * All operations on {@code VTGateTx} are asynchronous, including those whose ultimate return type
- * is {@link Void}, such as {@link #commit(Context)} and {@link #rollback(Context)}. You must still
- * wait for the futures returned by these methods to complete and check the error on them (such as
- * by calling {@code checkedGet()} before you can assume the operation has finished successfully.
+ *
All operations on {@code VTGateTx} are asynchronous, including those whose ultimate return
+ * type is {@link Void}, such as {@link #commit(Context)} and {@link #rollback(Context)}. You must
+ * still wait for the futures returned by these methods to complete and check the error on them
+ * (such as by calling {@code checkedGet()} before you can assume the operation has finished
+ * successfully.
*
- *
- * If you prefer a synchronous API, you can use {@link VTGateBlockingConn#begin(Context)}, which
+ *
If you prefer a synchronous API, you can use {@link VTGateBlockingConn#begin(Context)}, which
* returns a {@link VTGateBlockingTx} instead.
*/
public class VTGateTx {
@@ -94,15 +92,18 @@ public synchronized SQLFuture execute(Context ctx, String query, Map call =
- new SQLFuture<>(Futures.transformAsync(client.execute(ctx, requestBuilder.build()),
- new AsyncFunction() {
- @Override
- public ListenableFuture apply(ExecuteResponse response) throws Exception {
- setSession(response.getSession());
- Proto.checkError(response.getError());
- return Futures.immediateFuture(new SimpleCursor(response.getResult()));
- }
- }));
+ new SQLFuture<>(
+ transformAsync(
+ client.execute(ctx, requestBuilder.build()),
+ new AsyncFunction() {
+ @Override
+ public ListenableFuture apply(ExecuteResponse response) throws Exception {
+ setSession(response.getSession());
+ Proto.checkError(response.getError());
+ return Futures.immediateFuture(new SimpleCursor(response.getResult()));
+ }
+ },
+ directExecutor()));
lastCall = call;
return call;
}
@@ -125,16 +126,19 @@ public synchronized SQLFuture executeShards(Context ctx, String query, S
}
SQLFuture call =
- new SQLFuture<>(Futures.transformAsync(client.executeShards(ctx, requestBuilder.build()),
- new AsyncFunction() {
- @Override
- public ListenableFuture apply(ExecuteShardsResponse response)
- throws Exception {
- setSession(response.getSession());
- Proto.checkError(response.getError());
- return Futures.immediateFuture(new SimpleCursor(response.getResult()));
- }
- }));
+ new SQLFuture<>(
+ transformAsync(
+ client.executeShards(ctx, requestBuilder.build()),
+ new AsyncFunction() {
+ @Override
+ public ListenableFuture apply(ExecuteShardsResponse response)
+ throws Exception {
+ setSession(response.getSession());
+ Proto.checkError(response.getError());
+ return Futures.immediateFuture(new SimpleCursor(response.getResult()));
+ }
+ },
+ directExecutor()));
lastCall = call;
return call;
}
@@ -157,17 +161,20 @@ public synchronized SQLFuture executeKeyspaceIds(Context ctx, String que
requestBuilder.setCallerId(ctx.getCallerId());
}
- SQLFuture call = new SQLFuture<>(
- Futures.transformAsync(client.executeKeyspaceIds(ctx, requestBuilder.build()),
- new AsyncFunction() {
- @Override
- public ListenableFuture apply(ExecuteKeyspaceIdsResponse response)
- throws Exception {
- setSession(response.getSession());
- Proto.checkError(response.getError());
- return Futures.immediateFuture(new SimpleCursor(response.getResult()));
- }
- }));
+ SQLFuture call =
+ new SQLFuture<>(
+ transformAsync(
+ client.executeKeyspaceIds(ctx, requestBuilder.build()),
+ new AsyncFunction() {
+ @Override
+ public ListenableFuture apply(ExecuteKeyspaceIdsResponse response)
+ throws Exception {
+ setSession(response.getSession());
+ Proto.checkError(response.getError());
+ return Futures.immediateFuture(new SimpleCursor(response.getResult()));
+ }
+ },
+ directExecutor()));
lastCall = call;
return call;
}
@@ -191,16 +198,19 @@ public synchronized SQLFuture executeKeyRanges(Context ctx, String query
}
SQLFuture call =
- new SQLFuture<>(Futures.transformAsync(client.executeKeyRanges(ctx, requestBuilder.build()),
- new AsyncFunction() {
- @Override
- public ListenableFuture apply(ExecuteKeyRangesResponse response)
- throws Exception {
- setSession(response.getSession());
- Proto.checkError(response.getError());
- return Futures.immediateFuture(new SimpleCursor(response.getResult()));
- }
- }));
+ new SQLFuture<>(
+ transformAsync(
+ client.executeKeyRanges(ctx, requestBuilder.build()),
+ new AsyncFunction() {
+ @Override
+ public ListenableFuture apply(ExecuteKeyRangesResponse response)
+ throws Exception {
+ setSession(response.getSession());
+ Proto.checkError(response.getError());
+ return Futures.immediateFuture(new SimpleCursor(response.getResult()));
+ }
+ },
+ directExecutor()));
lastCall = call;
return call;
}
@@ -225,16 +235,19 @@ public synchronized SQLFuture executeEntityIds(Context ctx, String query
}
SQLFuture call =
- new SQLFuture<>(Futures.transformAsync(client.executeEntityIds(ctx, requestBuilder.build()),
- new AsyncFunction() {
- @Override
- public ListenableFuture apply(ExecuteEntityIdsResponse response)
- throws Exception {
- setSession(response.getSession());
- Proto.checkError(response.getError());
- return Futures.immediateFuture(new SimpleCursor(response.getResult()));
- }
- }));
+ new SQLFuture<>(
+ transformAsync(
+ client.executeEntityIds(ctx, requestBuilder.build()),
+ new AsyncFunction() {
+ @Override
+ public ListenableFuture apply(ExecuteEntityIdsResponse response)
+ throws Exception {
+ setSession(response.getSession());
+ Proto.checkError(response.getError());
+ return Futures.immediateFuture(new SimpleCursor(response.getResult()));
+ }
+ },
+ directExecutor()));
lastCall = call;
return call;
}
@@ -268,17 +281,20 @@ public SQLFuture> executeBatch(Context ctx, List q
requestBuilder.setCallerId(ctx.getCallerId());
}
- return new SQLFuture<>(Futures
- .transformAsync(client.executeBatch(ctx, requestBuilder.build()),
- new AsyncFunction>() {
- @Override public ListenableFuture> apply(
- Vtgate.ExecuteBatchResponse response) throws Exception {
- setSession(response.getSession());
- Proto.checkError(response.getError());
- return Futures.immediateFuture(
- Proto.fromQueryResponsesToCursorList(response.getResultsList()));
- }
- }));
+ return new SQLFuture<>(
+ transformAsync(
+ client.executeBatch(ctx, requestBuilder.build()),
+ new AsyncFunction>() {
+ @Override
+ public ListenableFuture> apply(
+ Vtgate.ExecuteBatchResponse response) throws Exception {
+ setSession(response.getSession());
+ Proto.checkError(response.getError());
+ return Futures.immediateFuture(
+ Proto.fromQueryResponsesToCursorList(response.getResultsList()));
+ }
+ },
+ directExecutor()));
}
public synchronized SQLFuture> executeBatchShards(Context ctx,
@@ -296,18 +312,21 @@ public synchronized SQLFuture> executeBatchShards(Context ctx,
requestBuilder.setCallerId(ctx.getCallerId());
}
- SQLFuture> call = new SQLFuture<>(
- Futures.transformAsync(client.executeBatchShards(ctx, requestBuilder.build()),
- new AsyncFunction>() {
- @Override
- public ListenableFuture> apply(ExecuteBatchShardsResponse response)
- throws Exception {
- setSession(response.getSession());
- Proto.checkError(response.getError());
- return Futures
- .>immediateFuture(Proto.toCursorList(response.getResultsList()));
- }
- }));
+ SQLFuture> call =
+ new SQLFuture<>(
+ transformAsync(
+ client.executeBatchShards(ctx, requestBuilder.build()),
+ new AsyncFunction>() {
+ @Override
+ public ListenableFuture> apply(ExecuteBatchShardsResponse response)
+ throws Exception {
+ setSession(response.getSession());
+ Proto.checkError(response.getError());
+ return Futures.>immediateFuture(
+ Proto.toCursorList(response.getResultsList()));
+ }
+ },
+ directExecutor()));
lastCall = call;
return call;
}
@@ -328,18 +347,21 @@ public synchronized SQLFuture> executeBatchKeyspaceIds(Context ctx,
requestBuilder.setCallerId(ctx.getCallerId());
}
- SQLFuture> call = new SQLFuture<>(
- Futures.transformAsync(client.executeBatchKeyspaceIds(ctx, requestBuilder.build()),
- new AsyncFunction>() {
- @Override
- public ListenableFuture> apply(ExecuteBatchKeyspaceIdsResponse response)
- throws Exception {
- setSession(response.getSession());
- Proto.checkError(response.getError());
- return Futures
- .>immediateFuture(Proto.toCursorList(response.getResultsList()));
- }
- }));
+ SQLFuture> call =
+ new SQLFuture<>(
+ transformAsync(
+ client.executeBatchKeyspaceIds(ctx, requestBuilder.build()),
+ new AsyncFunction>() {
+ @Override
+ public ListenableFuture> apply(
+ ExecuteBatchKeyspaceIdsResponse response) throws Exception {
+ setSession(response.getSession());
+ Proto.checkError(response.getError());
+ return Futures.>immediateFuture(
+ Proto.toCursorList(response.getResultsList()));
+ }
+ },
+ directExecutor()));
lastCall = call;
return call;
}
@@ -356,14 +378,17 @@ public synchronized SQLFuture commit(Context ctx, boolean atomic) throws S
requestBuilder.setCallerId(ctx.getCallerId());
}
SQLFuture call =
- new SQLFuture<>(Futures.transformAsync(client.commit(ctx, requestBuilder.build()),
- new AsyncFunction() {
- @Override
- public ListenableFuture apply(CommitResponse response) throws Exception {
- setSession(null);
- return Futures.immediateFuture(null);
- }
- }));
+ new SQLFuture<>(
+ transformAsync(
+ client.commit(ctx, requestBuilder.build()),
+ new AsyncFunction() {
+ @Override
+ public ListenableFuture apply(CommitResponse response) throws Exception {
+ setSession(null);
+ return Futures.immediateFuture(null);
+ }
+ },
+ directExecutor()));
lastCall = call;
return call;
}
@@ -375,14 +400,17 @@ public synchronized SQLFuture rollback(Context ctx) throws SQLException {
requestBuilder.setCallerId(ctx.getCallerId());
}
SQLFuture call =
- new SQLFuture<>(Futures.transformAsync(client.rollback(ctx, requestBuilder.build()),
- new AsyncFunction() {
- @Override
- public ListenableFuture apply(RollbackResponse response) throws Exception {
- setSession(null);
- return Futures.immediateFuture(null);
- }
- }));
+ new SQLFuture<>(
+ transformAsync(
+ client.rollback(ctx, requestBuilder.build()),
+ new AsyncFunction() {
+ @Override
+ public ListenableFuture apply(RollbackResponse response) throws Exception {
+ setSession(null);
+ return Futures.immediateFuture(null);
+ }
+ },
+ directExecutor()));
lastCall = call;
return call;
}
diff --git a/java/client/src/main/java/com/youtube/vitess/client/cursor/CursorWithError.java b/java/client/src/main/java/com/youtube/vitess/client/cursor/CursorWithError.java
index 4ca6589739e..21741772d6a 100644
--- a/java/client/src/main/java/com/youtube/vitess/client/cursor/CursorWithError.java
+++ b/java/client/src/main/java/com/youtube/vitess/client/cursor/CursorWithError.java
@@ -13,7 +13,7 @@ public class CursorWithError {
public CursorWithError(Query.ResultWithError resultWithError) {
if (!resultWithError.hasError() ||
- Vtrpc.ErrorCode.SUCCESS == resultWithError.getError().getCode()) {
+ Vtrpc.Code.OK == resultWithError.getError().getCode()) {
this.cursor = new SimpleCursor(resultWithError.getResult());
this.error = null;
} else {
diff --git a/java/client/src/test/java/com/youtube/vitess/client/TestUtil.java b/java/client/src/test/java/com/youtube/vitess/client/TestUtil.java
index 60e9db440b6..e292173f4a4 100644
--- a/java/client/src/test/java/com/youtube/vitess/client/TestUtil.java
+++ b/java/client/src/test/java/com/youtube/vitess/client/TestUtil.java
@@ -47,10 +47,10 @@ public static void setupTestEnv(TestEnv testEnv) throws Exception {
continue;
}
try {
- Type mapType = new TypeToken