浏览代码

test loop

wecisecode 5 天之前
父节点
当前提交
4a50f2e72a

+ 20 - 2
go.mod

@@ -5,15 +5,33 @@ go 1.24.0
 toolchain go1.24.5
 
 require (
-	git.wecise.com/wecise/odb-go v0.0.0-20260203025752-415766e914b1
-	gitee.com/wecisecode/util v0.7.1
+	git.wecise.com/wecise/odb-go v0.0.0-20260515085609-571e6a0bc4c8
+	gitee.com/wecisecode/util v0.8.9-0.20260429062429-11705cea96e9
 	github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e
 	github.com/spf13/cast v1.7.1
 	github.com/stretchr/testify v1.11.1
 )
 
+require (
+	github.com/cespare/xxhash v1.1.0 // indirect
+	github.com/dgraph-io/badger/v3 v3.2103.5 // indirect
+	github.com/dgraph-io/ristretto v0.1.1 // indirect
+	github.com/dustin/go-humanize v1.0.1 // indirect
+	github.com/golang/glog v1.2.4 // indirect
+	github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect
+	github.com/golang/snappy v0.0.3 // indirect
+	github.com/google/flatbuffers v2.0.0+incompatible // indirect
+	github.com/klauspost/compress v1.18.0 // indirect
+	github.com/mattn/go-runewidth v0.0.9 // indirect
+	github.com/olekukonko/tablewriter v0.0.5 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
+	github.com/xtaci/smux v1.5.16 // indirect
+	go.opencensus.io v0.22.5 // indirect
+)
+
 require (
 	git.wecise.com/wecise/common v0.0.0-20251229072030-500e09c5d16f
+	git.wecise.com/wecise/mring v0.0.0-20260514041217-6aeade775779
 	git.wecise.com/wecise/util v0.0.0-20251201044419-16d23ba20272 // indirect
 	github.com/bluele/gcache v0.0.2 // indirect
 	github.com/cespare/xxhash/v2 v2.3.0 // indirect

+ 105 - 4
go.sum

@@ -1,36 +1,60 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 git.wecise.com/wecise/common v0.0.0-20251229072030-500e09c5d16f h1:ASdIg5TrY/APxZAGqix/fm1C4G2S9CKyTyyLLsvTrr4=
 git.wecise.com/wecise/common v0.0.0-20251229072030-500e09c5d16f/go.mod h1:mSgt/uRLR0qO7SYSiox36h/tIKZrhDLrLjWuPSiC/Do=
-git.wecise.com/wecise/odb-go v0.0.0-20260203025752-415766e914b1 h1:Vilgb7++/LZo8jpw8+pENODq+vP0oxIyydhO1ICbP9k=
-git.wecise.com/wecise/odb-go v0.0.0-20260203025752-415766e914b1/go.mod h1:lnHE+OhtLDzkPzGWj2rbdDVQ52QzYY4WV8Sueso7/DA=
+git.wecise.com/wecise/mring v0.0.0-20260514041217-6aeade775779 h1:nzJsYvlvL7nfG8np5gyypaiDy7tmwtIqFHdMjsR9uvY=
+git.wecise.com/wecise/mring v0.0.0-20260514041217-6aeade775779/go.mod h1:EkXuEv7/b7vkcDNbKhA8qJXX9BFnMOF12pbNv2jfzW4=
+git.wecise.com/wecise/odb-go v0.0.0-20260515085609-571e6a0bc4c8 h1:JxYAgMqMgkfIB9YbKBc4Fd+mATR9zqC5/2z4EEoFqOI=
+git.wecise.com/wecise/odb-go v0.0.0-20260515085609-571e6a0bc4c8/go.mod h1:lnHE+OhtLDzkPzGWj2rbdDVQ52QzYY4WV8Sueso7/DA=
 git.wecise.com/wecise/util v0.0.0-20251201044419-16d23ba20272 h1:knHqq1/vn0FPI+9QPh2fjCUKHr8OKAfYhRtFJxLwvrE=
 git.wecise.com/wecise/util v0.0.0-20251201044419-16d23ba20272/go.mod h1:KmBNTlrqY5IH6gkuQ9cr43g7stJWNJ7VhIv1Faod5Z0=
-gitee.com/wecisecode/util v0.7.1 h1:cuS4mqqHi0tj+7LkXwAW7+3X7yqhjYrvG105ndrAAPA=
-gitee.com/wecisecode/util v0.7.1/go.mod h1:da49ZLqcSfpA+zYJhhc67Xw0sKVw8dCaaZSTzzr513s=
+gitee.com/wecisecode/util v0.8.9-0.20260429062429-11705cea96e9 h1:tSDvr2FoW585ZdOzrRAuWopK6a379/Pqn8JpchMlkkI=
+gitee.com/wecisecode/util v0.8.9-0.20260429062429-11705cea96e9/go.mod h1:da49ZLqcSfpA+zYJhhc67Xw0sKVw8dCaaZSTzzr513s=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
 github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
 github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao=
 github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
 github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
 github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
+github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
 github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
 github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 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=
+github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg=
+github.com/dgraph-io/badger/v3 v3.2103.5/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw=
+github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
+github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
+github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
 github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=
 github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
+github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
 github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
 github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
 github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA=
 github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI=
 github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
 github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
 github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
@@ -40,26 +64,57 @@ github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
 github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/glog v1.2.4 h1:CNNw5U8lSiiBk7druxtSHHTsRWcxKoac6kZKm2peBBc=
+github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
 github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
+github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
 github.com/gomodule/redigo v1.8.5 h1:nRAxCa+SVsyjSBrtZmG/cqb6VbTmuRzpg/PoTFlpumc=
 github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
+github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
+github.com/google/flatbuffers v2.0.0+incompatible h1:dicJ2oXwypfwUGnB2/TYWYEKiuk9eYQlQO/AnOHl5mI=
+github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+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/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
 github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
+github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -69,24 +124,40 @@ github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl
 github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
 github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
 github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ=
 github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e/go.mod h1:DkpGd78rljTxKAnTDPFqXSGxvETQnJyuSOQwsHycqfs=
 github.com/spacemonkeygo/errors v0.0.0-20201030155909-2f5f890dbc62 h1:X5+jSi+pL+sc2/Sp9mtrmRqDDnKkm9YVy1ik/jJDRD0=
 github.com/spacemonkeygo/errors v0.0.0-20201030155909-2f5f890dbc62/go.mod h1:7NL9UAYQnRM5iKHUCld3tf02fKb5Dft+41+VckASUy0=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
+github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
 github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
 github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
 github.com/tidwall/uhatools v0.5.1 h1:zMm7nDCZ4dbF+GQIonXpm6ST1V2JHZB4WgtqR84CFl4=
 github.com/tidwall/uhatools v0.5.1/go.mod h1:A0rmLPzOam2WhWA02UFtC3WRWcQJygCH/TwXR11Jd3w=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
 github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
 github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
 github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/xtaci/smux v1.5.16 h1:FBPYOkW8ZTjLKUM4LI4xnnuuDC8CQ/dB04HD519WoEk=
+github.com/xtaci/smux v1.5.16/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 go.etcd.io/etcd/api/v3 v3.5.22 h1:jRqZlcmndfKs1fO9I1Euqk3O5acEyBICyMKunxxhL94=
@@ -95,6 +166,8 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.22 h1:dFVbXCohuH0JKo6X+IznK6aohJSWcU+SF8QZF5E
 go.etcd.io/etcd/client/pkg/v3 v3.5.22/go.mod h1:cSURbmmXBOqyX839SdTbuO2zYtUjcZvKeQ7DEiYVOmU=
 go.etcd.io/etcd/client/v3 v3.5.22 h1:h+y9ocnclCPLPsoBoKwneZy7gy71VjvJ+8z4IPWxTn8=
 go.etcd.io/etcd/client/v3 v3.5.22/go.mod h1:p4ROLdCiv8gwQtnozOILXkqIjjSI0MR6Ly5F9qpFg+8=
+go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0=
+go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
 go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
 go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
 go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
@@ -113,24 +186,41 @@ go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
 go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
 go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
 go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/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.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
 golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
+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-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
 golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
@@ -139,6 +229,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
 golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@@ -146,15 +239,22 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24=
 google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI=
 google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
 google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
 google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
@@ -167,3 +267,4 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

+ 1 - 1
odbctest/mql/ootest.sh

@@ -19,7 +19,7 @@ else
 fi
 
 if [[ "$bgmode" == "" ]]; then
-    ps -ef | grep -v grep | grep "$0 bgmode=1" | awk '{print $2}' | xargs kill -9
+    ps -ef | grep -v grep | grep "$0 bgmode=1" | awk '{print $2}' | xargs kill -9 2>&1 >/dev/null
     echo "" > changed.log
     echo mql test running in background mode
     export bgmode=1

+ 23 - 0
odbctest/odbcmql/cfglog.go

@@ -1,9 +1,32 @@
 package odbcmql
 
 import (
+	"time"
+
+	"git.wecise.com/wecise/mring/mutil/selfmonitor"
+	"git.wecise.com/wecise/odb-go/odb/mql/parser"
 	mcfg "gitee.com/wecisecode/util/cfg"
 	mlog "gitee.com/wecisecode/util/logger"
+	"gitee.com/wecisecode/util/mio"
 )
 
 var config = mcfg.MConfig()
 var logger = mlog.New().WithConfig(config, "log")
+
+func init() {
+	pprofpath := config.GetString("pprof")
+	if pprofpath != "" {
+		go func() {
+			for {
+				selfmonitor.MemoryInfoCapture(pprofpath, 1*time.Minute)
+				(&mio.ClearFiles{Dir: pprofpath, NameMatch: `^.*\.prof$`, KeepLast: 10}).Do()
+			}
+		}()
+	}
+	config.OnChange(func() {
+		mql_parser_cache_size := config.GetInt("parser.cache", 100)
+		if mql_parser_cache_size > 0 {
+			parser.SetCacheSize(mql_parser_cache_size)
+		}
+	})
+}

+ 2 - 793
odbctest/odbcmql/mql_run.go

@@ -1,20 +1,14 @@
 package odbcmql
 
 import (
-	"bufio"
-	"context"
-	"encoding/json"
 	"fmt"
 	"log"
-	"math/rand"
 	"os"
 	"path/filepath"
 	"regexp"
 	"runtime"
-	"strconv"
 	"strings"
 	"sync"
-	"sync/atomic"
 	"testing"
 	"time"
 
@@ -24,9 +18,7 @@ import (
 	mcfg "gitee.com/wecisecode/util/cfg"
 	"gitee.com/wecisecode/util/filewalker"
 	"gitee.com/wecisecode/util/mfmt"
-	"gitee.com/wecisecode/util/pqc"
 	"gitee.com/wecisecode/util/set/strset"
-	"gitee.com/wecisecode/util/spliter"
 	"github.com/gofrs/flock"
 	"github.com/stretchr/testify/assert"
 )
@@ -136,7 +128,9 @@ options:
   odb=` + default_odbpaths + ` 指定odbserver路径,默认通过环境变量ODBPATH或通过ETCD相关配置获取
   keyspace=` + default_keyspace + `     指定keyspace,默认通过环境变量KEYSPACE获取
   ksnative=` + default_keyspace + `_native     指定native keyspace,默认通过环境变量KSNATIVE获取
+  loop=1              循环执行测试次数
   debug=true          开启调试模式,输出更多信息
+  pprof=.             设置pprof文件输出目录,开启自监控
 
 环境变量需求:
   KEYSPACE=` + default_keyspace + `
@@ -379,698 +373,6 @@ type GlobalVars struct {
 	ch_wait_mql_done     map[string]chan bool
 }
 
-func (mt *MQLTest) Run(t *testing.T, fw *filewalker.FileWalker) (retok bool) {
-	mt.t = t
-	mt.fw = fw
-	global := &GlobalVars{
-		CurrentVars:          &CurrentVars{},
-		wg_wait_fork_routine: make(map[string]*sync.WaitGroup),
-		ch_wait_mql_done:     make(map[string]chan bool),
-	}
-	mt.scopevars = &ScopeVars{
-		top: &Variables{
-			vars:       map[string]interface{}{},
-			loop_count: 1,
-			loop_from:  1,
-			loop_step:  1},
-		dir:  map[string]*Variables{},
-		file: map[string]*Variables{},
-		mql:  map[string]*Variables{}}
-	ctx, cancel := context.WithCancel(context.Background())
-	defer cancel()
-	st := time.Now()
-	loop_i := 0
-	parallel_queue := pqc.NewQueue[any](0)
-	mqlcount := int32(0)
-	defer func() {
-		mt.scopevars.RLock()
-		loop_count := mt.scopevars.top.loop_count
-		mt.scopevars.RUnlock()
-		ut := time.Since(st)
-		aut := time.Duration(0)
-		if mqlcount > 0 {
-			aut = global.totalusetime / time.Duration(mqlcount)
-		}
-		logger.Info("完成 ", loop_count, " 次执行,共", mqlcount, "次 MQL 请求,耗时", ut, "单条响应时间", global.minusetime, "~", global.maxusetime, "/", aut, "平均每秒吞吐量", (int64(mqlcount)*int64(time.Second))/int64(ut))
-	}()
-	var wg sync.WaitGroup
-	for {
-		mt.scopevars.Lock()
-		ok := loop_i < mt.scopevars.top.loop_count
-		mt.scopevars.Unlock()
-		if !ok {
-			break
-		}
-		loop_i++
-		//
-		ch_parallel_count := make(chan int)
-		topvars := &CurrentVars{
-			loop_i:            loop_i,
-			ch_parallel_count: ch_parallel_count,
-		}
-		//
-		ok_chan := make(chan bool, 1)
-		parallel_chan := make(chan bool, 1)
-		parallel := false
-		parallelcount := 0
-		done := false
-		wg.Add(1)
-		go func() {
-			defer func() {
-				atomic.AddInt32(&mqlcount, topvars.mqlcount)
-				wg.Done()
-			}()
-			ch_ok := make(chan bool)
-			go func() {
-				for {
-					select {
-					case <-ch_parallel_count:
-						if !done && !parallel {
-							parallel = true
-							// 加入并发控制队列
-							if parallelcount > 0 {
-								if parallelcount > parallel_queue.Size() {
-									parallel_queue.Growth(parallelcount)
-								}
-								parallel_queue.Push(1)
-							}
-							parallel_chan <- true
-						}
-					case ok := <-ch_ok:
-						ok_chan <- ok
-						if parallel {
-							if parallelcount > 0 {
-								// 从并发控制队列中移除
-								parallel_queue.Pop()
-							}
-						} else {
-							done = true
-						}
-						return
-					}
-				}
-			}()
-			ok := mt.RunAll(t, ctx,
-				global,
-				topvars)
-			ch_ok <- ok
-			if !ok {
-				cancel() // 并发测试,有一个线程出错,就全停
-				return
-			}
-		}()
-		success := true
-		select {
-		case success = <-ok_chan: // 非并发,等待完成
-		case <-parallel_chan: // 并发,执行继续下一次
-			logger.Info("第", topvars.loop_i, "次并发执行继续")
-		}
-		if !success {
-			return false // 失败,不等,直接返回
-		}
-	}
-	wg.Wait()
-	return
-}
-
-func (mt *MQLTest) RunAll(t *testing.T, ctx context.Context, global *GlobalVars, topvars *CurrentVars) bool {
-	logger.Info("开始第", topvars.loop_i, "次执行")
-	st := time.Now()
-	// 读取文件列表
-	listdirs := []string{}
-	dirfiles := map[string][]string{}
-	err := mt.fw.List(func(basedir, filename string) bool {
-		if dirfiles[basedir] == nil {
-			listdirs = append(listdirs, basedir)
-		}
-		dirfiles[basedir] = append(dirfiles[basedir], filename)
-		return true
-	})
-	if !assert.Nil(t, err, err) {
-		return false
-	}
-
-	// 执行所有目录
-	for _, basedir := range listdirs {
-		// 循环执行目录下所有文件
-		mt.scopevars.Lock()
-		if mt.scopevars.dir[basedir] == nil {
-			mt.scopevars.dir[basedir] = &Variables{
-				vars:       map[string]interface{}{},
-				loop_count: 1,
-				loop_from:  1,
-				loop_step:  1}
-		}
-		mt.scopevars.Unlock()
-		var wg sync.WaitGroup
-		st := time.Now()
-		loop_i := 0
-		parallel_queue := pqc.NewQueue[any](0)
-		mqlcount := int32(0)
-		for {
-			mt.scopevars.Lock()
-			ok := loop_i < mt.scopevars.dir[basedir].loop_count
-			mt.scopevars.Unlock()
-			if !ok {
-				break
-			}
-			loop_i++
-			//
-			ch_parallel_count := make(chan int)
-			dirvars := &CurrentVars{
-				loop_i:            loop_i,
-				ch_parallel_count: ch_parallel_count,
-			}
-			//
-			ok_chan := make(chan bool, 1)
-			parallel_chan := make(chan bool, 1)
-			parallel := false
-			parallelcount := 0
-			done := false
-			wg.Add(1)
-			go func(basedir string) {
-				defer func() {
-					atomic.AddInt32(&mqlcount, dirvars.mqlcount)
-					wg.Done()
-				}()
-				ch_ok := make(chan bool)
-				go func() {
-					for {
-						select {
-						case <-ch_parallel_count:
-							if !done && !parallel {
-								parallel = true
-								// 加入并发控制队列
-								if parallelcount > 0 {
-									if parallelcount > parallel_queue.Size() {
-										parallel_queue.Growth(parallelcount)
-									}
-									parallel_queue.Push(1)
-								}
-								parallel_chan <- true
-							}
-						case ok := <-ch_ok:
-							ok_chan <- ok
-							if parallel {
-								if parallelcount > 0 {
-									// 从并发控制队列中移除
-									parallel_queue.Pop()
-								}
-							} else {
-								done = true
-							}
-							return
-						}
-					}
-				}()
-				// logger.Info("dir", basedir, "第", fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i), "次执行开始")
-				files := dirfiles[basedir]
-				ch_ok <- mt.RunDir(t, ctx,
-					global,
-					topvars,
-					dirvars,
-					basedir, files)
-				// logger.Info("dir", basedir, "第", fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i), "次执行结束")
-			}(basedir)
-			success := true
-			select {
-			case success = <-ok_chan: // 非并发,等待完成
-				logger.Info("dir", basedir, "第", fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i), "次顺序执行完成")
-			case <-parallel_chan: // 并发,执行继续下一次
-				logger.Info("dir", basedir, "第", fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i), "次并发执行继续")
-			}
-			if !success {
-				return false
-			}
-		}
-		wg.Wait()
-		mt.scopevars.RLock()
-		loop_count := mt.scopevars.dir[basedir].loop_count
-		mt.scopevars.RUnlock()
-		if loop_count > 1 {
-			ut := time.Since(st)
-			logger.Info(fmt.Sprint("dir ", basedir, " loop ", loop_count, " times, run ", mqlcount, " mqls, usetime ", ut))
-		}
-	}
-	logger.Info("完成第", topvars.loop_i, "次执行,共", topvars.mqlcount, "次 MQL 请求,耗时", time.Since(st))
-	return true
-}
-
-func (mt *MQLTest) RunDir(t *testing.T, ctx context.Context,
-	global *GlobalVars,
-	topvars *CurrentVars,
-	dirvars *CurrentVars,
-	basedir string, files []string) bool {
-	for _, filename := range files {
-		ok := mt.RunFile(t, ctx, global, topvars, dirvars, basedir, filename)
-		if !ok {
-			return false
-		}
-	}
-	return true
-}
-
-func (mt *MQLTest) RunFile(t *testing.T, ctx context.Context,
-	global *GlobalVars,
-	topvars *CurrentVars,
-	dirvars *CurrentVars,
-	basedir, filename string) bool {
-	// 读取文件内容
-	ffpath := filepath.Join(basedir, filename)
-	bs, err := os.ReadFile(ffpath)
-	if !assert.Nil(t, err, err) {
-		return false
-	}
-	// mql语句切分
-	mqgr := NewMQLGroupRequest()
-	var multilines *MQLRequest
-	mqs := spliter.NewMQLSpliter(bufio.NewReader(strings.NewReader(string(bs))))
-	for {
-		mql, fromline, toline, fromchar, tochar, hasnext, _ := mqs.NextMQL()
-		if !hasnext {
-			break
-		}
-		// 去掉注释
-		clean_mql := strings.Join(spliter.MQLSplitClean(mql), ";")
-		if multilines != nil {
-			// 多条语句处理
-			if strings.TrimSpace(clean_mql) == "multilines end" {
-				// 保留语句中的注释
-				tm := strings.TrimSpace(strings.Replace(mql, "multilines end", "", 1))
-				if len(tm) > 0 {
-					if multilines.OriginQueryString != "" {
-						multilines.OriginQueryString += ";"
-					}
-					multilines.OriginQueryString += tm
-				}
-				multilines.Toline = toline
-				multilines.Tochar = tochar
-				// 加入请求组,并初始化,提取动作信息
-				e := mqgr.Append(multilines)
-				if !assert.Nil(t, e, e) {
-					return false
-				}
-				multilines = nil
-			} else {
-				if multilines.OriginQueryString != "" {
-					multilines.OriginQueryString += ";"
-				}
-				multilines.OriginQueryString += mql
-				multilines.Toline = toline
-				multilines.Tochar = tochar
-			}
-		} else if strings.TrimSpace(clean_mql) == "multilines begin" {
-			multilines = &MQLRequest{FilePath: ffpath}
-			// 保留语句中的注释
-			multilines.OriginQueryString = strings.TrimSpace(strings.Replace(mql, "multilines begin", "", 1))
-			multilines.Fromline = fromline
-			multilines.Fromchar = fromchar
-			multilines.Toline = toline
-			multilines.Tochar = tochar
-		} else {
-			// 单条语句
-			mqr := &MQLRequest{OriginQueryString: mql, FilePath: ffpath, Fromline: fromline, Toline: toline, Fromchar: fromchar, Tochar: tochar}
-			// 加入请求组,并初始化,提取动作信息
-			e := mqgr.Append(mqr)
-			if !assert.Nil(t, e, e) {
-				return false
-			}
-		}
-	}
-	// mqls := spliter.MQLSplit(string(bs))
-	mt.scopevars.Lock()
-	if mt.scopevars.file[ffpath] == nil {
-		mt.scopevars.file[ffpath] = &Variables{
-			vars:       map[string]interface{}{},
-			loop_count: 1,
-			loop_from:  1,
-			loop_step:  1}
-	}
-	mt.scopevars.Unlock()
-	var wg sync.WaitGroup
-	st := time.Now()
-	loop_i := 0
-	parallel_queue := pqc.NewQueue[any](0)
-	mqlcount := int32(0)
-	for {
-		mt.scopevars.Lock()
-		ok := loop_i < mt.scopevars.file[ffpath].loop_count
-		mt.scopevars.Unlock()
-		if !ok {
-			break
-		}
-		loop_i++
-		//
-		ch_parallel_count := make(chan int)
-		filevars := &CurrentVars{
-			loop_i:            loop_i,
-			ch_parallel_count: ch_parallel_count,
-		}
-		//
-		ok_chan := make(chan bool, 1)
-		parallel_chan := make(chan bool, 1)
-		parallel := false
-		parallelcount := 0
-		done := false
-		wg.Add(1)
-		go func() {
-			defer func() {
-				atomic.AddInt32(&mqlcount, filevars.mqlcount)
-				wg.Done()
-			}()
-			ch_ok := make(chan bool)
-			go func() {
-				for {
-					select {
-					case <-ch_parallel_count:
-						if !done && !parallel {
-							parallel = true
-							// 加入并发控制队列
-							if parallelcount > 0 {
-								if parallelcount > parallel_queue.Size() {
-									parallel_queue.Growth(parallelcount)
-								}
-								parallel_queue.Push(1)
-							}
-							parallel_chan <- true
-						}
-					case ok := <-ch_ok:
-						ok_chan <- ok
-						if parallel {
-							if parallelcount > 0 {
-								// 从并发控制队列中移除
-								parallel_queue.Pop()
-							}
-						} else {
-							done = true
-						}
-						return
-					}
-				}
-			}()
-			// logger.Info("file", ffpath, "第", fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i, ".", filevars.loop_i), "次执行开始")
-			ch_ok <- mt.RunMQLGroup(t, ctx,
-				global,
-				topvars,
-				dirvars,
-				filevars,
-				basedir, ffpath, mqgr)
-			// logger.Info("file", ffpath, "第", fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i, ".", filevars.loop_i), "次执行结束")
-		}()
-		success := true
-		select {
-		case success = <-ok_chan: // 非并发,等待完成
-			logger.Info("file", ffpath, "第", fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i, ".", filevars.loop_i), "次顺序执行完成")
-		case <-parallel_chan: // 并发,执行继续下一次
-			logger.Info("file", ffpath, "第", fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i, ".", filevars.loop_i), "次并发执行继续")
-		}
-		if !success {
-			return false
-		}
-	}
-	wg.Wait()
-	mt.scopevars.RLock()
-	loop_count := mt.scopevars.file[ffpath].loop_count
-	mt.scopevars.RUnlock()
-	if loop_count > 1 {
-		ut := time.Since(st)
-		sn := fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i)
-		logger.Info(fmt.Sprint("file ", ffpath+"/"+sn, " loop ", loop_count, " times, run ", mqlcount, " mqls, usetime ", ut))
-	}
-	return true
-}
-
-func (mt *MQLTest) RunMQLGroup(t *testing.T, ctx context.Context,
-	global *GlobalVars,
-	topvars *CurrentVars,
-	dirvars *CurrentVars,
-	filevars *CurrentVars,
-	basedir, ffpath string, mqgr *MQLGroupRequest) (pass bool) {
-	pass = true
-	var wg sync.WaitGroup
-	for _, mqs := range mqgr.mqrs {
-		wg.Add(1)
-		go func(mqs []*MQLRequest) {
-			defer wg.Done()
-			if len(mqs) == 0 {
-				return
-			}
-			if mqs[0].StaticActions.ForkName != nil {
-				forkname := *mqs[0].StaticActions.ForkName
-				global.Lock()
-				wg := global.wg_wait_fork_routine[forkname]
-				if wg == nil {
-					wg = &sync.WaitGroup{}
-					global.wg_wait_fork_routine[forkname] = wg
-				}
-				global.Unlock()
-				wg.Add(1)
-				defer wg.Done()
-			}
-			ok := mt.RunMQLs(t, ctx, global, topvars, dirvars, filevars, basedir, ffpath, mqs)
-			if !ok {
-				pass = false
-			}
-		}(mqs)
-	}
-	wg.Wait()
-	return
-}
-
-func (mt *MQLTest) RunMQLs(t *testing.T, ctx context.Context,
-	global *GlobalVars,
-	topvars *CurrentVars,
-	dirvars *CurrentVars,
-	filevars *CurrentVars,
-	basedir, ffpath string, mqrs []*MQLRequest) bool {
-	for _, mqr := range mqrs {
-		mqlkey := mqr.Key
-		mqlstr := mqr.OriginQueryString
-		if mqlstr == "" {
-			continue
-		}
-		staticactions := mqr.StaticActions
-		// 设置执行过程中的控制参数
-		mt.InitScopeVars(basedir, ffpath, mqlkey, staticactions)
-		ch_test_run_one_mql_result := make(chan bool)
-		go func() {
-			mqrinst := fmt.Sprint(mqlkey, "/", topvars.loop_i, ".", dirvars.loop_i, ".", filevars.loop_i)
-			global.Lock()
-			mqrdone := global.ch_wait_mql_done[mqrinst]
-			if mqrdone == nil {
-				mqrdone = make(chan bool)
-				global.ch_wait_mql_done[mqrinst] = mqrdone
-			}
-			var waitmqrdone chan bool
-			if mqr.WaitMQLRequest != nil {
-				waitmqrkey := mqr.WaitMQLRequest.Key
-				waitmqrinst := fmt.Sprint(waitmqrkey, "/", topvars.loop_i, ".", dirvars.loop_i, ".", filevars.loop_i)
-				waitmqrdone = global.ch_wait_mql_done[waitmqrinst]
-				if waitmqrdone == nil {
-					waitmqrdone = make(chan bool)
-					global.ch_wait_mql_done[waitmqrinst] = waitmqrdone
-				}
-			}
-			global.Unlock()
-			var ret bool
-			defer func() { mqrdone <- ret }()
-			if waitmqrdone != nil {
-				v := <-waitmqrdone
-				waitmqrdone <- v
-				if !v {
-					// 依赖MQR失败
-					ch_test_run_one_mql_result <- false
-					return
-				}
-			}
-			ret = mt.RunMQR(t, ctx, global, topvars, dirvars, filevars, basedir, ffpath, mqr)
-			ch_test_run_one_mql_result <- ret
-		}()
-		ret := <-ch_test_run_one_mql_result
-		if !ret {
-			return ret
-		}
-		// continue
-	}
-	return true
-}
-
-func (mt *MQLTest) RunMQR(t *testing.T, ctx context.Context,
-	global *GlobalVars,
-	topvars *CurrentVars,
-	dirvars *CurrentVars,
-	filevars *CurrentVars,
-	basedir, ffpath string, mqr *MQLRequest) bool {
-	mqlkey := mqr.Key
-	staticactionexprs := mqr.StaticActionExprs
-	staticactions := mqr.StaticActions
-	return t.Run(mqlkey, func(t *testing.T) {
-		var wg sync.WaitGroup
-		st := time.Now()
-		loop_i := 0
-		parallel_queue := pqc.NewQueue[any](0)
-		mqlcount := int32(0)
-		for {
-			mt.scopevars.Lock()
-			ok := loop_i < mt.scopevars.mql[mqlkey].loop_count
-			mt.scopevars.Unlock()
-			if !ok {
-				break
-			}
-			loop_i++
-			//
-			ch_parallel_count := make(chan int)
-			mqlvars := &CurrentVars{
-				loop_i:            loop_i,
-				ch_parallel_count: ch_parallel_count,
-			}
-			// 运行实例
-			mqlsn := fmt.Sprint(mqlkey, "/", topvars.loop_i, ".", dirvars.loop_i, ".", filevars.loop_i, ".", mqlvars.loop_i)
-			mt.scopevars.Lock()
-			if mt.scopevars.mql[mqlsn] == nil {
-				mt.scopevars.mql[mqlsn] = &Variables{
-					vars:       map[string]interface{}{},
-					loop_count: 1,
-					loop_from:  1,
-					loop_step:  1}
-			}
-			mt.scopevars.Unlock()
-			//
-			mt.scopevars.Lock()
-			top_loopi := mt.scopevars.top.loop_from + (topvars.loop_i-1)*mt.scopevars.top.loop_step
-			mt.scopevars.top.vars["topi"] = top_loopi
-			dir_loopi := mt.scopevars.dir[basedir].loop_from + (dirvars.loop_i-1)*mt.scopevars.dir[basedir].loop_step
-			mt.scopevars.dir[basedir].vars["diri"] = dir_loopi
-			mt.scopevars.dir[basedir].vars["topi"] = top_loopi
-			file_loopi := mt.scopevars.file[ffpath].loop_from + (filevars.loop_i-1)*mt.scopevars.file[ffpath].loop_step
-			mt.scopevars.file[ffpath].vars["filei"] = file_loopi
-			mt.scopevars.file[ffpath].vars["diri"] = dir_loopi
-			mt.scopevars.file[ffpath].vars["topi"] = top_loopi
-			mql_loopi := mt.scopevars.mql[mqlkey].loop_from + (mqlvars.loop_i-1)*mt.scopevars.mql[mqlkey].loop_step
-			mt.scopevars.mql[mqlkey].vars["mqli"] = mql_loopi
-			mt.scopevars.mql[mqlkey].vars["filei"] = file_loopi
-			mt.scopevars.mql[mqlkey].vars["diri"] = dir_loopi
-			mt.scopevars.mql[mqlkey].vars["topi"] = top_loopi
-			//
-			mt.scopevars.mql[mqlsn].vars["mqli"] = mql_loopi
-			mt.scopevars.mql[mqlsn].vars["filei"] = file_loopi
-			mt.scopevars.mql[mqlsn].vars["diri"] = dir_loopi
-			mt.scopevars.mql[mqlsn].vars["topi"] = top_loopi
-			mt.scopevars.Unlock()
-			mt.BeforeRunAction(basedir, ffpath, mqlkey, mqlsn, staticactions)
-			formatedmqlstr := mqr.FormatedQueryString
-			mql := mt.ReplaceLoopSN(formatedmqlstr,
-				global, topvars, dirvars, filevars, mqlvars,
-				top_loopi, dir_loopi, file_loopi, mql_loopi,
-				basedir, ffpath, mqlkey, mqlsn,
-			)
-			mqlreplace := rereplace_nbytes.FindAllStringSubmatch(mql, -1)
-			for _, mqr := range mqlreplace {
-				if len(mqr) == 2 {
-					bs := make([]byte, cast.ToInt(mqr[1]))
-					for i := 0; i < len(bs); i++ {
-						bs[i] = byte(32 + rand.Intn(91))
-					}
-					s := string(bs)
-					s = strings.ReplaceAll(s, ")", string([]byte{123}))
-					s = strings.ReplaceAll(s, "'", string([]byte{124}))
-					s = strings.ReplaceAll(s, "\"", string([]byte{125}))
-					s = strings.ReplaceAll(s, "\\", string([]byte{126}))
-					mql = strings.Replace(mql, mqr[0], s, 1)
-				}
-			}
-			for i, sat := range staticactionexprs {
-				mql = strings.Replace(mql, "["+strconv.Itoa(i)+"]", sat.SourceText, 1)
-			}
-			mql = strings.ReplaceAll(mql, "[[]", "[")
-			mqri := &MQLRequestInstance{
-				MQLRequest:          mqr,
-				PreparedQueryString: mql,
-			}
-			//
-			ok_chan := make(chan error, 1)
-			parallel_chan := make(chan bool, 1)
-			parallel := false
-			parallelcount := 0
-			done := false
-			wg.Add(1)
-			go func(mqlvars *CurrentVars) {
-				defer func() {
-					atomic.AddInt32(&mqlcount, mqlvars.mqlcount)
-					wg.Done()
-				}()
-				ch_ok := make(chan error)
-				go func() {
-					for {
-						select {
-						case parallelcount = <-ch_parallel_count:
-							if !done && !parallel {
-								parallel = true
-								// 加入并发控制队列
-								if parallelcount > 0 {
-									if parallelcount > parallel_queue.Size() {
-										parallel_queue.Growth(parallelcount)
-									}
-									parallel_queue.Push(1)
-								}
-								parallel_chan <- true
-							}
-						case ok := <-ch_ok:
-							ok_chan <- ok
-							if parallel {
-								if parallelcount > 0 {
-									// 从并发控制队列中移除
-									parallel_queue.Pop()
-								}
-							} else {
-								done = true
-							}
-							return
-						}
-					}
-				}()
-				nstaticactionexprs := map[int]*Action{}
-				for i, sat := range staticactionexprs {
-					nstaticactionexprs[i] = sat
-				}
-				// logger.Info(mqlkey, "第", fmt.Sprint(all_loop_i, ".", dir_loop_i, ".", file_loop_i, ".", curval.mql_i), "次执行开始")
-				ch_ok <- mt.RunMQL(t, ctx,
-					global,
-					topvars,
-					dirvars,
-					filevars,
-					mqlvars,
-					basedir, ffpath, mqlkey, mqlsn, mqri, staticactions, nstaticactionexprs)
-				// logger.Info(mqlkey, "第", fmt.Sprint(all_loop_i, ".", dir_loop_i, ".", file_loop_i, ".", curval.mql_i), "次执行结束")
-			}(mqlvars)
-			var err error
-			select {
-			case err = <-ok_chan: // 非并发,等待完成
-				// logger.Info(mqlkey, "第", fmt.Sprint(all_loop_i, ".", dir_loop_i, ".", file_loop_i, ".", curval.mql_i), "次顺序执行完成")
-			case <-parallel_chan: // 并发,执行继续下一次
-				// logger.Info(mqlkey, "第", fmt.Sprint(all_loop_i, ".", dir_loop_i, ".", file_loop_i, ".", curval.mql_i), "次并发执行继续")
-			}
-			if err != nil {
-				// logger.Error(err)
-				break
-			}
-		}
-		wg.Wait()
-		mt.scopevars.RLock()
-		loop_count := mt.scopevars.mql[mqlkey].loop_count
-		mt.scopevars.RUnlock()
-		if loop_count > 1 {
-			ut := time.Since(st)
-			sn := fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i, ".", filevars.loop_i)
-			as := ""
-			if topvars.sleeptime > 0 {
-				as = fmt.Sprint(", sleep ", topvars.sleeptime)
-			}
-			logger.Info(fmt.Sprint("mql ", mqlkey+"/"+sn, " loop ", loop_i, " times, usetime ", ut, as))
-		}
-	})
-}
-
 func (mt *MQLTest) getValue(basedir, ffpath, mqlkey, mqlsn string,
 	staticactions *StaticActions, a any) (ak string, va any) {
 	switch av := a.(type) {
@@ -1327,96 +629,3 @@ func (mt *MQLTest) InitScopeVars(basedir, ffpath, mqlkey string,
 		mt.scopevars.Unlock()
 	}
 }
-
-func (mt *MQLTest) RunMQL(t *testing.T, ctx context.Context,
-	global *GlobalVars,
-	topvars *CurrentVars,
-	dirvars *CurrentVars,
-	filevars *CurrentVars,
-	mqlvars *CurrentVars,
-	basedir, ffpath, mqlkey, mqlsn string, mqri *MQLRequestInstance, staticactions *StaticActions, staticactionexprs map[int]*Action) error {
-
-	mqlstr := mqri.PreparedQueryString
-
-	if staticactions.WaitName != nil {
-		global.Lock()
-		wgs := []*sync.WaitGroup{}
-		if *staticactions.WaitName == "" {
-			for _, wg := range global.wg_wait_fork_routine {
-				wgs = append(wgs, wg)
-			}
-		} else {
-			wg := global.wg_wait_fork_routine[*staticactions.WaitName]
-			if wg != nil {
-				wgs = append(wgs, wg)
-			}
-		}
-		global.Unlock()
-		for _, wg := range wgs {
-			wg.Wait()
-		}
-	}
-
-	if staticactions.ParallelCount != nil {
-		mt.scopevars.Lock()
-		switch staticactions.Scope {
-		case "top":
-			topvars.ch_parallel_count <- *staticactions.ParallelCount
-		case "dir":
-			dirvars.ch_parallel_count <- *staticactions.ParallelCount
-		case "file":
-			filevars.ch_parallel_count <- *staticactions.ParallelCount
-		default:
-			mqlvars.ch_parallel_count <- *staticactions.ParallelCount
-		}
-		mt.scopevars.Unlock()
-	}
-
-	// 重新获取修正后的动作
-	actionexprs, e := getActionExprs(mqlstr, staticactionexprs)
-	if !assert.Nil(t, e, e) {
-		return e
-	}
-	dynamicactions := actionexprs.DynamicActions()
-	if len(dynamicactions.SubscribeArgs) > 0 {
-		subscribe(dynamicactions.SubscribeArgs...)
-	}
-	if len(dynamicactions.UnsubscribeArgs) > 0 {
-		unsubscribe(dynamicactions.UnsubscribeArgs...)
-	}
-	values := []interface{}{}
-	if len(dynamicactions.Params) > 0 {
-		err := json.Unmarshal([]byte(dynamicactions.Params), &values)
-		if err != nil {
-			assert.Nil(t, fmt.Sprint("params参数只支持JSON Array,", dynamicactions.Params, mqlstr), err)
-			return err
-		}
-	}
-
-	x := atomic.AddInt32(&global.mqlcount, 1)
-	atomic.AddInt32(&topvars.mqlcount, 1)
-	atomic.AddInt32(&dirvars.mqlcount, 1)
-	atomic.AddInt32(&filevars.mqlcount, 1)
-	atomic.AddInt32(&mqlvars.mqlcount, 1)
-
-	err := mt.RunMQLTryDo(t, ctx,
-		global,
-		topvars,
-		dirvars,
-		filevars,
-		mqlvars,
-		basedir, ffpath, mqlkey, mqlsn+"("+strconv.Itoa(int(x))+")", mqri, values, staticactions, actionexprs)
-	if err != nil {
-		// 执行过程有错,停止继续执行
-		return err
-	}
-	if staticactions.SleepTime != nil {
-		global.sleeptime += *staticactions.SleepTime
-		topvars.sleeptime += *staticactions.SleepTime
-		dirvars.sleeptime += *staticactions.SleepTime
-		filevars.sleeptime += *staticactions.SleepTime
-		mqlvars.sleeptime += *staticactions.SleepTime
-		time.Sleep(*staticactions.SleepTime)
-	}
-	return nil
-}

+ 151 - 0
odbctest/odbcmql/mql_run_dir.go

@@ -0,0 +1,151 @@
+package odbcmql
+
+import (
+	"context"
+	"fmt"
+	"sync"
+	"sync/atomic"
+	"testing"
+	"time"
+
+	"gitee.com/wecisecode/util/pqc"
+	"github.com/stretchr/testify/assert"
+)
+
+func (mt *MQLTest) RunAllDirs(t *testing.T, ctx context.Context, global *GlobalVars, topvars *CurrentVars) bool {
+	logger.Info("开始第", topvars.loop_i, "次执行")
+	st := time.Now()
+	// 读取文件列表
+	listdirs := []string{}
+	dirfiles := map[string][]string{}
+	err := mt.fw.List(func(basedir, filename string) bool {
+		if dirfiles[basedir] == nil {
+			listdirs = append(listdirs, basedir)
+		}
+		dirfiles[basedir] = append(dirfiles[basedir], filename)
+		return true
+	})
+	if !assert.Nil(t, err, err) {
+		return false
+	}
+
+	// 执行所有目录
+	for _, basedir := range listdirs {
+		// 循环执行目录下所有文件
+		mt.scopevars.Lock()
+		if mt.scopevars.dir[basedir] == nil {
+			mt.scopevars.dir[basedir] = &Variables{
+				vars:       map[string]interface{}{},
+				loop_count: 1,
+				loop_from:  1,
+				loop_step:  1}
+		}
+		mt.scopevars.Unlock()
+		var wg sync.WaitGroup
+		st := time.Now()
+		loop_i := 0
+		parallel_queue := pqc.NewQueue[any](0)
+		mqlcount := int32(0)
+		for {
+			mt.scopevars.Lock()
+			ok := loop_i < mt.scopevars.dir[basedir].loop_count
+			mt.scopevars.Unlock()
+			if !ok {
+				break
+			}
+			loop_i++
+			//
+			ch_parallel_count := make(chan int)
+			dirvars := &CurrentVars{
+				loop_i:            loop_i,
+				ch_parallel_count: ch_parallel_count,
+			}
+			//
+			ok_chan := make(chan bool, 1)
+			parallel_chan := make(chan bool, 1)
+			parallel := false
+			parallelcount := 0
+			done := false
+			wg.Add(1)
+			go func(basedir string) {
+				defer func() {
+					atomic.AddInt32(&mqlcount, dirvars.mqlcount)
+					wg.Done()
+				}()
+				ch_ok := make(chan bool)
+				go func() {
+					for {
+						select {
+						case <-ch_parallel_count:
+							if !done && !parallel {
+								parallel = true
+								// 加入并发控制队列
+								if parallelcount > 0 {
+									if parallelcount > parallel_queue.Size() {
+										parallel_queue.Growth(parallelcount)
+									}
+									parallel_queue.Push(1)
+								}
+								parallel_chan <- true
+							}
+						case ok := <-ch_ok:
+							ok_chan <- ok
+							if parallel {
+								if parallelcount > 0 {
+									// 从并发控制队列中移除
+									parallel_queue.Pop()
+								}
+							} else {
+								done = true
+							}
+							// 一个目录执行完成
+							return
+						}
+					}
+				}()
+				// logger.Info("dir", basedir, "第", fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i), "次执行开始")
+				files := dirfiles[basedir]
+				ch_ok <- mt.RunDirFiles(t, ctx,
+					global,
+					topvars,
+					dirvars,
+					basedir, files)
+				// logger.Info("dir", basedir, "第", fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i), "次执行结束")
+			}(basedir)
+			success := true
+			select {
+			case success = <-ok_chan: // 非并发,等待完成
+				logger.Info("dir", basedir, "第", fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i), "次顺序执行完成")
+			case <-parallel_chan: // 并发,执行继续下一次
+				logger.Info("dir", basedir, "第", fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i), "次并发执行继续")
+			}
+			if !success {
+				return false
+			}
+		}
+		wg.Wait()
+		mt.scopevars.RLock()
+		loop_count := mt.scopevars.dir[basedir].loop_count
+		mt.scopevars.RUnlock()
+		if loop_count > 1 {
+			ut := time.Since(st)
+			logger.Info(fmt.Sprint("dir ", basedir, " loop ", loop_count, " times, run ", mqlcount, " mqls, usetime ", ut))
+		}
+	}
+	logger.Info("完成第", topvars.loop_i, "次执行,共", topvars.mqlcount, "次 MQL 请求,耗时", time.Since(st))
+	return true
+}
+
+func (mt *MQLTest) RunDirFiles(t *testing.T, ctx context.Context,
+	global *GlobalVars,
+	topvars *CurrentVars,
+	dirvars *CurrentVars,
+	basedir string, files []string) bool {
+	for _, filename := range files {
+		ok := mt.RunFile(t, ctx, global, topvars, dirvars, basedir, filename)
+		if !ok {
+			return false
+		}
+	}
+	return true
+}

+ 283 - 0
odbctest/odbcmql/mql_run_file.go

@@ -0,0 +1,283 @@
+package odbcmql
+
+import (
+	"bufio"
+	"context"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+	"sync"
+	"sync/atomic"
+	"testing"
+	"time"
+
+	"gitee.com/wecisecode/util/pqc"
+	"gitee.com/wecisecode/util/spliter"
+	"github.com/stretchr/testify/assert"
+)
+
+func (mt *MQLTest) RunFile(t *testing.T, ctx context.Context,
+	global *GlobalVars,
+	topvars *CurrentVars,
+	dirvars *CurrentVars,
+	basedir, filename string) bool {
+	// 读取文件内容
+	ffpath := filepath.Join(basedir, filename)
+	bs, err := os.ReadFile(ffpath)
+	if !assert.Nil(t, err, err) {
+		return false
+	}
+	// mql语句切分
+	mqgr := NewMQLGroupRequest()
+	var multilines *MQLRequest
+	mqs := spliter.NewMQLSpliter(bufio.NewReader(strings.NewReader(string(bs))))
+	for {
+		mql, fromline, toline, fromchar, tochar, hasnext, _ := mqs.NextMQL()
+		if !hasnext {
+			break
+		}
+		// 去掉注释
+		clean_mql := strings.Join(spliter.MQLSplitClean(mql), ";")
+		if multilines != nil {
+			// 多条语句处理
+			if strings.TrimSpace(clean_mql) == "multilines end" {
+				// 保留语句中的注释
+				tm := strings.TrimSpace(strings.Replace(mql, "multilines end", "", 1))
+				if len(tm) > 0 {
+					if multilines.OriginQueryString != "" {
+						multilines.OriginQueryString += ";"
+					}
+					multilines.OriginQueryString += tm
+				}
+				multilines.Toline = toline
+				multilines.Tochar = tochar
+				// 加入请求组,并初始化,提取动作信息
+				e := mqgr.Append(multilines)
+				if !assert.Nil(t, e, e) {
+					return false
+				}
+				multilines = nil
+			} else {
+				if multilines.OriginQueryString != "" {
+					multilines.OriginQueryString += ";"
+				}
+				multilines.OriginQueryString += mql
+				multilines.Toline = toline
+				multilines.Tochar = tochar
+			}
+		} else if strings.TrimSpace(clean_mql) == "multilines begin" {
+			multilines = &MQLRequest{FilePath: ffpath}
+			// 保留语句中的注释
+			multilines.OriginQueryString = strings.TrimSpace(strings.Replace(mql, "multilines begin", "", 1))
+			multilines.Fromline = fromline
+			multilines.Fromchar = fromchar
+			multilines.Toline = toline
+			multilines.Tochar = tochar
+		} else {
+			// 单条语句
+			mqr := &MQLRequest{OriginQueryString: mql, FilePath: ffpath, Fromline: fromline, Toline: toline, Fromchar: fromchar, Tochar: tochar}
+			// 加入请求组,并初始化,提取动作信息
+			e := mqgr.Append(mqr)
+			if !assert.Nil(t, e, e) {
+				return false
+			}
+		}
+	}
+	// mqls := spliter.MQLSplit(string(bs))
+	mt.scopevars.Lock()
+	if mt.scopevars.file[ffpath] == nil {
+		mt.scopevars.file[ffpath] = &Variables{
+			vars:       map[string]interface{}{},
+			loop_count: 1,
+			loop_from:  1,
+			loop_step:  1}
+	}
+	mt.scopevars.Unlock()
+	var wg sync.WaitGroup
+	st := time.Now()
+	loop_i := 0
+	parallel_queue := pqc.NewQueue[any](0)
+	mqlcount := int32(0)
+	for {
+		mt.scopevars.Lock()
+		ok := loop_i < mt.scopevars.file[ffpath].loop_count
+		mt.scopevars.Unlock()
+		if !ok {
+			break
+		}
+		loop_i++
+		//
+		ch_parallel_count := make(chan int)
+		filevars := &CurrentVars{
+			loop_i:            loop_i,
+			ch_parallel_count: ch_parallel_count,
+		}
+		//
+		ok_chan := make(chan bool, 1)
+		parallel_chan := make(chan bool, 1)
+		parallel := false
+		parallelcount := 0
+		done := false
+		wg.Add(1)
+		go func() {
+			defer func() {
+				atomic.AddInt32(&mqlcount, filevars.mqlcount)
+				wg.Done()
+			}()
+			ch_ok := make(chan bool)
+			go func() {
+				for {
+					select {
+					case <-ch_parallel_count:
+						if !done && !parallel {
+							parallel = true
+							// 加入并发控制队列
+							if parallelcount > 0 {
+								if parallelcount > parallel_queue.Size() {
+									parallel_queue.Growth(parallelcount)
+								}
+								parallel_queue.Push(1)
+							}
+							parallel_chan <- true
+						}
+					case ok := <-ch_ok:
+						ok_chan <- ok
+						if parallel {
+							if parallelcount > 0 {
+								// 从并发控制队列中移除
+								parallel_queue.Pop()
+							}
+						} else {
+							done = true
+						}
+						return
+					}
+				}
+			}()
+			// logger.Info("file", ffpath, "第", fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i, ".", filevars.loop_i), "次执行开始")
+			ch_ok <- mt.RunMQLGroup(t, ctx,
+				global,
+				topvars,
+				dirvars,
+				filevars,
+				basedir, ffpath, mqgr)
+			// logger.Info("file", ffpath, "第", fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i, ".", filevars.loop_i), "次执行结束")
+		}()
+		success := true
+		select {
+		case success = <-ok_chan: // 非并发,等待完成
+			logger.Info("file", ffpath, "第", fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i, ".", filevars.loop_i), "次顺序执行完成")
+		case <-parallel_chan: // 并发,执行继续下一次
+			logger.Info("file", ffpath, "第", fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i, ".", filevars.loop_i), "次并发执行继续")
+		}
+		if !success {
+			return false
+		}
+	}
+	wg.Wait()
+	mt.scopevars.RLock()
+	loop_count := mt.scopevars.file[ffpath].loop_count
+	mt.scopevars.RUnlock()
+	if loop_count > 1 {
+		ut := time.Since(st)
+		sn := fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i)
+		logger.Info(fmt.Sprint("file ", ffpath+"/"+sn, " loop ", loop_count, " times, run ", mqlcount, " mqls, usetime ", ut))
+	}
+	return true
+}
+
+func (mt *MQLTest) RunMQLGroup(t *testing.T, ctx context.Context,
+	global *GlobalVars,
+	topvars *CurrentVars,
+	dirvars *CurrentVars,
+	filevars *CurrentVars,
+	basedir, ffpath string, mqgr *MQLGroupRequest) (pass bool) {
+	pass = true
+	var wg sync.WaitGroup
+	for _, mqs := range mqgr.mqrs {
+		wg.Add(1)
+		go func(mqs []*MQLRequest) {
+			defer wg.Done()
+			if len(mqs) == 0 {
+				return
+			}
+			if mqs[0].StaticActions.ForkName != nil {
+				forkname := *mqs[0].StaticActions.ForkName
+				global.Lock()
+				wg := global.wg_wait_fork_routine[forkname]
+				if wg == nil {
+					wg = &sync.WaitGroup{}
+					global.wg_wait_fork_routine[forkname] = wg
+				}
+				global.Unlock()
+				wg.Add(1)
+				defer wg.Done()
+			}
+			ok := mt.RunMQLs(t, ctx, global, topvars, dirvars, filevars, basedir, ffpath, mqs)
+			if !ok {
+				pass = false
+			}
+		}(mqs)
+	}
+	wg.Wait()
+	return
+}
+
+func (mt *MQLTest) RunMQLs(t *testing.T, ctx context.Context,
+	global *GlobalVars,
+	topvars *CurrentVars,
+	dirvars *CurrentVars,
+	filevars *CurrentVars,
+	basedir, ffpath string, mqrs []*MQLRequest) bool {
+	for _, mqr := range mqrs {
+		mqlkey := mqr.Key
+		mqlstr := mqr.OriginQueryString
+		if mqlstr == "" {
+			continue
+		}
+		staticactions := mqr.StaticActions
+		// 设置执行过程中的控制参数
+		mt.InitScopeVars(basedir, ffpath, mqlkey, staticactions)
+		ch_test_run_one_mql_result := make(chan bool)
+		go func() {
+			mqrinst := fmt.Sprint(mqlkey, "/", topvars.loop_i, ".", dirvars.loop_i, ".", filevars.loop_i)
+			global.Lock()
+			mqrdone := global.ch_wait_mql_done[mqrinst]
+			if mqrdone == nil {
+				mqrdone = make(chan bool)
+				global.ch_wait_mql_done[mqrinst] = mqrdone
+			}
+			var waitmqrdone chan bool
+			if mqr.WaitMQLRequest != nil {
+				waitmqrkey := mqr.WaitMQLRequest.Key
+				waitmqrinst := fmt.Sprint(waitmqrkey, "/", topvars.loop_i, ".", dirvars.loop_i, ".", filevars.loop_i)
+				waitmqrdone = global.ch_wait_mql_done[waitmqrinst]
+				if waitmqrdone == nil {
+					waitmqrdone = make(chan bool)
+					global.ch_wait_mql_done[waitmqrinst] = waitmqrdone
+				}
+			}
+			global.Unlock()
+			var ret bool
+			defer func() { mqrdone <- ret }()
+			if waitmqrdone != nil {
+				v := <-waitmqrdone
+				waitmqrdone <- v
+				if !v {
+					// 依赖MQR失败
+					ch_test_run_one_mql_result <- false
+					return
+				}
+			}
+			ret = mt.RunMQR(t, ctx, global, topvars, dirvars, filevars, basedir, ffpath, mqr)
+			ch_test_run_one_mql_result <- ret
+		}()
+		ret := <-ch_test_run_one_mql_result
+		if !ret {
+			return ret
+		}
+		// continue
+	}
+	return true
+}

+ 288 - 0
odbctest/odbcmql/mql_run_mql.go

@@ -0,0 +1,288 @@
+package odbcmql
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"math/rand"
+	"strconv"
+	"strings"
+	"sync"
+	"sync/atomic"
+	"testing"
+	"time"
+
+	"gitee.com/wecisecode/util/cast"
+	"gitee.com/wecisecode/util/pqc"
+	"github.com/stretchr/testify/assert"
+)
+
+func (mt *MQLTest) RunMQR(t *testing.T, ctx context.Context,
+	global *GlobalVars,
+	topvars *CurrentVars,
+	dirvars *CurrentVars,
+	filevars *CurrentVars,
+	basedir, ffpath string, mqr *MQLRequest) bool {
+	mqlkey := mqr.Key
+	staticactionexprs := mqr.StaticActionExprs
+	staticactions := mqr.StaticActions
+	return t.Run(mqlkey, func(t *testing.T) {
+		var wg sync.WaitGroup
+		st := time.Now()
+		loop_i := 0
+		parallel_queue := pqc.NewQueue[any](0)
+		mqlcount := int32(0)
+		for {
+			mt.scopevars.Lock()
+			ok := loop_i < mt.scopevars.mql[mqlkey].loop_count
+			mt.scopevars.Unlock()
+			if !ok {
+				break
+			}
+			loop_i++
+			//
+			ch_parallel_count := make(chan int)
+			mqlvars := &CurrentVars{
+				loop_i:            loop_i,
+				ch_parallel_count: ch_parallel_count,
+			}
+			// 运行实例
+			mqlsn := fmt.Sprint(mqlkey, "/", topvars.loop_i, ".", dirvars.loop_i, ".", filevars.loop_i, ".", mqlvars.loop_i)
+			mt.scopevars.Lock()
+			if mt.scopevars.mql[mqlsn] == nil {
+				mt.scopevars.mql[mqlsn] = &Variables{
+					vars:       map[string]interface{}{},
+					loop_count: 1,
+					loop_from:  1,
+					loop_step:  1}
+			}
+			mt.scopevars.Unlock()
+			//
+			mt.scopevars.Lock()
+			top_loopi := mt.scopevars.top.loop_from + (topvars.loop_i-1)*mt.scopevars.top.loop_step
+			mt.scopevars.top.vars["topi"] = top_loopi
+			dir_loopi := mt.scopevars.dir[basedir].loop_from + (dirvars.loop_i-1)*mt.scopevars.dir[basedir].loop_step
+			mt.scopevars.dir[basedir].vars["diri"] = dir_loopi
+			mt.scopevars.dir[basedir].vars["topi"] = top_loopi
+			file_loopi := mt.scopevars.file[ffpath].loop_from + (filevars.loop_i-1)*mt.scopevars.file[ffpath].loop_step
+			mt.scopevars.file[ffpath].vars["filei"] = file_loopi
+			mt.scopevars.file[ffpath].vars["diri"] = dir_loopi
+			mt.scopevars.file[ffpath].vars["topi"] = top_loopi
+			mql_loopi := mt.scopevars.mql[mqlkey].loop_from + (mqlvars.loop_i-1)*mt.scopevars.mql[mqlkey].loop_step
+			mt.scopevars.mql[mqlkey].vars["mqli"] = mql_loopi
+			mt.scopevars.mql[mqlkey].vars["filei"] = file_loopi
+			mt.scopevars.mql[mqlkey].vars["diri"] = dir_loopi
+			mt.scopevars.mql[mqlkey].vars["topi"] = top_loopi
+			//
+			mt.scopevars.mql[mqlsn].vars["mqli"] = mql_loopi
+			mt.scopevars.mql[mqlsn].vars["filei"] = file_loopi
+			mt.scopevars.mql[mqlsn].vars["diri"] = dir_loopi
+			mt.scopevars.mql[mqlsn].vars["topi"] = top_loopi
+			mt.scopevars.Unlock()
+			mt.BeforeRunAction(basedir, ffpath, mqlkey, mqlsn, staticactions)
+			formatedmqlstr := mqr.FormatedQueryString
+			mql := mt.ReplaceLoopSN(formatedmqlstr,
+				global, topvars, dirvars, filevars, mqlvars,
+				top_loopi, dir_loopi, file_loopi, mql_loopi,
+				basedir, ffpath, mqlkey, mqlsn,
+			)
+			mqlreplace := rereplace_nbytes.FindAllStringSubmatch(mql, -1)
+			for _, mqr := range mqlreplace {
+				if len(mqr) == 2 {
+					bs := make([]byte, cast.ToInt(mqr[1]))
+					for i := 0; i < len(bs); i++ {
+						bs[i] = byte(32 + rand.Intn(91))
+					}
+					s := string(bs)
+					s = strings.ReplaceAll(s, ")", string([]byte{123}))
+					s = strings.ReplaceAll(s, "'", string([]byte{124}))
+					s = strings.ReplaceAll(s, "\"", string([]byte{125}))
+					s = strings.ReplaceAll(s, "\\", string([]byte{126}))
+					mql = strings.Replace(mql, mqr[0], s, 1)
+				}
+			}
+			for i, sat := range staticactionexprs {
+				mql = strings.Replace(mql, "["+strconv.Itoa(i)+"]", sat.SourceText, 1)
+			}
+			mql = strings.ReplaceAll(mql, "[[]", "[")
+			mqri := &MQLRequestInstance{
+				MQLRequest:          mqr,
+				PreparedQueryString: mql,
+			}
+			//
+			ok_chan := make(chan error, 1)
+			parallel_chan := make(chan bool, 1)
+			parallel := false
+			parallelcount := 0
+			done := false
+			wg.Add(1)
+			go func(mqlvars *CurrentVars) {
+				defer func() {
+					atomic.AddInt32(&mqlcount, mqlvars.mqlcount)
+					wg.Done()
+				}()
+				ch_ok := make(chan error)
+				go func() {
+					for {
+						select {
+						case parallelcount = <-ch_parallel_count:
+							if !done && !parallel {
+								parallel = true
+								// 加入并发控制队列
+								if parallelcount > 0 {
+									if parallelcount > parallel_queue.Size() {
+										parallel_queue.Growth(parallelcount)
+									}
+									parallel_queue.Push(1)
+								}
+								parallel_chan <- true
+							}
+						case ok := <-ch_ok:
+							ok_chan <- ok
+							if parallel {
+								if parallelcount > 0 {
+									// 从并发控制队列中移除
+									parallel_queue.Pop()
+								}
+							} else {
+								done = true
+							}
+							return
+						}
+					}
+				}()
+				nstaticactionexprs := map[int]*Action{}
+				for i, sat := range staticactionexprs {
+					nstaticactionexprs[i] = sat
+				}
+				// logger.Info(mqlkey, "第", fmt.Sprint(all_loop_i, ".", dir_loop_i, ".", file_loop_i, ".", curval.mql_i), "次执行开始")
+				ch_ok <- mt.RunMQL(t, ctx,
+					global,
+					topvars,
+					dirvars,
+					filevars,
+					mqlvars,
+					basedir, ffpath, mqlkey, mqlsn, mqri, staticactions, nstaticactionexprs)
+				// logger.Info(mqlkey, "第", fmt.Sprint(all_loop_i, ".", dir_loop_i, ".", file_loop_i, ".", curval.mql_i), "次执行结束")
+			}(mqlvars)
+			var err error
+			select {
+			case err = <-ok_chan: // 非并发,等待完成
+				// logger.Info(mqlkey, "第", fmt.Sprint(all_loop_i, ".", dir_loop_i, ".", file_loop_i, ".", curval.mql_i), "次顺序执行完成")
+			case <-parallel_chan: // 并发,执行继续下一次
+				// logger.Info(mqlkey, "第", fmt.Sprint(all_loop_i, ".", dir_loop_i, ".", file_loop_i, ".", curval.mql_i), "次并发执行继续")
+			}
+			if err != nil {
+				// logger.Error(err)
+				break
+			}
+		}
+		wg.Wait()
+		mt.scopevars.RLock()
+		loop_count := mt.scopevars.mql[mqlkey].loop_count
+		mt.scopevars.RUnlock()
+		if loop_count > 1 {
+			ut := time.Since(st)
+			sn := fmt.Sprint(topvars.loop_i, ".", dirvars.loop_i, ".", filevars.loop_i)
+			as := ""
+			if topvars.sleeptime > 0 {
+				as = fmt.Sprint(", sleep ", topvars.sleeptime)
+			}
+			logger.Info(fmt.Sprint("mql ", mqlkey+"/"+sn, " loop ", loop_i, " times, usetime ", ut, as))
+		}
+	})
+}
+
+// 执行一条命令
+func (mt *MQLTest) RunMQL(t *testing.T, ctx context.Context,
+	global *GlobalVars,
+	topvars *CurrentVars,
+	dirvars *CurrentVars,
+	filevars *CurrentVars,
+	mqlvars *CurrentVars,
+	basedir, ffpath, mqlkey, mqlsn string, mqri *MQLRequestInstance, staticactions *StaticActions, staticactionexprs map[int]*Action) error {
+
+	mqlstr := mqri.PreparedQueryString
+
+	if staticactions.WaitName != nil {
+		global.Lock()
+		wgs := []*sync.WaitGroup{}
+		if *staticactions.WaitName == "" {
+			for _, wg := range global.wg_wait_fork_routine {
+				wgs = append(wgs, wg)
+			}
+		} else {
+			wg := global.wg_wait_fork_routine[*staticactions.WaitName]
+			if wg != nil {
+				wgs = append(wgs, wg)
+			}
+		}
+		global.Unlock()
+		for _, wg := range wgs {
+			wg.Wait()
+		}
+	}
+
+	if staticactions.ParallelCount != nil {
+		mt.scopevars.Lock()
+		switch staticactions.Scope {
+		case "top":
+			topvars.ch_parallel_count <- *staticactions.ParallelCount
+		case "dir":
+			dirvars.ch_parallel_count <- *staticactions.ParallelCount
+		case "file":
+			filevars.ch_parallel_count <- *staticactions.ParallelCount
+		default:
+			mqlvars.ch_parallel_count <- *staticactions.ParallelCount
+		}
+		mt.scopevars.Unlock()
+	}
+
+	// 重新获取修正后的动作
+	actionexprs, e := getActionExprs(mqlstr, staticactionexprs)
+	if !assert.Nil(t, e, e) {
+		return e
+	}
+	dynamicactions := actionexprs.DynamicActions()
+	if len(dynamicactions.SubscribeArgs) > 0 {
+		subscribe(dynamicactions.SubscribeArgs...)
+	}
+	if len(dynamicactions.UnsubscribeArgs) > 0 {
+		unsubscribe(dynamicactions.UnsubscribeArgs...)
+	}
+	values := []interface{}{}
+	if len(dynamicactions.Params) > 0 {
+		err := json.Unmarshal([]byte(dynamicactions.Params), &values)
+		if err != nil {
+			assert.Nil(t, fmt.Sprint("params参数只支持JSON Array,", dynamicactions.Params, mqlstr), err)
+			return err
+		}
+	}
+
+	x := atomic.AddInt32(&global.mqlcount, 1)
+	atomic.AddInt32(&topvars.mqlcount, 1)
+	atomic.AddInt32(&dirvars.mqlcount, 1)
+	atomic.AddInt32(&filevars.mqlcount, 1)
+	atomic.AddInt32(&mqlvars.mqlcount, 1)
+
+	err := mt.RunMQLTryDo(t, ctx,
+		global,
+		topvars,
+		dirvars,
+		filevars,
+		mqlvars,
+		basedir, ffpath, mqlkey, mqlsn+"("+strconv.Itoa(int(x))+")", mqri, values, staticactions, actionexprs)
+	if err != nil {
+		// 执行过程有错,停止继续执行
+		return err
+	}
+	if staticactions.SleepTime != nil {
+		global.sleeptime += *staticactions.SleepTime
+		topvars.sleeptime += *staticactions.SleepTime
+		dirvars.sleeptime += *staticactions.SleepTime
+		filevars.sleeptime += *staticactions.SleepTime
+		mqlvars.sleeptime += *staticactions.SleepTime
+		time.Sleep(*staticactions.SleepTime)
+	}
+	return nil
+}

+ 137 - 0
odbctest/odbcmql/mql_run_top.go

@@ -0,0 +1,137 @@
+package odbcmql
+
+import (
+	"context"
+	"sync"
+	"sync/atomic"
+	"testing"
+	"time"
+
+	"gitee.com/wecisecode/util/filewalker"
+	"gitee.com/wecisecode/util/pqc"
+)
+
+func (mt *MQLTest) Run(t *testing.T, fw *filewalker.FileWalker) (retok bool) {
+	mt.t = t
+	mt.fw = fw
+	global := &GlobalVars{
+		CurrentVars:          &CurrentVars{},
+		wg_wait_fork_routine: make(map[string]*sync.WaitGroup),
+		ch_wait_mql_done:     make(map[string]chan bool),
+	}
+	toploop := config.GetInt("loop")
+	if toploop <= 0 {
+		toploop = 1
+	}
+	mt.scopevars = &ScopeVars{
+		top: &Variables{
+			vars:       map[string]interface{}{},
+			loop_count: toploop,
+			loop_from:  1,
+			loop_step:  1},
+		dir:  map[string]*Variables{},
+		file: map[string]*Variables{},
+		mql:  map[string]*Variables{}}
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+	st := time.Now()
+	loop_i := 0
+	parallel_queue := pqc.NewQueue[any](0)
+	mqlcount := int32(0)
+	defer func() {
+		mt.scopevars.RLock()
+		loop_count := mt.scopevars.top.loop_count
+		mt.scopevars.RUnlock()
+		ut := time.Since(st)
+		aut := time.Duration(0)
+		if mqlcount > 0 {
+			aut = global.totalusetime / time.Duration(mqlcount)
+		}
+		logger.Info("完成 ", loop_count, " 次执行,共", mqlcount, "次 MQL 请求,耗时", ut, "单条响应时间", global.minusetime, "~", global.maxusetime, "/", aut, "平均每秒吞吐量", (int64(mqlcount)*int64(time.Second))/int64(ut))
+	}()
+	var wg sync.WaitGroup
+	for {
+		mt.scopevars.Lock()
+		ok := loop_i < mt.scopevars.top.loop_count
+		mt.scopevars.Unlock()
+		if !ok {
+			break
+		}
+		loop_i++
+		//
+		ch_parallel_count := make(chan int)
+		topvars := &CurrentVars{
+			loop_i:            loop_i,
+			ch_parallel_count: ch_parallel_count,
+		}
+		//
+		ok_chan := make(chan bool, 1)
+		parallel_chan := make(chan bool, 1)
+		parallel := false
+		parallelcount := 0
+		done := false
+		wg.Add(1)
+		go func() {
+			defer func() {
+				atomic.AddInt32(&mqlcount, topvars.mqlcount)
+				wg.Done()
+			}()
+			ch_ok := make(chan bool)
+			go func() {
+				for {
+					select {
+					case <-ch_parallel_count:
+						if !done && !parallel {
+							parallel = true
+							// 加入并发控制队列
+							if parallelcount > 0 {
+								if parallelcount > parallel_queue.Size() {
+									parallel_queue.Growth(parallelcount)
+								}
+								parallel_queue.Push(1)
+							}
+							parallel_chan <- true
+						}
+					case ok := <-ch_ok:
+						ok_chan <- ok
+						if parallel {
+							if parallelcount > 0 {
+								// 从并发控制队列中移除
+								parallel_queue.Pop()
+							}
+						} else {
+							done = true
+						}
+						// 整个测试完成一次
+						// 只保留 top 级变量,清除变量
+						mt.scopevars.Lock()
+						mt.scopevars.dir = map[string]*Variables{}
+						mt.scopevars.file = map[string]*Variables{}
+						mt.scopevars.mql = map[string]*Variables{}
+						mt.scopevars.Unlock()
+						return
+					}
+				}
+			}()
+			ok := mt.RunAllDirs(t, ctx,
+				global,
+				topvars)
+			ch_ok <- ok
+			if !ok {
+				cancel() // 并发测试,有一个线程出错,就全停
+				return
+			}
+		}()
+		success := true
+		select {
+		case success = <-ok_chan: // 非并发,等待完成
+		case <-parallel_chan: // 并发,执行继续下一次
+			logger.Info("第", topvars.loop_i, "次并发执行继续")
+		}
+		if !success {
+			return false // 失败,不等,直接返回
+		}
+	}
+	wg.Wait()
+	return
+}