feat: introduce GetLocal() and BatchGet cache for PipelinedMemDB (#1212)

* feat: introduce GetLocal() for MemBuffer

Signed-off-by: ekexium <eke@fastmail.com>

* downgrade tools to 0.18.0

Signed-off-by: ekexium <eke@fastmail.com>

* fix: set flushingMemDB = nil when an error is returned from a flush

Signed-off-by: ekexium <eke@fastmail.com>

* impl BatchGet for MemBuffer

Signed-off-by: you06 <you1474600@gmail.com>

test membuffer batch get

Signed-off-by: you06 <you1474600@gmail.com>

* add Prefetch & GetPrefetchCache

Signed-off-by: you06 <you1474600@gmail.com>

* cache multi Prefetch call

Signed-off-by: you06 <you1474600@gmail.com>

* add tests

Signed-off-by: you06 <you1474600@gmail.com>

* workaround for golang ci lint failure

Signed-off-by: you06 <you1474600@gmail.com>

* replace assert with require

Signed-off-by: you06 <you1474600@gmail.com>

* workaround golangci lint

Signed-off-by: you06 <you1474600@gmail.com>

* lint

Signed-off-by: you06 <you1474600@gmail.com>

* remove prefetch interface, pipelined memdb will cache batch get result

Signed-off-by: you06 <you1474600@gmail.com>

* update tidb

Signed-off-by: you06 <you1474600@gmail.com>

* fix batch get cache when membuffer is empty

Signed-off-by: you06 <you1474600@gmail.com>

* fix returned delete value

Signed-off-by: you06 <you1474600@gmail.com>

* fix: handle resourceGroupTag of Flush

Signed-off-by: ekexium <eke@fastmail.com>

* fix: set resource group tag for committer if it's pipelined

Signed-off-by: ekexium <eke@fastmail.com>

* Update internal/unionstore/pipelined_memdb.go

Co-authored-by: ekexium <eke@fastmail.com>

* remove prefetch interface

Signed-off-by: you06 <you1474600@gmail.com>

* release mutex

Signed-off-by: you06 <you1474600@gmail.com>

* flush wait to avoid race

Signed-off-by: you06 <you1474600@gmail.com>

* fix unstopped test

Signed-off-by: you06 <you1474600@gmail.com>

* set resource group tags for committer

Signed-off-by: ekexium <eke@fastmail.com>

* skip test due to tikv image not updated yet

Signed-off-by: you06 <you1474600@gmail.com>

* skip more test

Signed-off-by: you06 <you1474600@gmail.com>

---------

Signed-off-by: ekexium <eke@fastmail.com>
Signed-off-by: you06 <you1474600@gmail.com>
Co-authored-by: you06 <you1474600@gmail.com>
This commit is contained in:
ekexium 2024-03-16 11:36:05 +08:00 committed by GitHub
parent 0606e74e8e
commit 87a984a72d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 496 additions and 155 deletions

View File

@ -45,8 +45,8 @@ require (
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240228224816-df926f6c8641 // indirect
google.golang.org/grpc v1.62.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240308144416-29370a3891b7 // indirect
google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
)

View File

@ -45,8 +45,8 @@ require (
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240228224816-df926f6c8641 // indirect
google.golang.org/grpc v1.62.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240308144416-29370a3891b7 // indirect
google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
)

View File

@ -45,8 +45,8 @@ require (
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240228224816-df926f6c8641 // indirect
google.golang.org/grpc v1.62.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240308144416-29370a3891b7 // indirect
google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
)

View File

@ -45,8 +45,8 @@ require (
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240228224816-df926f6c8641 // indirect
google.golang.org/grpc v1.62.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240308144416-29370a3891b7 // indirect
google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
)

View File

@ -45,8 +45,8 @@ require (
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240228224816-df926f6c8641 // indirect
google.golang.org/grpc v1.62.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240308144416-29370a3891b7 // indirect
google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
)

View File

@ -45,8 +45,8 @@ require (
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240228224816-df926f6c8641 // indirect
google.golang.org/grpc v1.62.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240308144416-29370a3891b7 // indirect
google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
)

View File

@ -45,8 +45,8 @@ require (
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240228224816-df926f6c8641 // indirect
google.golang.org/grpc v1.62.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240308144416-29370a3891b7 // indirect
google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
)

View File

@ -45,8 +45,8 @@ require (
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240228224816-df926f6c8641 // indirect
google.golang.org/grpc v1.62.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240308144416-29370a3891b7 // indirect
google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
)

View File

@ -4,15 +4,15 @@ go 1.21
require (
github.com/ninedraft/israce v0.0.3
github.com/pingcap/errors v0.11.5-0.20231212100244-799fae176cfb
github.com/pingcap/errors v0.11.5-0.20240311081613-f97970b88865
github.com/pingcap/failpoint v0.0.0-20220801062533-2eaa32854a6c
github.com/pingcap/kvproto v0.0.0-20240227073058-929ab83f9754
github.com/pingcap/tidb v1.1.0-beta.0.20240126041650-de177d85b19e
github.com/pingcap/tidb v1.1.0-beta.0.20240312154839-cd6dbc2af910
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.8.4
github.com/stretchr/testify v1.9.0
github.com/tidwall/gjson v1.14.1
github.com/tikv/client-go/v2 v2.0.8-0.20240205071126-11cb7985f0ec
github.com/tikv/pd/client v0.0.0-20240229062430-b207e514ece1
github.com/tikv/client-go/v2 v2.0.8-0.20240313022320-d59fea5757db
github.com/tikv/pd/client v0.0.0-20240229065730-92a31c12238e
go.uber.org/goleak v1.3.0
)
@ -69,18 +69,18 @@ require (
github.com/pingcap/goleveldb v0.0.0-20191226122134-f82aafb29989 // indirect
github.com/pingcap/log v1.1.1-0.20230317032135-a0d097d16e22 // indirect
github.com/pingcap/sysutil v1.0.1-0.20230407040306-fb007c5aff21 // indirect
github.com/pingcap/tidb/pkg/parser v0.0.0-20240111112854-1ad36eb0ef29 // indirect
github.com/pingcap/tidb/pkg/parser v0.0.0-20240312154839-cd6dbc2af910 // indirect
github.com/pingcap/tipb v0.0.0-20240116032918-9bb28c43bbfc // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
github.com/prometheus/client_golang v1.18.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.46.0 // indirect
github.com/prometheus/client_golang v1.19.0 // indirect
github.com/prometheus/client_model v0.6.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/sasha-s/go-deadlock v0.3.1 // indirect
github.com/shirou/gopsutil/v3 v3.23.10 // indirect
github.com/shirou/gopsutil/v3 v3.24.1 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/tiancaiamao/gp v0.0.0-20221230034425-4025bc8a4d4a // indirect
@ -97,7 +97,7 @@ require (
go.etcd.io/etcd/client/v3 v3.5.12 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 // indirect
golang.org/x/net v0.22.0 // indirect
@ -106,7 +106,6 @@ require (
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.19.0 // indirect
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240308144416-29370a3891b7 // indirect
google.golang.org/grpc v1.62.1 // indirect
@ -118,7 +117,7 @@ require (
replace (
github.com/go-ldap/ldap/v3 => github.com/YangKeao/ldap/v3 v3.4.5-0.20230421065457-369a3bab1117
github.com/pingcap/tidb => github.com/you06/tidb v1.1.0-beta.0.20240220121437-9d6b908d9a92
github.com/pingcap/tidb/pkg/parser => github.com/you06/tidb/pkg/parser v0.0.0-20240220121437-9d6b908d9a92
github.com/pingcap/tidb => github.com/ekexium/tidb v1.1.0-beta.0.20240314050256-444418ecf47b
github.com/pingcap/tidb/pkg/parser => github.com/ekexium/tidb/pkg/parser v0.0.0-20240314050256-444418ecf47b
github.com/tikv/client-go/v2 => ../
)

View File

@ -132,6 +132,10 @@ github.com/dolthub/swiss v0.2.1/go.mod h1:8AhKZZ1HK7g18j7v7k6c5cYIGEZJcPn0ARsai8
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/ekexium/tidb v1.1.0-beta.0.20240314050256-444418ecf47b h1:o19UOUiPY0xpX+YTM9zlpps50994eaZxIkYmphvTM1o=
github.com/ekexium/tidb v1.1.0-beta.0.20240314050256-444418ecf47b/go.mod h1:7Gfn8N2wA499HqM6Q6pOHtwN0G2ktYL9BeO+ik+iSo8=
github.com/ekexium/tidb/pkg/parser v0.0.0-20240314050256-444418ecf47b h1:J9U3bBzGPEkgrJypNzf5wCaZdCogvKxPB9FhqH+Naho=
github.com/ekexium/tidb/pkg/parser v0.0.0-20240314050256-444418ecf47b/go.mod h1:c/4la2yfv1vBYvtIG8WCDyDinLMDIUC5+zLRHiafY+Y=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
@ -259,8 +263,6 @@ github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@ -327,12 +329,12 @@ github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8=
github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
github.com/lestrrat-go/httprc v1.0.5 h1:bsTfiH8xaKOJPrg1R+E3iE/AWZr/x0Phj9PBTG/OLUk=
github.com/lestrrat-go/httprc v1.0.5/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
github.com/lestrrat-go/jwx/v2 v2.0.19 h1:ekv1qEZE6BVct89QA+pRF6+4pCpfVrOnEJnTnT4RXoY=
github.com/lestrrat-go/jwx/v2 v2.0.19/go.mod h1:l3im3coce1lL2cDeAjqmaR+Awx+X8Ih+2k8BuHNJ4CU=
github.com/lestrrat-go/jwx/v2 v2.0.21 h1:jAPKupy4uHgrHFEdjVjNkUgoBKtVDgrQPB/h55FHrR0=
github.com/lestrrat-go/jwx/v2 v2.0.21/go.mod h1:09mLW8zto6bWL9GbwnqAli+ArLf+5M33QLQPDggkUWM=
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
@ -402,8 +404,8 @@ github.com/pingcap/badger v1.5.1-0.20230103063557-828f39b09b6d/go.mod h1:p8QnkZn
github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pingcap/errors v0.11.5-0.20190809092503-95897b64e011/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pingcap/errors v0.11.5-0.20231212100244-799fae176cfb h1:yqyP+k0mgRPpXJQDOCrtaG2YZym0ZDD+vt5JzlBUkrw=
github.com/pingcap/errors v0.11.5-0.20231212100244-799fae176cfb/go.mod h1:X2r9ueLEUZgtx2cIogM0v4Zj5uvvzhuuiu7Pn8HzMPg=
github.com/pingcap/errors v0.11.5-0.20240311081613-f97970b88865 h1:xzbnMPvFtnqLsY5HLALUov6JU1ONyfQUXVurE/Nqb8Y=
github.com/pingcap/errors v0.11.5-0.20240311081613-f97970b88865/go.mod h1:X2r9ueLEUZgtx2cIogM0v4Zj5uvvzhuuiu7Pn8HzMPg=
github.com/pingcap/failpoint v0.0.0-20220801062533-2eaa32854a6c h1:CgbKAHto5CQgWM9fSBIvaxsJHuGP0uM74HXtv3MyyGQ=
github.com/pingcap/failpoint v0.0.0-20220801062533-2eaa32854a6c/go.mod h1:4qGtCB0QK0wBzKtFEGDhxXnSnbQApw1gc9siScUl8ew=
github.com/pingcap/fn v1.0.0 h1:CyA6AxcOZkQh52wIqYlAmaVmF6EvrcqFywP463pjA8g=
@ -434,15 +436,15 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:Om
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig=
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_golang v0.9.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y=
github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
@ -460,13 +462,15 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
github.com/scalalang2/golang-fifo v0.1.5 h1:cl70TQhlMGGpI2DZGcr+7/GFTJOjHMeor0t7wynEEoA=
github.com/scalalang2/golang-fifo v0.1.5/go.mod h1:IK3OZBg7iHbVdQVGPDjcW1MWPb6JcWjaS/w0iRBS8gs=
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shirou/gopsutil/v3 v3.21.12/go.mod h1:BToYZVTlSVlfazpDDYFnsVZLaoRG+g8ufT6fPQLdJzA=
github.com/shirou/gopsutil/v3 v3.23.10 h1:/N42opWlYzegYaVkWejXWJpbzKv2JDy3mrgGzKsh9hM=
github.com/shirou/gopsutil/v3 v3.23.10/go.mod h1:JIE26kpucQi+innVlAUnIEOSBhBUkirr5b44yr55+WE=
github.com/shirou/gopsutil/v3 v3.24.1 h1:R3t6ondCEvmARp3wxODhXMTLC/klMa87h2PHUw5m7QI=
github.com/shirou/gopsutil/v3 v3.24.1/go.mod h1:UU7a2MSBQa+kW1uuDq8DeEBS8kmrnQwsv2b5O513rwU=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
@ -498,8 +502,9 @@ github.com/spkg/bom v1.0.0/go.mod h1:lAz2VbTuYNcvs7iaFF8WW0ufXrHShJ7ck1fYFFbVXJs
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
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=
@ -507,20 +512,23 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tiancaiamao/appdash v0.0.0-20181126055449-889f96f722a2 h1:mbAskLJ0oJfDRtkanvQPiooDH8HvJ2FBh+iKT/OmiQQ=
github.com/tiancaiamao/appdash v0.0.0-20181126055449-889f96f722a2/go.mod h1:2PfKggNGDuadAa0LElHrByyrz4JPZ9fFx6Gs7nx7ZZU=
github.com/tiancaiamao/gp v0.0.0-20221230034425-4025bc8a4d4a h1:J/YdBZ46WKpXsxsW93SG+q0F8KI+yFrcIDT4c/RNoc4=
github.com/tiancaiamao/gp v0.0.0-20221230034425-4025bc8a4d4a/go.mod h1:h4xBhSNtOeEosLJ4P7JyKXX7Cabg7AVkWCK5gV2vOrM=
github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI=
github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY=
github.com/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo=
github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tikv/pd/client v0.0.0-20240229062430-b207e514ece1 h1:KKa2bxKHhM+CmLn5Pp2VmNfi9AN2pWd+3m2EWnicdvE=
github.com/tikv/pd/client v0.0.0-20240229062430-b207e514ece1/go.mod h1:Z/QAgOt29zvwBTd0H6pdx45VO6KRNc/O/DzGkVmSyZg=
github.com/tikv/pd/client v0.0.0-20240229065730-92a31c12238e h1:kHXMmskVCNyH53u43I73Y5cmZ6yqqder/jGOiI7ylxs=
github.com/tikv/pd/client v0.0.0-20240229065730-92a31c12238e/go.mod h1:Z/QAgOt29zvwBTd0H6pdx45VO6KRNc/O/DzGkVmSyZg=
github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
@ -555,10 +563,6 @@ github.com/xitongsys/parquet-go v1.5.5-0.20201110004701-b09c49d6d457 h1:tBbuFCty
github.com/xitongsys/parquet-go v1.5.5-0.20201110004701-b09c49d6d457/go.mod h1:pheqtXeHQFzxJk45lRQ0UIGIivKnLXvialZSFWs81A8=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
github.com/you06/tidb v1.1.0-beta.0.20240220121437-9d6b908d9a92 h1:2n6DSHF79pe9BGKMrZ6bUFdV1AL1BJaC3wbkS3TiL2E=
github.com/you06/tidb v1.1.0-beta.0.20240220121437-9d6b908d9a92/go.mod h1:WNewIzAOHkWHcmkh5Yw5HOU/fl9EhX4mBpNVmW6Eydk=
github.com/you06/tidb/pkg/parser v0.0.0-20240220121437-9d6b908d9a92 h1:W7CHjUFBHqpCitLUaK1X2gN+AKsX6kgmKZ2D+YGuoHw=
github.com/you06/tidb/pkg/parser v0.0.0-20240220121437-9d6b908d9a92/go.mod h1:MWQK6otJgZRI6zcCVPV22U4qE26qOGJnN4fq8XawgBs=
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
@ -634,8 +638,8 @@ go.uber.org/zap v1.12.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -687,8 +691,8 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -733,7 +737,7 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@ -782,11 +786,12 @@ 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=
gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca h1:PupagGYwj8+I4ubCxcmcBRk3VlUWtTg5huQpZR9flmE=
gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM=
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
google.golang.org/api v0.160.0 h1:SEspjXHVqE1m5a1fRy8JFB+5jSu+V0GEDKDghF3ttO4=
google.golang.org/api v0.160.0/go.mod h1:0mu0TpK33qnydLvWqbImq2b1eQ5FHRSDCBzAxX9ZHyw=
google.golang.org/api v0.162.0 h1:Vhs54HkaEpkMBdgGdOT2P6F0csGG/vxDS0hWHJzmmps=
google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0=
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/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=

View File

@ -261,3 +261,113 @@ func (s *testPipelinedMemDBSuite) TestPipelinedCommit() {
s.Equal(key, val)
}
}
func (s *testPipelinedMemDBSuite) TestPipelinedPrefetch() {
failpoint.Enable("tikvclient/beforeSendReqToRegion", "return")
defer failpoint.Disable("tikvclient/beforeSendReqToRegion")
txn, err := s.store.Begin(tikv.WithPipelinedMemDB())
s.Nil(err)
mustFlush := func(txn *transaction.KVTxn) {
flushed, err := txn.GetMemBuffer().Flush(true)
s.Nil(err)
s.True(flushed)
}
panicWhenReadingRemoteBuffer := func(key []byte) ([]byte, error) {
ctx := context.WithValue(context.Background(), "sendReqToRegionHook", func(req *tikvrpc.Request) {
if req.Type == tikvrpc.CmdBufferBatchGet {
panic("should not read remote buffer")
}
})
return txn.GetMemBuffer().Get(ctx, key)
}
prefetchKeys := make([][]byte, 0, 100)
prefetchResult := make(map[string][]byte, 100)
nonPrefetchKeys := make([][]byte, 0, 100)
for i := 0; i < 100; i++ {
key := []byte(strconv.Itoa(i))
value := key
txn.Set(key, value)
if i%2 == 0 {
prefetchKeys = append(prefetchKeys, key)
prefetchResult[string(key)] = value
} else {
nonPrefetchKeys = append(nonPrefetchKeys, key)
}
}
mustFlush(txn)
s.Nil(txn.GetMemBuffer().FlushWait())
for _, key := range prefetchKeys {
m, err := txn.BatchGet(context.Background(), [][]byte{key})
s.Nil(err)
result := map[string][]byte{string(key): prefetchResult[string(key)]}
s.Equal(m, result)
}
// can read prefetched keys from prefetch cache
for _, key := range prefetchKeys {
v, err := txn.GetMemBuffer().Get(context.Background(), key)
s.Nil(err)
s.Equal(v, prefetchResult[string(key)])
}
// panics when reading non-prefetched keys from prefetch cache
for _, key := range nonPrefetchKeys {
s.Panics(func() {
panicWhenReadingRemoteBuffer(key)
})
}
mustFlush(txn)
// prefetch cache is cleared after flush
for _, key := range prefetchKeys {
s.Panics(func() {
panicWhenReadingRemoteBuffer(key)
})
}
s.Nil(txn.Commit(context.Background()))
txn, err = s.store.Begin(tikv.WithPipelinedMemDB())
s.Nil(err)
txn.Set([]byte("100"), []byte("100")) // snapshot: [0, 1, ..., 99], membuffer: [100]
m, err := txn.BatchGet(context.Background(), [][]byte{[]byte("99"), []byte("100"), []byte("101")})
s.Nil(err)
s.Equal(m, map[string][]byte{"99": []byte("99"), "100": []byte("100")})
cache := txn.GetSnapshot().SnapCache()
// batch get cache: [99 -> not exist, 100 -> 100, 101 -> not exist]
// snapshot cache: [99 -> 99, 101 -> not exist]
_, err = panicWhenReadingRemoteBuffer([]byte("99"))
s.Error(err)
s.True(tikverr.IsErrNotFound(err))
s.Equal(cache["99"], []byte("99"))
v, err := panicWhenReadingRemoteBuffer([]byte("100"))
s.Nil(err)
s.Equal(v, []byte("100"))
_, err = panicWhenReadingRemoteBuffer([]byte("101"))
s.Error(err)
s.True(tikverr.IsErrNotFound(err))
s.Equal(cache["101"], []byte(nil))
txn.Delete([]byte("99"))
mustFlush(txn)
s.Nil(txn.GetMemBuffer().FlushWait())
m, err = txn.BatchGet(context.Background(), [][]byte{[]byte("99")})
s.Nil(err)
// restore this check after tikv return pairs for buffer batch get
//s.Equal(m, map[string][]byte{})
//v, err = panicWhenReadingRemoteBuffer([]byte("99"))
//s.Nil(err)
//s.Equal(v, []byte{})
txn.Rollback()
// empty memdb should also cache the not exist result.
txn, err = s.store.Begin(tikv.WithPipelinedMemDB())
// batch get cache: [99 -> not exist]
m, err = txn.BatchGet(context.Background(), [][]byte{[]byte("99")})
s.Nil(err)
s.Equal(m, map[string][]byte{"99": []byte("99")})
_, err = panicWhenReadingRemoteBuffer([]byte("99"))
s.Error(err)
s.True(tikverr.IsErrNotFound(err))
txn.Rollback()
}

View File

@ -35,6 +35,7 @@ import (
"github.com/tikv/client-go/v2/kv"
"github.com/tikv/client-go/v2/metrics"
"github.com/tikv/client-go/v2/tikvrpc"
"github.com/tikv/client-go/v2/util"
)
type testRegionCacheStaleReadSuite struct {
@ -259,22 +260,22 @@ func TestRegionCacheStaleRead(t *testing.T) {
{
do: followerDown,
leaderRegionValid: true,
leaderAsyncReload: Some(false),
leaderAsyncReload: util.Some(false),
leaderSuccessReplica: []string{"z1"},
leaderSuccessReadType: SuccessStaleRead,
followerRegionValid: true,
followerAsyncReload: Some(false),
followerAsyncReload: util.Some(false),
followerSuccessReplica: []string{"z1"},
followerSuccessReadType: SuccessLeaderRead,
},
{
do: followerDownAndUp,
leaderRegionValid: true,
leaderAsyncReload: None[bool](),
leaderAsyncReload: util.None[bool](),
leaderSuccessReplica: []string{"z1"},
leaderSuccessReadType: SuccessStaleRead,
followerRegionValid: true,
followerAsyncReload: Some(true),
followerAsyncReload: util.Some(true),
followerSuccessReplica: []string{"z1"},
// because follower's epoch is changed, leader will be selected.
followerSuccessReadType: SuccessStaleRead,
@ -283,11 +284,11 @@ func TestRegionCacheStaleRead(t *testing.T) {
do: followerMove,
recoverable: true,
leaderRegionValid: true,
leaderAsyncReload: Some(false),
leaderAsyncReload: util.Some(false),
leaderSuccessReplica: []string{"z1"},
leaderSuccessReadType: SuccessStaleRead,
followerRegionValid: false,
followerAsyncReload: Some(false),
followerAsyncReload: util.Some(false),
// may async reload region and access it from leader.
followerSuccessReplica: []string{},
followerSuccessReadType: ReadFail,
@ -295,67 +296,67 @@ func TestRegionCacheStaleRead(t *testing.T) {
{
do: evictLeader,
leaderRegionValid: true,
leaderAsyncReload: Some(false),
leaderAsyncReload: util.Some(false),
// leader is evicted, but can still serve as follower.
leaderSuccessReplica: []string{"z1"},
leaderSuccessReadType: SuccessStaleRead,
followerRegionValid: true,
followerAsyncReload: Some(false),
followerAsyncReload: util.Some(false),
followerSuccessReplica: []string{"z2"},
followerSuccessReadType: SuccessStaleRead,
},
{
do: leaderMove,
leaderRegionValid: false,
leaderAsyncReload: Some(false),
leaderAsyncReload: util.Some(false),
leaderSuccessReplica: []string{},
leaderSuccessReadType: ReadFail,
followerRegionValid: true,
followerAsyncReload: Some(false),
followerAsyncReload: util.Some(false),
followerSuccessReplica: []string{"z2"},
followerSuccessReadType: SuccessStaleRead,
},
{
do: leaderDown,
leaderRegionValid: true,
leaderAsyncReload: Some(true),
leaderAsyncReload: util.Some(true),
leaderSuccessReplica: []string{"z2", "z3"},
leaderSuccessReadType: SuccessFollowerRead,
followerRegionValid: true,
followerAsyncReload: Some(false),
followerAsyncReload: util.Some(false),
followerSuccessReplica: []string{"z2"},
followerSuccessReadType: SuccessStaleRead,
},
{
do: leaderDownAndUp,
leaderRegionValid: true,
leaderAsyncReload: Some(true),
leaderAsyncReload: util.Some(true),
leaderSuccessReplica: []string{"z2", "z3"},
leaderSuccessReadType: SuccessFollowerRead,
followerRegionValid: true,
followerAsyncReload: None[bool](),
followerAsyncReload: util.None[bool](),
followerSuccessReplica: []string{"z2"},
followerSuccessReadType: SuccessStaleRead,
},
{
do: leaderDownAndElect,
leaderRegionValid: true,
leaderAsyncReload: Some(true),
leaderAsyncReload: util.Some(true),
leaderSuccessReplica: []string{"z2", "z3"},
leaderSuccessReadType: SuccessFollowerRead,
followerRegionValid: true,
followerAsyncReload: None[bool](),
followerAsyncReload: util.None[bool](),
followerSuccessReplica: []string{"z2"},
followerSuccessReadType: SuccessStaleRead,
},
{
do: followerDataIsNotReady,
leaderRegionValid: true,
leaderAsyncReload: Some(false),
leaderAsyncReload: util.Some(false),
leaderSuccessReplica: []string{"z1"},
leaderSuccessReadType: SuccessStaleRead,
followerRegionValid: true,
followerAsyncReload: Some(false),
followerAsyncReload: util.Some(false),
followerSuccessReplica: []string{"z1"},
followerSuccessReadType: SuccessLeaderRead,
},
@ -364,11 +365,11 @@ func TestRegionCacheStaleRead(t *testing.T) {
do: leaderServerIsBusy,
recoverable: true,
leaderRegionValid: true,
leaderAsyncReload: Some(false),
leaderAsyncReload: util.Some(false),
leaderSuccessReplica: []string{"z2", "z3"},
leaderSuccessReadType: SuccessFollowerRead,
followerRegionValid: true,
followerAsyncReload: Some(false),
followerAsyncReload: util.Some(false),
followerSuccessReplica: []string{"z2"},
followerSuccessReadType: SuccessStaleRead,
},
@ -376,11 +377,11 @@ func TestRegionCacheStaleRead(t *testing.T) {
do: followerServerIsBusy,
recoverable: true,
leaderRegionValid: true,
leaderAsyncReload: Some(false),
leaderAsyncReload: util.Some(false),
leaderSuccessReplica: []string{"z1"},
leaderSuccessReadType: SuccessStaleRead,
followerRegionValid: true,
followerAsyncReload: Some(false),
followerAsyncReload: util.Some(false),
followerSuccessReplica: []string{"z1"},
followerSuccessReadType: SuccessLeaderRead,
},
@ -389,11 +390,11 @@ func TestRegionCacheStaleRead(t *testing.T) {
extra: []func(suite *testRegionCacheStaleReadSuite){followerServerIsBusy},
recoverable: true,
leaderRegionValid: true,
leaderAsyncReload: Some(false),
leaderAsyncReload: util.Some(false),
leaderSuccessReplica: []string{"z3"},
leaderSuccessReadType: SuccessFollowerRead,
followerRegionValid: true,
followerAsyncReload: Some(false),
followerAsyncReload: util.Some(false),
followerSuccessReplica: []string{"z3"},
followerSuccessReadType: SuccessFollowerRead,
},
@ -402,11 +403,11 @@ func TestRegionCacheStaleRead(t *testing.T) {
extra: []func(suite *testRegionCacheStaleReadSuite){followerDataIsNotReady},
recoverable: true,
leaderRegionValid: true,
leaderAsyncReload: Some(false),
leaderAsyncReload: util.Some(false),
leaderSuccessReplica: []string{"z2", "z3"},
leaderSuccessReadType: SuccessFollowerRead,
followerRegionValid: true,
followerAsyncReload: Some(false),
followerAsyncReload: util.Some(false),
followerSuccessReplica: []string{"z2", "z3"},
followerSuccessReadType: SuccessFollowerRead,
},
@ -415,11 +416,11 @@ func TestRegionCacheStaleRead(t *testing.T) {
extra: []func(suite *testRegionCacheStaleReadSuite){followerDown},
recoverable: true,
leaderRegionValid: true,
leaderAsyncReload: Some(false),
leaderAsyncReload: util.Some(false),
leaderSuccessReplica: []string{"z3"},
leaderSuccessReadType: SuccessFollowerRead,
followerRegionValid: true,
followerAsyncReload: Some(false),
followerAsyncReload: util.Some(false),
followerSuccessReplica: []string{"z3"},
followerSuccessReadType: SuccessFollowerRead,
},
@ -428,11 +429,11 @@ func TestRegionCacheStaleRead(t *testing.T) {
extra: []func(suite *testRegionCacheStaleReadSuite){followerDataIsNotReady},
recoverable: true,
leaderRegionValid: true,
leaderAsyncReload: Some(true),
leaderAsyncReload: util.Some(true),
leaderSuccessReplica: []string{"z2", "z3"},
leaderSuccessReadType: SuccessFollowerRead,
followerRegionValid: true,
followerAsyncReload: Some(true),
followerAsyncReload: util.Some(true),
followerSuccessReplica: []string{"z2", "z3"},
followerSuccessReadType: SuccessFollowerRead,
},
@ -441,11 +442,11 @@ func TestRegionCacheStaleRead(t *testing.T) {
extra: []func(suite *testRegionCacheStaleReadSuite){followerServerIsBusy},
recoverable: true,
leaderRegionValid: true,
leaderAsyncReload: Some(true),
leaderAsyncReload: util.Some(true),
leaderSuccessReplica: []string{"z3"},
leaderSuccessReadType: SuccessFollowerRead,
followerRegionValid: true,
followerAsyncReload: Some(true),
followerAsyncReload: util.Some(true),
followerSuccessReplica: []string{"z3"},
followerSuccessReadType: SuccessFollowerRead,
},
@ -454,11 +455,11 @@ func TestRegionCacheStaleRead(t *testing.T) {
extra: []func(suite *testRegionCacheStaleReadSuite){followerDown},
recoverable: true,
leaderRegionValid: true,
leaderAsyncReload: Some(true),
leaderAsyncReload: util.Some(true),
leaderSuccessReplica: []string{"z3"},
leaderSuccessReadType: SuccessFollowerRead,
followerRegionValid: true,
followerAsyncReload: Some(true),
followerAsyncReload: util.Some(true),
followerSuccessReplica: []string{"z3"},
followerSuccessReadType: SuccessFollowerRead,
},
@ -571,22 +572,6 @@ func testStaleRead(s *testRegionCacheStaleReadSuite, r *RegionCacheTestCase, zon
s.True(find, msg)
}
type Option[T interface{}] struct {
inner *T
}
func Some[T interface{}](inner T) Option[T] {
return Option[T]{inner: &inner}
}
func None[T interface{}]() Option[T] {
return Option[T]{inner: nil}
}
func (o Option[T]) Inner() *T {
return o.inner
}
type RegionCacheTestCase struct {
debug bool
do func(s *testRegionCacheStaleReadSuite)
@ -594,12 +579,12 @@ type RegionCacheTestCase struct {
recoverable bool
// local peer is leader
leaderRegionValid bool
leaderAsyncReload Option[bool]
leaderAsyncReload util.Option[bool]
leaderSuccessReplica []string
leaderSuccessReadType SuccessReadType
// local peer is follower
followerRegionValid bool
followerAsyncReload Option[bool]
followerAsyncReload util.Option[bool]
followerSuccessReplica []string
followerSuccessReadType SuccessReadType
}

View File

@ -48,6 +48,12 @@ type PipelinedMemDB struct {
generation uint64
entryLimit, bufferLimit uint64
flushOption flushOption
// prefetchCache is used to cache the result of BatchGet, it's invalidated when Flush.
// the values are wrapped by util.Option.
// None -> not found
// Some([...]) -> put
// Some([]) -> delete
batchGetCache map[string]util.Option[[]byte]
}
const (
@ -85,23 +91,13 @@ func newFlushOption() flushOption {
return opt
}
type pipelinedMemDBSkipRemoteBuffer struct{}
// TODO: skip remote buffer by context is too obscure, add a new method to read local buffer.
var pipelinedMemDBSkipRemoteBufferKey = pipelinedMemDBSkipRemoteBuffer{}
// WithPipelinedMemDBSkipRemoteBuffer is used to skip reading remote buffer for saving RPC.
func WithPipelinedMemDBSkipRemoteBuffer(ctx context.Context) context.Context {
return context.WithValue(ctx, pipelinedMemDBSkipRemoteBufferKey, struct{}{})
}
type FlushFunc func(uint64, *MemDB) error
type BufferBatchGetter func(ctx context.Context, keys [][]byte) (map[string][]byte, error)
func NewPipelinedMemDB(bufferBatchGetter BufferBatchGetter, flushFunc FlushFunc) *PipelinedMemDB {
memdb := newMemDB()
memdb.setSkipMutex(true)
flushOptoin := newFlushOption()
flushOpt := newFlushOption()
return &PipelinedMemDB{
memDB: memdb,
errCh: make(chan error, 1),
@ -111,7 +107,7 @@ func NewPipelinedMemDB(bufferBatchGetter BufferBatchGetter, flushFunc FlushFunc)
// keep entryLimit and bufferLimit same with the memdb's default values.
entryLimit: memdb.entrySizeLimit,
bufferLimit: memdb.bufferSizeLimit,
flushOption: flushOptoin,
flushOption: flushOpt,
}
}
@ -125,9 +121,7 @@ func (p *PipelinedMemDB) GetMemDB() *MemDB {
panic("GetMemDB should not be invoked for PipelinedMemDB")
}
// Get the value by given key, it returns tikverr.ErrNotExist if not exist.
// The priority of the value is MemBuffer > flushingMemDB > flushed memdbs.
func (p *PipelinedMemDB) Get(ctx context.Context, k []byte) ([]byte, error) {
func (p *PipelinedMemDB) get(ctx context.Context, k []byte, skipRemoteBuffer bool) ([]byte, error) {
v, err := p.memDB.Get(k)
if err == nil {
return v, nil
@ -144,9 +138,19 @@ func (p *PipelinedMemDB) Get(ctx context.Context, k []byte) ([]byte, error) {
return nil, err
}
}
if ctx.Value(pipelinedMemDBSkipRemoteBufferKey) != nil {
if skipRemoteBuffer {
return nil, tikverr.ErrNotExist
}
if p.batchGetCache != nil {
v, ok := p.batchGetCache[string(k)]
if ok {
inner := v.Inner()
if inner == nil {
return nil, tikverr.ErrNotExist
}
return *inner, nil
}
}
// read remote buffer
var (
dataMap map[string][]byte
@ -163,6 +167,19 @@ func (p *PipelinedMemDB) Get(ctx context.Context, k []byte) ([]byte, error) {
return v, nil
}
// Get the value by given key, it returns tikverr.ErrNotExist if not exist.
// The priority of the value is MemBuffer > flushingMemDB > flushed memdbs.
func (p *PipelinedMemDB) Get(ctx context.Context, k []byte) ([]byte, error) {
return p.get(ctx, k, false)
}
// GetLocal implements the MemBuffer interface.
// It only checks the mutable memdb and the immutable memdb.
// It does not check mutations that have been flushed to TiKV.
func (p *PipelinedMemDB) GetLocal(ctx context.Context, key []byte) ([]byte, error) {
return p.get(ctx, key, true)
}
func (p *PipelinedMemDB) GetFlags(k []byte) (kv.KeyFlags, error) {
f, err := p.memDB.GetFlags(k)
if p.flushingMemDB != nil && tikverr.IsErrNotFound(err) {
@ -174,6 +191,44 @@ func (p *PipelinedMemDB) GetFlags(k []byte) (kv.KeyFlags, error) {
return f, nil
}
func (p *PipelinedMemDB) BatchGet(ctx context.Context, keys [][]byte) (map[string][]byte, error) {
m := make(map[string][]byte, len(keys))
if p.batchGetCache == nil {
p.batchGetCache = make(map[string]util.Option[[]byte], len(keys))
}
shrinkKeys := make([][]byte, 0, len(keys))
for _, k := range keys {
v, err := p.GetLocal(ctx, k)
if err != nil {
if tikverr.IsErrNotFound(err) {
shrinkKeys = append(shrinkKeys, k)
continue
}
return nil, err
}
m[string(k)] = v
p.batchGetCache[string(k)] = util.Some(v)
}
storageValues, err := p.bufferBatchGetter(ctx, shrinkKeys)
if err != nil {
return nil, err
}
for _, k := range shrinkKeys {
v, ok := storageValues[string(k)]
if ok {
// the protobuf cast empty byte slice to nil, we need to cast it back when receiving values from storage.
if v == nil {
v = []byte{}
}
m[string(k)] = v
p.batchGetCache[string(k)] = util.Some(v)
} else {
p.batchGetCache[string(k)] = util.None[[]byte]()
}
}
return m, nil
}
func (p *PipelinedMemDB) UpdateFlags(k []byte, ops ...kv.FlagsOp) {
p.memDB.UpdateFlags(k, ops...)
}
@ -214,6 +269,8 @@ func (p *PipelinedMemDB) Flush(force bool) (bool, error) {
if p.flushFunc == nil {
return false, errors.New("flushFunc is not provided")
}
// invalidate the batch get cache whether the flush is really triggered.
p.batchGetCache = nil
if !force && !p.needFlush() {
return false, nil
}

View File

@ -17,6 +17,7 @@ package unionstore
import (
"context"
"strconv"
"sync"
"testing"
"time"
@ -35,7 +36,6 @@ func TestPipelinedFlushTrigger(t *testing.T) {
// block the flush goroutine for checking the flushingMemDB status.
blockCh := make(chan struct{})
defer close(blockCh)
// Will not flush when keys number >= MinFlushKeys and size < MinFlushSize
memdb := NewPipelinedMemDB(emptyBufferBatchGetter, func(_ uint64, db *MemDB) error {
<-blockCh
@ -94,6 +94,8 @@ func TestPipelinedFlushTrigger(t *testing.T) {
// the flushingMemDB length and size should be added to the total length and size.
require.Equal(t, memdb.Len(), MinFlushKeys)
require.Equal(t, memdb.Size(), memdb.flushingMemDB.Size())
close(blockCh)
require.Nil(t, memdb.FlushWait())
}
func TestPipelinedFlushSkip(t *testing.T) {
@ -131,11 +133,11 @@ func TestPipelinedFlushSkip(t *testing.T) {
require.Nil(t, err)
require.Equal(t, memdb.memDB.Len(), 0)
require.Equal(t, memdb.len, 2*MinFlushKeys)
require.Nil(t, memdb.FlushWait())
}
func TestPipelinedFlushBlock(t *testing.T) {
blockCh := make(chan struct{})
defer close(blockCh)
memdb := NewPipelinedMemDB(emptyBufferBatchGetter, func(_ uint64, db *MemDB) error {
<-blockCh
return nil
@ -176,11 +178,12 @@ func TestPipelinedFlushBlock(t *testing.T) {
blockCh <- struct{}{} // first flush done
<-flushReturned // second flush start
require.True(t, memdb.OnFlushing())
close(blockCh)
require.Nil(t, memdb.FlushWait())
}
func TestPipelinedFlushGet(t *testing.T) {
blockCh := make(chan struct{})
defer close(blockCh)
memdb := NewPipelinedMemDB(emptyBufferBatchGetter, func(_ uint64, db *MemDB) error {
<-blockCh
return nil
@ -222,6 +225,8 @@ func TestPipelinedFlushGet(t *testing.T) {
// now the key is guaranteed to be flushed into stores, though PipelinedMemDB.Get does not see it, snapshot get should get it.
_, err = memdb.Get(context.Background(), []byte("key"))
require.True(t, tikverr.IsErrNotFound(err))
close(blockCh)
require.Nil(t, memdb.FlushWait())
}
func TestPipelinedFlushSize(t *testing.T) {
@ -265,6 +270,7 @@ func TestPipelinedFlushSize(t *testing.T) {
require.True(t, flushed)
require.Equal(t, memdb.Len(), keys)
require.Equal(t, memdb.Size(), size)
require.Nil(t, memdb.FlushWait())
}
func TestPipelinedFlushGeneration(t *testing.T) {
@ -279,6 +285,7 @@ func TestPipelinedFlushGeneration(t *testing.T) {
// generation start from 1
require.Equal(t, <-generationCh, uint64(i+1))
}
require.Nil(t, memdb.FlushWait())
}
func TestErrorIterator(t *testing.T) {
@ -339,4 +346,110 @@ func TestPipelinedAdjustFlushCondition(t *testing.T) {
require.Nil(t, failpoint.Disable("tikvclient/pipelinedMemDBMinFlushKeys"))
require.Nil(t, failpoint.Disable("tikvclient/pipelinedMemDBMinFlushSize"))
require.Nil(t, failpoint.Disable("tikvclient/pipelinedMemDBForceFlushSizeThreshold"))
require.Nil(t, memdb.FlushWait())
}
func TestMemBufferBatchGetCache(t *testing.T) {
util.EnableFailpoints()
flushDone := make(chan struct{})
var remoteMutex sync.RWMutex
remoteBuffer := make(map[string][]byte, 16)
pipelinedMemdb := NewPipelinedMemDB(func(_ context.Context, keys [][]byte) (map[string][]byte, error) {
remoteMutex.RLock()
defer remoteMutex.RUnlock()
m := make(map[string][]byte, len(keys))
for _, k := range keys {
if val, ok := remoteBuffer[string(k)]; ok {
m[string(k)] = val
}
}
return m, nil
}, func(_ uint64, db *MemDB) error {
remoteMutex.Lock()
defer remoteMutex.Unlock()
for it, _ := db.Iter(nil, nil); it.Valid(); it.Next() {
remoteBuffer[string(it.Key())] = it.Value()
}
flushDone <- struct{}{}
return nil
})
mustGetFromCache := func(key []byte) ([]byte, error) {
require.NotNil(t, pipelinedMemdb.batchGetCache)
v, ok := pipelinedMemdb.batchGetCache[string(key)]
require.True(t, ok)
inner := v.Inner()
if inner == nil {
return nil, tikverr.ErrNotExist
}
return *inner, nil
}
mustNotExistInCache := func(key []byte) {
require.NotNil(t, pipelinedMemdb.batchGetCache)
_, ok := pipelinedMemdb.batchGetCache[string(key)]
require.False(t, ok)
}
mustFlush := func() {
flushed, err := pipelinedMemdb.Flush(true)
require.Nil(t, err)
require.True(t, flushed)
<-flushDone
}
err := pipelinedMemdb.Set([]byte("k1"), []byte("v11"))
require.Nil(t, err)
mustFlush()
err = pipelinedMemdb.Set([]byte("k2"), []byte("v21"))
require.Nil(t, err)
mustFlush()
// memdb: [], flushing memdb: [k2 -> v21], remoteBuffer: [k1 -> v11, k2 -> v21]
_, err = pipelinedMemdb.GetLocal(context.Background(), []byte("k1"))
require.Error(t, err)
require.True(t, tikverr.IsErrNotFound(err))
v, err := pipelinedMemdb.GetLocal(context.Background(), []byte("k2"))
require.Nil(t, err)
require.Equal(t, v, []byte("v21"))
// batch get caches the result
// cache: [k1 -> v11, k2 -> v21]
m, err := pipelinedMemdb.BatchGet(context.Background(), [][]byte{[]byte("k1"), []byte("k2")})
require.Nil(t, err)
require.Equal(t, m, map[string][]byte{"k1": []byte("v11"), "k2": []byte("v21")})
v, err = mustGetFromCache([]byte("k1"))
require.Nil(t, err)
require.Equal(t, v, []byte("v11"))
v, err = mustGetFromCache([]byte("k2"))
require.Nil(t, err)
require.Equal(t, v, []byte("v21"))
mustNotExistInCache([]byte("k3"))
// cache: [k1 -> v11, k2 -> v21, k3 -> not exist]
m, err = pipelinedMemdb.BatchGet(context.Background(), [][]byte{[]byte("k3")})
require.Nil(t, err)
require.Len(t, m, 0)
_, err = mustGetFromCache([]byte("k3"))
require.Error(t, err)
require.True(t, tikverr.IsErrNotFound(err))
// memdb: [], flushing memdb: [k1 -> [], k3 -> []], remoteBuffer: [k1 -> [], k2 -> v21, k3 -> []]
pipelinedMemdb.Delete([]byte("k1"))
pipelinedMemdb.Set([]byte("k2"), []byte("v22"))
pipelinedMemdb.Delete([]byte("k3"))
mustFlush()
require.Nil(t, pipelinedMemdb.batchGetCache)
// cache: [k1 -> [], k2 -> v22, k3 -> [], k4 -> not exist]
m, err = pipelinedMemdb.BatchGet(context.Background(), [][]byte{[]byte("k1"), []byte("k2"), []byte("k3"), []byte("k4")})
require.Nil(t, err)
require.Equal(t, m, map[string][]byte{"k1": {}, "k2": []byte("v22"), "k3": {}})
v, err = mustGetFromCache([]byte("k1"))
require.Nil(t, err)
require.Len(t, v, 0)
v, err = mustGetFromCache([]byte("k2"))
require.Nil(t, err)
require.Equal(t, v, []byte("v22"))
v, err = mustGetFromCache([]byte("k3"))
require.Nil(t, err)
require.Len(t, v, 0)
_, err = mustGetFromCache([]byte("k4"))
require.Error(t, err)
require.True(t, tikverr.IsErrNotFound(err))
require.Nil(t, pipelinedMemdb.FlushWait())
}

View File

@ -174,6 +174,11 @@ type MemBuffer interface {
RUnlock()
// Get gets the value for key k from the MemBuffer.
Get(context.Context, []byte) ([]byte, error)
// GetLocal gets the value from the buffer in local memory.
// It makes nonsense for MemDB, but makes a difference for pipelined DML.
GetLocal(context.Context, []byte) ([]byte, error)
// BatchGet gets the values for given keys from the MemBuffer and cache the result if there are remote buffer.
BatchGet(context.Context, [][]byte) (map[string][]byte, error)
// GetFlags gets the flags for key k from the MemBuffer.
GetFlags([]byte) (kv.KeyFlags, error)
// Set sets the value for key k in the MemBuffer.
@ -251,6 +256,10 @@ func (db *MemDBWithContext) Get(_ context.Context, k []byte) ([]byte, error) {
return db.MemDB.Get(k)
}
func (db *MemDBWithContext) GetLocal(_ context.Context, k []byte) ([]byte, error) {
return db.MemDB.Get(k)
}
func (db *MemDBWithContext) Flush(bool) (bool, error) { return false, nil }
func (db *MemDBWithContext) FlushWait() error { return nil }
@ -259,3 +268,22 @@ func (db *MemDBWithContext) FlushWait() error { return nil }
func (db *MemDBWithContext) GetMemDB() *MemDB {
return db.MemDB
}
// BatchGet returns the values for given keys from the MemBuffer.
func (db *MemDBWithContext) BatchGet(ctx context.Context, keys [][]byte) (map[string][]byte, error) {
if db.Len() == 0 {
return map[string][]byte{}, nil
}
m := make(map[string][]byte, len(keys))
for _, k := range keys {
v, err := db.Get(ctx, k)
if err != nil {
if tikverr.IsErrNotFound(err) {
continue
}
return nil, err
}
m[string(k)] = v
}
return m, nil
}

View File

@ -57,5 +57,3 @@ type MemBuffer = unionstore.MemBuffer
// MemDBCheckpoint is the checkpoint of memory DB.
type MemDBCheckpoint = unionstore.MemDBCheckpoint
var WithPipelinedMemDBSkipRemoteBuffer = unionstore.WithPipelinedMemDBSkipRemoteBuffer

View File

@ -37,7 +37,6 @@ package transaction
import (
"context"
tikverr "github.com/tikv/client-go/v2/error"
"github.com/tikv/client-go/v2/internal/unionstore"
)
@ -45,6 +44,7 @@ import (
type BatchBufferGetter interface {
Len() int
unionstore.Getter
BatchGetter
}
// BatchGetter is the interface for BatchGet.
@ -66,31 +66,31 @@ func NewBufferBatchGetter(buffer BatchBufferGetter, snapshot BatchGetter) *Buffe
// BatchGet gets a batch of values.
func (b *BufferBatchGetter) BatchGet(ctx context.Context, keys [][]byte) (map[string][]byte, error) {
if b.buffer.Len() == 0 {
return b.snapshot.BatchGet(ctx, keys)
}
bufferValues := make([][]byte, len(keys))
shrinkKeys := make([][]byte, 0, len(keys))
for i, key := range keys {
val, err := b.buffer.Get(ctx, key)
if err == nil {
bufferValues[i] = val
continue
}
if !tikverr.IsErrNotFound(err) {
bufferValues, err := b.buffer.BatchGet(ctx, keys)
if err != nil {
return nil, err
}
if len(bufferValues) == 0 {
return b.snapshot.BatchGet(ctx, keys)
}
shrinkKeys := make([][]byte, 0, len(keys)-len(bufferValues))
for _, key := range keys {
val, ok := bufferValues[string(key)]
if !ok {
shrinkKeys = append(shrinkKeys, key)
continue
}
// the deleted key should be removed from the result, and also no need to snapshot read it again.
if len(val) == 0 {
delete(bufferValues, string(key))
}
}
storageValues, err := b.snapshot.BatchGet(ctx, shrinkKeys)
if err != nil {
return nil, err
}
for i, key := range keys {
if len(bufferValues[i]) == 0 {
continue
for key, val := range storageValues {
bufferValues[key] = val
}
storageValues[string(key)] = bufferValues[i]
}
return storageValues, nil
return bufferValues, nil
}

View File

@ -105,6 +105,9 @@ func (c *twoPhaseCommitter) buildPipelinedFlushRequest(batch batchMutations, gen
},
},
)
if c.resourceGroupTag == nil && c.resourceGroupTagger != nil {
c.resourceGroupTagger(r)
}
return r
}

View File

@ -301,6 +301,9 @@ func (txn *KVTxn) SetPriority(pri txnutil.Priority) {
func (txn *KVTxn) SetResourceGroupTag(tag []byte) {
txn.resourceGroupTag = tag
txn.GetSnapshot().SetResourceGroupTag(tag)
if txn.committer != nil && txn.IsPipelined() {
txn.committer.resourceGroupTag = tag
}
}
// SetResourceGroupTagger sets the resource tagger for both write and read.
@ -309,12 +312,18 @@ func (txn *KVTxn) SetResourceGroupTag(tag []byte) {
func (txn *KVTxn) SetResourceGroupTagger(tagger tikvrpc.ResourceGroupTagger) {
txn.resourceGroupTagger = tagger
txn.GetSnapshot().SetResourceGroupTagger(tagger)
if txn.committer != nil && txn.IsPipelined() {
txn.committer.resourceGroupTagger = tagger
}
}
// SetResourceGroupName set resource group name for both read and write.
func (txn *KVTxn) SetResourceGroupName(name string) {
txn.resourceGroupName = name
txn.GetSnapshot().SetResourceGroupName(name)
if txn.committer != nil && txn.IsPipelined() {
txn.committer.resourceGroupName = name
}
}
// SetRPCInterceptor sets interceptor.RPCInterceptor for the transaction and its related snapshot.
@ -545,6 +554,11 @@ func (txn *KVTxn) InitPipelinedMemDB() error {
}
return txn.committer.pipelinedFlushMutations(bo, mutations, generation)
})
txn.committer.priority = txn.priority.ToPB()
txn.committer.syncLog = txn.syncLog
txn.committer.resourceGroupTag = txn.resourceGroupTag
txn.committer.resourceGroupTagger = txn.resourceGroupTagger
txn.committer.resourceGroupName = txn.resourceGroupName
txn.us = unionstore.NewUnionStore(pipelinedMemDB, txn.snapshot)
return nil
}
@ -666,7 +680,8 @@ func (txn *KVTxn) Commit(ctx context.Context) error {
}()
// latches disabled
// pessimistic transaction should also bypass latch.
if txn.store.TxnLatches() == nil || txn.IsPessimistic() {
// transaction with pipelined memdb should also bypass latch.
if txn.store.TxnLatches() == nil || txn.IsPessimistic() || txn.IsPipelined() {
err = committer.execute(ctx)
if val == nil || sessionID > 0 {
txn.onCommitted(err)

View File

@ -255,7 +255,8 @@ func (s *KVSnapshot) BatchGetWithTier(ctx context.Context, keys [][]byte, readTi
// Create a map to collect key-values from region servers.
var mu sync.Mutex
err := s.batchGetKeysByRegions(bo, keys, readTier, func(k, v []byte) {
if len(v) == 0 {
// when read buffer tier, empty value means a delete record, should also collect it.
if len(v) == 0 && readTier != BatchGetBufferTier {
return
}
@ -1009,10 +1010,21 @@ func (s *KVSnapshot) SnapCacheHitCount() int {
// SnapCacheSize gets the snapshot cache size. Only for test.
func (s *KVSnapshot) SnapCacheSize() int {
s.mu.RLock()
defer s.mu.RLock()
defer s.mu.RUnlock()
return len(s.mu.cached)
}
// SnapCache gets the copy of snapshot cache. Only for test.
func (s *KVSnapshot) SnapCache() map[string][]byte {
s.mu.RLock()
defer s.mu.RUnlock()
cp := make(map[string][]byte, len(s.mu.cached))
for k, v := range s.mu.cached {
cp[k] = v
}
return cp
}
// SetVars sets variables to the transaction.
func (s *KVSnapshot) SetVars(vars *kv.Variables) {
s.vars = vars

View File

@ -210,3 +210,19 @@ func HexRegionKey(key []byte) []byte {
func HexRegionKeyStr(key []byte) string {
return String(HexRegionKey(key))
}
type Option[T interface{}] struct {
inner *T
}
func Some[T interface{}](inner T) Option[T] {
return Option[T]{inner: &inner}
}
func None[T interface{}]() Option[T] {
return Option[T]{inner: nil}
}
func (o Option[T]) Inner() *T {
return o.inner
}