diff --git a/.gitignore b/.gitignore index 5657f6ea..10e8d7ac 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -vendor \ No newline at end of file +vendor +go.sum diff --git a/examples/proxy-server/proxy-server.go b/examples/proxy-server/proxy-server.go new file mode 100644 index 00000000..5b5ad8cf --- /dev/null +++ b/examples/proxy-server/proxy-server.go @@ -0,0 +1,24 @@ +// Copyright 2019 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "net/http" + + "github.com/tikv/client-go/proxy/httpproxy" +) + +func main() { + http.ListenAndServe(":8080", httpproxy.NewHTTPProxyHandler()) +} diff --git a/go.mod b/go.mod index d8826b2e..9db51d37 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,8 @@ require ( github.com/golang/protobuf v1.2.0 github.com/golang/snappy v0.0.1 // indirect github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c - github.com/gorilla/mux v1.7.0 // indirect + github.com/google/uuid v1.1.1 + github.com/gorilla/mux v1.7.0 github.com/gorilla/websocket v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 diff --git a/go.sum b/go.sum deleted file mode 100644 index eccf774e..00000000 --- a/go.sum +++ /dev/null @@ -1,179 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.12+incompatible h1:pAWNwdf7QiT1zfaWyqCtNZQWCLByQyA3JrSQyuYAqnQ= -github.com/coreos/etcd v3.3.12+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190212144455-93d5ec2c7f76 h1:FE783w8WFh+Rvg+7bZ5g8p7gP4SeVS4AoNwkvazlsBg= -github.com/coreos/go-systemd v0.0.0-20190212144455-93d5ec2c7f76/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso= -github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o= -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gogo/protobuf v0.0.0-20180717141946-636bf0302bc9 h1:EGUd+AQfZoi1OwZAoqekLbl4kq6tafFtKQSiN8nL21Y= -github.com/gogo/protobuf v0.0.0-20180717141946-636bf0302bc9/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v0.0.0-20180814211427-aa810b61a9c7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U= -github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.8.1 h1:VNUuLKyFcJ5IektwBKcZU4J5GJKEt+Odb8dl1d61BGQ= -github.com/grpc-ecosystem/grpc-gateway v1.8.1/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/montanaflynn/stats v0.5.0 h1:2EkzeTSqBB4V4bJwWrt5gIIrZmpJBcoIRGS2kWLgzmk= -github.com/montanaflynn/stats v0.5.0/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8 h1:USx2/E1bX46VG32FIw034Au6seQ2fY9NEILmNh/UlQg= -github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8/go.mod h1:B1+S9LNcuMyLH/4HMTViQOJevkGiik3wW2AN9zb2fNQ= -github.com/pingcap/gofail v0.0.0-20181217135706-6a951c1e42c3 h1:04yuCf5NMvLU8rB2m4Qs3rynH7EYpMno3lHkewIOdMo= -github.com/pingcap/gofail v0.0.0-20181217135706-6a951c1e42c3/go.mod h1:DazNTg0PTldtpsQiT9I5tVJwV1onHMKBBgXzmJUlMns= -github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e h1:P73/4dPCL96rGrobssy1nVy2VaVpNCuLpCbr+FEaTA8= -github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e/go.mod h1:O17XtbryoCJhkKGbT62+L2OlrniwqiGLSqrmdHCMzZw= -github.com/pingcap/kvproto v0.0.0-20190305055742-ab7debc182d9 h1:EsTt42btov+tFchxOFKnxBNmXOWyPKiddOwvr/WO90g= -github.com/pingcap/kvproto v0.0.0-20190305055742-ab7debc182d9/go.mod h1:QMdbTAXCHzzygQzqcG9uVUgU2fKeSN1GmfMiykdSzzY= -github.com/pingcap/pd v2.1.5+incompatible h1:vOLV2tSQdRjjmxaTXtJULoC94dYQOd+6fzn2yChODHc= -github.com/pingcap/pd v2.1.5+incompatible/go.mod h1:nD3+EoYes4+aNNODO99ES59V83MZSI+dFbhyr667a0E= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= -github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446 h1:/NRJ5vAYoqz+7sG51ubIDHXeWO8DlTSrToPu6q11ziA= -github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME= -github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.2 h1:JON3E2/GPW2iDNGoSAusl1KDf5TRQ8k8q7Tp097pZGs= -github.com/ugorji/go v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= -github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43 h1:BasDe+IErOQKrMVXab7UayvSlIpiyGwRvuX3EKYY7UA= -github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA= -github.com/unrolled/render v1.0.0 h1:XYtvhA3UkpB7PqkvhUFYmpKD55OudoIeygcfus4vcd4= -github.com/unrolled/render v1.0.0/go.mod h1:tu82oB5W2ykJRVioYsB+IQKcft7ryBr7w12qMBUPyXg= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8 h1:YoY1wS6JYVRpIfFngRf2HHo9R9dAne3xbkGOQ5rJXjU= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181004005441-af9cb2a35e7f h1:FU37niK8AQ59mHcskRyQL7H0ErSeNh650vdcj8HqdSI= -google.golang.org/genproto v0.0.0-20181004005441-af9cb2a35e7f/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/grpc v0.0.0-20180607172857-7a6a684ca69e/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/proxy/httpproxy/handler.go b/proxy/httpproxy/handler.go new file mode 100644 index 00000000..99261f1e --- /dev/null +++ b/proxy/httpproxy/handler.go @@ -0,0 +1,70 @@ +// Copyright 2019 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package httpproxy + +import ( + "net/http" + + "github.com/gorilla/mux" + "github.com/tikv/client-go/proxy" +) + +// NewHTTPProxyHandler creates an http.Handler that serves as a TiKV client proxy. +func NewHTTPProxyHandler() http.Handler { + router := mux.NewRouter() + rawkv := rawkvHandler{p: proxy.NewRaw()} + + router.HandleFunc("/rawkv/client/new", rawkv.handlerFunc(rawkv.New)) + router.HandleFunc("/rawkv/client/{id}/close", rawkv.handlerFunc(rawkv.Close)) + router.HandleFunc("/rawkv/client/{id}/get", rawkv.handlerFunc(rawkv.Get)) + router.HandleFunc("/rawkv/client/{id}/batch-get", rawkv.handlerFunc(rawkv.BatchGet)) + router.HandleFunc("/rawkv/client/{id}/put", rawkv.handlerFunc(rawkv.Put)) + router.HandleFunc("/rawkv/client/{id}/batch-put", rawkv.handlerFunc(rawkv.BatchPut)) + router.HandleFunc("/rawkv/client/{id}/delete", rawkv.handlerFunc(rawkv.Delete)) + router.HandleFunc("/rawkv/client/{id}/batch-delete", rawkv.handlerFunc(rawkv.BatchDelete)) + router.HandleFunc("/rawkv/client/{id}/delete-range", rawkv.handlerFunc(rawkv.DeleteRange)) + router.HandleFunc("/rawkv/client/{id}/scan", rawkv.handlerFunc(rawkv.Scan)) + + txnkv := txnkvHandler{p: proxy.NewTxn()} + router.HandleFunc("/txnkv/client/new", txnkv.handlerFunc(txnkv.New)) + router.HandleFunc("/txnkv/client/{id}/close", txnkv.handlerFunc(txnkv.Close)) + router.HandleFunc("/txnkv/client/{id}/begin", txnkv.handlerFunc(txnkv.Begin)) + router.HandleFunc("/txnkv/client/{id}/begin-with-ts", txnkv.handlerFunc(txnkv.BeginWithTS)) + router.HandleFunc("/txnkv/client/{id}/get-ts", txnkv.handlerFunc(txnkv.GetTS)) + router.HandleFunc("/txnkv/txn/{id}/get", txnkv.handlerFunc(txnkv.TxnGet)) + router.HandleFunc("/txnkv/txn/{id}/batch-get", txnkv.handlerFunc(txnkv.TxnBatchGet)) + router.HandleFunc("/txnkv/txn/{id}/set", txnkv.handlerFunc(txnkv.TxnSet)) + router.HandleFunc("/txnkv/txn/{id}/iter", txnkv.handlerFunc(txnkv.TxnIter)) + router.HandleFunc("/txnkv/txn/{id}/iter-reverse", txnkv.handlerFunc(txnkv.TxnIterReverse)) + router.HandleFunc("/txnkv/txn/{id}/readonly", txnkv.handlerFunc(txnkv.TxnIsReadOnly)) + router.HandleFunc("/txnkv/txn/{id}/delete", txnkv.handlerFunc(txnkv.TxnDelete)) + router.HandleFunc("/txnkv/txn/{id}/commit", txnkv.handlerFunc(txnkv.TxnCommit)) + router.HandleFunc("/txnkv/txn/{id}/rollback", txnkv.handlerFunc(txnkv.TxnRollback)) + router.HandleFunc("/txnkv/txn/{id}/lock-keys", txnkv.handlerFunc(txnkv.TxnLockKeys)) + router.HandleFunc("/txnkv/txn/{id}/valid", txnkv.handlerFunc(txnkv.TxnValid)) + router.HandleFunc("/txnkv/txn/{id}/len", txnkv.handlerFunc(txnkv.TxnLen)) + router.HandleFunc("/txnkv/txn/{id}/size", txnkv.handlerFunc(txnkv.TxnSize)) + router.HandleFunc("/txnkv/iter/{id}/valid", txnkv.handlerFunc(txnkv.IterValid)) + router.HandleFunc("/txnkv/iter/{id}/key", txnkv.handlerFunc(txnkv.IterKey)) + router.HandleFunc("/txnkv/iter/{id}/value", txnkv.handlerFunc(txnkv.IterValue)) + router.HandleFunc("/txnkv/iter/{id}/next", txnkv.handlerFunc(txnkv.IterNext)) + router.HandleFunc("/txnkv/iter/{id}/close", txnkv.handlerFunc(txnkv.IterClose)) + + router.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) + w.Write([]byte("not implemented")) + }) + + return router +} diff --git a/proxy/httpproxy/rawkv.go b/proxy/httpproxy/rawkv.go new file mode 100644 index 00000000..3f21f151 --- /dev/null +++ b/proxy/httpproxy/rawkv.go @@ -0,0 +1,159 @@ +// Copyright 2019 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package httpproxy + +import ( + "encoding/json" + "io/ioutil" + "net/http" + + "github.com/gorilla/mux" + "github.com/tikv/client-go/config" + "github.com/tikv/client-go/proxy" +) + +type rawkvHandler struct { + p proxy.RawKVProxy +} + +// RawRequest is the structure of a rawkv request that the http proxy accepts. +type RawRequest struct { + PDAddrs []string `json:"pd_addrs,omitempty"` // for new + Key []byte `json:"key,omitempty"` // for get, put, delete + Keys [][]byte `json:"keys,omitempty"` // for batchGet, batchPut, batchDelete + Value []byte `json:"value,omitempty"` // for put + Values [][]byte `json:"values,omitmepty"` // for batchPut + StartKey []byte `json:"start_key,omitempty"` // for scan, deleteRange + EndKey []byte `json:"end_key,omitempty"` // for scan, deleteRange + Limit int `json:"limit,omitempty"` // for scan +} + +// RawResponse is the structure of a rawkv response that the http proxy sends. +type RawResponse struct { + ID string `json:"id,omitempty"` // for new + Value []byte `json:"value,omitempty"` // for get + Keys [][]byte `json:"keys,omitempty"` // for scan + Values [][]byte `json:"values,omitempty"` // for batchGet +} + +func (h rawkvHandler) New(vars map[string]string, r *RawRequest) (*RawResponse, int, error) { + id, err := h.p.New(r.PDAddrs, config.Security{}) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &RawResponse{ID: string(id)}, http.StatusCreated, nil +} + +func (h rawkvHandler) Close(vars map[string]string, r *RawRequest) (*RawResponse, int, error) { + if err := h.p.Close(proxy.UUID(vars["id"])); err != nil { + return nil, http.StatusInternalServerError, err + } + return &RawResponse{}, http.StatusOK, nil +} + +func (h rawkvHandler) Get(vars map[string]string, r *RawRequest) (*RawResponse, int, error) { + val, err := h.p.Get(proxy.UUID(vars["id"]), r.Key) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &RawResponse{Value: val}, http.StatusOK, nil +} + +func (h rawkvHandler) BatchGet(vars map[string]string, r *RawRequest) (*RawResponse, int, error) { + vals, err := h.p.BatchGet(proxy.UUID(vars["id"]), r.Keys) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &RawResponse{Values: vals}, http.StatusOK, nil +} + +func (h rawkvHandler) Put(vars map[string]string, r *RawRequest) (*RawResponse, int, error) { + err := h.p.Put(proxy.UUID(vars["id"]), r.Key, r.Value) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &RawResponse{}, http.StatusOK, nil +} + +func (h rawkvHandler) BatchPut(vars map[string]string, r *RawRequest) (*RawResponse, int, error) { + err := h.p.BatchPut(proxy.UUID(vars["id"]), r.Keys, r.Values) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &RawResponse{}, http.StatusOK, nil +} + +func (h rawkvHandler) Delete(vars map[string]string, r *RawRequest) (*RawResponse, int, error) { + err := h.p.Delete(proxy.UUID(vars["id"]), r.Key) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &RawResponse{}, http.StatusOK, nil +} + +func (h rawkvHandler) BatchDelete(vars map[string]string, r *RawRequest) (*RawResponse, int, error) { + err := h.p.BatchDelete(proxy.UUID(vars["id"]), r.Keys) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &RawResponse{}, http.StatusOK, nil +} + +func (h rawkvHandler) DeleteRange(vars map[string]string, r *RawRequest) (*RawResponse, int, error) { + err := h.p.DeleteRange(proxy.UUID(vars["id"]), r.StartKey, r.EndKey) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &RawResponse{}, http.StatusOK, nil +} + +func (h rawkvHandler) Scan(vars map[string]string, r *RawRequest) (*RawResponse, int, error) { + keys, values, err := h.p.Scan(proxy.UUID(vars["id"]), r.StartKey, r.EndKey, r.Limit) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &RawResponse{Keys: keys, Values: values}, http.StatusOK, nil +} + +func (h rawkvHandler) handlerFunc(f func(map[string]string, *RawRequest) (*RawResponse, int, error)) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + data, err := ioutil.ReadAll(r.Body) + if err != nil { + sendError(w, err, http.StatusBadRequest) + return + } + var req RawRequest + if err = json.Unmarshal(data, &req); err != nil { + sendError(w, err, http.StatusBadRequest) + return + } + res, status, err := f(mux.Vars(r), &req) + if err != nil { + sendError(w, err, status) + return + } + data, err = json.Marshal(res) + if err != nil { + sendError(w, err, http.StatusInternalServerError) + return + } + w.WriteHeader(status) + w.Write(data) + } +} + +func sendError(w http.ResponseWriter, err error, status int) { + w.WriteHeader(status) + w.Write([]byte(err.Error())) +} diff --git a/proxy/httpproxy/txnkv.go b/proxy/httpproxy/txnkv.go new file mode 100644 index 00000000..55644e66 --- /dev/null +++ b/proxy/httpproxy/txnkv.go @@ -0,0 +1,268 @@ +// Copyright 2019 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package httpproxy + +import ( + "encoding/json" + "io/ioutil" + "net/http" + + "github.com/gorilla/mux" + "github.com/tikv/client-go/config" + "github.com/tikv/client-go/proxy" +) + +type txnkvHandler struct { + p proxy.TxnKVProxy +} + +// TxnRequest is the structure of a txnkv request that the http proxy accepts. +type TxnRequest struct { + PDAddrs []string `json:"pd_addrs,omitempty"` // for new + TS uint64 `json:"ts,omitempty"` // for beginWithTS + Key []byte `json:"key,omitempty"` // for get, set, delete, iter, iterReverse + Value []byte `json:"value,omitempty"` // for set + Keys [][]byte `json:"keys,omitempty"` // for batchGet, lockKeys + UpperBound []byte `json:"upper_bound,omitempty"` // for iter +} + +// TxnResponse is the structure of a txnkv response that the http proxy sends. +type TxnResponse struct { + ID string `json:"id,omitempty"` // for new, begin, beginWithTS, iter, iterReverse + TS uint64 `json:"ts,omitempty"` // for getTS + Key []byte `json:"key,omitempty"` // for iterKey + Value []byte `json:"value,omitempty"` // for get, iterValue + Keys [][]byte `json:"keys,omitempty"` // for batchGet + Values [][]byte `json:"values,omitempty"` // for batchGet + IsValid bool `json:"is_valid,omitempty"` // for valid, iterValid + IsReadOnly bool `json:"is_readonly,omitempty"` // for isReadOnly + Size int `json:"size,omitempty"` // for size + Length int `json:"length,omitempty"` // for length +} + +func (h txnkvHandler) New(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + id, err := h.p.New(r.PDAddrs, config.Security{}) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{ID: string(id)}, http.StatusOK, nil +} + +func (h txnkvHandler) Close(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + err := h.p.Close(proxy.UUID(vars["id"])) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{}, http.StatusOK, nil +} + +func (h txnkvHandler) Begin(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + txnID, err := h.p.Begin(proxy.UUID(vars["id"])) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{ID: string(txnID)}, http.StatusCreated, nil +} + +func (h txnkvHandler) BeginWithTS(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + txnID, err := h.p.BeginWithTS(proxy.UUID(vars["id"]), r.TS) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{ID: string(txnID)}, http.StatusOK, nil +} + +func (h txnkvHandler) GetTS(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + ts, err := h.p.GetTS(proxy.UUID(vars["id"])) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{TS: ts}, http.StatusOK, nil +} + +func (h txnkvHandler) TxnGet(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + val, err := h.p.TxnGet(proxy.UUID(vars["id"]), r.Key) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{Value: val}, http.StatusOK, nil +} + +func (h txnkvHandler) TxnBatchGet(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + m, err := h.p.TxnBatchGet(proxy.UUID(vars["id"]), r.Keys) + if err != nil { + return nil, http.StatusInternalServerError, err + } + keys, values := make([][]byte, 0, len(m)), make([][]byte, 0, len(m)) + for k, v := range m { + keys = append(keys, []byte(k)) + values = append(values, v) + } + return &TxnResponse{Keys: keys, Values: values}, http.StatusOK, nil +} + +func (h txnkvHandler) TxnSet(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + err := h.p.TxnSet(proxy.UUID(vars["id"]), r.Key, r.Value) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{}, http.StatusOK, nil +} + +func (h txnkvHandler) TxnIter(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + iterID, err := h.p.TxnIter(proxy.UUID(vars["id"]), r.Key, r.UpperBound) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{ID: string(iterID)}, http.StatusCreated, nil +} + +func (h txnkvHandler) TxnIterReverse(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + iterID, err := h.p.TxnIterReverse(proxy.UUID(vars["id"]), r.Key) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{ID: string(iterID)}, http.StatusCreated, nil +} + +func (h txnkvHandler) TxnIsReadOnly(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + readonly, err := h.p.TxnIsReadOnly(proxy.UUID(vars["id"])) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{IsReadOnly: readonly}, http.StatusOK, nil +} + +func (h txnkvHandler) TxnDelete(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + err := h.p.TxnDelete(proxy.UUID(vars["id"]), r.Key) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{}, http.StatusOK, nil +} + +func (h txnkvHandler) TxnCommit(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + err := h.p.TxnCommit(proxy.UUID(vars["id"])) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{}, http.StatusOK, nil +} + +func (h txnkvHandler) TxnRollback(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + err := h.p.TxnRollback(proxy.UUID(vars["id"])) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{}, http.StatusOK, nil +} + +func (h txnkvHandler) TxnLockKeys(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + err := h.p.TxnLockKeys(proxy.UUID(vars["id"]), r.Keys) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{}, http.StatusOK, nil +} + +func (h txnkvHandler) TxnValid(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + valid, err := h.p.TxnValid(proxy.UUID(vars["id"])) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{IsValid: valid}, http.StatusOK, nil +} + +func (h txnkvHandler) TxnLen(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + length, err := h.p.TxnLen(proxy.UUID(vars["id"])) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{Length: length}, http.StatusOK, nil +} + +func (h txnkvHandler) TxnSize(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + size, err := h.p.TxnSize(proxy.UUID(vars["id"])) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{Size: size}, http.StatusOK, nil +} + +func (h txnkvHandler) IterValid(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + valid, err := h.p.IterValid(proxy.UUID(vars["id"])) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{IsValid: valid}, http.StatusOK, nil +} + +func (h txnkvHandler) IterKey(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + key, err := h.p.IterKey(proxy.UUID(vars["id"])) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{Key: key}, http.StatusOK, nil +} + +func (h txnkvHandler) IterValue(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + val, err := h.p.IterValue(proxy.UUID(vars["id"])) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{Value: val}, http.StatusOK, nil +} + +func (h txnkvHandler) IterNext(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + err := h.p.IterNext(proxy.UUID(vars["id"])) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{}, http.StatusOK, nil +} + +func (h txnkvHandler) IterClose(vars map[string]string, r *TxnRequest) (*TxnResponse, int, error) { + err := h.p.IterClose(proxy.UUID(vars["id"])) + if err != nil { + return nil, http.StatusInternalServerError, err + } + return &TxnResponse{}, http.StatusOK, nil +} + +func (h txnkvHandler) handlerFunc(f func(map[string]string, *TxnRequest) (*TxnResponse, int, error)) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + data, err := ioutil.ReadAll(r.Body) + if err != nil { + sendError(w, err, http.StatusBadRequest) + return + } + var req TxnRequest + if err = json.Unmarshal(data, &req); err != nil { + sendError(w, err, http.StatusBadRequest) + return + } + res, status, err := f(mux.Vars(r), &req) + if err != nil { + sendError(w, err, status) + return + } + data, err = json.Marshal(res) + if err != nil { + sendError(w, err, http.StatusInternalServerError) + return + } + w.WriteHeader(status) + w.Write(data) + } +} diff --git a/proxy/rawkv.go b/proxy/rawkv.go new file mode 100644 index 00000000..dd5b43d6 --- /dev/null +++ b/proxy/rawkv.go @@ -0,0 +1,129 @@ +// Copyright 2019 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package proxy + +import ( + "sync" + + "github.com/pkg/errors" + "github.com/tikv/client-go/config" + "github.com/tikv/client-go/rawkv" +) + +// RawKVProxy implements proxy to use rawkv API. +// It is safe to copy by value or access concurrently. +type RawKVProxy struct { + clients *sync.Map +} + +// NewRaw creates a RawKVProxy instance. +func NewRaw() RawKVProxy { + return RawKVProxy{ + clients: &sync.Map{}, + } +} + +// New creates a new client and returns the client's UUID. +func (p RawKVProxy) New(pdAddrs []string, security config.Security) (UUID, error) { + client, err := rawkv.NewClient(pdAddrs, security) + if err != nil { + return "", err + } + return insertWithRetry(p.clients, client), nil +} + +// Close releases a rawkv client. +func (p RawKVProxy) Close(id UUID) error { + client, ok := p.clients.Load(id) + if !ok { + return errors.WithStack(ErrClientNotFound) + } + if err := client.(*rawkv.Client).Close(); err != nil { + return err + } + p.clients.Delete(id) + return nil +} + +// Get queries value with the key. +func (p RawKVProxy) Get(id UUID, key []byte) ([]byte, error) { + client, ok := p.clients.Load(id) + if !ok { + return nil, errors.WithStack(ErrClientNotFound) + } + return client.(*rawkv.Client).Get(key) +} + +// BatchGet queries values with the keys. +func (p RawKVProxy) BatchGet(id UUID, keys [][]byte) ([][]byte, error) { + client, ok := p.clients.Load(id) + if !ok { + return nil, errors.WithStack(ErrClientNotFound) + } + return client.(*rawkv.Client).BatchGet(keys) +} + +// Put stores a key-value pair to TiKV. +func (p RawKVProxy) Put(id UUID, key, value []byte) error { + client, ok := p.clients.Load(id) + if !ok { + return errors.WithStack(ErrClientNotFound) + } + return client.(*rawkv.Client).Put(key, value) +} + +// BatchPut stores key-value pairs to TiKV. +func (p RawKVProxy) BatchPut(id UUID, keys, values [][]byte) error { + client, ok := p.clients.Load(id) + if !ok { + return errors.WithStack(ErrClientNotFound) + } + return client.(*rawkv.Client).BatchPut(keys, values) +} + +// Delete deletes a key-value pair from TiKV. +func (p RawKVProxy) Delete(id UUID, key []byte) error { + client, ok := p.clients.Load(id) + if !ok { + return errors.WithStack(ErrClientNotFound) + } + return client.(*rawkv.Client).Delete(key) +} + +// BatchDelete deletes key-value pairs from TiKV. +func (p RawKVProxy) BatchDelete(id UUID, keys [][]byte) error { + client, ok := p.clients.Load(id) + if !ok { + return errors.WithStack(ErrClientNotFound) + } + return client.(*rawkv.Client).BatchDelete(keys) +} + +// DeleteRange deletes all key-value pairs in a range from TiKV. +func (p RawKVProxy) DeleteRange(id UUID, startKey []byte, endKey []byte) error { + client, ok := p.clients.Load(id) + if !ok { + return errors.WithStack(ErrClientNotFound) + } + return client.(*rawkv.Client).DeleteRange(startKey, endKey) +} + +// Scan queries continuous kv pairs in range [startKey, endKey), up to limit pairs. +func (p RawKVProxy) Scan(id UUID, startKey, endKey []byte, limit int) ([][]byte, [][]byte, error) { + client, ok := p.clients.Load(id) + if !ok { + return nil, nil, errors.WithStack(ErrClientNotFound) + } + return client.(*rawkv.Client).Scan(startKey, endKey, limit) +} diff --git a/proxy/txnkv.go b/proxy/txnkv.go new file mode 100644 index 00000000..617b9627 --- /dev/null +++ b/proxy/txnkv.go @@ -0,0 +1,274 @@ +// Copyright 2019 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package proxy + +import ( + "context" + "sync" + "unsafe" + + "github.com/pkg/errors" + "github.com/tikv/client-go/config" + "github.com/tikv/client-go/key" + "github.com/tikv/client-go/txnkv" + "github.com/tikv/client-go/txnkv/kv" +) + +// TxnKVProxy implements proxy to use txnkv API. +// It is safe to copy by value or access concurrently. +type TxnKVProxy struct { + clients *sync.Map + txns *sync.Map + iterators *sync.Map +} + +// NewTxn creates a TxnKVProxy instance. +func NewTxn() TxnKVProxy { + return TxnKVProxy{ + clients: &sync.Map{}, + txns: &sync.Map{}, + iterators: &sync.Map{}, + } +} + +// New creates a new client and returns the client's UUID. +func (p TxnKVProxy) New(pdAddrs []string, security config.Security) (UUID, error) { + client, err := txnkv.NewClient(pdAddrs, security) + if err != nil { + return "", err + } + return insertWithRetry(p.clients, client), nil +} + +// Close releases a txnkv client. +func (p TxnKVProxy) Close(id UUID) error { + client, ok := p.clients.Load(id) + if !ok { + return errors.WithStack(ErrClientNotFound) + } + if err := client.(*txnkv.Client).Close(); err != nil { + return err + } + p.clients.Delete(id) + return nil +} + +// Begin starts a new transaction and returns its UUID. +func (p TxnKVProxy) Begin(id UUID) (UUID, error) { + client, ok := p.clients.Load(id) + if !ok { + return "", errors.WithStack(ErrClientNotFound) + } + txn, err := client.(*txnkv.Client).Begin() + if err != nil { + return "", err + } + return insertWithRetry(p.txns, txn), nil +} + +// BeginWithTS starts a new transaction with given ts and returns its UUID. +func (p TxnKVProxy) BeginWithTS(id UUID, ts uint64) (UUID, error) { + client, ok := p.clients.Load(id) + if !ok { + return "", errors.WithStack(ErrClientNotFound) + } + return insertWithRetry(p.txns, client.(*txnkv.Client).BeginWithTS(ts)), nil +} + +// GetTS returns a latest timestamp. +func (p TxnKVProxy) GetTS(id UUID) (uint64, error) { + client, ok := p.clients.Load(id) + if !ok { + return 0, errors.WithStack(ErrClientNotFound) + } + return client.(*txnkv.Client).GetTS() +} + +// TxnGet queries value for the given key from TiKV server. +func (p TxnKVProxy) TxnGet(id UUID, key []byte) ([]byte, error) { + txn, ok := p.txns.Load(id) + if !ok { + return nil, errors.WithStack(ErrTxnNotFound) + } + return txn.(*txnkv.Transaction).Get(key) +} + +// TxnBatchGet gets a batch of values from TiKV server. +func (p TxnKVProxy) TxnBatchGet(id UUID, keys [][]byte) (map[string][]byte, error) { + txn, ok := p.txns.Load(id) + if !ok { + return nil, errors.WithStack(ErrTxnNotFound) + } + ks := *(*[]key.Key)(unsafe.Pointer(&keys)) + return txn.(*txnkv.Transaction).BatchGet(ks) +} + +// TxnSet sets the value for key k as v into TiKV server. +func (p TxnKVProxy) TxnSet(id UUID, k []byte, v []byte) error { + txn, ok := p.txns.Load(id) + if !ok { + return errors.WithStack(ErrTxnNotFound) + } + return txn.(*txnkv.Transaction).Set(k, v) +} + +// TxnIter creates an Iterator positioned on the first entry that key <= entry's +// key and returns the Iterator's UUID. +func (p TxnKVProxy) TxnIter(id UUID, key []byte, upperBound []byte) (UUID, error) { + txn, ok := p.txns.Load(id) + if !ok { + return "", errors.WithStack(ErrTxnNotFound) + } + iter, err := txn.(*txnkv.Transaction).Iter(key, upperBound) + if err != nil { + return "", err + } + return insertWithRetry(p.iterators, iter), nil +} + +// TxnIterReverse creates a reversed Iterator positioned on the first entry +// which key is less than key and returns the Iterator's UUID. +func (p TxnKVProxy) TxnIterReverse(id UUID, key []byte) (UUID, error) { + txn, ok := p.txns.Load(id) + if !ok { + return "", errors.WithStack(ErrTxnNotFound) + } + iter, err := txn.(*txnkv.Transaction).IterReverse(key) + if err != nil { + return "", err + } + return insertWithRetry(p.iterators, iter), nil +} + +// TxnIsReadOnly returns if there are pending key-value to commit in the transaction. +func (p TxnKVProxy) TxnIsReadOnly(id UUID) (bool, error) { + txn, ok := p.txns.Load(id) + if !ok { + return false, errors.WithStack(ErrTxnNotFound) + } + return txn.(*txnkv.Transaction).IsReadOnly(), nil +} + +// TxnDelete removes the entry for key from TiKV server. +func (p TxnKVProxy) TxnDelete(id UUID, key []byte) error { + txn, ok := p.txns.Load(id) + if !ok { + return errors.WithStack(ErrTxnNotFound) + } + return txn.(*txnkv.Transaction).Delete(key) +} + +// TxnCommit commits the transaction operations to TiKV server. +func (p TxnKVProxy) TxnCommit(id UUID) error { + txn, ok := p.txns.Load(id) + if !ok { + return errors.WithStack(ErrTxnNotFound) + } + defer p.txns.Delete(id) + return txn.(*txnkv.Transaction).Commit(context.Background()) +} + +// TxnRollback undoes the transaction operations to TiKV server. +func (p TxnKVProxy) TxnRollback(id UUID) error { + txn, ok := p.txns.Load(id) + if !ok { + return errors.WithStack(ErrTxnNotFound) + } + defer p.txns.Delete(id) + return txn.(*txnkv.Transaction).Rollback() +} + +// TxnLockKeys tries to lock the entries with the keys in TiKV server. +func (p TxnKVProxy) TxnLockKeys(id UUID, keys [][]byte) error { + txn, ok := p.txns.Load(id) + if !ok { + return errors.WithStack(ErrTxnNotFound) + } + ks := *(*[]key.Key)(unsafe.Pointer(&keys)) + return txn.(*txnkv.Transaction).LockKeys(ks...) +} + +// TxnValid returns if the transaction is valid. +func (p TxnKVProxy) TxnValid(id UUID) (bool, error) { + txn, ok := p.txns.Load(id) + if !ok { + return false, errors.WithStack(ErrTxnNotFound) + } + return txn.(*txnkv.Transaction).Valid(), nil +} + +// TxnLen returns the count of key-value pairs in the transaction's memory buffer. +func (p TxnKVProxy) TxnLen(id UUID) (int, error) { + txn, ok := p.txns.Load(id) + if !ok { + return 0, errors.WithStack(ErrTxnNotFound) + } + return txn.(*txnkv.Transaction).Len(), nil +} + +// TxnSize returns the length (in bytes) of the transaction's memory buffer. +func (p TxnKVProxy) TxnSize(id UUID) (int, error) { + txn, ok := p.txns.Load(id) + if !ok { + return 0, errors.WithStack(ErrTxnNotFound) + } + return txn.(*txnkv.Transaction).Size(), nil +} + +// IterValid returns if the iterator is valid to use. +func (p TxnKVProxy) IterValid(id UUID) (bool, error) { + iter, ok := p.iterators.Load(id) + if !ok { + return false, errors.WithStack(ErrIterNotFound) + } + return iter.(kv.Iterator).Valid(), nil +} + +// IterKey returns the key which the iterator points to. +func (p TxnKVProxy) IterKey(id UUID) ([]byte, error) { + iter, ok := p.iterators.Load(id) + if !ok { + return nil, errors.WithStack(ErrIterNotFound) + } + return iter.(kv.Iterator).Key(), nil +} + +// IterValue returns the value which the iterator points to. +func (p TxnKVProxy) IterValue(id UUID) ([]byte, error) { + iter, ok := p.iterators.Load(id) + if !ok { + return nil, errors.WithStack(ErrIterNotFound) + } + return iter.(kv.Iterator).Value(), nil +} + +// IterNext moves the iterator to next entry. +func (p TxnKVProxy) IterNext(id UUID) error { + iter, ok := p.iterators.Load(id) + if !ok { + return errors.WithStack(ErrIterNotFound) + } + return iter.(kv.Iterator).Next() +} + +// IterClose releases an iterator. +func (p TxnKVProxy) IterClose(id UUID) error { + iter, ok := p.iterators.Load(id) + if !ok { + return errors.WithStack(ErrIterNotFound) + } + iter.(kv.Iterator).Close() + p.iterators.Delete(id) + return nil +} diff --git a/proxy/utils.go b/proxy/utils.go new file mode 100644 index 00000000..edece822 --- /dev/null +++ b/proxy/utils.go @@ -0,0 +1,40 @@ +// Copyright 2019 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package proxy + +import ( + "errors" + "sync" + + "github.com/google/uuid" +) + +// Proxy errors. Use errors.Cause() to determine error type. +var ( + ErrClientNotFound = errors.New("client not found") + ErrTxnNotFound = errors.New("txn not found") + ErrIterNotFound = errors.New("iterator not found") +) + +// UUID is a global unique ID to identify clients, transactions, or iterators. +type UUID string + +func insertWithRetry(m *sync.Map, d interface{}) UUID { + for { + id := UUID(uuid.New().String()) + if _, hasOld := m.LoadOrStore(id, d); !hasOld { + return id + } + } +} diff --git a/rawkv/rawkv.go b/rawkv/rawkv.go index 09742e5d..f1b3ad93 100644 --- a/rawkv/rawkv.go +++ b/rawkv/rawkv.go @@ -205,7 +205,7 @@ func (c *Client) Delete(key []byte) error { return nil } -// BatchDelete deletes key-value pairs from TiKV +// BatchDelete deletes key-value pairs from TiKV. func (c *Client) BatchDelete(keys [][]byte) error { start := time.Now() defer func() {