diff --git a/cluster.test b/cluster.test new file mode 100755 index 00000000000..9feb1d39aa8 Binary files /dev/null and b/cluster.test differ diff --git a/conf/config.toml b/conf/config.toml index 5abde1ae861..9d9d097a019 100644 --- a/conf/config.toml +++ b/conf/config.toml @@ -116,6 +116,10 @@ ## When PD fails to receive the heartbeat from a store after the specified period of time, ## it adds replicas at other nodes. # max-store-down-time = "30m" +## Controls the time interval between write hot regions info into leveldb +# hot-regions-write-interval= "20m" +## the day of hot regions data to be reserved +# hot-regions-reserved-days= "30" ## The number of Leader scheduling tasks performed at the same time. # leader-schedule-limit = 4 ## The number of Region scheduling tasks performed at the same time. diff --git a/conf/simconfig.toml b/conf/simconfig.toml index 428ee61e508..f7465f3cf24 100644 --- a/conf/simconfig.toml +++ b/conf/simconfig.toml @@ -29,3 +29,7 @@ leader-schedule-limit = 32 region-schedule-limit = 128 replica-schedule-limit = 32 merge-schedule-limit = 32 +##TODO +##Find a better place to put this config +hot-regions-reserved-days= "30" +hot-regions-write-interval= "20m" \ No newline at end of file diff --git a/go.mod b/go.mod index dc4a30e4e2a..e3e5b07a2ac 100644 --- a/go.mod +++ b/go.mod @@ -10,14 +10,19 @@ require ( github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e github.com/coreos/go-semver v0.3.0 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f + github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect github.com/docker/go-units v0.4.0 github.com/go-echarts/go-echarts v1.0.0 + github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/spec v0.20.3 // indirect + github.com/go-openapi/swag v0.19.15 // indirect github.com/gogo/protobuf v1.3.1 github.com/golang/protobuf v1.3.4 github.com/google/btree v1.0.0 github.com/gorilla/mux v1.7.4 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/juju/ratelimit v1.0.1 + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-shellwords v1.0.3 github.com/mgechev/revive v1.0.2 github.com/montanaflynn/stats v0.5.0 @@ -41,16 +46,19 @@ require ( github.com/spf13/cobra v1.0.0 github.com/spf13/pflag v1.0.5 github.com/swaggo/http-swagger v0.0.0-20200308142732-58ac5e232fba - github.com/swaggo/swag v1.6.6-0.20200529100950-7c765ddd0476 + github.com/swaggo/swag v1.6.7 github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 github.com/unrolled/render v1.0.1 + github.com/urfave/cli/v2 v2.3.0 // indirect github.com/urfave/negroni v0.3.0 // Fix panic in unit test with go >= 1.14, ref: etcd-io/bbolt#201 https://github.com/etcd-io/bbolt/pull/201 go.etcd.io/bbolt v1.3.5 // indirect go.etcd.io/etcd v0.5.0-alpha.5.0.20191023171146-3cf2f69b5738 go.uber.org/goleak v1.1.10 go.uber.org/zap v1.16.0 - golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064 + golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect + golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e // indirect + golang.org/x/tools v0.1.5 google.golang.org/grpc v1.26.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) diff --git a/go.sum b/go.sum index ac7ddec0c7a..7612f0dfc6a 100644 --- a/go.sum +++ b/go.sum @@ -64,9 +64,11 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/corona10/goimagehash v1.0.2 h1:pUfB0LnsJASMPGEZLj7tGY251vF+qLGqOgEP4rUs6kA= github.com/corona10/goimagehash v1.0.2/go.mod h1:/l9umBhvcHQXVtQO1V6Gp1yD20STawkhRnnX0D1bvVI= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= @@ -114,20 +116,26 @@ github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo= github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ= +github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg= github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc= github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= github.com/go-playground/overalls v0.0.0-20180201144345-22ec1a223b7c/go.mod h1:UqxAgEOt89sCiXlrc/ycnx00LVvUO/eS8tMUkWX4R7w= @@ -214,6 +222,8 @@ github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/joomcode/errorx v1.0.1 h1:CalpDWz14ZHd68fIqluJasJosAewpz2TFaJALrUxjrk= github.com/joomcode/errorx v1.0.1/go.mod h1:kgco15ekB6cs+4Xjzo7SPeXzx38PbJzBwbnu9qfVNHQ= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= @@ -227,19 +237,21 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o 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/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -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/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -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/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -279,6 +291,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 h1:BvoENQQU+fZ9uukda/RzCAL/191HHwJA5b13R6diVlY= github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nicksnyder/go-i18n v1.10.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/oleiade/reflections v1.0.1 h1:D1XO3LVEYroYskEsoSiGItp9RUxG6jWnCVvrqH0HHQM= github.com/oleiade/reflections v1.0.1/go.mod h1:rdFxbxq4QXVZWj0F+e9jqjDkc7dbp97vkRixKo2JR60= @@ -360,8 +374,9 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sasha-s/go-deadlock v0.2.0 h1:lMqc+fUb7RrFS3gQLtoQsJ7/6TV/pAIFvBsqX73DK8Y= github.com/sasha-s/go-deadlock v0.2.0/go.mod h1:StQn567HiB1fF2yJ44N9au7wOhrPS3iZqiDbRupzT10= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= @@ -373,7 +388,6 @@ github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/httpgzip v0.0.0-20190720172056-320755c1c1b0 h1:mj/nMDAwTBiaCqMEs4cYCqF7pO6Np7vhy1D1wcQGz+E= github.com/shurcooL/httpgzip v0.0.0-20190720172056-320755c1c1b0/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= @@ -410,8 +424,9 @@ github.com/swaggo/http-swagger v0.0.0-20200308142732-58ac5e232fba h1:lUPlXKqgbqT github.com/swaggo/http-swagger v0.0.0-20200308142732-58ac5e232fba/go.mod h1:O1lAbCgAAX/KZ80LM/OXwtWFI/5TvZlwxSg8Cq08PV0= github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y= github.com/swaggo/swag v1.6.3/go.mod h1:wcc83tB4Mb2aNiL/HP4MFeQdpHUrca+Rp/DRNgWAUio= -github.com/swaggo/swag v1.6.6-0.20200529100950-7c765ddd0476 h1:UjnSXdNPIG+5FJ6xLQODEdk7gSnJlMldu3sPAxxCO+4= github.com/swaggo/swag v1.6.6-0.20200529100950-7c765ddd0476/go.mod h1:xDhTyuFIujYiN3DKWC/H/83xcfHp+UE/IzWWampG7Zc= +github.com/swaggo/swag v1.6.7 h1:e8GC2xDllJZr3omJkm9YfmK0Y56+rMO3cg0JBKNz09s= +github.com/swaggo/swag v1.6.7/go.mod h1:xDhTyuFIujYiN3DKWC/H/83xcfHp+UE/IzWWampG7Zc= github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw= github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= github.com/thoas/go-funk v0.8.0 h1:JP9tKSvnpFVclYgDM0Is7FD9M4fhPvqA0s0BsXmzSRQ= @@ -441,8 +456,9 @@ github.com/unrolled/render v1.0.1 h1:VDDnQQVfBMsOsp3VaCJszSO0nkBIVEYoPWeRThk9spY github.com/unrolled/render v1.0.1/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM= github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k= github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= +github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/urfave/negroni v0.3.0 h1:PaXOb61mWeZJxc1Ji2xJjpVg9QfPo0rrB+lHyBxGNSU= github.com/urfave/negroni v0.3.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/vmihailenco/msgpack/v4 v4.3.11/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= @@ -455,6 +471,7 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yookoala/realpath v1.0.0/go.mod h1:gJJMA9wuX7AcqLy1+ffPatSCySA1FQ2S8Ya9AIoYBpE= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= @@ -507,8 +524,9 @@ golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= @@ -530,8 +548,11 @@ golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= 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= @@ -561,13 +582,19 @@ golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -592,8 +619,9 @@ golang.org/x/tools v0.0.0-20191114200427-caa0b0f7d508/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200225230052-807dcd883420/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064 h1:BmCFkEH4nJrYcAc2L08yX5RhYGD4j58PTMkEUDkpz2I= golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= @@ -620,8 +648,9 @@ gopkg.in/alecthomas/gometalinter.v2 v2.0.12/go.mod h1:NDRytsqEZyolNuAgTzJkZMkSQM gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c/go.mod h1:3HH7i1SgMqlzxCcBmUHW657sD4Kvv9sC3HpL3YukzwA= 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/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= @@ -641,10 +670,12 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/mysql v1.0.6 h1:mA0XRPjIKi4bkE9nv+NKs6qj6QWOchqUSdWOcpd3x1E= gorm.io/driver/mysql v1.0.6/go.mod h1:KdrTanmfLPPyAOeYGyG+UpDys7/7eeWT1zCq+oekYnU= gorm.io/driver/sqlite v1.1.4 h1:PDzwYE+sI6De2+mxAneV9Xs11+ZyKV6oxD3wDGkaNvM= diff --git a/server/api/hot_status.go b/server/api/hot_status.go index 54b0965d409..a924ad3fd71 100644 --- a/server/api/hot_status.go +++ b/server/api/hot_status.go @@ -14,7 +14,9 @@ package api import ( + "encoding/json" "fmt" + "io" "net/http" "strconv" @@ -141,3 +143,30 @@ func (h *hotStatusHandler) GetHotStores(w http.ResponseWriter, r *http.Request) } h.rd.JSON(w, http.StatusOK, stats) } + +// @Tags hotspot +// @Summary List the history hot regions. +// @Accept json +// @Produce json +// @Success 200 {object} statistics.HistoryHotRegions +// @Router /hotspot/regions/history [post] +func (h *hotStatusHandler) GetHistoryHotRegions(w http.ResponseWriter, r *http.Request) { + data, err := io.ReadAll(r.Body) + r.Body.Close() + if err != nil { + h.rd.JSON(w, http.StatusInternalServerError, err.Error()) + return + } + historyHotRegionsRequest := &statistics.HistoryHotRegionsRequest{} + err = json.Unmarshal(data, historyHotRegionsRequest) + if err != nil { + h.rd.JSON(w, http.StatusInternalServerError, err.Error()) + return + } + results, err := h.Handler.GetAllRequestHistroyHotRegion(historyHotRegionsRequest) + if err != nil { + h.rd.JSON(w, http.StatusInternalServerError, err.Error()) + return + } + h.rd.JSON(w, http.StatusOK, results) +} diff --git a/server/api/hot_status_test.go b/server/api/hot_status_test.go index b17c020fcc5..5a23768f07b 100644 --- a/server/api/hot_status_test.go +++ b/server/api/hot_status_test.go @@ -14,11 +14,18 @@ package api import ( + "encoding/json" "fmt" + "reflect" + "time" . "github.com/pingcap/check" + "github.com/syndtr/goleveldb/leveldb" "github.com/tikv/pd/server" + "github.com/tikv/pd/server/cluster" + "github.com/tikv/pd/server/kv" _ "github.com/tikv/pd/server/schedulers" + "github.com/tikv/pd/server/statistics" ) var _ = Suite(&testHotStatusSuite{}) @@ -48,3 +55,231 @@ func (s testHotStatusSuite) TestGetHotStore(c *C) { err := readJSON(testDialClient, s.urlPrefix+"/stores", &stat) c.Assert(err, IsNil) } +func (s testHotStatusSuite) TestGetHistoryHotRegionsBasic(c *C) { + request := statistics.HistoryHotRegionsRequest{ + StartTime: 0, + EndTime: time.Now().AddDate(0, 2, 0).Unix(), + } + data, err := json.Marshal(request) + c.Assert(err, IsNil) + err = postJSON(testDialClient, s.urlPrefix+"/regions/history", data) + c.Assert(err, IsNil) +} + +func (s testHotStatusSuite) TestGetHistoryHotRegionsTimeRange(c *C) { + storage := s.svr.GetHistoryHotRegionStorage() + now := time.Now() + hotRegions := []*statistics.HistoryHotRegion{ + { + RegionID: 1, + UpdateTime: now.Unix(), + }, + { + RegionID: 1, + UpdateTime: now.Add(10 * time.Minute).Unix(), + }, + } + request := statistics.HistoryHotRegionsRequest{ + StartTime: now.Unix(), + EndTime: now.Add(10 * time.Second).Unix(), + } + check := func(res []byte, statusCode int) { + c.Assert(statusCode, Equals, 200) + historyHotRegions := &statistics.HistoryHotRegions{} + json.Unmarshal(res, historyHotRegions) + for _, region := range historyHotRegions.HistoryHotRegion { + c.Assert(region.UpdateTime, GreaterEqual, request.StartTime) + c.Assert(region.UpdateTime, LessEqual, request.EndTime) + } + } + writeToDB(c, storage.LeveldbKV, hotRegions) + data, err := json.Marshal(request) + c.Assert(err, IsNil) + err = postJSON(testDialClient, s.urlPrefix+"/regions/history", data, check) + c.Assert(err, IsNil) +} + +func (s testHotStatusSuite) TestGetHistoryHotRegionsIDAndTypes(c *C) { + storage := s.svr.GetHistoryHotRegionStorage() + now := time.Now() + hotRegions := []*statistics.HistoryHotRegion{ + { + RegionID: 1, + StoreID: 1, + PeerID: 1, + HotRegionType: "read", + UpdateTime: now.Unix(), + }, + { + RegionID: 1, + StoreID: 2, + PeerID: 1, + HotRegionType: "read", + UpdateTime: now.Add(10 * time.Second).Unix(), + }, + { + RegionID: 1, + StoreID: 1, + PeerID: 2, + HotRegionType: "read", + UpdateTime: now.Add(20 * time.Second).Unix(), + }, + { + RegionID: 1, + StoreID: 1, + PeerID: 1, + HotRegionType: "write", + UpdateTime: now.Add(30 * time.Second).Unix(), + }, + } + request := statistics.HistoryHotRegionsRequest{ + RegionIDs: []uint64{1}, + StoreIDs: []uint64{1}, + PeerIDs: []uint64{1}, + HotRegionTypes: []string{"read"}, + EndTime: now.Add(10 * time.Minute).Unix(), + } + check := func(res []byte, statusCode int) { + c.Assert(statusCode, Equals, 200) + historyHotRegions := &statistics.HistoryHotRegions{} + json.Unmarshal(res, historyHotRegions) + c.Assert(len(historyHotRegions.HistoryHotRegion), Equals, 1) + c.Assert(reflect.DeepEqual(historyHotRegions.HistoryHotRegion[0], hotRegions[0]), IsTrue) + } + writeToDB(c, storage.LeveldbKV, hotRegions) + data, err := json.Marshal(request) + c.Assert(err, IsNil) + err = postJSON(testDialClient, s.urlPrefix+"/regions/history", data, check) + c.Assert(err, IsNil) +} + +func (s testHotStatusSuite) TestGetHistoryHotRegionsBetween(c *C) { + storage := s.svr.GetHistoryHotRegionStorage() + now := time.Now() + hotRegions := []*statistics.HistoryHotRegion{ + { + RegionID: 1, + HotDegree: 10, + FlowBytes: 10.0, + KeyRate: 10.0, + QueryRate: 10.0, + StartKey: []byte("3"), + EndKey: []byte("5"), + UpdateTime: now.Unix(), + }, + { + RegionID: 2, + HotDegree: 20, + FlowBytes: 10.0, + KeyRate: 10.0, + QueryRate: 10.0, + StartKey: []byte("3"), + EndKey: []byte("5"), + UpdateTime: now.Unix(), + }, + { + RegionID: 3, + HotDegree: 1, + FlowBytes: 10.0, + KeyRate: 10.0, + QueryRate: 10.0, + StartKey: []byte("3"), + EndKey: []byte("5"), + UpdateTime: now.Unix(), + }, + { + RegionID: 4, + HotDegree: 10, + FlowBytes: 20.0, + KeyRate: 10.0, + QueryRate: 10.0, + StartKey: []byte("3"), + EndKey: []byte("5"), + UpdateTime: now.Unix(), + }, + { + RegionID: 5, + HotDegree: 10, + FlowBytes: 1.0, + KeyRate: 10.0, + QueryRate: 10.0, + StartKey: []byte("3"), + EndKey: []byte("5"), + UpdateTime: now.Unix(), + }, + { + RegionID: 6, + HotDegree: 10, + FlowBytes: 10.0, + KeyRate: 20.0, + QueryRate: 10.0, + StartKey: []byte("3"), + EndKey: []byte("5"), + UpdateTime: now.Unix(), + }, + { + RegionID: 7, + HotDegree: 10, + FlowBytes: 10.0, + KeyRate: 1.0, + QueryRate: 10.0, + StartKey: []byte("3"), + EndKey: []byte("5"), + UpdateTime: now.Unix(), + }, + { + RegionID: 8, + HotDegree: 10, + FlowBytes: 10.0, + KeyRate: 10.0, + QueryRate: 20.0, + StartKey: []byte("3"), + EndKey: []byte("5"), + UpdateTime: now.Unix(), + }, + { + RegionID: 9, + HotDegree: 10, + FlowBytes: 10.0, + KeyRate: 10.0, + QueryRate: 1.0, + StartKey: []byte("3"), + EndKey: []byte("5"), + UpdateTime: now.Unix(), + }, + } + request := statistics.HistoryHotRegionsRequest{ + HighHotDegree: 11, + LowHotDegree: 10, + HighFlowBytes: 11.0, + LowFlowBytes: 10.0, + HighKeyRate: 11.0, + LowKeyRate: 10.0, + HighQueryRate: 11.0, + LowQueryRate: 10.0, + EndTime: now.Unix(), + } + check := func(res []byte, statusCode int) { + c.Assert(statusCode, Equals, 200) + historyHotRegions := &statistics.HistoryHotRegions{} + json.Unmarshal(res, historyHotRegions) + c.Assert(len(historyHotRegions.HistoryHotRegion), Equals, 1) + c.Assert(reflect.DeepEqual(historyHotRegions.HistoryHotRegion[0], hotRegions[0]), IsTrue) + } + writeToDB(c, storage.LeveldbKV, hotRegions) + data, err := json.Marshal(request) + c.Assert(err, IsNil) + err = postJSON(testDialClient, s.urlPrefix+"/regions/history", data, check) + c.Assert(err, IsNil) +} + +func writeToDB(c *C, kv *kv.LeveldbKV, hotRegions []*statistics.HistoryHotRegion) { + batch := new(leveldb.Batch) + for _, region := range hotRegions { + key := cluster.HotRegionStorePath("read", region.UpdateTime, region.RegionID) + value, err := json.Marshal(region) + c.Assert(err, IsNil) + batch.Put([]byte(key), value) + } + kv.Write(batch, nil) +} diff --git a/server/api/router.go b/server/api/router.go index 0d4ca9115e8..7a4a6f3a157 100644 --- a/server/api/router.go +++ b/server/api/router.go @@ -136,10 +136,12 @@ func createRouter(prefix string, svr *server.Server) *mux.Router { clusterRouter.HandleFunc("/labels", labelsHandler.Get).Methods("GET") clusterRouter.HandleFunc("/labels/stores", labelsHandler.GetStores).Methods("GET") + hotStatusHandler := newHotStatusHandler(handler, rd) apiRouter.HandleFunc("/hotspot/regions/write", hotStatusHandler.GetHotWriteRegions).Methods("GET") apiRouter.HandleFunc("/hotspot/regions/read", hotStatusHandler.GetHotReadRegions).Methods("GET") apiRouter.HandleFunc("/hotspot/stores", hotStatusHandler.GetHotStores).Methods("GET") + apiRouter.HandleFunc("/hotspot/regions/history", hotStatusHandler.GetHistoryHotRegions).Methods("POST") regionHandler := newRegionHandler(svr, rd) clusterRouter.HandleFunc("/region/id/{id}", regionHandler.GetRegionByID).Methods("GET") diff --git a/server/cluster/hot_region_storage.go b/server/cluster/hot_region_storage.go new file mode 100644 index 00000000000..9e24e5c1424 --- /dev/null +++ b/server/cluster/hot_region_storage.go @@ -0,0 +1,323 @@ +package cluster + +import ( + "bytes" + "context" + "encoding/gob" + "encoding/json" + "fmt" + "math" + "path" + "sync" + "time" + + "github.com/pingcap/kvproto/pkg/metapb" + "github.com/pingcap/log" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/util" + "github.com/tikv/pd/pkg/encryption" + "github.com/tikv/pd/pkg/errs" + "github.com/tikv/pd/server/encryptionkm" + "github.com/tikv/pd/server/kv" + "github.com/tikv/pd/server/member" + "github.com/tikv/pd/server/statistics" +) + +type HotRegionStorage struct { + *kv.LeveldbKV + encryptionKeyManager *encryptionkm.KeyManager + mu sync.RWMutex + batchHotInfo map[string]*statistics.HistoryHotRegion + remianedDays int64 + pullInterval time.Duration + compactionCountdown int + hotRegionInfoCtx context.Context + hotRegionInfoCancel context.CancelFunc + cluster *RaftCluster + member *member.Member +} + +const ( + defaultCompactionTime = 30 +) + +var hotRegionTypes = []string{ + "read", + "write", +} + +func NewHotRegionsHistoryStorage( + ctx context.Context, + path string, + encryptionKeyManager *encryptionkm.KeyManager, + cluster *RaftCluster, + member *member.Member, + remianedDays int64, + pullInterval time.Duration, +) (*HotRegionStorage, error) { + levelDB, err := kv.NewLeveldbKV(path) + if err != nil { + return nil, err + } + hotRegionInfoCtx, hotRegionInfoCancle := context.WithCancel(ctx) + h := HotRegionStorage{ + LeveldbKV: levelDB, + encryptionKeyManager: encryptionKeyManager, + batchHotInfo: make(map[string]*statistics.HistoryHotRegion), + remianedDays: remianedDays, + pullInterval: pullInterval, + compactionCountdown: defaultCompactionTime, + hotRegionInfoCtx: hotRegionInfoCtx, + hotRegionInfoCancel: hotRegionInfoCancle, + cluster: cluster, + member: member, + } + h.backgroundFlush() + h.backgroundDelete() + return &h, nil +} + +//delete hot_region info which update_time is smaller than time.Now() minus /remain day in the backgroud +func (h *HotRegionStorage) backgroundDelete() { + //make delete happend in 0 clock + now := time.Now() + next := now.Add(time.Hour * 24) + next = time.Date(next.Year(), next.Month(), next.Day(), 0, 0, 0, 0, next.Location()) + t := time.NewTicker(next.Sub(now)) + go func() { + select { + case <-t.C: + + case <-h.hotRegionInfoCtx.Done(): + return + } + ticker := time.NewTicker(24 * time.Hour) + defer ticker.Stop() + for { + select { + case <-ticker.C: + h.delete() + case <-h.hotRegionInfoCtx.Done(): + return + } + } + }() + +} + +//write hot_region info into db in the backgroud +func (h *HotRegionStorage) backgroundFlush() { + ticker := time.NewTicker(h.pullInterval) + go func() { + defer ticker.Stop() + for { + select { + case <-ticker.C: + if h.member.IsLeader() { + if err := h.pullHotRegionInfo(); err != nil { + log.Error("get hot_region stat meet error", errs.ZapError(err)) + } + if err := h.flush(); err != nil { + log.Error("get hot_region stat meet error", errs.ZapError(err)) + } + } + case <-h.hotRegionInfoCtx.Done(): + return + } + } + }() +} + +//return a iterator which can traverse from start_time to end_time +func (h *HotRegionStorage) NewIterator(requireTypes []string, startTime, endTime int64) HotRegionStorageIterator { + iters := make([]iterator.Iterator, len(requireTypes)) + for index, requireType := range requireTypes { + startKey := HotRegionStorePath(requireType, startTime, 0) + endKey := HotRegionStorePath(requireType, endTime, math.MaxInt64) + iter := h.LeveldbKV.NewIterator(&util.Range{Start: []byte(startKey), Limit: []byte(endKey)}, nil) + iters[index] = iter + } + return HotRegionStorageIterator{ + iters: iters, + encryptionKeyManager: h.encryptionKeyManager, + } +} + +func (h *HotRegionStorage) Close() error { + h.hotRegionInfoCancel() + if err := h.LeveldbKV.Close(); err != nil { + return errs.ErrLevelDBClose.Wrap(err).GenWithStackByArgs() + } + return nil +} + +func (h *HotRegionStorage) pullHotRegionInfo() error { + cluster := h.cluster + hotReadLeaderInfo := cluster.coordinator.getHotReadRegions().AsLeader + if err := h.packHotRegionInfo(hotReadLeaderInfo, + "read"); err != nil { + return err + } + hotWriteLeaderInfo := cluster.coordinator.getHotWriteRegions().AsLeader + if err := h.packHotRegionInfo(hotWriteLeaderInfo, + "write"); err != nil { + return err + } + return nil +} + +func (h *HotRegionStorage) packHotRegionInfo(hotLeaderInfo statistics.StoreHotPeersStat, + hotRegionType string) error { + cluster := h.cluster + batchHotInfo := h.batchHotInfo + for _, hotPeersStat := range hotLeaderInfo { + stats := hotPeersStat.Stats + for _, hotPeerStat := range stats { + region := cluster.GetRegion(hotPeerStat.RegionID).GetMeta() + region, err := encryption.EncryptRegion(region, h.encryptionKeyManager) + if err != nil { + return err + } + var peerID uint64 + for _, peer := range region.Peers { + if peer.StoreId == hotPeerStat.StoreID { + peerID = peer.Id + } + } + stat := statistics.HistoryHotRegion{ + UpdateTime: hotPeerStat.LastUpdateTime.Unix(), + RegionID: hotPeerStat.RegionID, + StoreID: hotPeerStat.StoreID, + PeerID: peerID, + HotDegree: int64(hotPeerStat.HotDegree), + FlowBytes: hotPeerStat.ByteRate, + KeyRate: hotPeerStat.KeyRate, + QueryRate: hotPeerStat.QueryRate, + StartKey: region.StartKey, + EndKey: region.EndKey, + EncryptionMeta: region.EncryptionMeta, + HotRegionType: hotRegionType, + } + batchHotInfo[HotRegionStorePath( + stat.HotRegionType, + hotPeerStat.LastUpdateTime.Unix(), + hotPeerStat.RegionID)] = &stat + } + } + return nil +} + +func (h *HotRegionStorage) flush() error { + h.mu.Lock() + defer h.mu.Unlock() + batch := new(leveldb.Batch) + for key, stat := range h.batchHotInfo { + value, err := json.Marshal(stat) + if err != nil { + return errs.ErrProtoMarshal.Wrap(err).GenWithStackByCause() + } + batch.Put([]byte(key), value) + } + if err := h.LeveldbKV.Write(batch, nil); err != nil { + return errs.ErrLevelDBWrite.Wrap(err).GenWithStackByCause() + } + h.batchHotInfo = make(map[string]*statistics.HistoryHotRegion) + return nil +} + +func (h *HotRegionStorage) delete() error { + h.mu.Lock() + defer h.mu.Unlock() + db := h.LeveldbKV + batch := new(leveldb.Batch) + for _, hotRegionType := range hotRegionTypes { + startKey := HotRegionStorePath(hotRegionType, 0, 0) + endTime := time.Now().AddDate(0, 0, 0-int(h.remianedDays)).Unix() + endKey := HotRegionStorePath(hotRegionType, endTime, math.MaxInt64) + iter := db.NewIterator(&util.Range{ + Start: []byte(startKey), Limit: []byte(endKey)}, nil) + for iter.Next() { + batch.Delete(iter.Key()) + } + } + if err := db.Write(batch, nil); err != nil { + return errs.ErrLevelDBWrite.Wrap(err).GenWithStackByCause() + } + h.compactionCountdown-- + if h.compactionCountdown == 0 { + h.compactionCountdown = defaultCompactionTime + for _, hotRegionType := range hotRegionTypes { + startKey := HotRegionStorePath(hotRegionType, 0, 0) + endTime := time.Now().AddDate(0, 0, 0-int(h.remianedDays)).Unix() + endKey := HotRegionStorePath(hotRegionType, endTime, math.MaxInt64) + db.CompactRange(util.Range{Start: []byte(startKey), Limit: []byte(endKey)}) + } + } + return nil +} + +type HotRegionStorageIterator struct { + iters []iterator.Iterator + encryptionKeyManager *encryptionkm.KeyManager +} + +//next will return next history_hot_region, +//there is no more historyhotregion,it will return nil +func (it *HotRegionStorageIterator) Next() (*statistics.HistoryHotRegion, error) { + iter := it.iters[0] + if !iter.Next() { + iter.Release() + if len(it.iters) == 1 { + return nil, nil + } + it.iters = it.iters[1:] + iter = it.iters[0] + } + item := iter.Value() + value := make([]byte, len(item)) + copy(value, item) + var message statistics.HistoryHotRegion + err := json.Unmarshal(value, &message) + if err != nil { + return nil, err + } + region := &metapb.Region{ + Id: message.RegionID, + StartKey: message.StartKey, + EndKey: message.EndKey, + EncryptionMeta: message.EncryptionMeta, + } + if err := encryption.DecryptRegion(region, it.encryptionKeyManager); err != nil { + return nil, err + } + message.StartKey = region.StartKey + message.EndKey = region.EndKey + message.EncryptionMeta = nil + return &message, nil +} + +//TODO +//find a better place to put this function +func HotRegionStorePath(hotRegionType string, update_time int64, region_id uint64) string { + return path.Join( + "schedule", + "hot_region", + hotRegionType, + fmt.Sprintf("%020d", update_time), + fmt.Sprintf("%020d", region_id), + ) +} + +func EncodeToBytes(p interface{}) ([]byte, error) { + buf := bytes.Buffer{} + enc := gob.NewEncoder(&buf) + err := enc.Encode(p) + return buf.Bytes(), err +} + +func DecodeToStruct(s []byte, p interface{}) error { + dec := gob.NewDecoder(bytes.NewReader(s)) + err := dec.Decode(p) + return err +} diff --git a/server/cluster/hot_region_storage_test.go b/server/cluster/hot_region_storage_test.go new file mode 100644 index 00000000000..fb09734545b --- /dev/null +++ b/server/cluster/hot_region_storage_test.go @@ -0,0 +1,410 @@ +package cluster + +import ( + "context" + "fmt" + "log" + "math/rand" + "os" + "path/filepath" + "testing" + "time" + + . "github.com/pingcap/check" + "github.com/pingcap/kvproto/pkg/metapb" + "github.com/tikv/pd/server/core" + "github.com/tikv/pd/server/statistics" +) + +var _ = Suite(&testHotRegionStorage{}) + +type testHotRegionStorage struct { + ctx context.Context + cancel context.CancelFunc +} + +func (t *testHotRegionStorage) SetUpSuite(c *C) { + t.ctx, t.cancel = context.WithCancel(context.Background()) +} + +func (t *testHotRegionStorage) TestHotRegionWrite(c *C) { + regionStorage, clear, err := newTestHotRegionStorage(10*time.Hour, -1) + defer clear() + c.Assert(err, IsNil) + raft := regionStorage.cluster + stats := statistics.StoreHotPeersStat{} + statShows := []statistics.HotPeerStatShow{} + regions, statShows, start_time, end_time := + newTestHotRegionHistory(raft, time.Now(), 3, 3) + end_time = statShows[len(statShows)-2].LastUpdateTime.Unix() + stats[1] = &statistics.HotPeersStat{ + Stats: statShows, + } + c.Assert(err, IsNil) + regionStorage.packHotRegionInfo(stats, "read") + regionStorage.flush() + iter := regionStorage.NewIterator(hotRegionTypes, start_time, end_time) + index := 0 + for r, err := iter.Next(); r != nil && err == nil; r, err = iter.Next() { + c.Assert(r.RegionID, Equals, statShows[index].RegionID) + c.Assert(r.UpdateTime, Equals, statShows[index].LastUpdateTime.Unix()) + c.Assert(r.HotRegionType, Equals, "read") + c.Assert(r.StartKey, Equals, regions[index].GetMeta().StartKey) + c.Assert(r.EndKey, Equals, regions[index].GetMeta().EndKey) + index++ + } + c.Assert(err, IsNil) + c.Assert(index, Equals, len(statShows)-1) +} + +func (t *testHotRegionStorage) TestHotRegionDelete(c *C) { + regionStorage, clear, err := newTestHotRegionStorage(10*time.Hour, -1) + defer clear() + c.Assert(err, IsNil) + raft := regionStorage.cluster + stats := statistics.StoreHotPeersStat{} + now := time.Now() + next := now.AddDate(0, 0, -1) + _, statShows, start_time, end_time := + newTestHotRegionHistory(raft, next, 3, 3) + stats[1] = &statistics.HotPeersStat{ + Stats: statShows, + } + statShows[2].LastUpdateTime = time.Now() + end_time = statShows[2].LastUpdateTime.Unix() + c.Assert(err, IsNil) + regionStorage.packHotRegionInfo(stats, "read") + regionStorage.flush() + regionStorage.delete() + iter := regionStorage.NewIterator(hotRegionTypes, start_time, end_time) + r, err := iter.Next() + c.Assert(err, IsNil) + c.Assert(r, NotNil) + c.Assert(r.RegionID, Equals, statShows[2].RegionID) + c.Assert(r.UpdateTime, Equals, statShows[2].LastUpdateTime.Unix()) + c.Assert(r.HotRegionType, Equals, "read") + r, err = iter.Next() + c.Assert(err, IsNil) + c.Assert(r, IsNil) +} + +func BenchmarkInsert(b *testing.B) { + regionStorage, clear, err := newTestHotRegionStorage(10*time.Hour, -1) + defer clear() + if err != nil { + b.Fatal(err) + } + raft := regionStorage.cluster + regions := newTestHotRegions(1000, 3) + for _, region := range regions { + raft.putRegion(region) + } + stat := newBenchmarkHotRegoinHistory(raft, time.Now(), regions) + b.ResetTimer() + err = regionStorage.packHotRegionInfo(stat, "read") + if err != nil { + log.Fatal(err) + } + regionStorage.flush() + b.StopTimer() +} + +func BenchmarkInsertAfterMonth(b *testing.B) { + regionStorage, clear, err := newTestHotRegionStorage(10*time.Hour, -1) + if err != nil { + b.Fatal(err) + } + defer clear() + raft := regionStorage.cluster + endTime := time.Now() + regions := newTestHotRegions(1000, 3) + for _, region := range regions { + raft.putRegion(region) + } + //4320=(60*24*30)/10 + writeIntoDB(regionStorage, regions, 4464, endTime) + stat := newBenchmarkHotRegoinHistory(raft, endTime, regions) + b.ResetTimer() + err = regionStorage.packHotRegionInfo(stat, "read") + if err != nil { + log.Fatal(err) + } + regionStorage.flush() +} + +func BenchmarkDelete(b *testing.B) { + regionStorage, clear, err := newTestHotRegionStorage(10*time.Hour, -1) + if err != nil { + b.Fatal(err) + } + defer clear() + raft := regionStorage.cluster + endTime := time.Now() + regions := newTestHotRegions(1000, 3) + for _, region := range regions { + raft.putRegion(region) + } + //4464=(60*24*31)/10 + writeIntoDB(regionStorage, regions, 4464, endTime) + b.ResetTimer() + regionStorage.delete() +} + +func BenchmarkRead(b *testing.B) { + //delete data in between today and tomrrow + regionStorage, clear, err := newTestHotRegionStorage(10*time.Hour, 30) + if err != nil { + b.Fatal(err) + } + defer clear() + raft := regionStorage.cluster + endTime := time.Now() + startTime := endTime + regions := newTestHotRegions(1000, 3) + for _, region := range regions { + raft.putRegion(region) + } + //4320=(60*24*31)/10 + endTime = writeIntoDB(regionStorage, regions, 4320, endTime) + // f, _ := os.OpenFile("/root/cpu.pprof", os.O_CREATE|os.O_RDWR, 0644) + // defer f.Close() + // pprof.StartCPUProfile(f) + // defer pprof.StopCPUProfile() + b.ResetTimer() + iter := regionStorage.NewIterator(hotRegionTypes, startTime.Unix(), endTime.AddDate(0, 1, 0).Unix()) + for next, err := iter.Next(); next != nil && err == nil; next, err = iter.Next() { + + } + b.StopTimer() +} + +func BenchmarkCompaction(b *testing.B) { + //delete data in between today and tomrrow + regionStorage, clear, err := newTestHotRegionStorage(10*time.Hour, 30) + if err != nil { + b.Fatal(err) + } + defer clear() + raft := regionStorage.cluster + endTime := time.Now() + regions := newTestHotRegions(1000, 3) + for _, region := range regions { + raft.putRegion(region) + } + //leveldb will compaction after 30 times delete + for i := 0; i < defaultCompactionTime-1; i++ { + //144=24*60/10 + endTime = writeIntoDB(regionStorage, regions, 144, endTime) + regionStorage.delete() + regionStorage.remianedDays-- + } + b.ResetTimer() + regionStorage.delete() + b.StopTimer() +} + +func BenchmarkTwoTimesCompaction(b *testing.B) { + //delete data in between today and tomrrow + regionStorage, clear, err := newTestHotRegionStorage(10*time.Hour, 30) + if err != nil { + b.Fatal(err) + } + defer clear() + raft := regionStorage.cluster + endTime := time.Now() + //4464=(60*24*31)/10 + regions := newTestHotRegions(1000, 3) + for _, region := range regions { + raft.putRegion(region) + } + //leveldb will compaction after 30 times delete + for i := 0; i < 2*defaultCompactionTime-1; i++ { + //144=24*60/10 + endTime = writeIntoDB(regionStorage, regions, 144, endTime) + regionStorage.delete() + regionStorage.remianedDays-- + } + b.ResetTimer() + regionStorage.delete() + b.StopTimer() +} + +func BenchmarkDeleteAfterYear(b *testing.B) { + regionStorage, clear, err := newTestHotRegionStorage(10*time.Hour, -1) + if err != nil { + b.Fatal(err) + } + defer clear() + raft := regionStorage.cluster + regions := newTestHotRegions(1000, 3) + for _, region := range regions { + raft.putRegion(region) + } + endTime := time.Now() + //4464=(60*24*31)/10 + endTime = writeIntoDB(regionStorage, regions, 4464, endTime) + //334=365-31 + for i := 0; i < 334; i++ { + regionStorage.delete() + //144=24*60/10 + endTime = writeIntoDB(regionStorage, regions, 144, endTime) + } + b.ResetTimer() + regionStorage.delete() + b.StopTimer() +} + +func newTestHotRegionStorage(pullInterval time.Duration, remianedDays int64) ( + hotRegionStorage *HotRegionStorage, + clear func(), err error) { + writePath := "./tmp" + ctx := context.Background() + _, opt, err := newTestScheduleConfig() + if err != nil { + return nil, nil, err + } + raft := newTestCluster(ctx, opt).RaftCluster + //delete data in between today and tomrrow + hotRegionStorage, err = NewHotRegionsHistoryStorage(ctx, + writePath, nil, raft, nil, remianedDays, pullInterval) + if err != nil { + return nil, nil, err + } + clear = func() { + hotRegionStorage.Close() + PrintDirSize(writePath) + // os.RemoveAll(writePath) + } + return +} +func writeIntoDB(regionStorage *HotRegionStorage, + regions []*core.RegionInfo, times int, + endTime time.Time) time.Time { + raft := regionStorage.cluster + for i := 0; i < times; i++ { + if i%1000 == 0 { + fmt.Println(i) + } + stats := newBenchmarkHotRegoinHistory(raft, endTime, regions) + err := regionStorage.packHotRegionInfo(stats, hotRegionTypes[i%len(hotRegionTypes)]) + if err != nil { + log.Fatal(err) + } + regionStorage.flush() + endTime = endTime.Add(10 * time.Minute) + } + return endTime +} + +func newTestHotRegionHistory( + raft *RaftCluster, + start time.Time, + n, np uint64) (regions []*core.RegionInfo, + statShows []statistics.HotPeerStatShow, + start_time, end_time int64) { + regions = newTestHotRegions(n, np) + start_time = start.Unix() + for _, region := range regions { + raft.putRegion(region) + statShow := statistics.HotPeerStatShow{ + RegionID: region.GetMeta().Id, + LastUpdateTime: start, + } + statShows = append(statShows, statShow) + start = start.Add(10 * time.Second) + } + end_time = start.Unix() + return +} + +func newBenchmarkHotRegoinHistory( + raft *RaftCluster, + start time.Time, + regions []*core.RegionInfo) ( + stats statistics.StoreHotPeersStat) { + stats = statistics.StoreHotPeersStat{} + for _, region := range regions { + peers := region.GetPeers() + peer := peers[rand.Intn(len(peers))] + if stats[peer.StoreId] == nil { + stats[peer.StoreId] = &statistics.HotPeersStat{} + } + statShow := statistics.HotPeerStatShow{ + RegionID: region.GetMeta().Id, + StoreID: peer.StoreId, + LastUpdateTime: start, + HotDegree: rand.Int(), + ByteRate: rand.Float64() * 100, + KeyRate: rand.Float64() * 100, + QueryRate: rand.Float64() * 100, + AntiCount: rand.Int(), + } + stats[peer.StoreId].Stats = + append(stats[peer.StoreId].Stats, statShow) + } + return +} + +// Create n regions (0..n) of n stores (0..n). +// Each region contains np peers, the first peer is the leader. +func newTestHotRegions(n, np uint64) []*core.RegionInfo { + regions := make([]*core.RegionInfo, 0, n) + for i := uint64(0); i < n; i++ { + peers := make([]*metapb.Peer, 0, np) + for j := uint64(0); j < np; j++ { + peer := &metapb.Peer{ + Id: i*np + j, + } + peer.StoreId = (i + j) % n + peers = append(peers, peer) + } + region := &metapb.Region{ + Id: i, + Peers: peers, + StartKey: []byte(fmt.Sprintf("%020d", i)), + EndKey: []byte(fmt.Sprintf("%020d", i+1)), + RegionEpoch: &metapb.RegionEpoch{ConfVer: 2, Version: 2}, + } + regions = append(regions, core.NewRegionInfo(region, peers[0])) + } + return regions +} + +//Print dir size +func PrintDirSize(path string) { + size, err := DirSizeB(path) + if err != nil { + log.Fatal(err) + } + fmt.Printf("file size %d\n", size) +} + +//getFileSize get file size by path(B) +func DirSizeB(path string) (int64, error) { + var size int64 + err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { + if !info.IsDir() { + size += info.Size() + } + return err + }) + return size, err +} + +//getFileSize get file size by path(B) +func getFileSize(path string) int64 { + if !exists(path) { + return 0 + } + fileInfo, err := os.Stat(path) + if err != nil { + return 0 + } + return fileInfo.Size() +} + +//exists Whether the path exists +func exists(path string) bool { + _, err := os.Stat(path) + return err == nil || os.IsExist(err) +} diff --git a/server/config/config.go b/server/config/config.go index c76bb0ee94f..c929d7104fc 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -735,6 +735,10 @@ type ScheduleConfig struct { // is overwritten, the value is fixed until it is deleted. // Default: manual StoreLimitMode string `toml:"store-limit-mode" json:"store-limit-mode"` + + HotRegionsWriteInterval typeutil.Duration `toml:"hot-regions-write-interval" json:"hot-regions-write-interval"` + + HotRegionsResevervedDays int64 `toml:"hot-regions-reserved-days" json:"hot-regions-reserved-days"` } // Clone returns a cloned scheduling configuration. @@ -780,6 +784,8 @@ const ( defaultStoreLimitMode = "manual" defaultEnableJointConsensus = true defaultEnableCrossTableMerge = true + defaultHotRegionsWriteInterval = 20 * time.Minute + defaultHotRegionsResevervedDays = 30 ) func (c *ScheduleConfig) adjust(meta *configMetaData, reloading bool) error { @@ -861,6 +867,14 @@ func (c *ScheduleConfig) adjust(meta *configMetaData, reloading bool) error { c.StoreLimit = make(map[uint64]StoreLimitConfig) } + if !meta.IsDefined("hot-regions-write-interval") { + adjustDuration(&c.HotRegionsWriteInterval, defaultHotRegionsWriteInterval) + } + + if !meta.IsDefined("hot-regions-reserved-days") { + adjustInt64(&c.HotRegionsResevervedDays, defaultHotRegionsResevervedDays) + } + return c.Validate() } diff --git a/server/config/config_test.go b/server/config/config_test.go index db78722da4f..12170cf4b9a 100644 --- a/server/config/config_test.go +++ b/server/config/config_test.go @@ -459,6 +459,21 @@ wait-store-timeout = "120s" c.Assert(cfg.ReplicationMode.ReplicationMode, Equals, "majority") } +func (s *testConfigSuite) TestHotRegionConfig(c *C) { + cfgData := ` +[schedule] +hot-regions-reserved-days= 30 +hot-regions-write-interval= "30m" +` + cfg := NewConfig() + meta, err := toml.Decode(cfgData, &cfg) + c.Assert(err, IsNil) + err = cfg.Adjust(&meta, false) + c.Assert(err, IsNil) + c.Assert(cfg.Schedule.HotRegionsWriteInterval.Duration, Equals, time.Minute*30) + c.Assert(cfg.Schedule.HotRegionsResevervedDays, Equals, int64(30)) +} + func (s *testConfigSuite) TestConfigClone(c *C) { cfg := &Config{} cfg.Adjust(nil, false) diff --git a/server/grpc_service.go b/server/grpc_service.go index edf1ff8d37a..b0da547ff66 100644 --- a/server/grpc_service.go +++ b/server/grpc_service.go @@ -1519,4 +1519,4 @@ func checkStream(streamCtx context.Context, cancel context.CancelFunc, done chan case <-streamCtx.Done(): } <-done -} +} \ No newline at end of file diff --git a/server/handler.go b/server/handler.go index 1cff10d7fb9..d161ea42d2f 100644 --- a/server/handler.go +++ b/server/handler.go @@ -898,6 +898,52 @@ func (h *Handler) SetStoreLimitTTL(data string, value float64, ttl time.Duration }, ttl) } +func (h *Handler) GetAllRequestHistroyHotRegion(request *statistics.HistoryHotRegionsRequest) (*statistics.HistoryHotRegions, error) { + iter := h.s.hotRegionStorage.NewIterator(request.HotRegionTypes, request.StartTime, request.EndTime) + results := make([]*statistics.HistoryHotRegion, 0) + regionSet := make(map[uint64]bool, 0) + storeSet := make(map[uint64]bool, 0) + peerSet := make(map[uint64]bool, 0) + for _, id := range request.RegionIDs { + regionSet[id] = true + } + for _, id := range request.StoreIDs { + storeSet[id] = true + } + for _, id := range request.PeerIDs { + peerSet[id] = true + } + var next *statistics.HistoryHotRegion + var err error + for next, err = iter.Next(); next != nil && err == nil; next, err = iter.Next() { + if len(regionSet) != 0 && !regionSet[next.RegionID] { + continue + } + if len(storeSet) != 0 && !storeSet[next.StoreID] { + continue + } + if len(peerSet) != 0 && !peerSet[next.PeerID] { + continue + } + if request.HighHotDegree < next.HotDegree || request.LowHotDegree > next.HotDegree { + continue + } + if request.HighFlowBytes < next.FlowBytes || request.LowFlowBytes > next.FlowBytes { + continue + } + if request.HighKeyRate < next.KeyRate || request.LowKeyRate > next.KeyRate { + continue + } + if request.HighQueryRate < next.QueryRate || request.LowQueryRate > next.QueryRate { + continue + } + results = append(results, next) + } + return &statistics.HistoryHotRegions{ + HistoryHotRegion: results, + }, err +} + func checkStoreState(rc *cluster.RaftCluster, storeID uint64) error { store := rc.GetStore(storeID) if store == nil { diff --git a/server/server.go b/server/server.go index f5375859517..4f2e17379a2 100644 --- a/server/server.go +++ b/server/server.go @@ -143,6 +143,8 @@ type Server struct { // Store as map[string]*grpc.ClientConn clientConns sync.Map + //hot region history info storeage + hotRegionStorage *cluster.HotRegionStorage } // HandlerBuilder builds a server HTTP handler. @@ -391,7 +393,15 @@ func (s *Server) startServer(ctx context.Context) error { s.basicCluster = core.NewBasicCluster() s.cluster = cluster.NewRaftCluster(ctx, s.GetClusterRootPath(), s.clusterID, syncer.NewRegionSyncer(s), s.client, s.httpClient) s.hbStreams = hbstream.NewHeartbeatStreams(ctx, s.clusterID, s.cluster) - + //initial hot_region_storage in here + hotregion_path := filepath.Join(s.cfg.DataDir, "hot-region") + s.hotRegionStorage, err = cluster.NewHotRegionsHistoryStorage( + ctx, hotregion_path, encryptionKeyManager, s.cluster, s.member, + s.cfg.Schedule.HotRegionsResevervedDays, + s.cfg.Schedule.HotRegionsWriteInterval.Duration) + if err != nil { + return err + } // Run callbacks for _, cb := range s.startCallbacks { cb() @@ -455,6 +465,10 @@ func (s *Server) Close() { log.Error("close storage meet error", errs.ZapError(err)) } + if err := s.hotRegionStorage.Close(); err != nil { + log.Error("close hot region storage meet error", errs.ZapError(err)) + } + // Run callbacks for _, cb := range s.closeCallbacks { cb() @@ -703,6 +717,11 @@ func (s *Server) GetStorage() *core.Storage { return s.storage } +// GetHistoryHotRegionStorage returns the backend storage of historyHotRegion. +func (s *Server) GetHistoryHotRegionStorage() *cluster.HotRegionStorage { + return s.hotRegionStorage +} + // SetStorage changes the storage only for test purpose. // When we use it, we should prevent calling GetStorage, otherwise, it may cause a data race problem. func (s *Server) SetStorage(storage *core.Storage) { diff --git a/server/statistics/history_hot_region.go b/server/statistics/history_hot_region.go new file mode 100644 index 00000000000..a00cd831e5a --- /dev/null +++ b/server/statistics/history_hot_region.go @@ -0,0 +1,45 @@ +package statistics + +import "github.com/pingcap/kvproto/pkg/encryptionpb" + +type HistoryHotRegions struct { + HistoryHotRegion []*HistoryHotRegion `json:"historyHotRegion"` +} + +type HistoryHotRegion struct { + UpdateTime int64 `json:"update_time,omitempty"` + RegionID uint64 `json:"region_id,omitempty"` + PeerID uint64 `json:"peer_id,omitempty"` + StoreID uint64 `json:"store_id,omitempty"` + HotRegionType string `json:"hot_region_type,omitempty"` + HotDegree int64 `json:"hot_degree,omitempty"` + FlowBytes float64 `json:"flow_bytes,omitempty"` + KeyRate float64 `json:"key_rate,omitempty"` + QueryRate float64 `json:"query_rate,omitempty"` + StartKey []byte `json:"start_key,omitempty"` + EndKey []byte `json:"end_key,omitempty"` + // Encryption metadata for start_key and end_key. encryption_meta.iv is IV for start_key. + // IV for end_key is calculated from (encryption_meta.iv + len(start_key)). + // The field is only used by PD and should be ignored otherwise. + // If encryption_meta is empty (i.e. nil), it means start_key and end_key are unencrypted. + EncryptionMeta *encryptionpb.EncryptionMeta `json:"encryption_meta,omitempty"` +} + +//TODO +//find a better place to put this struct +type HistoryHotRegionsRequest struct { + StartTime int64 `json:"start_time,omitempty"` + EndTime int64 `json:"end_time,omitempty"` + RegionIDs []uint64 `json:"region_ids,omitempty"` + StoreIDs []uint64 `json:"store_ids,omitempty"` + PeerIDs []uint64 `json:"peer_ids,omitempty"` + HotRegionTypes []string `json:"hot_region_type,omitempty"` + LowHotDegree int64 `json:"low_hot_degree,omitempty"` + HighHotDegree int64 `json:"high_hot_degree,omitempty"` + LowFlowBytes float64 `json:"low_flow_bytes,omitempty"` + HighFlowBytes float64 `json:"high_flow_bytes,omitempty"` + LowKeyRate float64 `json:"low_key_rate,omitempty"` + HighKeyRate float64 `json:"high_key_rate,omitempty"` + LowQueryRate float64 `json:"low_query_rate,omitempty"` + HighQueryRate float64 `json:"high_query_rate,omitempty"` +}